動機
Railsプロジェクトで複数のfeatureブランチで並行して開発していてかつ共にマイグレーションが絡む処理をしている時に、ブランチを横断して作業している時に、 db:migrate:status
でマイグレーションの状況を見てみると
up 20160913044702 ********** NO FILE **********
のような履歴が出来ている時がありました。ふと気になって、どういう時にこのような履歴が出来るのか実際にコードを追いかけてみました。
バージョン
activerecord-5.0.0
コードリーディング
lib/active_record/railties/databases.rake:109
rake -W
を使うと、そのタスクがどこに記述されているかの一覧を見ることが出来ます。便利ですね。
こちらが rake db:migrate:status
を走らせたときに通るコードです。
task :status => [:environment, :load_config] do
unless ActiveRecord::SchemaMigration.table_exists?
abort 'Schema migrations table does not exist yet.'
end
db_list = ActiveRecord::SchemaMigration.normalized_versions
file_list =
ActiveRecord::Tasks::DatabaseTasks.migrations_paths.flat_map do |path|
Dir.foreach(path).map do |file|
next unless ActiveRecord::Migrator.match_to_migration_filename?(file)
version, name, scope = ActiveRecord::Migrator.parse_migration_filename(file)
version = ActiveRecord::SchemaMigration.normalize_migration_number(version)
status = db_list.delete(version) ? 'up' : 'down'
[status, version, (name + scope).humanize]
end.compact
end
db_list.map! do |version|
['up', version, '********** NO FILE **********']
end
# output
puts "\ndatabase: #{ActiveRecord::Base.connection_config[:database]}\n\n"
puts "#{'Status'.center(8)} #{'Migration ID'.ljust(14)} Migration Name"
puts "-" * 50
(db_list + file_list).sort_by { |_, version, _| version }.each do |status, version, name|
puts "#{status.center(8)} #{version.ljust(14)} #{name}"
end
puts
end
db_list = ActiveRecord::SchemaMigration.normalized_versions
でschema_migrationsテーブルのversionカラムの一覧を配列で取得します。
schema_migrationsテーブルはマイグレートされたバージョンを保持するテーブルです。
つまり、db_listには現在のマイグレート済ファイルのバージョン一覧が入っていることになります。
次のfile_listには db/migrations
のマイグレーションファイル名一覧と先程取得したdb_listを元に [ステータス('up' or 'down', バージョン), マイグレーション名]
の配列が作られます。
ここの処理で興味深いところは、 status = db_list.delete(version) ? 'up' : 'down'
です。
そのマイグレーションファイル名のバージョンがschema_migrationsのversionカラムに含まれていればdb_listから削除できる -> up
(マイグレーション済)。含まれていれば削除できないので down
(まだマイグレーションされていない) になります。
読み進めていきましょう。
db_list.map! do |version|
['up', version, '********** NO FILE **********']
end
気になっていた箇所に到達しました!
どうやら、db_listに残っていたものが NO FILE
になるようですね。
確かに、migrationファイルが無いのでdb_listに残るかつschema_migrationsテーブルにはあるのでupになっていると。納得です。
最後は表示のところですが、file_listと先程のNO FILEのものが入っているdb_listを結合してバージョン順に並べて表示してます。
まとめ
結論として、 NO FILE が出来る条件が
- schema_migrationsテーブルのversionカラムにあって
- db/migrations には無いファイル
ということがコードベースで理解できました。