From f02d2185ebbe01f455a9a91216ff7094b014ea72 Mon Sep 17 00:00:00 2001 From: Josh Susser Date: Sun, 2 Dec 2012 21:16:32 -0800 Subject: Add migration history to schema.rb dump --- activerecord/CHANGELOG.md | 11 +++++ .../abstract/schema_statements.rb | 4 +- activerecord/lib/active_record/schema.rb | 40 ++++++++++++----- activerecord/lib/active_record/schema_dumper.rb | 17 +++++++- activerecord/lib/active_record/schema_migration.rb | 12 +++++ activerecord/test/cases/ar_schema_test.rb | 51 ++++++++++++++++++++++ activerecord/test/cases/schema_dumper_test.rb | 18 +++++++- .../migrations/always_safe/1001_always_safe.rb | 5 +++ .../test/migrations/always_safe/1002_still_safe.rb | 5 +++ 9 files changed, 146 insertions(+), 17 deletions(-) create mode 100644 activerecord/test/migrations/always_safe/1001_always_safe.rb create mode 100644 activerecord/test/migrations/always_safe/1002_still_safe.rb (limited to 'activerecord') diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index ecec187329..fdeefa4f7b 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,5 +1,16 @@ ## Rails 4.0.0 (unreleased) ## +* Add migration history to schema.rb dump. + Loading schema.rb with full migration history + restores the exact list of migrations that created + that schema (including names and fingerprints). This + avoids possible mistakes caused by assuming all + migrations with a lower version have been run when + loading schema.rb. Old schema.rb files without migration + history but with the :version setting still work as before. + + *Josh Susser* + * Add metadata columns to schema_migrations table. New columns are: migrated_at (timestamp), fingerprint (md5 hash of migration source), and diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 7802cb02d8..a470e8de07 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -490,8 +490,8 @@ module ActiveRecord sm_table = ActiveRecord::Migrator.schema_migrations_table_name ActiveRecord::SchemaMigration.order('version').map { |sm| - "INSERT INTO #{sm_table} (version, migrated_at, name) VALUES ('#{sm.version}',LOCALTIMESTAMP,'#{sm.name}');" - }.join "\n\n" + "INSERT INTO #{sm_table} (version, migrated_at, fingerprint, name) VALUES ('#{sm.version}',LOCALTIMESTAMP,'#{sm.fingerprint}','#{sm.name}');" + }.join("\n\n") end # Should not be called normally, but this operation is non-destructive. diff --git a/activerecord/lib/active_record/schema.rb b/activerecord/lib/active_record/schema.rb index eaa4aa7086..7ec06e699b 100644 --- a/activerecord/lib/active_record/schema.rb +++ b/activerecord/lib/active_record/schema.rb @@ -34,27 +34,45 @@ module ActiveRecord end def define(info, &block) + @using_deprecated_version_setting = info[:version].present? + SchemaMigration.drop_table + initialize_schema_migrations_table + instance_eval(&block) - unless info[:version].blank? - initialize_schema_migrations_table - assume_migrated_upto_version(info[:version], migrations_paths) - end + # handle files from pre-4.0 that used :version option instead of dumping migration table + assume_migrated_upto_version(info[:version], migrations_paths) if @using_deprecated_version_setting end # Eval the given block. All methods available to the current connection # adapter are available within the block, so you can easily use the # database definition DSL to build up your schema (+create_table+, # +add_index+, etc.). - # - # The +info+ hash is optional, and if given is used to define metadata - # about the current schema (currently, only the schema's version): - # - # ActiveRecord::Schema.define(version: 20380119000001) do - # ... - # end def self.define(info={}, &block) new.define(info, &block) end + + # Create schema migration history. Include migration statements in a block to this method. + # + # migrations do + # migration 20121128235959, "44f1397e3b92442ca7488a029068a5ad", "add_horn_color_to_unicorns" + # migration 20121129235959, "4a1eb3965d94406b00002b370854eae8", "add_magic_power_to_unicorns" + # end + def migrations + raise(ArgumentError, "Can't set migrations while using :version option") if @using_deprecated_version_setting + yield + end + + # Add a migration to the ActiveRecord::SchemaMigration table. + # + # The +version+ argument is an integer. + # The +fingerprint+ and +name+ arguments are required but may be empty strings. + # The migration's +migrated_at+ attribute is set to the current time, + # instead of being set explicitly as an argument to the method. + # + # migration 20121129235959, "4a1eb3965d94406b00002b370854eae8", "add_magic_power_to_unicorns" + def migration(version, fingerprint, name) + SchemaMigration.create!(version: version, migrated_at: Time.now, fingerprint: fingerprint, name: name) + end end end diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb index 36bde44e7c..73c0f5b9eb 100644 --- a/activerecord/lib/active_record/schema_dumper.rb +++ b/activerecord/lib/active_record/schema_dumper.rb @@ -24,6 +24,7 @@ module ActiveRecord def dump(stream) header(stream) + migrations(stream) tables(stream) trailer(stream) stream @@ -44,7 +45,7 @@ module ActiveRecord stream.puts "# encoding: #{stream.external_encoding.name}" end - stream.puts <
v, :name => "anon", :migrated_at => Time.now) + ActiveRecord::SchemaMigration.create!( + :version => v, :migrated_at => Time.now, + :fingerprint => "123456789012345678901234567890ab", :name => "anon") end schema_info = ActiveRecord::Base.connection.dump_schema_information assert_match(/20100201010101.*20100301010101/m, schema_info) + target_line = %q{INSERT INTO schema_migrations (version, migrated_at, fingerprint, name) VALUES ('20100101010101',LOCALTIMESTAMP,'123456789012345678901234567890ab','anon');} + assert_match target_line, schema_info end def test_magic_comment @@ -36,6 +40,16 @@ class SchemaDumperTest < ActiveRecord::TestCase assert_no_match %r{create_table "schema_migrations"}, output end + def test_schema_dump_includes_migrations + ActiveRecord::SchemaMigration.delete_all + ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/always_safe") + + output = standard_dump + assert_match %r{migrations do}, output, "Missing migrations block" + assert_match %r{migration 1001, "[0-9a-f]{32}", "always_safe"}, output, "Missing migration line" + assert_match %r{migration 1002, "[0-9a-f]{32}", "still_safe"}, output, "Missing migration line" + end + def test_schema_dump_excludes_sqlite_sequence output = standard_dump assert_no_match %r{create_table "sqlite_sequence"}, output diff --git a/activerecord/test/migrations/always_safe/1001_always_safe.rb b/activerecord/test/migrations/always_safe/1001_always_safe.rb new file mode 100644 index 0000000000..454b972507 --- /dev/null +++ b/activerecord/test/migrations/always_safe/1001_always_safe.rb @@ -0,0 +1,5 @@ +class AlwaysSafe < ActiveRecord::Migration + def change + # do nothing to avoid side-effect conflicts from running multiple times + end +end diff --git a/activerecord/test/migrations/always_safe/1002_still_safe.rb b/activerecord/test/migrations/always_safe/1002_still_safe.rb new file mode 100644 index 0000000000..7398ae27a2 --- /dev/null +++ b/activerecord/test/migrations/always_safe/1002_still_safe.rb @@ -0,0 +1,5 @@ +class StillSafe < ActiveRecord::Migration + def change + # do nothing to avoid side-effect conflicts from running multiple times + end +end -- cgit v1.2.3