今日は rake db 系のコードを読んでみた。 いつもお世話になっているコマンドなので, 内部の処理をあまり理解せずに使っていることに違和感を感じていた。 とくにいくつか,名前がにていて何が異なるのかがわからないコマンドがいくつかあった。
rake db 系のコマンドは以下で定義されている。
/activerecord/lib/activerecord/tasks/database.rake
気になったコマンドをいくつか見てみた。
rake db:migrate:reset
drop, create, migrate をまとめてやる。
desc がコメントアウトされていて -T で表示されないようになっている。 理由はわからない。
# desc 'Resets your database using your migrations for the current environment' task :reset => ['db:drop', 'db:create', 'db:migrate']
rake db:reset
drop, reset をする。 db/schema.rb から復元する。 seeds の読み込みも行う。 (とコメントに書いてある。)
# desc 'Drops and recreates the database from db/schema.rb for the current environment and loads the seeds.' task :reset => [:environment, :load_config] do db_namespace["drop"].invoke db_namespace["setup"].invoke end
次にrake db コマンドの実行からどのようにDB操作の処理が行われているのかを追ってみた
Railsは MySQL, PostgreSQL, SQLite に対応しているので, database.rb にある設定を読み込んで 各 DB に対応する DB操作を行うクラスを選択している。 この処理は gems/activerecord-4.2.3/lib/active_record/tasks/database_tasks.rbにある register_task が行っている。
ActiveRecord::Tasks::DatabaseTasks が include された時点で, MySQL, PostgreSLQ, SQLite のタスクとキーのペアが登録される。
def register_task(pattern, task) @tasks ||= {} @tasks[pattern] = task end register_task(/mysql/, ActiveRecord::Tasks::MySQLDatabaseTasks) register_task(/postgresql/, ActiveRecord::Tasks::PostgreSQLDatabaseTasks) register_task(/sqlite/, ActiveRecord::Tasks::SQLiteDatabaseTasks)
class_for_adapter メソッドが, DBアダプタから, そのDBに対する ActiveRecord::Tasks を返す。 class_for_adapter の内部処理的には, adapter 引数で指定した DBタイプが @tasks にキーとしてあるかを確認して あれば, そのキーを返すという処理をしている。 detect を使っているのは 正規表現でマッチングさせたいからだと思う。 Array#detect メソッドは Array#find のエイリアス。
def class_for_adapter(adapter) key = @tasks.keys.detect { |pattern| adapter[pattern] } unless key raise DatabaseNotSupported, "Rake tasks not supported by '#{adapter}' adapter" end @tasks[key] end
database_configuration メソッドは database.yaml を読み込むメソッドで railties-4.2.3/lib/rails/application/configuration.rb にある。
単純に yaml をパースしてオブジェクトを返すようだが, なぜ ERB.new しているのかはまだ調べ切れていない。
# Loads and returns the entire raw configuration of database from # values stored in `config/database.yml`. def database_configuration path = paths["config/database"].existent.first yaml = Pathname.new(path) if path config = if yaml && yaml.exist? require "yaml" require "erb" YAML.load(ERB.new(yaml.read).result) || {} elsif ENV['DATABASE_URL'] # Value from ENV['DATABASE_URL'] is set to default database connection # by Active Record. {} else raise "Could not load database configuration. No such file - #{paths["config/database"].instance_variable_get(:@paths)}" end config rescue Psych::SyntaxError => e raise "YAML syntax error occurred while parsing #{paths["config/database"].first}. " \ "Please note that YAML must be consistently indented using spaces. Tabs are not allowed. " \ "Error: #{e.message}" rescue => e raise e, "Cannot load `Rails.application.database_configuration`:\n#{e.message}", e.backtrace end