Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rake db:test:prepare ってどこでどういうふうに定義されているんだろう #965

Open
yucao24hours opened this issue Apr 20, 2015 · 12 comments

Comments

@yucao24hours
Copy link
Member

Rails チュートリアル 6.2.1 で出てきた rake db:test:prepare という rake タスクは、どこで定義されていて、どういう内容になってるんだろう?
チュートリアルの記述を見るに、なんとなく、development 環境の DB の状態を test 環境にもコピーする、みたいな動きなんだろうとは推測できるけど。(ほぼ @highwide さんの台詞ママ)

@yucao24hours
Copy link
Member Author

rake コマンドのヘルプを見ると、

$ rake -h
rake [-f rakefile] {options} targets...

Options are ...
    (中略)
    -W, --where [PATTERN]            Describe the tasks (matching optional PATTERN), then exit.

というオプションがある。これを使うと、そのタスクが定義されているファイルと行がわかる。

この -W を使って db:test:prepare タスクを調べると、

$ rake -W db:test:prepare
rake db:test:prepare                /usr/local/rbenv/versions/2.0.0-p598/lib/ruby/gems/2.0.0/gems/activerecord-4.0.4/lib/active_record/railties/databases.rake:368:in `block (2 levels) in <top (required)>'

と表示されるので、ここをエディタで開いてみよう 👀

@yucao24hours
Copy link
Member Author

db_namespace = namespace :db do
  (中略)
  namespace :test do
    (中略)
    # desc 'Check for pending migrations and load the test schema'
    task :prepare => [:environment, :load_config] do
      unless ActiveRecord::Base.configurations.blank?
        db_namespace['test:load'].invoke
      end
    end
  end
end

@yucao24hours
Copy link
Member Author

深い…

@yucao24hours
Copy link
Member Author

タスクの中核は

unless ActiveRecord::Base.configurations.blank?
  db_namespace['test:load'].invoke
end

となっている。

ActiveRecord::Base.configurations

https://github.com/rails/rails/blob/2e9c3b0888286c8ff4e39f237514c1bb5b68ba7d/activerecord/lib/active_record/core.rb#L51

      # Returns fully resolved configurations hash
      def self.configurations
        @@configurations
      end

で、設定される値としては、直上に

      ##
      # Contains the database configuration - as is typically stored in config/database.yml -
      # as a Hash.
      #
      # For example, the following database.yml...
      #
      #   development:
      #     adapter: sqlite3
      #     database: db/development.sqlite3
      #
      #   production:
      #     adapter: sqlite3
      #     database: db/production.sqlite3
      #
      # ...would result in ActiveRecord::Base.configurations to look like this:
      #
      #   {
      #      'development' => {
      #         'adapter'  => 'sqlite3',
      #         'database' => 'db/development.sqlite3'
      #      },
      #      'production' => {
      #         'adapter'  => 'sqlite3',
      #         'database' => 'db/production.sqlite3'
      #      }
      #   }
      def self.configurations=(config)
        @@configurations = ActiveRecord::ConnectionHandling::MergeAndResolveDefaultUrlConfig.new(config).resolve
      end

と定義してある。

@yucao24hours
Copy link
Member Author

DB の情報がハッシュになってるのね。

@yucao24hours
Copy link
Member Author

つまり、DB の情報がひとつもなければ test:load タスクを実行する。
肝心の test:load タスクはというと

# /usr/local/rbenv/versions/2.0.0-p598/lib/ruby/gems/2.0.0/gems/activerecord-4.0.4/lib/active_record/railties/databases.rake:313 

  namespace :test do

    # desc "Recreate the test database from the current schema"
    task :load => 'db:test:purge' do
      case ActiveRecord::Base.schema_format
        when :ruby
          db_namespace["test:load_schema"].invoke
        when :sql
          db_namespace["test:load_structure"].invoke
      end
    end

となっている。
ActiveRecord::Base.schema_format

http://railsguides.jp/configuring.html

config.active_record.schema_formatは、データベーススキーマをファイルに書き出す際のフォーマットを指定します。デフォルトは:rubyで、データベースには依存せず、マイグレーションに依存します。:sqlを指定するとSQL文で書き出されますが、この場合潜在的にデータベースに依存する可能性があります。

というもの。( structure.sql というファイルに見覚えがある人がいるはず)
上記のとおり、特別なことをしないかぎり Ruby で書かれる(し、Ruby で書かれていたほうがいいと思う)。チュートリアルもそうなっているので、結果としては test:load_schema が実行されているということになる。

@yucao24hours
Copy link
Member Author

test:load_schema タスクの定義は以下

# /usr/local/rbenv/versions/2.0.0-p598/lib/ruby/gems/2.0.0/gems/activerecord-4.0.4/lib/active_record/railties/databases.rake:323
    # desc "Recreate the test database from an existent schema.rb file"
    task :load_schema => 'db:test:purge' do
      begin
        should_reconnect = ActiveRecord::Base.connection_pool.active_connection?
        ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test'])
        ActiveRecord::Schema.verbose = false
        db_namespace["schema:load"].invoke
      ensure
        if should_reconnect
          ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations[ActiveRecord::Tasks::DatabaseTasks.env])
        end
      end
    end

おお!!それらしい処理!!

@yucao24hours
Copy link
Member Author

なんかきっといろいろやってるんだけど db_namespace["schema:load"].invoke がわかれば満足できる予感

@yucao24hours
Copy link
Member Author

(だいぶ雑になってまいりました 🍙 )

@yucao24hours
Copy link
Member Author

# /usr/local/rbenv/versions/2.0.0-p598/lib/ruby/gems/2.0.0/gems/activerecord-4.0.4/lib/active_record/railties/databases.rake:250 
    desc 'Load a schema.rb file into the database'
    task :load => [:environment, :load_config] do
      file = ENV['SCHEMA'] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, 'schema.rb')
      if File.exist?(file)
        load(file)
      else
        abort %{#{file} doesn't exist yet. Run `rake db:migrate` to create it, then try again. If you do not intend to use a database, you should instead alter #{Rails.root}/config/application.rb to limit the frameworks that will be loaded.}
      end
    end

@yucao24hours
Copy link
Member Author

おおおーー schema.rb を読み込んでいる!!
つまりその時点での schema.rb を そのまま適用しているのですね。マイグレーションじゃなくて。

@yucao24hours
Copy link
Member Author

ちなみに

task に渡されている load_config

# /usr/local/rbenv/versions/2.0.0-p598/lib/ruby/gems/2.0.0/gems/activerecord-4.0.4/lib/active_record/railties/databases.rake:4
  task :load_config do
    ActiveRecord::Base.configurations = ActiveRecord::Tasks::DatabaseTasks.database_configuration || {}
    ActiveRecord::Migrator.migrations_paths = ActiveRecord::Tasks::DatabaseTasks.migrations_paths
  end

で、

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant