migration_revert 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -6,14 +6,20 @@ Some commands are not reversible, for example the removal of a table or a column
6
6
 
7
7
  Also, one downside is that it is now more difficult to write the reverse of a migration if `change` is used. When `up` and `down` were used, one could simply swap the code around.
8
8
 
9
- These commits introduce `Migration#revert` that makes it trivial to revert a past migration, in part or in whole, or do the doing a reversible removal of a table/column.
9
+ This gem introduces `Migration#revert` that makes it trivial to revert a past migration, in part or in whole, or do the doing a reversible removal of a table/column.
10
10
 
11
11
  Note that `revert` can even be called from legacy migrations using `up` & `down` and that it can revert legacy-style migrations too. For anyone changing their mind every second day, `revert` is fully nestable.
12
12
 
13
- To have complete revertible capability, I would like to introduce a modified syntax for `change_column` that would allow it to be revertible; pull request upcoming when I get a chance...
13
+ Sounds useful? Give a +1 to https://github.com/rails/rails/pull/7627
14
+
15
+ Also introduces `Migration#reversible` for data operations that can be reverted.
16
+
17
+ Sounds useful also? Give a +1 to https://github.com/rails/rails/pull/8177
14
18
 
15
19
  ## Usage
16
20
 
21
+ ### revert
22
+
17
23
  Reverses the migration commands for the given block and
18
24
  the given migrations.
19
25
 
@@ -21,8 +27,6 @@ The following migration will remove the table 'horses'
21
27
  and create the table 'apples' on the way up, and the reverse
22
28
  on the way down.
23
29
 
24
- This command can be nested.
25
-
26
30
  class FixTLMigration < ActiveRecord::Migration
27
31
  def change
28
32
  revert do
@@ -37,7 +41,7 @@ This command can be nested.
37
41
  end
38
42
  end
39
43
 
40
- Or equivalently, if +TenderloveMigration+ is defined as in the
44
+ Or equivalently, if `TenderloveMigration` is defined as in the
41
45
  documentation for Migration:
42
46
 
43
47
  class FixupTLMigration < ActiveRecord::Migration
@@ -50,6 +54,36 @@ documentation for Migration:
50
54
  end
51
55
  end
52
56
 
57
+ This command can be nested.
58
+
59
+ ### reversible
60
+
61
+ Used to specify an operation that can be run in one direction or another.
62
+ Call the methods +up+ and +down+ of the yielded object to run a block
63
+ only in one given direction.
64
+ The whole block will be called in the right order within the migration.
65
+
66
+ In the following example, the looping on users will always be done
67
+ when the three columns 'first_name', 'last_name' and 'full_name' exist,
68
+ even when migrating down:
69
+
70
+ class SplitNameMigration < ActiveRecord::Migration
71
+ def change
72
+ add_column :users, :first_name, :string
73
+ add_column :users, :last_name, :string
74
+
75
+ reversible do |dir|
76
+ User.reset_column_information
77
+ User.all.each do |u|
78
+ dir.up { u.first_name, u.last_name = u.full_name.split(' ') }
79
+ dir.down { u.full_name = "#{u.first_name} #{u.last_name}" }
80
+ u.save
81
+ end
82
+ end
83
+
84
+ revert { add_column :users, :full_name, :string }
85
+ end
86
+ end
53
87
 
54
88
  ## Installation
55
89
 
@@ -18,13 +18,26 @@ module ActiveRecord
18
18
  #
19
19
  def record(*command, &block)
20
20
  if @reverting
21
- @commands << inverse_of(*command)
21
+ @commands << inverse_of(*command, &block)
22
22
  else
23
23
  @commands << (command << block)
24
24
  end
25
25
  end
26
26
 
27
- [:create_table, :create_join_table, :change_table, :rename_table, :add_column, :remove_column, :rename_index, :rename_column, :add_index, :remove_index, :add_timestamps, :remove_timestamps, :change_column, :change_column_default, :add_reference, :remove_reference].each do |method|
27
+ # Returns the inverse of the given command
28
+ #
29
+ # recorder.inverse_of(:rename_table, [:old, :new])
30
+ # # => [:rename_table, [:new, :old]]
31
+ #
32
+ # This method will raise an +IrreversibleMigration+ exception if it cannot
33
+ # invert the +commands+.
34
+ def inverse_of(command, args, &block)
35
+ method = :"invert_#{command}"
36
+ raise IrreversibleMigration unless respond_to?(method, true)
37
+ send(method, args, &block)
38
+ end
39
+
40
+ [:create_table, :create_join_table, :change_table, :rename_table, :add_column, :remove_column, :rename_index, :rename_column, :add_index, :remove_index, :add_timestamps, :remove_timestamps, :change_column, :change_column_default, :add_reference, :remove_reference, :transaction].each do |method|
28
41
  class_eval <<-EOV, __FILE__, __LINE__ + 1
29
42
  def #{method}(*args, &block) # def create_table(*args, &block)
30
43
  record(:"#{method}", args, &block) # record(:create_table, args, &block)
@@ -34,18 +47,10 @@ module ActiveRecord
34
47
  alias :add_belongs_to :add_reference
35
48
  alias :remove_belongs_to :remove_reference
36
49
 
37
- # Returns the inverse of the given command
38
- #
39
- # recorder.inverse_of(:rename_table, [:old, :new])
40
- # # => [:rename_table, [:new, :old]]
41
- #
42
- # This method will raise an +IrreversibleMigration+ exception if it cannot
43
- # invert the +commands+.
44
- #
45
- def inverse_of(name, args)
46
- method = :"invert_#{name}"
47
- raise IrreversibleMigration unless respond_to?(method, true)
48
- send(method, args)
50
+ private
51
+
52
+ def invert_transaction(args, &block)
53
+ [:transaction, args, block]
49
54
  end
50
55
  end
51
56
  end
@@ -7,8 +7,6 @@ module ActiveRecord
7
7
  # and create the table 'apples' on the way up, and the reverse
8
8
  # on the way down.
9
9
  #
10
- # This command can be nested.
11
- #
12
10
  # class FixTLMigration < ActiveRecord::Migration
13
11
  # def change
14
12
  # revert do
@@ -36,6 +34,8 @@ module ActiveRecord
36
34
  # end
37
35
  # end
38
36
  #
37
+ # This command can be nested.
38
+ #
39
39
  def revert(*migration_classes)
40
40
  run(*migration_classes.reverse, :revert => true) unless migration_classes.empty?
41
41
  if block_given?
@@ -59,6 +59,46 @@ module ActiveRecord
59
59
  @connection.respond_to?(:reverting) && @connection.reverting
60
60
  end
61
61
 
62
+ class ReversibleBlockHelper < Struct.new(:reverting)
63
+ def up
64
+ yield unless reverting
65
+ end
66
+
67
+ def down
68
+ yield if reverting
69
+ end
70
+ end
71
+
72
+ # Used to specify an operation that can be run in one direction or another.
73
+ # Call the methods +up+ and +down+ of the yielded object to run a block
74
+ # only in one given direction.
75
+ # The whole block will be called in the right order within the migration.
76
+ # In the following example, the looping on users will always be done
77
+ # when the three columns 'first_name', 'last_name' and 'full_name' exist,
78
+ # even when migrating down:
79
+ #
80
+ # class SplitName < ActiveRecord::Migration
81
+ # def change
82
+ # add_column :users, :first_name, :string
83
+ # add_column :users, :last_name, :string
84
+ #
85
+ # reversible do |dir|
86
+ # User.reset_column_information
87
+ # User.all.each do |u|
88
+ # dir.up { u.first_name, u.last_name = u.full_name.split(' ') }
89
+ # dir.down { u.full_name = "#{u.first_name} #{u.last_name}" }
90
+ # u.save
91
+ # end
92
+ # end
93
+ #
94
+ # revert { add_column :users, :full_name, :string }
95
+ # end
96
+ # end
97
+ def reversible
98
+ helper = ReversibleBlockHelper.new(reverting?)
99
+ transaction{ yield helper }
100
+ end
101
+
62
102
  # Runs the given migration classes.
63
103
  # Last argument can specify options:
64
104
  # - :direction (default is :up)
@@ -1,3 +1,3 @@
1
1
  module MigrationRevert
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: migration_revert
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-08-07 00:00:00.000000000 Z
12
+ date: 2012-11-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord