activerecord 6.1.3.2 → 7.0.0.alpha2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (229) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +734 -1058
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/active_record/aggregations.rb +1 -1
  6. data/lib/active_record/association_relation.rb +0 -10
  7. data/lib/active_record/associations/association.rb +35 -7
  8. data/lib/active_record/associations/association_scope.rb +1 -3
  9. data/lib/active_record/associations/belongs_to_association.rb +16 -6
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  11. data/lib/active_record/associations/builder/association.rb +8 -2
  12. data/lib/active_record/associations/builder/belongs_to.rb +19 -6
  13. data/lib/active_record/associations/builder/collection_association.rb +1 -1
  14. data/lib/active_record/associations/builder/has_many.rb +3 -2
  15. data/lib/active_record/associations/builder/has_one.rb +2 -1
  16. data/lib/active_record/associations/builder/singular_association.rb +2 -2
  17. data/lib/active_record/associations/collection_association.rb +24 -25
  18. data/lib/active_record/associations/collection_proxy.rb +8 -3
  19. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  20. data/lib/active_record/associations/has_many_association.rb +1 -1
  21. data/lib/active_record/associations/has_many_through_association.rb +2 -1
  22. data/lib/active_record/associations/has_one_association.rb +10 -7
  23. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  24. data/lib/active_record/associations/preloader/association.rb +161 -49
  25. data/lib/active_record/associations/preloader/batch.rb +51 -0
  26. data/lib/active_record/associations/preloader/branch.rb +147 -0
  27. data/lib/active_record/associations/preloader/through_association.rb +37 -11
  28. data/lib/active_record/associations/preloader.rb +46 -110
  29. data/lib/active_record/associations/singular_association.rb +8 -2
  30. data/lib/active_record/associations/through_association.rb +1 -1
  31. data/lib/active_record/associations.rb +76 -81
  32. data/lib/active_record/asynchronous_queries_tracker.rb +57 -0
  33. data/lib/active_record/attribute_assignment.rb +1 -1
  34. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  35. data/lib/active_record/attribute_methods/dirty.rb +41 -16
  36. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  37. data/lib/active_record/attribute_methods/query.rb +2 -2
  38. data/lib/active_record/attribute_methods/read.rb +7 -5
  39. data/lib/active_record/attribute_methods/serialization.rb +66 -12
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
  41. data/lib/active_record/attribute_methods/write.rb +7 -10
  42. data/lib/active_record/attribute_methods.rb +6 -9
  43. data/lib/active_record/attributes.rb +24 -35
  44. data/lib/active_record/autosave_association.rb +3 -18
  45. data/lib/active_record/base.rb +19 -1
  46. data/lib/active_record/callbacks.rb +2 -2
  47. data/lib/active_record/coders/yaml_column.rb +11 -1
  48. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +312 -0
  49. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
  50. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +31 -558
  52. data/lib/active_record/connection_adapters/abstract/database_statements.rb +45 -21
  53. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
  54. data/lib/active_record/connection_adapters/abstract/quoting.rb +14 -7
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +5 -18
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -9
  57. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +60 -16
  58. data/lib/active_record/connection_adapters/abstract/transaction.rb +17 -6
  59. data/lib/active_record/connection_adapters/abstract_adapter.rb +115 -69
  60. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +96 -81
  61. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +6 -2
  62. data/lib/active_record/connection_adapters/mysql/database_statements.rb +33 -21
  63. data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -1
  64. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +3 -0
  65. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  66. data/lib/active_record/connection_adapters/pool_config.rb +1 -3
  67. data/lib/active_record/connection_adapters/pool_manager.rb +5 -1
  68. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -12
  69. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  72. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  73. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +28 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  76. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  77. data/lib/active_record/connection_adapters/postgresql/quoting.rb +6 -6
  78. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -0
  79. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +5 -1
  80. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +12 -12
  81. data/lib/active_record/connection_adapters/postgresql_adapter.rb +157 -100
  82. data/lib/active_record/connection_adapters/schema_cache.rb +35 -4
  83. data/lib/active_record/connection_adapters/sql_type_metadata.rb +0 -2
  84. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +23 -17
  85. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -2
  86. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -30
  87. data/lib/active_record/connection_adapters.rb +8 -5
  88. data/lib/active_record/connection_handling.rb +20 -38
  89. data/lib/active_record/core.rb +129 -117
  90. data/lib/active_record/database_configurations/database_config.rb +12 -0
  91. data/lib/active_record/database_configurations/hash_config.rb +27 -1
  92. data/lib/active_record/database_configurations/url_config.rb +2 -2
  93. data/lib/active_record/database_configurations.rb +18 -9
  94. data/lib/active_record/delegated_type.rb +33 -11
  95. data/lib/active_record/destroy_association_async_job.rb +1 -1
  96. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  97. data/lib/active_record/dynamic_matchers.rb +1 -1
  98. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  99. data/lib/active_record/encryption/cipher.rb +53 -0
  100. data/lib/active_record/encryption/config.rb +44 -0
  101. data/lib/active_record/encryption/configurable.rb +61 -0
  102. data/lib/active_record/encryption/context.rb +35 -0
  103. data/lib/active_record/encryption/contexts.rb +72 -0
  104. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  105. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  106. data/lib/active_record/encryption/encryptable_record.rb +208 -0
  107. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  108. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  109. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  110. data/lib/active_record/encryption/encryptor.rb +155 -0
  111. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  112. data/lib/active_record/encryption/errors.rb +15 -0
  113. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  114. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +29 -0
  115. data/lib/active_record/encryption/key.rb +28 -0
  116. data/lib/active_record/encryption/key_generator.rb +42 -0
  117. data/lib/active_record/encryption/key_provider.rb +46 -0
  118. data/lib/active_record/encryption/message.rb +33 -0
  119. data/lib/active_record/encryption/message_serializer.rb +80 -0
  120. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  121. data/lib/active_record/encryption/properties.rb +76 -0
  122. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  123. data/lib/active_record/encryption/scheme.rb +99 -0
  124. data/lib/active_record/encryption.rb +55 -0
  125. data/lib/active_record/enum.rb +44 -46
  126. data/lib/active_record/errors.rb +66 -3
  127. data/lib/active_record/fixture_set/file.rb +15 -1
  128. data/lib/active_record/fixture_set/table_row.rb +40 -5
  129. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  130. data/lib/active_record/fixtures.rb +16 -11
  131. data/lib/active_record/future_result.rb +139 -0
  132. data/lib/active_record/gem_version.rb +4 -4
  133. data/lib/active_record/inheritance.rb +55 -17
  134. data/lib/active_record/insert_all.rb +39 -6
  135. data/lib/active_record/integration.rb +1 -1
  136. data/lib/active_record/internal_metadata.rb +3 -5
  137. data/lib/active_record/legacy_yaml_adapter.rb +1 -1
  138. data/lib/active_record/locking/optimistic.rb +10 -9
  139. data/lib/active_record/log_subscriber.rb +6 -2
  140. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  141. data/lib/active_record/middleware/database_selector.rb +8 -3
  142. data/lib/active_record/migration/command_recorder.rb +4 -4
  143. data/lib/active_record/migration/compatibility.rb +83 -1
  144. data/lib/active_record/migration/join_table.rb +1 -1
  145. data/lib/active_record/migration.rb +109 -79
  146. data/lib/active_record/model_schema.rb +46 -32
  147. data/lib/active_record/nested_attributes.rb +3 -3
  148. data/lib/active_record/no_touching.rb +2 -2
  149. data/lib/active_record/null_relation.rb +2 -6
  150. data/lib/active_record/persistence.rb +134 -45
  151. data/lib/active_record/query_cache.rb +2 -2
  152. data/lib/active_record/query_logs.rb +203 -0
  153. data/lib/active_record/querying.rb +15 -5
  154. data/lib/active_record/railtie.rb +117 -17
  155. data/lib/active_record/railties/controller_runtime.rb +1 -1
  156. data/lib/active_record/railties/databases.rake +83 -58
  157. data/lib/active_record/readonly_attributes.rb +11 -0
  158. data/lib/active_record/reflection.rb +45 -44
  159. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  160. data/lib/active_record/relation/batches.rb +3 -3
  161. data/lib/active_record/relation/calculations.rb +42 -25
  162. data/lib/active_record/relation/delegation.rb +6 -6
  163. data/lib/active_record/relation/finder_methods.rb +32 -23
  164. data/lib/active_record/relation/merger.rb +20 -13
  165. data/lib/active_record/relation/predicate_builder.rb +1 -6
  166. data/lib/active_record/relation/query_attribute.rb +5 -11
  167. data/lib/active_record/relation/query_methods.rb +233 -50
  168. data/lib/active_record/relation/record_fetch_warning.rb +2 -2
  169. data/lib/active_record/relation/spawn_methods.rb +2 -2
  170. data/lib/active_record/relation/where_clause.rb +22 -15
  171. data/lib/active_record/relation.rb +170 -87
  172. data/lib/active_record/result.rb +17 -2
  173. data/lib/active_record/runtime_registry.rb +2 -4
  174. data/lib/active_record/sanitization.rb +11 -7
  175. data/lib/active_record/schema_dumper.rb +3 -3
  176. data/lib/active_record/schema_migration.rb +0 -4
  177. data/lib/active_record/scoping/default.rb +62 -15
  178. data/lib/active_record/scoping/named.rb +3 -11
  179. data/lib/active_record/scoping.rb +40 -22
  180. data/lib/active_record/serialization.rb +1 -1
  181. data/lib/active_record/signed_id.rb +1 -1
  182. data/lib/active_record/statement_cache.rb +2 -2
  183. data/lib/active_record/tasks/database_tasks.rb +107 -23
  184. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  185. data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -11
  186. data/lib/active_record/test_databases.rb +1 -1
  187. data/lib/active_record/test_fixtures.rb +45 -4
  188. data/lib/active_record/timestamp.rb +3 -4
  189. data/lib/active_record/transactions.rb +9 -14
  190. data/lib/active_record/translation.rb +2 -2
  191. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  192. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  193. data/lib/active_record/type/internal/timezone.rb +2 -2
  194. data/lib/active_record/type/serialized.rb +1 -1
  195. data/lib/active_record/type/type_map.rb +17 -20
  196. data/lib/active_record/type.rb +1 -2
  197. data/lib/active_record/validations/associated.rb +1 -1
  198. data/lib/active_record/validations/numericality.rb +1 -1
  199. data/lib/active_record.rb +170 -2
  200. data/lib/arel/attributes/attribute.rb +0 -8
  201. data/lib/arel/collectors/bind.rb +2 -2
  202. data/lib/arel/collectors/composite.rb +3 -3
  203. data/lib/arel/collectors/sql_string.rb +1 -1
  204. data/lib/arel/collectors/substitute_binds.rb +1 -1
  205. data/lib/arel/crud.rb +18 -22
  206. data/lib/arel/delete_manager.rb +2 -4
  207. data/lib/arel/insert_manager.rb +2 -3
  208. data/lib/arel/nodes/casted.rb +1 -1
  209. data/lib/arel/nodes/delete_statement.rb +8 -13
  210. data/lib/arel/nodes/homogeneous_in.rb +4 -0
  211. data/lib/arel/nodes/insert_statement.rb +2 -2
  212. data/lib/arel/nodes/select_core.rb +2 -2
  213. data/lib/arel/nodes/select_statement.rb +2 -2
  214. data/lib/arel/nodes/update_statement.rb +3 -2
  215. data/lib/arel/predications.rb +3 -3
  216. data/lib/arel/select_manager.rb +10 -4
  217. data/lib/arel/table.rb +0 -1
  218. data/lib/arel/tree_manager.rb +0 -12
  219. data/lib/arel/update_manager.rb +2 -4
  220. data/lib/arel/visitors/dot.rb +80 -90
  221. data/lib/arel/visitors/mysql.rb +6 -1
  222. data/lib/arel/visitors/postgresql.rb +0 -10
  223. data/lib/arel/visitors/to_sql.rb +44 -3
  224. data/lib/arel.rb +1 -1
  225. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  226. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  227. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  228. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  229. metadata +55 -16
@@ -177,7 +177,7 @@ module ActiveRecord
177
177
  def can_use_fast_cache_version?(timestamp)
178
178
  timestamp.is_a?(String) &&
179
179
  cache_timestamp_format == :usec &&
180
- default_timezone == :utc &&
180
+ ActiveRecord.default_timezone == :utc &&
181
181
  !updated_at_came_from_user?
182
182
  end
183
183
 
@@ -10,15 +10,13 @@ module ActiveRecord
10
10
  # This is enabled by default. To disable this functionality set
11
11
  # `use_metadata_table` to false in your database configuration.
12
12
  class InternalMetadata < ActiveRecord::Base # :nodoc:
13
+ self.record_timestamps = true
14
+
13
15
  class << self
14
16
  def enabled?
15
17
  ActiveRecord::Base.connection.use_metadata_table?
16
18
  end
17
19
 
18
- def _internal?
19
- true
20
- end
21
-
22
20
  def primary_key
23
21
  "key"
24
22
  end
@@ -36,7 +34,7 @@ module ActiveRecord
36
34
  def [](key)
37
35
  return unless enabled?
38
36
 
39
- where(key: key).pluck(:value).first
37
+ where(key: key).pick(:value)
40
38
  end
41
39
 
42
40
  # Creates an internal metadata table with columns +key+ and +value+
@@ -10,7 +10,7 @@ module ActiveRecord
10
10
  else
11
11
  ActiveSupport::Deprecation.warn(<<-MSG.squish)
12
12
  YAML loading from legacy format older than Rails 5.0 is deprecated
13
- and will be removed in Rails 6.2.
13
+ and will be removed in Rails 7.0.
14
14
  MSG
15
15
  if coder["attributes"].is_a?(ActiveModel::AttributeSet)
16
16
  Rails420.convert(klass, coder)
@@ -56,11 +56,11 @@ module ActiveRecord
56
56
  class_attribute :lock_optimistically, instance_writer: false, default: true
57
57
  end
58
58
 
59
- def locking_enabled? #:nodoc:
59
+ def locking_enabled? # :nodoc:
60
60
  self.class.locking_enabled?
61
61
  end
62
62
 
63
- def increment!(*, **) #:nodoc:
63
+ def increment!(*, **) # :nodoc:
64
64
  super.tap do
65
65
  if locking_enabled?
66
66
  self[self.class.locking_column] += 1
@@ -90,7 +90,9 @@ module ActiveRecord
90
90
  begin
91
91
  locking_column = self.class.locking_column
92
92
  lock_attribute_was = @attributes[locking_column]
93
- lock_value_for_database = _lock_value_for_database(locking_column)
93
+
94
+ update_constraints = _primary_key_constraints_hash
95
+ update_constraints[locking_column] = _lock_value_for_database(locking_column)
94
96
 
95
97
  attribute_names = attribute_names.dup if attribute_names.frozen?
96
98
  attribute_names << locking_column
@@ -99,8 +101,7 @@ module ActiveRecord
99
101
 
100
102
  affected_rows = self.class._update_record(
101
103
  attributes_with_values(attribute_names),
102
- @primary_key => id_in_database,
103
- locking_column => lock_value_for_database
104
+ update_constraints
104
105
  )
105
106
 
106
107
  if affected_rows != 1
@@ -121,10 +122,10 @@ module ActiveRecord
121
122
 
122
123
  locking_column = self.class.locking_column
123
124
 
124
- affected_rows = self.class._delete_record(
125
- @primary_key => id_in_database,
126
- locking_column => _lock_value_for_database(locking_column)
127
- )
125
+ delete_constraints = _primary_key_constraints_hash
126
+ delete_constraints[locking_column] = _lock_value_for_database(locking_column)
127
+
128
+ affected_rows = self.class._delete_record(delete_constraints)
128
129
 
129
130
  if affected_rows != 1
130
131
  raise ActiveRecord::StaleObjectError.new(self, "destroy")
@@ -37,7 +37,11 @@ module ActiveRecord
37
37
 
38
38
  return if IGNORE_PAYLOAD_NAMES.include?(payload[:name])
39
39
 
40
- name = "#{payload[:name]} (#{event.duration.round(1)}ms)"
40
+ name = if payload[:async]
41
+ "ASYNC #{payload[:name]} (#{payload[:lock_wait].round(1)}ms) (db time #{event.duration.round(1)}ms)"
42
+ else
43
+ "#{payload[:name]} (#{event.duration.round(1)}ms)"
44
+ end
41
45
  name = "CACHE #{name}" if payload[:cached]
42
46
  sql = payload[:sql]
43
47
  binds = nil
@@ -115,7 +119,7 @@ module ActiveRecord
115
119
  def debug(progname = nil, &block)
116
120
  return unless super
117
121
 
118
- if ActiveRecord::Base.verbose_query_logs
122
+ if ActiveRecord.verbose_query_logs
119
123
  log_query_source
120
124
  end
121
125
  end
@@ -50,23 +50,19 @@ module ActiveRecord
50
50
 
51
51
  private
52
52
  def read_from_primary(&blk)
53
- ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role, prevent_writes: true) do
54
- instrumenter.instrument("database_selector.active_record.read_from_primary") do
55
- yield
56
- end
53
+ ActiveRecord::Base.connected_to(role: ActiveRecord.writing_role, prevent_writes: true) do
54
+ instrumenter.instrument("database_selector.active_record.read_from_primary", &blk)
57
55
  end
58
56
  end
59
57
 
60
58
  def read_from_replica(&blk)
61
- ActiveRecord::Base.connected_to(role: ActiveRecord::Base.reading_role, prevent_writes: true) do
62
- instrumenter.instrument("database_selector.active_record.read_from_replica") do
63
- yield
64
- end
59
+ ActiveRecord::Base.connected_to(role: ActiveRecord.reading_role, prevent_writes: true) do
60
+ instrumenter.instrument("database_selector.active_record.read_from_replica", &blk)
65
61
  end
66
62
  end
67
63
 
68
- def write_to_primary(&blk)
69
- ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role, prevent_writes: false) do
64
+ def write_to_primary
65
+ ActiveRecord::Base.connected_to(role: ActiveRecord.writing_role, prevent_writes: false) do
70
66
  instrumenter.instrument("database_selector.active_record.wrote_to_primary") do
71
67
  yield
72
68
  ensure
@@ -22,9 +22,14 @@ module ActiveRecord
22
22
  # To use the DatabaseSelector in your application with default settings add
23
23
  # the following options to your environment config:
24
24
  #
25
- # config.active_record.database_selector = { delay: 2.seconds }
26
- # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
27
- # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
25
+ # # This require is only necessary when using `rails new app --minimal`
26
+ # require "active_support/core_ext/integer/time"
27
+ #
28
+ # class Application < Rails::Application
29
+ # config.active_record.database_selector = { delay: 2.seconds }
30
+ # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
31
+ # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
32
+ # end
28
33
  #
29
34
  # New applications will include these lines commented out in the production.rb.
30
35
  #
@@ -112,7 +112,7 @@ module ActiveRecord
112
112
  record(:"#{method}", args, &block) # record(:create_table, args, &block)
113
113
  end # end
114
114
  EOV
115
- ruby2_keywords(method) if respond_to?(:ruby2_keywords, true)
115
+ ruby2_keywords(method)
116
116
  end
117
117
  alias :add_belongs_to :add_reference
118
118
  alias :remove_belongs_to :remove_reference
@@ -154,9 +154,9 @@ module ActiveRecord
154
154
 
155
155
  include StraightReversions
156
156
 
157
- def invert_transaction(args)
157
+ def invert_transaction(args, &block)
158
158
  sub_recorder = CommandRecorder.new(delegate)
159
- sub_recorder.revert { yield }
159
+ sub_recorder.revert(&block)
160
160
 
161
161
  invertions_proc = proc {
162
162
  sub_recorder.replay(self)
@@ -286,7 +286,7 @@ module ActiveRecord
286
286
  super
287
287
  end
288
288
  end
289
- ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
289
+ ruby2_keywords(:method_missing)
290
290
  end
291
291
  end
292
292
  end
@@ -13,7 +13,77 @@ module ActiveRecord
13
13
  const_get(name)
14
14
  end
15
15
 
16
- V6_1 = Current
16
+ # This file exists to ensure that old migrations run the same way they did before a Rails upgrade.
17
+ # e.g. if you write a migration on Rails 6.1, then upgrade to Rails 7, the migration should do the same thing to your
18
+ # database as it did when you were running Rails 6.1
19
+ #
20
+ # "Current" is an alias for `ActiveRecord::Migration`, it represents the current Rails version.
21
+ # New migration functionality that will never be backward compatible should be added directly to `ActiveRecord::Migration`.
22
+ #
23
+ # There are classes for each prior Rails version. Each class descends from the *next* Rails version, so:
24
+ # 6.1 < 7.0
25
+ # 5.2 < 6.0 < 6.1 < 7.0
26
+ #
27
+ # If you are introducing new migration functionality that should only apply from Rails 7 onward, then you should
28
+ # find the class that immediately precedes it (6.1), and override the relevant migration methods to undo your changes.
29
+ #
30
+ # For example, Rails 6 added a default value for the `precision` option on datetime columns. So in this file, the `V5_2`
31
+ # class sets the value of `precision` to `nil` if it's not explicitly provided. This way, the default value will not apply
32
+ # for migrations written for 5.2, but will for migrations written for 6.0.
33
+ V7_0 = Current
34
+
35
+ class V6_1 < V7_0
36
+ class PostgreSQLCompat
37
+ def self.compatible_timestamp_type(type, connection)
38
+ if connection.adapter_name == "PostgreSQL"
39
+ # For Rails <= 6.1, :datetime was aliased to :timestamp
40
+ # See: https://github.com/rails/rails/blob/v6.1.3.2/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb#L108
41
+ # From Rails 7 onwards, you can define what :datetime resolves to (the default is still :timestamp)
42
+ # See `ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type`
43
+ type.to_sym == :datetime ? :timestamp : type
44
+ else
45
+ type
46
+ end
47
+ end
48
+ end
49
+
50
+ def add_column(table_name, column_name, type, **options)
51
+ if type == :datetime
52
+ options[:precision] ||= nil
53
+ end
54
+
55
+ type = PostgreSQLCompat.compatible_timestamp_type(type, connection)
56
+ super
57
+ end
58
+
59
+ def create_table(table_name, **options)
60
+ if block_given?
61
+ super { |t| yield compatible_table_definition(t) }
62
+ else
63
+ super
64
+ end
65
+ end
66
+
67
+ module TableDefinition
68
+ def new_column_definition(name, type, **options)
69
+ type = PostgreSQLCompat.compatible_timestamp_type(type, @conn)
70
+ super
71
+ end
72
+
73
+ def column(name, type, index: nil, **options)
74
+ options[:precision] ||= nil
75
+ super
76
+ end
77
+ end
78
+
79
+ private
80
+ def compatible_table_definition(t)
81
+ class << t
82
+ prepend TableDefinition
83
+ end
84
+ t
85
+ end
86
+ end
17
87
 
18
88
  class V6_0 < V6_1
19
89
  class ReferenceDefinition < ConnectionAdapters::ReferenceDefinition
@@ -29,6 +99,11 @@ module ActiveRecord
29
99
  end
30
100
  end
31
101
  alias :belongs_to :references
102
+
103
+ def column(name, type, index: nil, **options)
104
+ options[:precision] ||= nil
105
+ super
106
+ end
32
107
  end
33
108
 
34
109
  def create_table(table_name, **options)
@@ -76,6 +151,11 @@ module ActiveRecord
76
151
  options[:precision] ||= nil
77
152
  super
78
153
  end
154
+
155
+ def column(name, type, index: nil, **options)
156
+ options[:precision] ||= nil
157
+ super
158
+ end
79
159
  end
80
160
 
81
161
  module CommandRecorder
@@ -204,6 +284,8 @@ module ActiveRecord
204
284
  if type == :primary_key
205
285
  type = :integer
206
286
  options[:primary_key] = true
287
+ elsif type == :datetime
288
+ options[:precision] ||= nil
207
289
  end
208
290
  super
209
291
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module ActiveRecord
4
4
  class Migration
5
- module JoinTable #:nodoc:
5
+ module JoinTable # :nodoc:
6
6
  private
7
7
  def find_join_table_name(table_1, table_2, options = {})
8
8
  options.delete(:table_name) || join_table_name(table_1, table_2)
@@ -9,7 +9,7 @@ require "active_support/core_ext/module/attribute_accessors"
9
9
  require "active_support/actionable_error"
10
10
 
11
11
  module ActiveRecord
12
- class MigrationError < ActiveRecordError #:nodoc:
12
+ class MigrationError < ActiveRecordError # :nodoc:
13
13
  def initialize(message = nil)
14
14
  message = "\n\n#{message}\n\n" if message
15
15
  super
@@ -20,7 +20,7 @@ module ActiveRecord
20
20
  # For example the following migration is not reversible.
21
21
  # Rolling back this migration will raise an ActiveRecord::IrreversibleMigration error.
22
22
  #
23
- # class IrreversibleMigrationExample < ActiveRecord::Migration[6.0]
23
+ # class IrreversibleMigrationExample < ActiveRecord::Migration[7.0]
24
24
  # def change
25
25
  # create_table :distributors do |t|
26
26
  # t.string :zipcode
@@ -38,7 +38,7 @@ module ActiveRecord
38
38
  #
39
39
  # 1. Define <tt>#up</tt> and <tt>#down</tt> methods instead of <tt>#change</tt>:
40
40
  #
41
- # class ReversibleMigrationExample < ActiveRecord::Migration[6.0]
41
+ # class ReversibleMigrationExample < ActiveRecord::Migration[7.0]
42
42
  # def up
43
43
  # create_table :distributors do |t|
44
44
  # t.string :zipcode
@@ -63,7 +63,7 @@ module ActiveRecord
63
63
  #
64
64
  # 2. Use the #reversible method in <tt>#change</tt> method:
65
65
  #
66
- # class ReversibleMigrationExample < ActiveRecord::Migration[6.0]
66
+ # class ReversibleMigrationExample < ActiveRecord::Migration[7.0]
67
67
  # def change
68
68
  # create_table :distributors do |t|
69
69
  # t.string :zipcode
@@ -90,7 +90,7 @@ module ActiveRecord
90
90
  class IrreversibleMigration < MigrationError
91
91
  end
92
92
 
93
- class DuplicateMigrationVersionError < MigrationError #:nodoc:
93
+ class DuplicateMigrationVersionError < MigrationError # :nodoc:
94
94
  def initialize(version = nil)
95
95
  if version
96
96
  super("Multiple migrations have the version number #{version}.")
@@ -100,7 +100,7 @@ module ActiveRecord
100
100
  end
101
101
  end
102
102
 
103
- class DuplicateMigrationNameError < MigrationError #:nodoc:
103
+ class DuplicateMigrationNameError < MigrationError # :nodoc:
104
104
  def initialize(name = nil)
105
105
  if name
106
106
  super("Multiple migrations have the name #{name}.")
@@ -110,7 +110,7 @@ module ActiveRecord
110
110
  end
111
111
  end
112
112
 
113
- class UnknownMigrationVersionError < MigrationError #:nodoc:
113
+ class UnknownMigrationVersionError < MigrationError # :nodoc:
114
114
  def initialize(version = nil)
115
115
  if version
116
116
  super("No migration with version number #{version}.")
@@ -120,7 +120,7 @@ module ActiveRecord
120
120
  end
121
121
  end
122
122
 
123
- class IllegalMigrationNameError < MigrationError #:nodoc:
123
+ class IllegalMigrationNameError < MigrationError # :nodoc:
124
124
  def initialize(name = nil)
125
125
  if name
126
126
  super("Illegal name for migration file: #{name}\n\t(only lower case letters, numbers, and '_' allowed).")
@@ -130,13 +130,13 @@ module ActiveRecord
130
130
  end
131
131
  end
132
132
 
133
- class PendingMigrationError < MigrationError #:nodoc:
133
+ class PendingMigrationError < MigrationError # :nodoc:
134
134
  include ActiveSupport::ActionableError
135
135
 
136
136
  action "Run pending migrations" do
137
137
  ActiveRecord::Tasks::DatabaseTasks.migrate
138
138
 
139
- if ActiveRecord::Base.dump_schema_after_migration
139
+ if ActiveRecord.dump_schema_after_migration
140
140
  ActiveRecord::Tasks::DatabaseTasks.dump_schema(
141
141
  ActiveRecord::Base.connection_db_config
142
142
  )
@@ -165,7 +165,7 @@ module ActiveRecord
165
165
  end
166
166
  end
167
167
 
168
- class ConcurrentMigrationError < MigrationError #:nodoc:
168
+ class ConcurrentMigrationError < MigrationError # :nodoc:
169
169
  DEFAULT_MESSAGE = "Cannot run migrations because another migration process is currently running."
170
170
  RELEASE_LOCK_FAILED_MESSAGE = "Failed to release advisory lock"
171
171
 
@@ -174,7 +174,7 @@ module ActiveRecord
174
174
  end
175
175
  end
176
176
 
177
- class NoEnvironmentInSchemaError < MigrationError #:nodoc:
177
+ class NoEnvironmentInSchemaError < MigrationError # :nodoc:
178
178
  def initialize
179
179
  msg = "Environment data not found in the schema. To resolve this issue, run: \n\n bin/rails db:environment:set"
180
180
  if defined?(Rails.env)
@@ -185,7 +185,7 @@ module ActiveRecord
185
185
  end
186
186
  end
187
187
 
188
- class ProtectedEnvironmentError < ActiveRecordError #:nodoc:
188
+ class ProtectedEnvironmentError < ActiveRecordError # :nodoc:
189
189
  def initialize(env = "production")
190
190
  msg = +"You are attempting to run a destructive action against your '#{env}' database.\n"
191
191
  msg << "If you are sure you want to continue, run the same command with the environment variable:\n"
@@ -228,7 +228,7 @@ module ActiveRecord
228
228
  #
229
229
  # Example of a simple migration:
230
230
  #
231
- # class AddSsl < ActiveRecord::Migration[6.0]
231
+ # class AddSsl < ActiveRecord::Migration[7.0]
232
232
  # def up
233
233
  # add_column :accounts, :ssl_enabled, :boolean, default: true
234
234
  # end
@@ -248,7 +248,7 @@ module ActiveRecord
248
248
  #
249
249
  # Example of a more complex migration that also needs to initialize data:
250
250
  #
251
- # class AddSystemSettings < ActiveRecord::Migration[6.0]
251
+ # class AddSystemSettings < ActiveRecord::Migration[7.0]
252
252
  # def up
253
253
  # create_table :system_settings do |t|
254
254
  # t.string :name
@@ -376,7 +376,7 @@ module ActiveRecord
376
376
  # bin/rails generate migration add_fieldname_to_tablename fieldname:string
377
377
  #
378
378
  # This will generate the file <tt>timestamp_add_fieldname_to_tablename.rb</tt>, which will look like this:
379
- # class AddFieldnameToTablename < ActiveRecord::Migration[6.0]
379
+ # class AddFieldnameToTablename < ActiveRecord::Migration[7.0]
380
380
  # def change
381
381
  # add_column :tablenames, :fieldname, :string
382
382
  # end
@@ -402,7 +402,7 @@ module ActiveRecord
402
402
  #
403
403
  # Not all migrations change the schema. Some just fix the data:
404
404
  #
405
- # class RemoveEmptyTags < ActiveRecord::Migration[6.0]
405
+ # class RemoveEmptyTags < ActiveRecord::Migration[7.0]
406
406
  # def up
407
407
  # Tag.all.each { |tag| tag.destroy if tag.pages.empty? }
408
408
  # end
@@ -415,7 +415,7 @@ module ActiveRecord
415
415
  #
416
416
  # Others remove columns when they migrate up instead of down:
417
417
  #
418
- # class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration[6.0]
418
+ # class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration[7.0]
419
419
  # def up
420
420
  # remove_column :items, :incomplete_items_count
421
421
  # remove_column :items, :completed_items_count
@@ -429,7 +429,7 @@ module ActiveRecord
429
429
  #
430
430
  # And sometimes you need to do something in SQL not abstracted directly by migrations:
431
431
  #
432
- # class MakeJoinUnique < ActiveRecord::Migration[6.0]
432
+ # class MakeJoinUnique < ActiveRecord::Migration[7.0]
433
433
  # def up
434
434
  # execute "ALTER TABLE `pages_linked_pages` ADD UNIQUE `page_id_linked_page_id` (`page_id`,`linked_page_id`)"
435
435
  # end
@@ -446,7 +446,7 @@ module ActiveRecord
446
446
  # <tt>Base#reset_column_information</tt> in order to ensure that the model has the
447
447
  # latest column data from after the new column was added. Example:
448
448
  #
449
- # class AddPeopleSalary < ActiveRecord::Migration[6.0]
449
+ # class AddPeopleSalary < ActiveRecord::Migration[7.0]
450
450
  # def up
451
451
  # add_column :people, :salary, :integer
452
452
  # Person.reset_column_information
@@ -504,7 +504,7 @@ module ActiveRecord
504
504
  # To define a reversible migration, define the +change+ method in your
505
505
  # migration like this:
506
506
  #
507
- # class TenderloveMigration < ActiveRecord::Migration[6.0]
507
+ # class TenderloveMigration < ActiveRecord::Migration[7.0]
508
508
  # def change
509
509
  # create_table(:horses) do |t|
510
510
  # t.column :content, :text
@@ -534,7 +534,7 @@ module ActiveRecord
534
534
  # can't execute inside a transaction though, and for these situations
535
535
  # you can turn the automatic transactions off.
536
536
  #
537
- # class ChangeEnum < ActiveRecord::Migration[6.0]
537
+ # class ChangeEnum < ActiveRecord::Migration[7.0]
538
538
  # disable_ddl_transaction!
539
539
  #
540
540
  # def up
@@ -550,16 +550,18 @@ module ActiveRecord
550
550
  autoload :JoinTable, "active_record/migration/join_table"
551
551
 
552
552
  # This must be defined before the inherited hook, below
553
- class Current < Migration #:nodoc:
553
+ class Current < Migration # :nodoc:
554
554
  end
555
555
 
556
- def self.inherited(subclass) #:nodoc:
556
+ def self.inherited(subclass) # :nodoc:
557
557
  super
558
558
  if subclass.superclass == Migration
559
+ major = ActiveRecord::VERSION::MAJOR
560
+ minor = ActiveRecord::VERSION::MINOR
559
561
  raise StandardError, "Directly inheriting from ActiveRecord::Migration is not supported. " \
560
- "Please specify the Rails release the migration was written for:\n" \
562
+ "Please specify the Active Record release the migration was written for:\n" \
561
563
  "\n" \
562
- " class #{subclass} < ActiveRecord::Migration[4.2]"
564
+ " class #{subclass} < ActiveRecord::Migration[#{major}.#{minor}]"
563
565
  end
564
566
  end
565
567
 
@@ -571,7 +573,7 @@ module ActiveRecord
571
573
  ActiveRecord::VERSION::STRING.to_f
572
574
  end
573
575
 
574
- MigrationFilenameRegexp = /\A([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/ #:nodoc:
576
+ MigrationFilenameRegexp = /\A([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/ # :nodoc:
575
577
 
576
578
  # This class is used to verify that all migrations have been run before
577
579
  # loading a web page if <tt>config.active_record.migration_error</tt> is set to :page_load
@@ -613,10 +615,10 @@ module ActiveRecord
613
615
  end
614
616
 
615
617
  class << self
616
- attr_accessor :delegate #:nodoc:
617
- attr_accessor :disable_ddl_transaction #:nodoc:
618
+ attr_accessor :delegate # :nodoc:
619
+ attr_accessor :disable_ddl_transaction # :nodoc:
618
620
 
619
- def nearest_delegate #:nodoc:
621
+ def nearest_delegate # :nodoc:
620
622
  delegate || superclass.nearest_delegate
621
623
  end
622
624
 
@@ -630,7 +632,7 @@ module ActiveRecord
630
632
  all_configs = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env)
631
633
 
632
634
  needs_update = !all_configs.all? do |db_config|
633
- Tasks::DatabaseTasks.schema_up_to_date?(db_config, ActiveRecord::Base.schema_format)
635
+ Tasks::DatabaseTasks.schema_up_to_date?(db_config, ActiveRecord.schema_format)
634
636
  end
635
637
 
636
638
  if needs_update
@@ -648,16 +650,16 @@ module ActiveRecord
648
650
  check_pending!
649
651
  end
650
652
 
651
- def maintain_test_schema! #:nodoc:
652
- if ActiveRecord::Base.maintain_test_schema
653
+ def maintain_test_schema! # :nodoc:
654
+ if ActiveRecord.maintain_test_schema
653
655
  suppress_messages { load_schema_if_pending! }
654
656
  end
655
657
  end
656
658
 
657
- def method_missing(name, *args, &block) #:nodoc:
659
+ def method_missing(name, *args, &block) # :nodoc:
658
660
  nearest_delegate.send(name, *args, &block)
659
661
  end
660
- ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
662
+ ruby2_keywords(:method_missing)
661
663
 
662
664
  def migrate(direction)
663
665
  new.migrate direction
@@ -672,7 +674,7 @@ module ActiveRecord
672
674
  end
673
675
  end
674
676
 
675
- def disable_ddl_transaction #:nodoc:
677
+ def disable_ddl_transaction # :nodoc:
676
678
  self.class.disable_ddl_transaction
677
679
  end
678
680
 
@@ -696,7 +698,7 @@ module ActiveRecord
696
698
  # and create the table 'apples' on the way up, and the reverse
697
699
  # on the way down.
698
700
  #
699
- # class FixTLMigration < ActiveRecord::Migration[6.0]
701
+ # class FixTLMigration < ActiveRecord::Migration[7.0]
700
702
  # def change
701
703
  # revert do
702
704
  # create_table(:horses) do |t|
@@ -715,7 +717,7 @@ module ActiveRecord
715
717
  #
716
718
  # require_relative "20121212123456_tenderlove_migration"
717
719
  #
718
- # class FixupTLMigration < ActiveRecord::Migration[6.0]
720
+ # class FixupTLMigration < ActiveRecord::Migration[7.0]
719
721
  # def change
720
722
  # revert TenderloveMigration
721
723
  #
@@ -726,16 +728,16 @@ module ActiveRecord
726
728
  # end
727
729
  #
728
730
  # This command can be nested.
729
- def revert(*migration_classes)
731
+ def revert(*migration_classes, &block)
730
732
  run(*migration_classes.reverse, revert: true) unless migration_classes.empty?
731
733
  if block_given?
732
734
  if connection.respond_to? :revert
733
- connection.revert { yield }
735
+ connection.revert(&block)
734
736
  else
735
737
  recorder = command_recorder
736
738
  @connection = recorder
737
739
  suppress_messages do
738
- connection.revert { yield }
740
+ connection.revert(&block)
739
741
  end
740
742
  @connection = recorder.delegate
741
743
  recorder.replay(self)
@@ -747,7 +749,7 @@ module ActiveRecord
747
749
  connection.respond_to?(:reverting) && connection.reverting
748
750
  end
749
751
 
750
- ReversibleBlockHelper = Struct.new(:reverting) do #:nodoc:
752
+ ReversibleBlockHelper = Struct.new(:reverting) do # :nodoc:
751
753
  def up
752
754
  yield unless reverting
753
755
  end
@@ -766,7 +768,7 @@ module ActiveRecord
766
768
  # when the three columns 'first_name', 'last_name' and 'full_name' exist,
767
769
  # even when migrating down:
768
770
  #
769
- # class SplitNameMigration < ActiveRecord::Migration[6.0]
771
+ # class SplitNameMigration < ActiveRecord::Migration[7.0]
770
772
  # def change
771
773
  # add_column :users, :first_name, :string
772
774
  # add_column :users, :last_name, :string
@@ -794,7 +796,7 @@ module ActiveRecord
794
796
  # In the following example, the new column +published+ will be given
795
797
  # the value +true+ for all existing records.
796
798
  #
797
- # class AddPublishedToPosts < ActiveRecord::Migration[6.0]
799
+ # class AddPublishedToPosts < ActiveRecord::Migration[7.0]
798
800
  # def change
799
801
  # add_column :posts, :published, :boolean, default: false
800
802
  # up_only do
@@ -802,8 +804,8 @@ module ActiveRecord
802
804
  # end
803
805
  # end
804
806
  # end
805
- def up_only
806
- execute_block { yield } unless reverting?
807
+ def up_only(&block)
808
+ execute_block(&block) unless reverting?
807
809
  end
808
810
 
809
811
  # Runs the given migration classes.
@@ -919,7 +921,7 @@ module ActiveRecord
919
921
  unless connection.respond_to? :revert
920
922
  unless arguments.empty? || [:execute, :enable_extension, :disable_extension].include?(method)
921
923
  arguments[0] = proper_table_name(arguments.first, table_name_options)
922
- if [:rename_table, :add_foreign_key].include?(method) ||
924
+ if method == :rename_table ||
923
925
  (method == :remove_foreign_key && !arguments.second.is_a?(Hash))
924
926
  arguments[1] = proper_table_name(arguments.second, table_name_options)
925
927
  end
@@ -929,7 +931,7 @@ module ActiveRecord
929
931
  connection.send(method, *arguments, &block)
930
932
  end
931
933
  end
932
- ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
934
+ ruby2_keywords(:method_missing)
933
935
 
934
936
  def copy(destination, sources, options = {})
935
937
  copied = []
@@ -955,6 +957,12 @@ module ActiveRecord
955
957
  magic_comments << magic_comment; ""
956
958
  end || break
957
959
  end
960
+
961
+ if !magic_comments.empty? && source.start_with?("\n")
962
+ magic_comments << "\n"
963
+ source = source[1..-1]
964
+ end
965
+
958
966
  source = "#{magic_comments}#{inserted_comment}#{source}"
959
967
 
960
968
  if duplicate = destination_migrations.detect { |m| m.name == migration.name }
@@ -992,7 +1000,7 @@ module ActiveRecord
992
1000
 
993
1001
  # Determines the version number of the next migration.
994
1002
  def next_migration_number(number)
995
- if ActiveRecord::Base.timestamped_migrations
1003
+ if ActiveRecord.timestamped_migrations
996
1004
  [Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % number].max
997
1005
  else
998
1006
  SchemaMigration.normalize_migration_number(number)
@@ -1001,7 +1009,7 @@ module ActiveRecord
1001
1009
 
1002
1010
  # Builds a hash for use in ActiveRecord::Migration#proper_table_name using
1003
1011
  # the Active Record object's table_name prefix and suffix
1004
- def table_name_options(config = ActiveRecord::Base) #:nodoc:
1012
+ def table_name_options(config = ActiveRecord::Base) # :nodoc:
1005
1013
  {
1006
1014
  table_name_prefix: config.table_name_prefix,
1007
1015
  table_name_suffix: config.table_name_suffix
@@ -1042,19 +1050,41 @@ module ActiveRecord
1042
1050
  end
1043
1051
 
1044
1052
  def load_migration
1045
- require(File.expand_path(filename))
1053
+ Object.send(:remove_const, name) rescue nil
1054
+
1055
+ load(File.expand_path(filename))
1046
1056
  name.constantize.new(name, version)
1047
1057
  end
1048
1058
  end
1049
1059
 
1050
- class MigrationContext #:nodoc:
1060
+ # MigrationContext sets the context in which a migration is run.
1061
+ #
1062
+ # A migration context requires the path to the migrations is set
1063
+ # in the +migrations_paths+ parameter. Optionally a +schema_migration+
1064
+ # class can be provided. For most applications, +SchemaMigration+ is
1065
+ # sufficient. Multiple database applications need a +SchemaMigration+
1066
+ # per primary database.
1067
+ class MigrationContext
1051
1068
  attr_reader :migrations_paths, :schema_migration
1052
1069
 
1053
- def initialize(migrations_paths, schema_migration)
1070
+ def initialize(migrations_paths, schema_migration = SchemaMigration)
1054
1071
  @migrations_paths = migrations_paths
1055
1072
  @schema_migration = schema_migration
1056
1073
  end
1057
1074
 
1075
+ # Runs the migrations in the +migrations_path+.
1076
+ #
1077
+ # If +target_version+ is +nil+, +migrate+ will run +up+.
1078
+ #
1079
+ # If the +current_version+ and +target_version+ are both
1080
+ # 0 then an empty array will be returned and no migrations
1081
+ # will be run.
1082
+ #
1083
+ # If the +current_version+ in the schema is less than
1084
+ # the +target_version+, then +down+ will be run.
1085
+ #
1086
+ # If none of the conditions are met, +up+ will be run with
1087
+ # the +target_version+.
1058
1088
  def migrate(target_version = nil, &block)
1059
1089
  case
1060
1090
  when target_version.nil?
@@ -1068,17 +1098,17 @@ module ActiveRecord
1068
1098
  end
1069
1099
  end
1070
1100
 
1071
- def rollback(steps = 1)
1101
+ def rollback(steps = 1) # :nodoc:
1072
1102
  move(:down, steps)
1073
1103
  end
1074
1104
 
1075
- def forward(steps = 1)
1105
+ def forward(steps = 1) # :nodoc:
1076
1106
  move(:up, steps)
1077
1107
  end
1078
1108
 
1079
- def up(target_version = nil)
1109
+ def up(target_version = nil, &block) # :nodoc:
1080
1110
  selected_migrations = if block_given?
1081
- migrations.select { |m| yield m }
1111
+ migrations.select(&block)
1082
1112
  else
1083
1113
  migrations
1084
1114
  end
@@ -1086,9 +1116,9 @@ module ActiveRecord
1086
1116
  Migrator.new(:up, selected_migrations, schema_migration, target_version).migrate
1087
1117
  end
1088
1118
 
1089
- def down(target_version = nil)
1119
+ def down(target_version = nil, &block) # :nodoc:
1090
1120
  selected_migrations = if block_given?
1091
- migrations.select { |m| yield m }
1121
+ migrations.select(&block)
1092
1122
  else
1093
1123
  migrations
1094
1124
  end
@@ -1096,15 +1126,15 @@ module ActiveRecord
1096
1126
  Migrator.new(:down, selected_migrations, schema_migration, target_version).migrate
1097
1127
  end
1098
1128
 
1099
- def run(direction, target_version)
1129
+ def run(direction, target_version) # :nodoc:
1100
1130
  Migrator.new(direction, migrations, schema_migration, target_version).run
1101
1131
  end
1102
1132
 
1103
- def open
1133
+ def open # :nodoc:
1104
1134
  Migrator.new(:up, migrations, schema_migration)
1105
1135
  end
1106
1136
 
1107
- def get_all_versions
1137
+ def get_all_versions # :nodoc:
1108
1138
  if schema_migration.table_exists?
1109
1139
  schema_migration.all_versions.map(&:to_i)
1110
1140
  else
@@ -1112,20 +1142,20 @@ module ActiveRecord
1112
1142
  end
1113
1143
  end
1114
1144
 
1115
- def current_version
1145
+ def current_version # :nodoc:
1116
1146
  get_all_versions.max || 0
1117
1147
  rescue ActiveRecord::NoDatabaseError
1118
1148
  end
1119
1149
 
1120
- def needs_migration?
1121
- (migrations.collect(&:version) - get_all_versions).size > 0
1150
+ def needs_migration? # :nodoc:
1151
+ pending_migration_versions.size > 0
1122
1152
  end
1123
1153
 
1124
- def any_migrations?
1125
- migrations.any?
1154
+ def pending_migration_versions # :nodoc:
1155
+ migrations.collect(&:version) - get_all_versions
1126
1156
  end
1127
1157
 
1128
- def migrations
1158
+ def migrations # :nodoc:
1129
1159
  migrations = migration_files.map do |file|
1130
1160
  version, name, scope = parse_migration_filename(file)
1131
1161
  raise IllegalMigrationNameError.new(file) unless version
@@ -1138,33 +1168,33 @@ module ActiveRecord
1138
1168
  migrations.sort_by(&:version)
1139
1169
  end
1140
1170
 
1141
- def migrations_status
1171
+ def migrations_status # :nodoc:
1142
1172
  db_list = schema_migration.normalized_versions
1143
1173
 
1144
- file_list = migration_files.map do |file|
1174
+ file_list = migration_files.filter_map do |file|
1145
1175
  version, name, scope = parse_migration_filename(file)
1146
1176
  raise IllegalMigrationNameError.new(file) unless version
1147
1177
  version = schema_migration.normalize_migration_number(version)
1148
1178
  status = db_list.delete(version) ? "up" : "down"
1149
1179
  [status, version, (name + scope).humanize]
1150
- end.compact
1180
+ end
1151
1181
 
1152
1182
  db_list.map! do |version|
1153
1183
  ["up", version, "********** NO FILE **********"]
1154
1184
  end
1155
1185
 
1156
- (db_list + file_list).sort_by { |_, version, _| version }
1186
+ (db_list + file_list).sort_by { |_, version, _| version.to_i }
1157
1187
  end
1158
1188
 
1159
- def current_environment
1189
+ def current_environment # :nodoc:
1160
1190
  ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
1161
1191
  end
1162
1192
 
1163
- def protected_environment?
1193
+ def protected_environment? # :nodoc:
1164
1194
  ActiveRecord::Base.protected_environments.include?(last_stored_environment) if last_stored_environment
1165
1195
  end
1166
1196
 
1167
- def last_stored_environment
1197
+ def last_stored_environment # :nodoc:
1168
1198
  return nil unless ActiveRecord::InternalMetadata.enabled?
1169
1199
  return nil if current_version == 0
1170
1200
  raise NoEnvironmentInSchemaError unless ActiveRecord::InternalMetadata.table_exists?
@@ -1375,9 +1405,9 @@ module ActiveRecord
1375
1405
  end
1376
1406
 
1377
1407
  # Wrap the migration in a transaction only if supported by the adapter.
1378
- def ddl_transaction(migration)
1408
+ def ddl_transaction(migration, &block)
1379
1409
  if use_transaction?(migration)
1380
- Base.transaction { yield }
1410
+ Base.transaction(&block)
1381
1411
  else
1382
1412
  yield
1383
1413
  end
@@ -1408,12 +1438,12 @@ module ActiveRecord
1408
1438
  end
1409
1439
  end
1410
1440
 
1411
- def with_advisory_lock_connection
1441
+ def with_advisory_lock_connection(&block)
1412
1442
  pool = ActiveRecord::ConnectionAdapters::ConnectionHandler.new.establish_connection(
1413
1443
  ActiveRecord::Base.connection_db_config
1414
1444
  )
1415
1445
 
1416
- pool.with_connection { |connection| yield(connection) }
1446
+ pool.with_connection(&block)
1417
1447
  ensure
1418
1448
  pool&.disconnect!
1419
1449
  end