activerecord-turntable 2.5.0 → 3.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +7 -0
  3. data/.travis.yml +3 -7
  4. data/CHANGELOG.md +4 -14
  5. data/Gemfile +3 -0
  6. data/Guardfile +12 -7
  7. data/README.md +11 -19
  8. data/Rakefile +14 -15
  9. data/activerecord-turntable.gemspec +24 -27
  10. data/gemfiles/rails5_0.gemfile +6 -0
  11. data/lib/active_record/turntable/active_record_ext/abstract_adapter.rb +24 -20
  12. data/lib/active_record/turntable/active_record_ext/activerecord_import_ext.rb +6 -16
  13. data/lib/active_record/turntable/active_record_ext/acts_as_archive_extension.rb +25 -16
  14. data/lib/active_record/turntable/active_record_ext/association.rb +33 -14
  15. data/lib/active_record/turntable/active_record_ext/association_preloader.rb +4 -24
  16. data/lib/active_record/turntable/active_record_ext/clever_load.rb +2 -2
  17. data/lib/active_record/turntable/active_record_ext/connection_handler_extension.rb +11 -15
  18. data/lib/active_record/turntable/active_record_ext/database_tasks.rb +9 -9
  19. data/lib/active_record/turntable/active_record_ext/fixtures.rb +11 -41
  20. data/lib/active_record/turntable/active_record_ext/locking_optimistic.rb +40 -147
  21. data/lib/active_record/turntable/active_record_ext/log_subscriber.rb +6 -37
  22. data/lib/active_record/turntable/active_record_ext/migration_proxy.rb +1 -1
  23. data/lib/active_record/turntable/active_record_ext/persistence.rb +54 -148
  24. data/lib/active_record/turntable/active_record_ext/relation.rb +17 -45
  25. data/lib/active_record/turntable/active_record_ext/schema_dumper.rb +80 -78
  26. data/lib/active_record/turntable/active_record_ext/sequencer.rb +6 -15
  27. data/lib/active_record/turntable/active_record_ext/transactions.rb +14 -9
  28. data/lib/active_record/turntable/active_record_ext.rb +15 -16
  29. data/lib/active_record/turntable/algorithm/modulo_algorithm.rb +1 -1
  30. data/lib/active_record/turntable/algorithm/range_algorithm.rb +9 -9
  31. data/lib/active_record/turntable/algorithm/range_bsearch_algorithm.rb +12 -12
  32. data/lib/active_record/turntable/base.rb +27 -14
  33. data/lib/active_record/turntable/cluster.rb +10 -13
  34. data/lib/active_record/turntable/cluster_helper_methods.rb +6 -6
  35. data/lib/active_record/turntable/config.rb +3 -3
  36. data/lib/active_record/turntable/connection_proxy/mixable.rb +1 -1
  37. data/lib/active_record/turntable/connection_proxy.rb +23 -22
  38. data/lib/active_record/turntable/helpers/test_helper.rb +4 -4
  39. data/lib/active_record/turntable/master_shard.rb +12 -7
  40. data/lib/active_record/turntable/migration.rb +41 -47
  41. data/lib/active_record/turntable/mixer/fader/calculate_shards_sum_result.rb +7 -7
  42. data/lib/active_record/turntable/mixer/fader/select_shards_merge_result.rb +12 -12
  43. data/lib/active_record/turntable/mixer.rb +121 -121
  44. data/lib/active_record/turntable/plugin.rb +1 -1
  45. data/lib/active_record/turntable/pool_proxy.rb +7 -19
  46. data/lib/active_record/turntable/query_cache.rb +41 -0
  47. data/lib/active_record/turntable/railtie.rb +7 -5
  48. data/lib/active_record/turntable/railties/databases.rake +19 -20
  49. data/lib/active_record/turntable/seq_shard.rb +15 -15
  50. data/lib/active_record/turntable/sequencer/api.rb +7 -7
  51. data/lib/active_record/turntable/sequencer/barrage.rb +6 -7
  52. data/lib/active_record/turntable/sequencer/mysql.rb +2 -2
  53. data/lib/active_record/turntable/sequencer.rb +15 -15
  54. data/lib/active_record/turntable/shard.rb +23 -20
  55. data/lib/active_record/turntable/sql_tree_patch.rb +59 -57
  56. data/lib/active_record/turntable/util.rb +1 -21
  57. data/lib/active_record/turntable/version.rb +1 -1
  58. data/lib/active_record/turntable.rb +14 -19
  59. data/lib/activerecord-turntable.rb +2 -2
  60. data/script/performance/algorithm +8 -9
  61. data/spec/active_record/turntable/active_record_ext/association_preloader_spec.rb +4 -4
  62. data/spec/active_record/turntable/active_record_ext/association_spec.rb +5 -14
  63. data/spec/active_record/turntable/active_record_ext/clever_load_spec.rb +6 -6
  64. data/spec/active_record/turntable/active_record_ext/fixture_set_spec.rb +4 -4
  65. data/spec/active_record/turntable/active_record_ext/locking_optimistic_spec.rb +2 -15
  66. data/spec/active_record/turntable/active_record_ext/migration_spec.rb +4 -4
  67. data/spec/active_record/turntable/active_record_ext/persistence_spec.rb +17 -15
  68. data/spec/active_record/turntable/active_record_ext/sequencer_spec.rb +1 -1
  69. data/spec/active_record/turntable/active_record_ext/test_fixtures_spec.rb +3 -3
  70. data/spec/active_record/turntable/algorithm/modulo_algorithm_spec.rb +2 -3
  71. data/spec/active_record/turntable/algorithm/range_algorithm_spec.rb +2 -3
  72. data/spec/active_record/turntable/algorithm/range_bsearch_algorithm_spec.rb +2 -3
  73. data/spec/active_record/turntable/algorithm_spec.rb +5 -5
  74. data/spec/active_record/turntable/base_spec.rb +1 -1
  75. data/spec/active_record/turntable/cluster_spec.rb +1 -1
  76. data/spec/active_record/turntable/config_spec.rb +1 -1
  77. data/spec/active_record/turntable/connection_proxy_spec.rb +16 -17
  78. data/spec/active_record/turntable/finder_spec.rb +1 -1
  79. data/spec/active_record/turntable/mixer/fader_spec.rb +1 -1
  80. data/spec/active_record/turntable/mixer_spec.rb +2 -4
  81. data/spec/active_record/turntable/query_cache_spec.rb +28 -0
  82. data/spec/active_record/turntable/sequencer/api_spec.rb +4 -4
  83. data/spec/active_record/turntable/sequencer/barrage_spec.rb +2 -2
  84. data/spec/active_record/turntable/sequencer/mysql_spec.rb +1 -1
  85. data/spec/active_record/turntable/shard_spec.rb +1 -1
  86. data/spec/active_record/turntable/sql_tree_patch_spec.rb +2 -2
  87. data/spec/active_record/turntable/transaction_spec.rb +2 -2
  88. data/spec/active_record/turntable_spec.rb +3 -3
  89. data/spec/migrations/002_create_user_statuses.rb +4 -4
  90. data/spec/migrations/003_create_cards.rb +3 -3
  91. data/spec/migrations/004_create_cards_users.rb +2 -2
  92. data/spec/models/user_status.rb +0 -1
  93. data/spec/spec_helper.rb +15 -14
  94. data/spec/support/turntable_helper.rb +4 -4
  95. metadata +98 -59
  96. data/gemfiles/rails4_0.gemfile +0 -7
  97. data/gemfiles/rails4_1.gemfile +0 -7
  98. data/gemfiles/rails4_2.gemfile +0 -7
  99. data/lib/active_record/turntable/rack/connection_management.rb +0 -18
  100. data/lib/active_record/turntable/rack/query_cache.rb +0 -40
  101. data/lib/active_record/turntable/rack.rb +0 -8
  102. data/spec/active_record/turntable/rack/query_cache_spec.rb +0 -19
@@ -1,24 +1,24 @@
1
- require 'active_record/tasks/database_tasks'
1
+ require "active_record/tasks/database_tasks"
2
2
 
3
3
  module ActiveRecord
4
4
  module Tasks
5
5
  module DatabaseTasks
6
6
  def create_all_turntable_cluster
7
- each_local_turntable_cluster_configuration { |name, configuration|
7
+ each_local_turntable_cluster_configuration { |_name, configuration|
8
8
  puts "[turntable] *** executing to database: #{configuration['database']}"
9
9
  create configuration
10
10
  }
11
11
  end
12
12
 
13
13
  def drop_all_turntable_cluster
14
- each_local_turntable_cluster_configuration { |name, configuration|
14
+ each_local_turntable_cluster_configuration { |_name, configuration|
15
15
  puts "[turntable] *** executing to database: #{configuration['database']}"
16
16
  drop configuration
17
17
  }
18
18
  end
19
19
 
20
20
  def create_current_turntable_cluster(environment = env)
21
- each_current_turntable_cluster_configuration(true, environment) { |name, configuration|
21
+ each_current_turntable_cluster_configuration(true, environment) { |_name, configuration|
22
22
  puts "[turntable] *** executing to database: #{configuration['database']}"
23
23
  create configuration
24
24
  }
@@ -26,7 +26,7 @@ module ActiveRecord
26
26
  end
27
27
 
28
28
  def drop_current_turntable_cluster(environment = env)
29
- each_current_turntable_cluster_configuration(true, environment) { |name, configuration|
29
+ each_current_turntable_cluster_configuration(true, environment) { |_name, configuration|
30
30
  puts "[turntable] *** executing to database: #{configuration['database']}"
31
31
  drop configuration
32
32
  }
@@ -45,20 +45,20 @@ module ActiveRecord
45
45
 
46
46
  def each_current_turntable_cluster_configuration(with_test = false, environment = env)
47
47
  environments = [environment]
48
- environments << 'test' if with_test and environment == 'development'
48
+ environments << "test" if with_test && environment == "development"
49
49
 
50
50
  current_turntable_cluster_configurations(*environments).each do |name, configuration|
51
- yield(name, configuration) unless configuration['database'].blank?
51
+ yield(name, configuration) unless configuration["database"].blank?
52
52
  end
53
53
  end
54
54
 
55
55
  def each_local_turntable_cluster_configuration
56
56
  ActiveRecord::Base.configurations.keys.each do |k|
57
57
  current_turntable_cluster_configurations(k).each do |name, configuration|
58
- next if configuration['database'].blank?
58
+ next if configuration["database"].blank?
59
59
 
60
60
  if local_database?(configuration)
61
- yield(name,configuration)
61
+ yield(name, configuration)
62
62
  else
63
63
  $stderr.puts "This task only modifies local databases. #{configuration['database']} is on a remote host."
64
64
  end
@@ -1,7 +1,7 @@
1
1
  #
2
2
  # force TestFixtures to begin transaction with all shards.
3
3
  #
4
- require 'active_record/fixtures'
4
+ require "active_record/fixtures"
5
5
 
6
6
  module ActiveRecord
7
7
  class FixtureSet
@@ -9,11 +9,7 @@ module ActiveRecord
9
9
 
10
10
  def self.create_fixtures(fixtures_directory, fixture_set_names, class_names = {}, config = ActiveRecord::Base)
11
11
  fixture_set_names = Array(fixture_set_names).map(&:to_s)
12
- class_names = if ar41_or_later?
13
- ClassCache.new class_names, config
14
- else
15
- class_names = class_names.stringify_keys
16
- end
12
+ class_names = ClassCache.new class_names, config
17
13
 
18
14
  # FIXME: Apparently JK uses this.
19
15
  connection = block_given? ? yield : ActiveRecord::Base.connection
@@ -27,11 +23,7 @@ module ActiveRecord
27
23
  fixtures_map = {}
28
24
 
29
25
  fixture_sets = files_to_read.map do |fs_name|
30
- klass = if ar41_or_later?
31
- class_names[fs_name]
32
- else
33
- class_names[fs_name] || default_fixture_model_name(fs_name)
34
- end
26
+ klass = class_names[fs_name]
35
27
  conn = klass.is_a?(String) ? connection : klass.connection
36
28
  fixtures_map[fs_name] = new( # ActiveRecord::FixtureSet.new
37
29
  conn,
@@ -40,19 +32,15 @@ module ActiveRecord
40
32
  ::File.join(fixtures_directory, fs_name))
41
33
  end
42
34
 
43
- if ar42_or_later?
44
- update_all_loaded_fixtures fixtures_map
45
- else
46
- all_loaded_fixtures.update(fixtures_map)
47
- end
35
+ update_all_loaded_fixtures fixtures_map
48
36
 
49
- ActiveRecord::Base.force_transaction_all_shards!(:requires_new => true) do
37
+ ActiveRecord::Base.force_transaction_all_shards!(requires_new: true) do
50
38
  fixture_sets.each do |fs|
51
39
  conn = fs.model_class.respond_to?(:connection) ? fs.model_class.connection : connection
52
40
  table_rows = fs.table_rows
53
41
 
54
42
  table_rows.each_key do |table|
55
- conn.delete "DELETE FROM #{conn.quote_table_name(table)}", 'Fixture Delete'
43
+ conn.delete "DELETE FROM #{conn.quote_table_name(table)}", "Fixture Delete"
56
44
  end
57
45
 
58
46
  table_rows.each do |fixture_set_name, rows|
@@ -81,10 +69,10 @@ module ActiveRecord
81
69
  include ActiveRecord::Turntable::Util
82
70
 
83
71
  def setup_fixtures(config = ActiveRecord::Base)
84
- return unless !ActiveRecord::Base.configurations.blank?
72
+ return if ActiveRecord::Base.configurations.blank?
85
73
 
86
74
  if pre_loaded_fixtures && !use_transactional_fixtures
87
- raise RuntimeError, 'pre_loaded_fixtures requires use_transactional_fixtures'
75
+ raise "pre_loaded_fixtures requires use_transactional_fixtures"
88
76
  end
89
77
 
90
78
  @fixture_cache = {}
@@ -96,7 +84,7 @@ module ActiveRecord
96
84
  if @@already_loaded_fixtures[self.class]
97
85
  @loaded_fixtures = @@already_loaded_fixtures[self.class]
98
86
  else
99
- @loaded_fixtures = turntable_load_fixtures(config)
87
+ @loaded_fixtures = load_fixtures(config)
100
88
  @@already_loaded_fixtures[self.class] = @loaded_fixtures
101
89
  end
102
90
  ActiveRecord::Base.force_connect_all_shards!
@@ -108,34 +96,16 @@ module ActiveRecord
108
96
  else
109
97
  ActiveRecord::Fixtures.reset_cache
110
98
  @@already_loaded_fixtures[self.class] = nil
111
- @loaded_fixtures = turntable_load_fixtures(config)
99
+ @loaded_fixtures = load_fixtures(config)
112
100
  end
113
101
 
114
102
  # Instantiate fixtures for every test if requested.
115
- turntable_instantiate_fixtures(config) if use_instantiated_fixtures
103
+ instantiate_fixtures(config) if use_instantiated_fixtures
116
104
  end
117
105
 
118
106
  def enlist_fixture_connections
119
107
  ActiveRecord::Base.connection_handler.connection_pool_list.map(&:connection) +
120
108
  ActiveRecord::Base.turntable_connections.values.map(&:connection)
121
109
  end
122
-
123
- private
124
-
125
- def turntable_load_fixtures(config)
126
- if ar41_or_later?
127
- load_fixtures(config)
128
- else
129
- load_fixtures
130
- end
131
- end
132
-
133
- def turntable_instantiate_fixtures(config)
134
- if ar41_or_later?
135
- instantiate_fixtures(config)
136
- else
137
- instantiate_fixtures
138
- end
139
- end
140
110
  end
141
111
  end
@@ -1,160 +1,53 @@
1
1
  module ActiveRecord::Turntable
2
2
  module ActiveRecordExt
3
3
  module LockingOptimistic
4
- # @note Override to add sharding condition on optimistic locking
5
- ::ActiveRecord::Locking::Optimistic.class_eval do
6
-
7
- ar_version = ActiveRecord::VERSION::STRING
8
- if Util.earlier_than_ar41?
9
- method_name = Util.ar_version_earlier_than?("4.0.6") ? "update_record" : "_update_record"
10
-
11
- class_eval <<-EOD
12
- def #{method_name}(attribute_names = @attributes.keys) #:nodoc:
13
- return super unless locking_enabled?
14
- return 0 if attribute_names.empty?
15
-
16
- klass = self.class
17
- lock_col = self.class.locking_column
18
- previous_lock_value = send(lock_col).to_i
19
- increment_lock
20
-
21
- attribute_names += [lock_col]
22
- attribute_names.uniq!
23
-
24
- begin
25
- relation = self.class.unscoped
26
-
27
- condition_scope = relation.where(
28
- relation.table[self.class.primary_key].eq(id).and(
29
- relation.table[lock_col].eq(self.class.quote_value(previous_lock_value, column_for_attribute(lock_col)))
30
- )
31
- )
32
- if klass.turntable_enabled? and klass.primary_key != klass.turntable_shard_key.to_s
33
- condition_scope = condition_scope.where(
34
- relation.table[klass.turntable_shard_key].eq(
35
- self.class.quote_value(self.send(turntable_shard_key), column_for_attribute(klass.turntable_shard_key))
36
- )
37
- )
38
- end
39
- stmt = condition_scope.arel.compile_update(arel_attributes_with_values_for_update(attribute_names))
40
-
41
- affected_rows = self.class.connection.update stmt
42
-
43
- unless affected_rows == 1
44
- raise ActiveRecord::StaleObjectError.new(self, "update")
45
- end
46
-
47
- affected_rows
48
-
49
- # If something went wrong, revert the version.
50
- rescue Exception
51
- send(lock_col + '=', previous_lock_value)
52
- raise
53
- end
4
+ ::ActiveRecord::Locking::Optimistic.class_eval <<-EOD
5
+ private
6
+ # @note Override to add sharding condition on optimistic locking
7
+ def _update_record(attribute_names = self.attribute_names) #:nodoc:
8
+ return super unless locking_enabled?
9
+ return 0 if attribute_names.empty?
10
+
11
+ klass = self.class
12
+ lock_col = self.class.locking_column
13
+ previous_lock_value = send(lock_col).to_i
14
+ increment_lock
15
+
16
+ attribute_names += [lock_col]
17
+ attribute_names.uniq!
18
+
19
+ begin
20
+ relation = self.class.unscoped
21
+
22
+ condition_scope = relation.where(
23
+ self.class.primary_key => id,
24
+ lock_col => previous_lock_value,
25
+ )
26
+ if klass.turntable_enabled? and klass.primary_key != klass.turntable_shard_key.to_s
27
+ condition_scope = condition_scope.where(
28
+ klass.turntable_shard_key => self.send(klass.turntable_shard_key)
29
+ )
54
30
  end
55
- EOD
56
- elsif Util.earlier_than_ar42?
57
- method_name = Util.ar_version_earlier_than?("4.1.2") ? "update_record" : "_update_record"
58
-
59
- class_eval <<-EOD
60
- def _update_record(attribute_names = @attributes.keys) #:nodoc:
61
- return super unless locking_enabled?
62
- return 0 if attribute_names.empty?
63
-
64
- klass = self.class
65
- lock_col = self.class.locking_column
66
- previous_lock_value = send(lock_col).to_i
67
- increment_lock
68
-
69
- attribute_names += [lock_col]
70
- attribute_names.uniq!
71
31
 
72
- begin
73
- relation = self.class.unscoped
32
+ affected_rows = condition_scope.update_all(
33
+ attributes_for_update(attribute_names).map do |name|
34
+ [name, _read_attribute(name)]
35
+ end.to_h
36
+ )
74
37
 
75
- condition_scope = relation.where(
76
- relation.table[self.class.primary_key].eq(id).and(
77
- relation.table[lock_col].eq(self.class.quote_value(previous_lock_value, column_for_attribute(lock_col)))
78
- )
79
- )
80
- if klass.turntable_enabled? and klass.primary_key != klass.turntable_shard_key.to_s
81
- condition_scope = condition_scope.where(
82
- relation.table[klass.turntable_shard_key].eq(
83
- self.class.quote_value(self.send(turntable_shard_key), column_for_attribute(klass.turntable_shard_key))
84
- )
85
- )
86
- end
87
- stmt = condition_scope.arel.compile_update(
88
- arel_attributes_with_values_for_update(attribute_names),
89
- self.class.primary_key
90
- )
91
-
92
- affected_rows = self.class.connection.update stmt
93
-
94
- unless affected_rows == 1
95
- raise ActiveRecord::StaleObjectError.new(self, "update")
96
- end
97
-
98
- affected_rows
99
-
100
- # If something went wrong, revert the version.
101
- rescue Exception
102
- send(lock_col + '=', previous_lock_value)
103
- raise
104
- end
38
+ unless affected_rows == 1
39
+ raise ActiveRecord::StaleObjectError.new(self, "update")
105
40
  end
106
- EOD
107
- else
108
- class_eval <<-EOD
109
- def _update_record(attribute_names = self.attribute_names) #:nodoc:
110
- return super unless locking_enabled?
111
- return 0 if attribute_names.empty?
112
-
113
- klass = self.class
114
- lock_col = self.class.locking_column
115
- previous_lock_value = send(lock_col).to_i
116
- increment_lock
117
-
118
- attribute_names += [lock_col]
119
- attribute_names.uniq!
120
41
 
121
- begin
122
- relation = self.class.unscoped
42
+ affected_rows
123
43
 
124
- condition_scope = relation.where(
125
- self.class.primary_key => id,
126
- lock_col => previous_lock_value,
127
- )
128
-
129
- if klass.turntable_enabled? and klass.primary_key != klass.turntable_shard_key.to_s
130
- condition_scope = condition_scope.where(
131
- relation.table[klass.turntable_shard_key].eq(
132
- self.class.quote_value(self.send(turntable_shard_key), column_for_attribute(klass.turntable_shard_key))
133
- )
134
- )
135
- end
136
-
137
- affected_rows = condition_scope.update_all(
138
- Hash[attributes_for_update(attribute_names).map do |name|
139
- [name, _read_attribute(name)]
140
- end]
141
- )
142
-
143
- unless affected_rows == 1
144
- raise ActiveRecord::StaleObjectError.new(self, "update")
145
- end
146
-
147
- affected_rows
148
-
149
- # If something went wrong, revert the version.
150
- rescue Exception
151
- send(lock_col + '=', previous_lock_value)
152
- raise
153
- end
154
- end
155
- EOD
44
+ # If something went wrong, revert the version.
45
+ rescue Exception
46
+ send(lock_col + '=', previous_lock_value)
47
+ raise
48
+ end
156
49
  end
157
- end
50
+ EOD
158
51
  end
159
52
  end
160
53
  end
@@ -1,46 +1,15 @@
1
- require 'active_record/log_subscriber'
1
+ require "active_record/log_subscriber"
2
2
 
3
3
  module ActiveRecord::Turntable
4
4
  module ActiveRecordExt
5
5
  module LogSubscriber
6
- extend ActiveSupport::Concern
7
-
8
- included do
9
- alias_method_chain :sql, :turntable
10
- end
11
-
12
- protected
13
-
14
- # @note Override to add shard name logging
15
- def sql_with_turntable(event)
16
- self.class.runtime += event.duration
17
- return unless logger.debug?
18
-
6
+ # @note prepend to add shard name logging
7
+ def sql(event)
19
8
  payload = event.payload
20
-
21
- return if ActiveRecord::LogSubscriber::IGNORE_PAYLOAD_NAMES.include?(payload[:name])
22
-
23
- name = "#{payload[:name]} (#{event.duration.round(1)}ms)"
24
- shard = '[Shard: %s]' % (event.payload[:turntable_shard_name] ? event.payload[:turntable_shard_name] : nil)
25
- sql = payload[:sql].squeeze(' ')
26
- binds = nil
27
-
28
- unless (payload[:binds] || []).empty?
29
- binds = " " + payload[:binds].map { |col,v|
30
- render_bind(col, v)
31
- }.inspect
9
+ if payload[:turntable_shard_name]
10
+ payload[:name] = "#{payload[:name]} [Shard: #{payload[:turntable_shard_name]}]"
32
11
  end
33
-
34
- if odd?
35
- name = color(name, ActiveRecord::LogSubscriber::CYAN, true)
36
- shard = color(shard, ActiveRecord::LogSubscriber::CYAN, true)
37
- sql = color(sql, nil, true)
38
- else
39
- name = color(name, ActiveRecord::LogSubscriber::MAGENTA, true)
40
- shard = color(shard, ActiveRecord::LogSubscriber::MAGENTA, true)
41
- end
42
-
43
- debug " #{name} #{shard} #{sql}#{binds}"
12
+ super
44
13
  end
45
14
  end
46
15
  end
@@ -1,4 +1,4 @@
1
- require 'active_record/migration'
1
+ require "active_record/migration"
2
2
 
3
3
  module ActiveRecord
4
4
  class MigrationProxy