diff options
| -rw-r--r-- | activerecord/CHANGELOG.md | 7 | ||||
| -rw-r--r-- | activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb | 6 | ||||
| -rw-r--r-- | activerecord/lib/active_record/migration.rb | 24 | ||||
| -rw-r--r-- | activerecord/lib/active_record/schema_migration.rb | 29 | ||||
| -rw-r--r-- | activerecord/test/cases/migration/logger_test.rb | 4 | ||||
| -rw-r--r-- | activerecord/test/cases/migration/table_and_index_test.rb | 24 | ||||
| -rw-r--r-- | activerecord/test/cases/migration_test.rb | 9 | ||||
| -rw-r--r-- | activerecord/test/cases/migrator_test.rb | 9 | ||||
| -rw-r--r-- | activerecord/test/cases/schema_dumper_test.rb | 2 | ||||
| -rw-r--r-- | activerecord/test/cases/schema_migration_test.rb | 54 | 
10 files changed, 125 insertions, 43 deletions
| diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 130d0f05d2..ecec187329 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,5 +1,12 @@  ## Rails 4.0.0 (unreleased) ## +*   Add metadata columns to schema_migrations table. +    New columns are: migrated_at (timestamp), +    fingerprint (md5 hash of migration source), and +    name (filename minus version and extension) +     +    *Josh Susser* +  *   Add STI support to init and building associations.      Allows you to do BaseClass.new(:type => "SubClass") as well as      parent.children.build(:type => "SubClass") or parent.build_child 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 f1e42dfbbe..7802cb02d8 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -490,7 +490,7 @@ module ActiveRecord          sm_table = ActiveRecord::Migrator.schema_migrations_table_name          ActiveRecord::SchemaMigration.order('version').map { |sm| -          "INSERT INTO #{sm_table} (version) VALUES ('#{sm.version}');" +          "INSERT INTO #{sm_table} (version, migrated_at, name) VALUES ('#{sm.version}',LOCALTIMESTAMP,'#{sm.name}');"          }.join "\n\n"        end @@ -512,7 +512,7 @@ module ActiveRecord          end          unless migrated.include?(version) -          execute "INSERT INTO #{sm_table} (version) VALUES ('#{version}')" +          ActiveRecord::SchemaMigration.create!(:version => version, :migrated_at => Time.now)          end          inserted = Set.new @@ -520,7 +520,7 @@ module ActiveRecord            if inserted.include?(v)              raise "Duplicate migration #{v}. Please renumber your migrations to resolve the conflict."            elsif v < version -            execute "INSERT INTO #{sm_table} (version) VALUES ('#{v}')" +            ActiveRecord::SchemaMigration.create!(:version => v, :migrated_at => Time.now)              inserted << v            end          end diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 22347fcaef..4ce276d4bf 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -1,5 +1,6 @@  require "active_support/core_ext/class/attribute_accessors"  require 'set' +require 'digest/md5'  module ActiveRecord    # Exception that can be raised to stop migrations from going backwards. @@ -554,6 +555,10 @@ module ActiveRecord      delegate :migrate, :announce, :write, :to => :migration +    def fingerprint +      @fingerprint ||= Digest::MD5.hexdigest(File.read(filename)) +    end +      private        def migration @@ -724,7 +729,7 @@ module ActiveRecord        raise UnknownMigrationVersionError.new(@target_version) if target.nil?        unless (up? && migrated.include?(target.version.to_i)) || (down? && !migrated.include?(target.version.to_i))          target.migrate(@direction) -        record_version_state_after_migrating(target.version) +        record_version_state_after_migrating(target)        end      end @@ -747,7 +752,7 @@ module ActiveRecord          begin            ddl_transaction do              migration.migrate(@direction) -            record_version_state_after_migrating(migration.version) +            record_version_state_after_migrating(migration)            end          rescue => e            canceled_msg = Base.connection.supports_ddl_transactions? ? "this and " : "" @@ -805,13 +810,18 @@ module ActiveRecord        raise DuplicateMigrationVersionError.new(version) if version      end -    def record_version_state_after_migrating(version) +    def record_version_state_after_migrating(target)        if down? -        migrated.delete(version) -        ActiveRecord::SchemaMigration.where(:version => version.to_s).delete_all +        migrated.delete(target.version) +        ActiveRecord::SchemaMigration.where(:version => target.version.to_s).delete_all        else -        migrated << version -        ActiveRecord::SchemaMigration.create!(:version => version.to_s) +        migrated << target.version +        ActiveRecord::SchemaMigration.create!( +          :version => target.version.to_s, +          :migrated_at => Time.now, +          :fingerprint => target.fingerprint, +          :name => File.basename(target.filename,'.rb').gsub(/^\d+_/,'') +        )        end      end diff --git a/activerecord/lib/active_record/schema_migration.rb b/activerecord/lib/active_record/schema_migration.rb index 9830abe7d8..fd226d5eba 100644 --- a/activerecord/lib/active_record/schema_migration.rb +++ b/activerecord/lib/active_record/schema_migration.rb @@ -14,17 +14,38 @@ module ActiveRecord      end      def self.create_table -      unless connection.table_exists?(table_name) +      if connection.table_exists?(table_name) +        cols = connection.columns(table_name).collect { |col| col.name } +        unless cols.include?("migrated_at") +          connection.add_column(table_name, "migrated_at", :datetime) +          q_table_name = connection.quote_table_name(table_name) +          q_timestamp = connection.quoted_date(Time.now) +          connection.update("UPDATE #{q_table_name} SET migrated_at = '#{q_timestamp}' WHERE migrated_at IS NULL") +          connection.change_column(table_name, "migrated_at", :datetime, :null => false) +        end +        unless cols.include?("fingerprint") +          connection.add_column(table_name, "fingerprint", :string, :limit => 32) +        end +        unless cols.include?("name") +          connection.add_column(table_name, "name", :string) +        end +      else          connection.create_table(table_name, :id => false) do |t| -          t.column :version, :string, :null => false +          t.column "version", :string, :null => false +          t.column "migrated_at", :datetime, :null => false +          t.column "fingerprint", :string, :limit => 32 +          t.column "name", :string          end -        connection.add_index table_name, :version, :unique => true, :name => index_name +        connection.add_index(table_name, "version", :unique => true, :name => index_name)        end +      reset_column_information      end      def self.drop_table +      if connection.index_exists?(table_name, "version", :unique => true, :name => index_name) +        connection.remove_index(table_name, :name => index_name) +      end        if connection.table_exists?(table_name) -        connection.remove_index table_name, :name => index_name          connection.drop_table(table_name)        end      end diff --git a/activerecord/test/cases/migration/logger_test.rb b/activerecord/test/cases/migration/logger_test.rb index ee0c20747e..6e62c1fcbe 100644 --- a/activerecord/test/cases/migration/logger_test.rb +++ b/activerecord/test/cases/migration/logger_test.rb @@ -6,10 +6,12 @@ module ActiveRecord        # mysql can't roll back ddl changes        self.use_transactional_fixtures = false -      Migration = Struct.new(:name, :version) do +      Migration = Struct.new(:name, :version, :filename, :fingerprint) do          def migrate direction            # do nothing          end +        def filename; "anon.rb"; end +        def fingerprint; "123456789012345678901234567890ab"; end        end        def setup diff --git a/activerecord/test/cases/migration/table_and_index_test.rb b/activerecord/test/cases/migration/table_and_index_test.rb deleted file mode 100644 index 8fd770abd1..0000000000 --- a/activerecord/test/cases/migration/table_and_index_test.rb +++ /dev/null @@ -1,24 +0,0 @@ -require "cases/helper" - -module ActiveRecord -  class Migration -    class TableAndIndexTest < ActiveRecord::TestCase -      def test_add_schema_info_respects_prefix_and_suffix -        conn = ActiveRecord::Base.connection - -        conn.drop_table(ActiveRecord::Migrator.schema_migrations_table_name) if conn.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name) -        # Use shorter prefix and suffix as in Oracle database identifier cannot be larger than 30 characters -        ActiveRecord::Base.table_name_prefix = 'p_' -        ActiveRecord::Base.table_name_suffix = '_s' -        conn.drop_table(ActiveRecord::Migrator.schema_migrations_table_name) if conn.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name) - -        conn.initialize_schema_migrations_table - -        assert_equal "p_unique_schema_migrations_s", conn.indexes(ActiveRecord::Migrator.schema_migrations_table_name)[0][:name] -      ensure -        ActiveRecord::Base.table_name_prefix = "" -        ActiveRecord::Base.table_name_suffix = "" -      end -    end -  end -end diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index c155f29973..2c74bb6719 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -59,12 +59,21 @@ class MigrationTest < ActiveRecord::TestCase    def test_migrator_versions      migrations_path = MIGRATIONS_ROOT + "/valid"      ActiveRecord::Migrator.migrations_paths = migrations_path +    m0_path = File.join(migrations_path, "1_valid_people_have_last_names.rb") +    m0_fingerprint = Digest::MD5.hexdigest(File.read(m0_path))      ActiveRecord::Migrator.up(migrations_path)      assert_equal 3, ActiveRecord::Migrator.current_version      assert_equal 3, ActiveRecord::Migrator.last_version      assert_equal false, ActiveRecord::Migrator.needs_migration? +    rows = connection.select_all("SELECT * FROM #{connection.quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)}") +    assert_equal m0_fingerprint, rows[0]["fingerprint"] +    assert_equal "valid_people_have_last_names", rows[0]["name"] +    rows.each do |row| +      assert_match(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/, row["migrated_at"], "missing migrated_at") +    end +      ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid")      assert_equal 0, ActiveRecord::Migrator.current_version      assert_equal 3, ActiveRecord::Migrator.last_version diff --git a/activerecord/test/cases/migrator_test.rb b/activerecord/test/cases/migrator_test.rb index 1e16addcf3..0f0384382f 100644 --- a/activerecord/test/cases/migrator_test.rb +++ b/activerecord/test/cases/migrator_test.rb @@ -18,6 +18,9 @@ module ActiveRecord        def up; @went_up = true; end        def down; @went_down = true; end +      # also used in place of a MigrationProxy +      def filename; "anon.rb"; end +      def fingerprint; "123456789012345678901234567890ab"; end      end      def setup @@ -102,7 +105,7 @@ module ActiveRecord      end      def test_finds_pending_migrations -      ActiveRecord::SchemaMigration.create!(:version => '1') +      ActiveRecord::SchemaMigration.create!(:version => '1', :name => "anon", :migrated_at => Time.now)        migration_list = [ Migration.new('foo', 1), Migration.new('bar', 3) ]        migrations = ActiveRecord::Migrator.new(:up, migration_list).pending_migrations @@ -152,7 +155,7 @@ module ActiveRecord      end      def test_current_version -      ActiveRecord::SchemaMigration.create!(:version => '1000') +      ActiveRecord::SchemaMigration.create!(:version => '1000', :name => "anon", :migrated_at => Time.now)        assert_equal 1000, ActiveRecord::Migrator.current_version      end @@ -320,7 +323,7 @@ module ActiveRecord      def test_only_loads_pending_migrations        # migrate up to 1 -      ActiveRecord::SchemaMigration.create!(:version => '1') +      ActiveRecord::SchemaMigration.create!(:version => '1', :name => "anon", :migrated_at => Time.now)        calls, migrator = migrator_class(3)        migrator.migrate("valid", nil) diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index 7ff0044bd4..2f75eb0995 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -18,7 +18,7 @@ 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) +      ActiveRecord::SchemaMigration.create!(:version => v, :name => "anon", :migrated_at => Time.now)      end      schema_info = ActiveRecord::Base.connection.dump_schema_information diff --git a/activerecord/test/cases/schema_migration_test.rb b/activerecord/test/cases/schema_migration_test.rb new file mode 100644 index 0000000000..34b9fa520a --- /dev/null +++ b/activerecord/test/cases/schema_migration_test.rb @@ -0,0 +1,54 @@ +require "cases/helper" + +class SchemaMigrationTest < ActiveRecord::TestCase +  def sm_table_name +    ActiveRecord::SchemaMigration.table_name +  end + +  def connection +    ActiveRecord::Base.connection +  end + +  def test_add_schema_info_respects_prefix_and_suffix +    connection.drop_table(sm_table_name) if connection.table_exists?(sm_table_name) +    # Use shorter prefix and suffix as in Oracle database identifier cannot be larger than 30 characters +    ActiveRecord::Base.table_name_prefix = 'p_' +    ActiveRecord::Base.table_name_suffix = '_s' +    connection.drop_table(sm_table_name) if connection.table_exists?(sm_table_name) + +    ActiveRecord::SchemaMigration.create_table + +    assert_equal "p_unique_schema_migrations_s", connection.indexes(sm_table_name)[0][:name] +  ensure +    ActiveRecord::Base.table_name_prefix = "" +    ActiveRecord::Base.table_name_suffix = "" +  end + +  def test_add_metadata_columns_to_exisiting_schema_migrations +    # creates the old table schema from pre-Rails4.0, so we can test adding to it below +    if connection.table_exists?(sm_table_name) +      connection.drop_table(sm_table_name) +    end +    connection.create_table(sm_table_name, :id => false) do |schema_migrations_table| +      schema_migrations_table.column("version", :string, :null => false) +    end + +    connection.insert "INSERT INTO #{connection.quote_table_name(sm_table_name)} (version) VALUES (100)" +    connection.insert "INSERT INTO #{connection.quote_table_name(sm_table_name)} (version) VALUES (200)" + +    ActiveRecord::SchemaMigration.create_table + +    rows = connection.select_all("SELECT * FROM #{connection.quote_table_name(sm_table_name)}") +    assert rows[0].has_key?("migrated_at"), "missing column `migrated_at`" +    assert_match(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/, rows[0]["migrated_at"]) +    assert rows[0].has_key?("fingerprint"), "missing column `fingerprint`" +    assert rows[0].has_key?("name"), "missing column `name`" +  end + +  def test_schema_migrations_columns +    ActiveRecord::SchemaMigration.create_table + +    columns =  connection.columns(sm_table_name).collect(&:name) +    %w[version migrated_at fingerprint name].each { |col| assert columns.include?(col), "missing column `#{col}`" } +  end +end | 
