aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc-Andre Lafortune <github@marc-andre.ca>2012-08-06 22:25:01 -0400
committerMarc-Andre Lafortune <github@marc-andre.ca>2012-12-21 13:54:51 -0500
commit65e154f33b54acf40b51082fc5b681ba605015d9 (patch)
tree2db947cc5aea5cc1815a1e13122a23ed68271e78
parent24653c945ad3fdce4cb5890a9cc7565753decda0 (diff)
downloadrails-65e154f33b54acf40b51082fc5b681ba605015d9.tar.gz
rails-65e154f33b54acf40b51082fc5b681ba605015d9.tar.bz2
rails-65e154f33b54acf40b51082fc5b681ba605015d9.zip
Allow revert of whole migration [#8267]
-rw-r--r--activerecord/lib/active_record/migration.rb42
-rw-r--r--activerecord/test/cases/invertible_migration_test.rb63
2 files changed, 102 insertions, 3 deletions
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index 14c1fb9ae2..c605c2da7f 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -379,7 +379,8 @@ module ActiveRecord
self.verbose = true
self.delegate = new
- # Reverses the migration commands for the given block.
+ # Reverses the migration commands for the given block and
+ # the given migrations.
#
# The following migration will remove the table 'horses'
# and create the table 'apples' on the way up, and the reverse
@@ -399,9 +400,25 @@ module ActiveRecord
# end
# end
#
- # This command can be nested.
+ # Or equivalently, if +TenderloveMigration+ is defined as in the
+ # documentation for Migration:
+ #
+ # require_relative '2012121212_tenderlove_migration'
+ #
+ # class FixupTLMigration < ActiveRecord::Migration
+ # def change
+ # revert TenderloveMigration
+ #
+ # create_table(:apples) do |t|
+ # t.string :variety
+ # end
+ # end
+ # end
#
- def revert
+ # This command can be nested.
+ def revert(*migration_classes)
+ run(*migration_classes.reverse, revert: true) unless migration_classes.empty?
+ if block_given?
if @connection.respond_to? :revert
@connection.revert { yield }
else
@@ -415,12 +432,31 @@ module ActiveRecord
send(cmd, *args, &block)
end
end
+ end
end
def reverting?
@connection.respond_to?(:reverting) && @connection.reverting
end
+ # Runs the given migration classes.
+ # Last argument can specify options:
+ # - :direction (default is :up)
+ # - :revert (default is false)
+ def run(*migration_classes)
+ opts = migration_classes.extract_options!
+ dir = opts[:direction] || :up
+ dir = (dir == :down ? :up : :down) if opts[:revert]
+ if reverting?
+ # If in revert and going :up, say, we want to execute :down without reverting, so
+ revert { run(*migration_classes, direction: dir, revert: true) }
+ else
+ migration_classes.each do |migration_class|
+ migration_class.new.exec_migration(@connection, dir)
+ end
+ end
+ end
+
def up
self.class.delegate = self
return unless self.class.respond_to?(:up)
diff --git a/activerecord/test/cases/invertible_migration_test.rb b/activerecord/test/cases/invertible_migration_test.rb
index 6d285e23d7..6b81e600af 100644
--- a/activerecord/test/cases/invertible_migration_test.rb
+++ b/activerecord/test/cases/invertible_migration_test.rb
@@ -51,6 +51,23 @@ module ActiveRecord
end
end
+ class RevertWholeMigration < SilentMigration
+ def initialize(name = self.class.name, version = nil, migration)
+ @migration = migration
+ super(name, version)
+ end
+
+ def change
+ revert @migration
+ end
+ end
+
+ class NestedRevertWholeMigration < RevertWholeMigration
+ def change
+ revert { super }
+ end
+ end
+
def teardown
if ActiveRecord::Base.connection.table_exists?("horses")
ActiveRecord::Base.connection.drop_table("horses")
@@ -90,6 +107,52 @@ module ActiveRecord
assert !migration.connection.table_exists?("horses")
end
+ def test_migrate_revert_whole_migration
+ migration = InvertibleMigration.new
+ [LegacyMigration, InvertibleMigration].each do |klass|
+ revert = RevertWholeMigration.new(klass)
+ migration.migrate :up
+ revert.migrate :up
+ assert !migration.connection.table_exists?("horses")
+ revert.migrate :down
+ assert migration.connection.table_exists?("horses")
+ migration.migrate :down
+ assert !migration.connection.table_exists?("horses")
+ end
+ end
+
+ def test_migrate_nested_revert_whole_migration
+ revert = NestedRevertWholeMigration.new(InvertibleRevertMigration)
+ revert.migrate :down
+ assert revert.connection.table_exists?("horses")
+ revert.migrate :up
+ assert !revert.connection.table_exists?("horses")
+ end
+
+ def test_revert_order
+ block = Proc.new{|t| t.string :name }
+ recorder = ActiveRecord::Migration::CommandRecorder.new(ActiveRecord::Base.connection)
+ recorder.instance_eval do
+ create_table("apples", &block)
+ revert do
+ create_table("bananas", &block)
+ revert do
+ create_table("clementines")
+ create_table("dates")
+ end
+ create_table("elderberries")
+ end
+ revert do
+ create_table("figs")
+ create_table("grapes")
+ end
+ end
+ assert_equal [[:create_table, ["apples"], block], [:drop_table, ["elderberries"]],
+ [:create_table, ["clementines"], nil], [:create_table, ["dates"], nil],
+ [:drop_table, ["bananas"]], [:drop_table, ["grapes"]],
+ [:drop_table, ["figs"]]], recorder.commands
+ end
+
def test_legacy_up
LegacyMigration.migrate :up
assert ActiveRecord::Base.connection.table_exists?("horses"), "horses should exist"