diff options
author | Josh Susser <josh@hasmanythrough.com> | 2012-12-02 21:16:32 -0800 |
---|---|---|
committer | Josh Susser <josh@hasmanythrough.com> | 2012-12-02 21:16:32 -0800 |
commit | f02d2185ebbe01f455a9a91216ff7094b014ea72 (patch) | |
tree | 078c6183d84a9b3c7e72fe77450083df13d86b7e /activerecord | |
parent | 0a5afa229d769bce9e221f34053bb93b60817a5a (diff) | |
download | rails-f02d2185ebbe01f455a9a91216ff7094b014ea72.tar.gz rails-f02d2185ebbe01f455a9a91216ff7094b014ea72.tar.bz2 rails-f02d2185ebbe01f455a9a91216ff7094b014ea72.zip |
Add migration history to schema.rb dump
Diffstat (limited to 'activerecord')
-rw-r--r-- | activerecord/CHANGELOG.md | 11 | ||||
-rw-r--r-- | activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb | 4 | ||||
-rw-r--r-- | activerecord/lib/active_record/schema.rb | 40 | ||||
-rw-r--r-- | activerecord/lib/active_record/schema_dumper.rb | 17 | ||||
-rw-r--r-- | activerecord/lib/active_record/schema_migration.rb | 12 | ||||
-rw-r--r-- | activerecord/test/cases/ar_schema_test.rb | 51 | ||||
-rw-r--r-- | activerecord/test/cases/schema_dumper_test.rb | 18 | ||||
-rw-r--r-- | activerecord/test/migrations/always_safe/1001_always_safe.rb | 5 | ||||
-rw-r--r-- | activerecord/test/migrations/always_safe/1002_still_safe.rb | 5 |
9 files changed, 146 insertions, 17 deletions
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 <<HEADER + header_text = <<HEADER_RUBY # This file is auto-generated from the current state of the database. Instead # of editing this file, please use the migrations feature of Active Record to # incrementally modify your database, and then regenerate this schema definition. @@ -59,13 +60,25 @@ module ActiveRecord ActiveRecord::Schema.define(#{define_params}) do -HEADER +HEADER_RUBY + stream.puts header_text end def trailer(stream) stream.puts "end" end + def migrations(stream) + all_migrations = ActiveRecord::SchemaMigration.all.to_a + if all_migrations.any? + stream.puts(" migrations do") + all_migrations.each do |migration| + stream.puts(migration.schema_line(" ")) + end + stream.puts(" end") + end + end + def tables(stream) @connection.tables.sort.each do |tbl| next if ['schema_migrations', ignore_tables].flatten.any? do |ignored| diff --git a/activerecord/lib/active_record/schema_migration.rb b/activerecord/lib/active_record/schema_migration.rb index fd226d5eba..4464f54145 100644 --- a/activerecord/lib/active_record/schema_migration.rb +++ b/activerecord/lib/active_record/schema_migration.rb @@ -53,5 +53,17 @@ module ActiveRecord def version super.to_i end + + # Construct ruby source to include in schema.rb dump for this migration. + # Pass a string of spaces as +indent+ to allow calling code to control how deeply indented the line is. + # The generated line includes the migration version, fingerprint, and name. Either fingerprint or name + # can be an empty string. + # + # Example output: + # + # migration 20121129235959, "ee4be703f9e6e2fc0f4baddebe6eb8f7", "add_magic_power_to_unicorns" + def schema_line(indent) + %Q(#{indent}migration %s, "%s", "%s") % [version, fingerprint, name] + end end end diff --git a/activerecord/test/cases/ar_schema_test.rb b/activerecord/test/cases/ar_schema_test.rb index b2eac0349b..bd47ba8741 100644 --- a/activerecord/test/cases/ar_schema_test.rb +++ b/activerecord/test/cases/ar_schema_test.rb @@ -46,4 +46,55 @@ if ActiveRecord::Base.connection.supports_migrations? end end + class ActiveRecordSchemaMigrationsTest < ActiveRecordSchemaTest + def setup + super + ActiveRecord::SchemaMigration.delete_all + end + + def test_migration_adds_row_to_migrations_table + schema = ActiveRecord::Schema.new + schema.migration(1001, "", "") + schema.migration(1002, "123456789012345678901234567890ab", "add_magic_power_to_unicorns") + + migrations = ActiveRecord::SchemaMigration.all.to_a + assert_equal 2, migrations.length + + assert_equal 1001, migrations[0].version + assert_match %r{^2\d\d\d-}, migrations[0].migrated_at.to_s(:db) + assert_equal "", migrations[0].fingerprint + assert_equal "", migrations[0].name + + assert_equal 1002, migrations[1].version + assert_match %r{^2\d\d\d-}, migrations[1].migrated_at.to_s(:db) + assert_equal "123456789012345678901234567890ab", migrations[1].fingerprint + assert_equal "add_magic_power_to_unicorns", migrations[1].name + end + + def test_define_clears_schema_migrations + assert_nothing_raised do + ActiveRecord::Schema.define do + migrations do + migration(123001, "", "") + end + end + ActiveRecord::Schema.define do + migrations do + migration(123001, "", "") + end + end + end + end + + def test_define_raises_if_both_version_and_explicit_migrations + assert_raise(ArgumentError) do + ActiveRecord::Schema.define(version: 123001) do + migrations do + migration(123001, "", "") + end + end + end + end + end + end diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index 2f75eb0995..373463c8ab 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -1,5 +1,5 @@ require "cases/helper" - +# require "cases/migration/helper" class SchemaDumperTest < ActiveRecord::TestCase def setup @@ -18,11 +18,15 @@ class SchemaDumperTest < ActiveRecord::TestCase def test_dump_schema_information_outputs_lexically_ordered_versions versions = %w{ 20100101010101 20100201010101 20100301010101 } versions.reverse.each do |v| - ActiveRecord::SchemaMigration.create!(:version => 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 |