activerecord 7.0.8.7 → 7.1.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1339 -1572
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +15 -16
  5. data/lib/active_record/aggregations.rb +16 -13
  6. data/lib/active_record/association_relation.rb +1 -1
  7. data/lib/active_record/associations/association.rb +18 -3
  8. data/lib/active_record/associations/association_scope.rb +16 -9
  9. data/lib/active_record/associations/belongs_to_association.rb +14 -6
  10. data/lib/active_record/associations/builder/association.rb +3 -3
  11. data/lib/active_record/associations/builder/belongs_to.rb +21 -8
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
  13. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  14. data/lib/active_record/associations/collection_association.rb +17 -9
  15. data/lib/active_record/associations/collection_proxy.rb +16 -11
  16. data/lib/active_record/associations/foreign_association.rb +10 -3
  17. data/lib/active_record/associations/has_many_association.rb +20 -13
  18. data/lib/active_record/associations/has_many_through_association.rb +10 -6
  19. data/lib/active_record/associations/has_one_association.rb +10 -3
  20. data/lib/active_record/associations/join_dependency.rb +10 -8
  21. data/lib/active_record/associations/preloader/association.rb +27 -6
  22. data/lib/active_record/associations/preloader.rb +12 -9
  23. data/lib/active_record/associations/singular_association.rb +1 -1
  24. data/lib/active_record/associations/through_association.rb +22 -11
  25. data/lib/active_record/associations.rb +193 -97
  26. data/lib/active_record/attribute_assignment.rb +0 -2
  27. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  28. data/lib/active_record/attribute_methods/dirty.rb +40 -26
  29. data/lib/active_record/attribute_methods/primary_key.rb +76 -24
  30. data/lib/active_record/attribute_methods/query.rb +28 -16
  31. data/lib/active_record/attribute_methods/read.rb +18 -5
  32. data/lib/active_record/attribute_methods/serialization.rb +150 -31
  33. data/lib/active_record/attribute_methods/write.rb +3 -3
  34. data/lib/active_record/attribute_methods.rb +105 -21
  35. data/lib/active_record/attributes.rb +3 -3
  36. data/lib/active_record/autosave_association.rb +55 -9
  37. data/lib/active_record/base.rb +7 -2
  38. data/lib/active_record/callbacks.rb +10 -24
  39. data/lib/active_record/coders/column_serializer.rb +61 -0
  40. data/lib/active_record/coders/json.rb +1 -1
  41. data/lib/active_record/coders/yaml_column.rb +70 -42
  42. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +163 -88
  43. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  44. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
  45. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +63 -43
  46. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  47. data/lib/active_record/connection_adapters/abstract/database_statements.rb +109 -32
  48. data/lib/active_record/connection_adapters/abstract/query_cache.rb +60 -22
  49. data/lib/active_record/connection_adapters/abstract/quoting.rb +41 -6
  50. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  51. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  52. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
  53. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +289 -122
  54. data/lib/active_record/connection_adapters/abstract/transaction.rb +280 -58
  55. data/lib/active_record/connection_adapters/abstract_adapter.rb +502 -91
  56. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +200 -108
  57. data/lib/active_record/connection_adapters/column.rb +9 -0
  58. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  59. data/lib/active_record/connection_adapters/mysql/database_statements.rb +22 -143
  60. data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -12
  61. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  62. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
  63. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  64. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +17 -12
  65. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
  66. data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
  67. data/lib/active_record/connection_adapters/pool_config.rb +14 -5
  68. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  69. data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
  70. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +76 -29
  71. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  72. data/lib/active_record/connection_adapters/postgresql/quoting.rb +9 -6
  73. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  74. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  75. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
  76. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +42 -0
  77. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +351 -54
  78. data/lib/active_record/connection_adapters/postgresql_adapter.rb +336 -168
  79. data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
  80. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  81. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +42 -36
  82. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +4 -3
  83. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +1 -0
  84. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +26 -7
  85. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +162 -77
  86. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  87. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
  88. data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
  89. data/lib/active_record/connection_adapters.rb +3 -1
  90. data/lib/active_record/connection_handling.rb +71 -94
  91. data/lib/active_record/core.rb +128 -138
  92. data/lib/active_record/counter_cache.rb +46 -25
  93. data/lib/active_record/database_configurations/database_config.rb +9 -3
  94. data/lib/active_record/database_configurations/hash_config.rb +22 -12
  95. data/lib/active_record/database_configurations/url_config.rb +17 -11
  96. data/lib/active_record/database_configurations.rb +86 -33
  97. data/lib/active_record/delegated_type.rb +8 -3
  98. data/lib/active_record/deprecator.rb +7 -0
  99. data/lib/active_record/destroy_association_async_job.rb +2 -0
  100. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  101. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  102. data/lib/active_record/encryption/config.rb +25 -1
  103. data/lib/active_record/encryption/configurable.rb +12 -19
  104. data/lib/active_record/encryption/context.rb +10 -3
  105. data/lib/active_record/encryption/contexts.rb +5 -1
  106. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  107. data/lib/active_record/encryption/encryptable_record.rb +36 -18
  108. data/lib/active_record/encryption/encrypted_attribute_type.rb +17 -6
  109. data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -54
  110. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +2 -2
  111. data/lib/active_record/encryption/key_generator.rb +12 -1
  112. data/lib/active_record/encryption/message_serializer.rb +2 -0
  113. data/lib/active_record/encryption/properties.rb +3 -3
  114. data/lib/active_record/encryption/scheme.rb +19 -22
  115. data/lib/active_record/encryption.rb +1 -0
  116. data/lib/active_record/enum.rb +113 -26
  117. data/lib/active_record/errors.rb +89 -15
  118. data/lib/active_record/explain.rb +23 -3
  119. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  120. data/lib/active_record/fixture_set/render_context.rb +2 -0
  121. data/lib/active_record/fixture_set/table_row.rb +29 -8
  122. data/lib/active_record/fixtures.rb +119 -71
  123. data/lib/active_record/future_result.rb +30 -5
  124. data/lib/active_record/gem_version.rb +4 -4
  125. data/lib/active_record/inheritance.rb +30 -16
  126. data/lib/active_record/insert_all.rb +55 -8
  127. data/lib/active_record/integration.rb +8 -8
  128. data/lib/active_record/internal_metadata.rb +118 -30
  129. data/lib/active_record/locking/pessimistic.rb +5 -2
  130. data/lib/active_record/log_subscriber.rb +29 -12
  131. data/lib/active_record/marshalling.rb +56 -0
  132. data/lib/active_record/message_pack.rb +124 -0
  133. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  134. data/lib/active_record/middleware/database_selector.rb +5 -7
  135. data/lib/active_record/middleware/shard_selector.rb +3 -1
  136. data/lib/active_record/migration/command_recorder.rb +100 -4
  137. data/lib/active_record/migration/compatibility.rb +131 -5
  138. data/lib/active_record/migration/default_strategy.rb +23 -0
  139. data/lib/active_record/migration/execution_strategy.rb +19 -0
  140. data/lib/active_record/migration.rb +213 -109
  141. data/lib/active_record/model_schema.rb +47 -27
  142. data/lib/active_record/nested_attributes.rb +28 -3
  143. data/lib/active_record/normalization.rb +158 -0
  144. data/lib/active_record/persistence.rb +183 -33
  145. data/lib/active_record/promise.rb +84 -0
  146. data/lib/active_record/query_cache.rb +3 -21
  147. data/lib/active_record/query_logs.rb +77 -52
  148. data/lib/active_record/query_logs_formatter.rb +41 -0
  149. data/lib/active_record/querying.rb +15 -2
  150. data/lib/active_record/railtie.rb +107 -45
  151. data/lib/active_record/railties/controller_runtime.rb +10 -5
  152. data/lib/active_record/railties/databases.rake +139 -145
  153. data/lib/active_record/railties/job_runtime.rb +23 -0
  154. data/lib/active_record/readonly_attributes.rb +32 -5
  155. data/lib/active_record/reflection.rb +169 -45
  156. data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
  157. data/lib/active_record/relation/batches.rb +190 -61
  158. data/lib/active_record/relation/calculations.rb +152 -63
  159. data/lib/active_record/relation/delegation.rb +22 -8
  160. data/lib/active_record/relation/finder_methods.rb +85 -15
  161. data/lib/active_record/relation/merger.rb +2 -0
  162. data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -2
  163. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  164. data/lib/active_record/relation/predicate_builder.rb +26 -14
  165. data/lib/active_record/relation/query_attribute.rb +2 -1
  166. data/lib/active_record/relation/query_methods.rb +351 -62
  167. data/lib/active_record/relation/spawn_methods.rb +18 -1
  168. data/lib/active_record/relation.rb +76 -35
  169. data/lib/active_record/result.rb +19 -5
  170. data/lib/active_record/runtime_registry.rb +10 -1
  171. data/lib/active_record/sanitization.rb +51 -11
  172. data/lib/active_record/schema.rb +2 -3
  173. data/lib/active_record/schema_dumper.rb +41 -7
  174. data/lib/active_record/schema_migration.rb +68 -33
  175. data/lib/active_record/scoping/default.rb +15 -5
  176. data/lib/active_record/scoping/named.rb +2 -2
  177. data/lib/active_record/scoping.rb +2 -1
  178. data/lib/active_record/secure_password.rb +60 -0
  179. data/lib/active_record/secure_token.rb +21 -3
  180. data/lib/active_record/signed_id.rb +7 -5
  181. data/lib/active_record/store.rb +8 -8
  182. data/lib/active_record/suppressor.rb +3 -1
  183. data/lib/active_record/table_metadata.rb +10 -1
  184. data/lib/active_record/tasks/database_tasks.rb +127 -105
  185. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  186. data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
  187. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -7
  188. data/lib/active_record/test_fixtures.rb +113 -96
  189. data/lib/active_record/timestamp.rb +26 -14
  190. data/lib/active_record/token_for.rb +113 -0
  191. data/lib/active_record/touch_later.rb +11 -6
  192. data/lib/active_record/transactions.rb +36 -10
  193. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  194. data/lib/active_record/type/internal/timezone.rb +7 -2
  195. data/lib/active_record/type/time.rb +4 -0
  196. data/lib/active_record/validations/absence.rb +1 -1
  197. data/lib/active_record/validations/numericality.rb +5 -4
  198. data/lib/active_record/validations/presence.rb +5 -28
  199. data/lib/active_record/validations/uniqueness.rb +47 -2
  200. data/lib/active_record/validations.rb +8 -4
  201. data/lib/active_record/version.rb +1 -1
  202. data/lib/active_record.rb +121 -16
  203. data/lib/arel/errors.rb +10 -0
  204. data/lib/arel/factory_methods.rb +4 -0
  205. data/lib/arel/nodes/binary.rb +6 -1
  206. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  207. data/lib/arel/nodes/cte.rb +36 -0
  208. data/lib/arel/nodes/fragments.rb +35 -0
  209. data/lib/arel/nodes/homogeneous_in.rb +0 -8
  210. data/lib/arel/nodes/leading_join.rb +8 -0
  211. data/lib/arel/nodes/node.rb +111 -2
  212. data/lib/arel/nodes/sql_literal.rb +6 -0
  213. data/lib/arel/nodes/table_alias.rb +4 -0
  214. data/lib/arel/nodes.rb +4 -0
  215. data/lib/arel/predications.rb +2 -0
  216. data/lib/arel/table.rb +9 -5
  217. data/lib/arel/visitors/mysql.rb +8 -1
  218. data/lib/arel/visitors/to_sql.rb +81 -17
  219. data/lib/arel/visitors/visitor.rb +2 -2
  220. data/lib/arel.rb +16 -2
  221. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  222. data/lib/rails/generators/active_record/migration.rb +3 -1
  223. data/lib/rails/generators/active_record/model/USAGE +113 -0
  224. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  225. metadata +52 -17
  226. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  227. data/lib/active_record/null_relation.rb +0 -63
@@ -33,26 +33,94 @@ module ActiveRecord
33
33
  Base.instance_methods +
34
34
  Base.private_instance_methods -
35
35
  Base.superclass.instance_methods -
36
- Base.superclass.private_instance_methods
36
+ Base.superclass.private_instance_methods +
37
+ %i[__id__ dup freeze frozen? hash object_id class clone]
37
38
  ).map { |m| -m.to_s }.to_set.freeze
38
39
  end
39
40
  end
40
41
 
41
42
  module ClassMethods
42
- def inherited(child_class) # :nodoc:
43
- child_class.initialize_generated_modules
44
- super
45
- end
46
-
47
43
  def initialize_generated_modules # :nodoc:
48
44
  @generated_attribute_methods = const_set(:GeneratedAttributeMethods, GeneratedAttributeMethods.new)
49
45
  private_constant :GeneratedAttributeMethods
50
46
  @attribute_methods_generated = false
47
+ @alias_attributes_mass_generated = false
51
48
  include @generated_attribute_methods
52
49
 
53
50
  super
54
51
  end
55
52
 
53
+ def alias_attribute(new_name, old_name)
54
+ super
55
+
56
+ if @alias_attributes_mass_generated
57
+ ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |code_generator|
58
+ generate_alias_attribute_methods(code_generator, new_name, old_name)
59
+ end
60
+ end
61
+ end
62
+
63
+ def eagerly_generate_alias_attribute_methods(_new_name, _old_name) # :nodoc:
64
+ # alias attributes in Active Record are lazily generated
65
+ end
66
+
67
+ def generate_alias_attributes # :nodoc:
68
+ superclass.generate_alias_attributes unless superclass == Base
69
+ return if @alias_attributes_mass_generated
70
+
71
+ generated_attribute_methods.synchronize do
72
+ return if @alias_attributes_mass_generated
73
+ ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |code_generator|
74
+ aliases_by_attribute_name.each do |old_name, new_names|
75
+ new_names.each do |new_name|
76
+ generate_alias_attribute_methods(code_generator, new_name, old_name)
77
+ end
78
+ end
79
+ end
80
+
81
+ @alias_attributes_mass_generated = true
82
+ end
83
+ end
84
+
85
+ def alias_attribute_method_definition(code_generator, pattern, new_name, old_name)
86
+ method_name = pattern.method_name(new_name).to_s
87
+ target_name = pattern.method_name(old_name).to_s
88
+ parameters = pattern.parameters
89
+ old_name = old_name.to_s
90
+
91
+ method_defined = method_defined?(target_name) || private_method_defined?(target_name)
92
+ manually_defined = method_defined &&
93
+ !self.instance_method(target_name).owner.is_a?(GeneratedAttributeMethods)
94
+ reserved_method_name = ::ActiveRecord::AttributeMethods.dangerous_attribute_methods.include?(target_name)
95
+
96
+ if !abstract_class? && !has_attribute?(old_name)
97
+ # We only need to issue this deprecation warning once, so we issue it when defining the original reader method.
98
+ should_warn = target_name == old_name
99
+ if should_warn
100
+ ActiveRecord.deprecator.warn(
101
+ "#{self} model aliases `#{old_name}`, but `#{old_name}` is not an attribute. " \
102
+ "Starting in Rails 7.2, alias_attribute with non-attribute targets will raise. " \
103
+ "Use `alias_method :#{new_name}, :#{old_name}` or define the method manually."
104
+ )
105
+ end
106
+ super
107
+ elsif manually_defined && !reserved_method_name
108
+ aliased_method_redefined_as_well = method_defined_within?(method_name, self)
109
+ return if aliased_method_redefined_as_well
110
+
111
+ ActiveRecord.deprecator.warn(
112
+ "#{self} model aliases `#{old_name}` and has a method called `#{target_name}` defined. " \
113
+ "Starting in Rails 7.2 `#{method_name}` will not be calling `#{target_name}` anymore. " \
114
+ "You may want to additionally define `#{method_name}` to preserve the current behavior."
115
+ )
116
+ super
117
+ else
118
+ define_proxy_call(code_generator, method_name, pattern.proxy_target, parameters, old_name,
119
+ namespace: :proxy_alias_attribute
120
+ )
121
+ end
122
+ end
123
+
56
124
  # Generates all the attribute related methods for columns in the database
57
125
  # accessors, mutators and query methods.
58
126
  def define_attribute_methods # :nodoc:
@@ -71,6 +139,7 @@ module ActiveRecord
71
139
  generated_attribute_methods.synchronize do
72
140
  super if defined?(@attribute_methods_generated) && @attribute_methods_generated
73
141
  @attribute_methods_generated = false
142
+ @alias_attributes_mass_generated = false
74
143
  end
75
144
  end
76
145
 
@@ -186,6 +255,16 @@ module ActiveRecord
186
255
  def _has_attribute?(attr_name) # :nodoc:
187
256
  attribute_types.key?(attr_name)
188
257
  end
258
+
259
+ private
260
+ def inherited(child_class)
261
+ super
262
+ child_class.initialize_generated_modules
263
+ child_class.class_eval do
264
+ @alias_attributes_mass_generated = false
265
+ @attribute_names = nil
266
+ end
267
+ end
189
268
  end
190
269
 
191
270
  # A Person object with a name attribute can ask <tt>person.respond_to?(:name)</tt>,
@@ -309,36 +388,40 @@ module ActiveRecord
309
388
  !value.nil? && !(value.respond_to?(:empty?) && value.empty?)
310
389
  end
311
390
 
312
- # Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
313
- # "2004-12-12" in a date column is cast to a date object, like <tt>Date.new(2004, 12, 12)</tt>). It raises
314
- # ActiveModel::MissingAttributeError if the identified attribute is missing.
315
- #
316
- # Note: +:id+ is always present.
391
+ # Returns the value of the attribute identified by +attr_name+ after it has
392
+ # been type cast. (For information about specific type casting behavior, see
393
+ # the types under ActiveModel::Type.)
317
394
  #
318
395
  # class Person < ActiveRecord::Base
319
396
  # belongs_to :organization
320
397
  # end
321
398
  #
322
- # person = Person.new(name: 'Francesco', age: '22')
323
- # person[:name] # => "Francesco"
324
- # person[:age] # => 22
399
+ # person = Person.new(name: "Francesco", date_of_birth: "2004-12-12")
400
+ # person[:name] # => "Francesco"
401
+ # person[:date_of_birth] # => Date.new(2004, 12, 12)
402
+ # person[:organization_id] # => nil
403
+ #
404
+ # Raises ActiveModel::MissingAttributeError if the attribute is missing.
405
+ # Note, however, that the +id+ attribute will never be considered missing.
325
406
  #
326
- # person = Person.select('id').first
327
- # person[:name] # => ActiveModel::MissingAttributeError: missing attribute: name
328
- # person[:organization_id] # => ActiveModel::MissingAttributeError: missing attribute: organization_id
407
+ # person = Person.select(:name).first
408
+ # person[:name] # => "Francesco"
409
+ # person[:date_of_birth] # => ActiveModel::MissingAttributeError: missing attribute 'date_of_birth' for Person
410
+ # person[:organization_id] # => ActiveModel::MissingAttributeError: missing attribute 'organization_id' for Person
411
+ # person[:id] # => nil
329
412
  def [](attr_name)
330
413
  read_attribute(attr_name) { |n| missing_attribute(n, caller) }
331
414
  end
332
415
 
333
- # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
416
+ # Updates the attribute identified by +attr_name+ using the specified
417
+ # +value+. The attribute value will be type cast upon being read.
334
418
  #
335
419
  # class Person < ActiveRecord::Base
336
420
  # end
337
421
  #
338
422
  # person = Person.new
339
- # person[:age] = '22'
340
- # person[:age] # => 22
341
- # person[:age].class # => Integer
423
+ # person[:date_of_birth] = "2004-12-12"
424
+ # person[:date_of_birth] # => Date.new(2004, 12, 12)
342
425
  def []=(attr_name, value)
343
426
  write_attribute(attr_name, value)
344
427
  end
@@ -390,6 +473,7 @@ module ActiveRecord
390
473
  attribute_names &= self.class.column_names
391
474
  attribute_names.delete_if do |name|
392
475
  self.class.readonly_attribute?(name) ||
476
+ self.class.counter_cache_column?(name) ||
393
477
  column_for_attribute(name).virtual?
394
478
  end
395
479
  end
@@ -10,7 +10,7 @@ module ActiveRecord
10
10
  included do
11
11
  class_attribute :attributes_to_define_after_schema_loads, instance_accessor: false, default: {} # :internal:
12
12
  end
13
-
13
+ # = Active Record \Attributes
14
14
  module ClassMethods
15
15
  # Defines an attribute with a type on this model. It will override the
16
16
  # type of existing attributes if needed. This allows control over how
@@ -194,10 +194,10 @@ module ActiveRecord
194
194
  # end
195
195
  #
196
196
  # Product.where(price_in_bitcoins: Money.new(5, "USD"))
197
- # # => SELECT * FROM products WHERE price_in_bitcoins = 0.02230
197
+ # # SELECT * FROM products WHERE price_in_bitcoins = 0.02230
198
198
  #
199
199
  # Product.where(price_in_bitcoins: Money.new(5, "GBP"))
200
- # # => SELECT * FROM products WHERE price_in_bitcoins = 0.03412
200
+ # # SELECT * FROM products WHERE price_in_bitcoins = 0.03412
201
201
  #
202
202
  # ==== Dirty Tracking
203
203
  #
@@ -26,7 +26,7 @@ module ActiveRecord
26
26
  #
27
27
  # Child records are validated unless <tt>:validate</tt> is +false+.
28
28
  #
29
- # == Callbacks
29
+ # == \Callbacks
30
30
  #
31
31
  # Association with autosave option defines several callbacks on your
32
32
  # model (around_save, before_save, after_create, after_update). Please note that
@@ -273,6 +273,11 @@ module ActiveRecord
273
273
  end
274
274
 
275
275
  private
276
+ def init_internals
277
+ super
278
+ @_already_called = nil
279
+ end
280
+
276
281
  # Returns the record for an association collection that should be validated
277
282
  # or saved. If +autosave+ is +false+ only new records will be returned,
278
283
  # unless the parent is/was a new record itself.
@@ -444,11 +449,17 @@ module ActiveRecord
444
449
  if autosave && record.marked_for_destruction?
445
450
  record.destroy
446
451
  elsif autosave != false
447
- key = reflection.options[:primary_key] ? public_send(reflection.options[:primary_key]) : id
452
+ primary_key = Array(compute_primary_key(reflection, self)).map(&:to_s)
453
+ primary_key_value = primary_key.map { |key| _read_attribute(key) }
448
454
 
449
- if (autosave && record.changed_for_autosave?) || _record_changed?(reflection, record, key)
455
+ if (autosave && record.changed_for_autosave?) || _record_changed?(reflection, record, primary_key_value)
450
456
  unless reflection.through_reflection
451
- record[reflection.foreign_key] = key
457
+ foreign_key = Array(reflection.foreign_key)
458
+ primary_key_foreign_key_pairs = primary_key.zip(foreign_key)
459
+
460
+ primary_key_foreign_key_pairs.each do |primary_key, foreign_key|
461
+ record[foreign_key] = _read_attribute(primary_key)
462
+ end
452
463
  association.set_inverse_instance(record)
453
464
  end
454
465
 
@@ -463,14 +474,26 @@ module ActiveRecord
463
474
  # If the record is new or it has changed, returns true.
464
475
  def _record_changed?(reflection, record, key)
465
476
  record.new_record? ||
466
- association_foreign_key_changed?(reflection, record, key) ||
477
+ (association_foreign_key_changed?(reflection, record, key) ||
478
+ inverse_polymorphic_association_changed?(reflection, record)) ||
467
479
  record.will_save_change_to_attribute?(reflection.foreign_key)
468
480
  end
469
481
 
470
482
  def association_foreign_key_changed?(reflection, record, key)
471
483
  return false if reflection.through_reflection?
472
484
 
473
- record._has_attribute?(reflection.foreign_key) && record._read_attribute(reflection.foreign_key) != key
485
+ foreign_key = Array(reflection.foreign_key)
486
+ return false unless foreign_key.all? { |key| record._has_attribute?(key) }
487
+
488
+ foreign_key.map { |key| record._read_attribute(key) } != Array(key)
489
+ end
490
+
491
+ def inverse_polymorphic_association_changed?(reflection, record)
492
+ return false unless reflection.inverse_of&.polymorphic?
493
+
494
+ class_name = record._read_attribute(reflection.inverse_of.foreign_type)
495
+
496
+ reflection.active_record != record.class.polymorphic_class_for(class_name)
474
497
  end
475
498
 
476
499
  # Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
@@ -485,14 +508,21 @@ module ActiveRecord
485
508
  autosave = reflection.options[:autosave]
486
509
 
487
510
  if autosave && record.marked_for_destruction?
488
- self[reflection.foreign_key] = nil
511
+ foreign_key = Array(reflection.foreign_key)
512
+ foreign_key.each { |key| self[key] = nil }
489
513
  record.destroy
490
514
  elsif autosave != false
491
515
  saved = record.save(validate: !autosave) if record.new_record? || (autosave && record.changed_for_autosave?)
492
516
 
493
517
  if association.updated?
494
- association_id = record.public_send(reflection.options[:primary_key] || :id)
495
- self[reflection.foreign_key] = association_id
518
+ primary_key = Array(compute_primary_key(reflection, record)).map(&:to_s)
519
+ foreign_key = Array(reflection.foreign_key)
520
+
521
+ primary_key_foreign_key_pairs = primary_key.zip(foreign_key)
522
+ primary_key_foreign_key_pairs.each do |primary_key, foreign_key|
523
+ association_id = record._read_attribute(primary_key)
524
+ self[foreign_key] = association_id unless self[foreign_key] == association_id
525
+ end
496
526
  association.loaded!
497
527
  end
498
528
 
@@ -501,6 +531,22 @@ module ActiveRecord
501
531
  end
502
532
  end
503
533
 
534
+ def compute_primary_key(reflection, record)
535
+ if primary_key_options = reflection.options[:primary_key]
536
+ primary_key_options
537
+ elsif reflection.options[:query_constraints] && (query_constraints = record.class.query_constraints_list)
538
+ query_constraints
539
+ elsif record.class.has_query_constraints? && !reflection.options[:foreign_key]
540
+ record.class.query_constraints_list
541
+ elsif record.class.composite_primary_key?
542
+ # If record has composite primary key of shape [:<tenant_key>, :id], infer primary_key as :id
543
+ primary_key = record.class.primary_key
544
+ primary_key.include?("id") ? "id" : primary_key
545
+ else
546
+ record.class.primary_key
547
+ end
548
+ end
549
+
504
550
  def custom_validation_context?
505
551
  validation_context && [:create, :update].exclude?(validation_context)
506
552
  end
@@ -311,11 +311,12 @@ module ActiveRecord # :nodoc:
311
311
  include Attributes
312
312
  include Locking::Optimistic
313
313
  include Locking::Pessimistic
314
+ include Encryption::EncryptableRecord
314
315
  include AttributeMethods
315
316
  include Callbacks
316
317
  include Timestamp
317
318
  include Associations
318
- include ActiveModel::SecurePassword
319
+ include SecurePassword
319
320
  include AutosaveAssociation
320
321
  include NestedAttributes
321
322
  include Transactions
@@ -325,9 +326,13 @@ module ActiveRecord # :nodoc:
325
326
  include Serialization
326
327
  include Store
327
328
  include SecureToken
329
+ include TokenFor
328
330
  include SignedId
329
331
  include Suppressor
330
- include Encryption::EncryptableRecord
332
+ include Normalization
333
+ include Marshalling::Methods
334
+
335
+ self.param_delimiter = "_"
331
336
  end
332
337
 
333
338
  ActiveSupport.run_load_hooks(:active_record, Base)
@@ -84,7 +84,7 @@ module ActiveRecord
84
84
  # == Types of callbacks
85
85
  #
86
86
  # There are three types of callbacks accepted by the callback macros: method references (symbol), callback objects,
87
- # inline methods (using a proc). Method references and callback objects are the recommended approaches,
87
+ # inline methods (using a proc). \Method references and callback objects are the recommended approaches,
88
88
  # inline methods using a proc are sometimes appropriate (such as for creating mix-ins).
89
89
  #
90
90
  # The method reference callbacks work by specifying a protected or private method available in the object, like this:
@@ -173,7 +173,7 @@ module ActiveRecord
173
173
  #
174
174
  # If a <tt>before_*</tt> callback throws +:abort+, all the later callbacks and
175
175
  # the associated action are cancelled.
176
- # Callbacks are generally run in the order they are defined, with the exception of callbacks defined as
176
+ # \Callbacks are generally run in the order they are defined, with the exception of callbacks defined as
177
177
  # methods on the model, which are called last.
178
178
  #
179
179
  # == Ordering callbacks
@@ -234,30 +234,16 @@ module ActiveRecord
234
234
  # end
235
235
  #
236
236
  # In this case the +log_children+ is executed before +do_something_else+.
237
- # The same applies to all non-transactional callbacks.
237
+ # This applies to all non-transactional callbacks, and to +before_commit+.
238
238
  #
239
- # As seen below, in case there are multiple transactional callbacks the order
240
- # is reversed.
239
+ # For transactional +after_+ callbacks (+after_commit+, +after_rollback+, etc), the order
240
+ # can be set via configuration.
241
241
  #
242
- # For example:
243
- #
244
- # class Topic < ActiveRecord::Base
245
- # has_many :children
246
- #
247
- # after_commit :log_children
248
- # after_commit :do_something_else
249
- #
250
- # private
251
- # def log_children
252
- # # Child processing
253
- # end
254
- #
255
- # def do_something_else
256
- # # Something else
257
- # end
258
- # end
242
+ # config.active_record.run_after_transaction_callbacks_in_order_defined = false
259
243
  #
260
- # In this case the +do_something_else+ is executed before +log_children+.
244
+ # If +true+ (the default from \Rails 7.1), callbacks are executed in the order they
245
+ # are defined, just like the example above. If +false+, the order is reversed, so
246
+ # +do_something_else+ is executed before +log_children+.
261
247
  #
262
248
  # == \Transactions
263
249
  #
@@ -460,7 +446,7 @@ module ActiveRecord
460
446
  end
461
447
 
462
448
  def _update_record
463
- _run_update_callbacks { super }
449
+ _run_update_callbacks { record_update_timestamps { super } }
464
450
  end
465
451
  end
466
452
  end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Coders # :nodoc:
5
+ class ColumnSerializer # :nodoc:
6
+ attr_reader :object_class
7
+ attr_reader :coder
8
+
9
+ def initialize(attr_name, coder, object_class = Object)
10
+ @attr_name = attr_name
11
+ @object_class = object_class
12
+ @coder = coder
13
+ check_arity_of_constructor
14
+ end
15
+
16
+ def init_with(coder) # :nodoc:
17
+ @attr_name = coder["attr_name"]
18
+ @object_class = coder["object_class"]
19
+ @coder = coder["coder"]
20
+ end
21
+
22
+ def dump(object)
23
+ return if object.nil?
24
+
25
+ assert_valid_value(object, action: "dump")
26
+ coder.dump(object)
27
+ end
28
+
29
+ def load(payload)
30
+ if payload.nil?
31
+ if @object_class != ::Object
32
+ return @object_class.new
33
+ end
34
+ return nil
35
+ end
36
+
37
+ object = coder.load(payload)
38
+
39
+ assert_valid_value(object, action: "load")
40
+ object ||= object_class.new if object_class != Object
41
+
42
+ object
43
+ end
44
+
45
+ # Public because it's called by Type::Serialized
46
+ def assert_valid_value(object, action:)
47
+ unless object.nil? || object_class === object
48
+ raise SerializationTypeMismatch,
49
+ "can't #{action} `#{@attr_name}`: was supposed to be a #{object_class}, but was a #{object.class}. -- #{object.inspect}"
50
+ end
51
+ end
52
+
53
+ private
54
+ def check_arity_of_constructor
55
+ load(nil)
56
+ rescue ArgumentError
57
+ raise ArgumentError, "Cannot serialize #{object_class}. Classes passed to `serialize` must have a 0 argument constructor."
58
+ end
59
+ end
60
+ end
61
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module Coders # :nodoc:
5
- class JSON # :nodoc:
5
+ module JSON # :nodoc:
6
6
  def self.dump(obj)
7
7
  ActiveSupport::JSON.encode(obj)
8
8
  end
@@ -4,37 +4,83 @@ require "yaml"
4
4
 
5
5
  module ActiveRecord
6
6
  module Coders # :nodoc:
7
- class YAMLColumn # :nodoc:
8
- attr_accessor :object_class
9
-
10
- def initialize(attr_name, object_class = Object)
11
- @attr_name = attr_name
12
- @object_class = object_class
13
- check_arity_of_constructor
14
- end
7
+ class YAMLColumn < ColumnSerializer # :nodoc:
8
+ class SafeCoder
9
+ def initialize(permitted_classes: [], unsafe_load: nil)
10
+ @permitted_classes = permitted_classes
11
+ @unsafe_load = unsafe_load
12
+ end
15
13
 
16
- def dump(obj)
17
- return if obj.nil?
14
+ if Gem::Version.new(Psych::VERSION) >= Gem::Version.new("5.1")
15
+ def dump(object)
16
+ if @unsafe_load.nil? ? ActiveRecord.use_yaml_unsafe_load : @unsafe_load
17
+ ::YAML.dump(object)
18
+ else
19
+ ::YAML.safe_dump(
20
+ object,
21
+ permitted_classes: @permitted_classes + ActiveRecord.yaml_column_permitted_classes,
22
+ aliases: true,
23
+ )
24
+ end
25
+ end
26
+ else
27
+ def dump(object)
28
+ YAML.dump(object)
29
+ end
30
+ end
18
31
 
19
- assert_valid_value(obj, action: "dump")
20
- YAML.dump obj
32
+ if YAML.respond_to?(:unsafe_load)
33
+ def load(payload)
34
+ if @unsafe_load.nil? ? ActiveRecord.use_yaml_unsafe_load : @unsafe_load
35
+ YAML.unsafe_load(payload)
36
+ else
37
+ YAML.safe_load(
38
+ payload,
39
+ permitted_classes: @permitted_classes + ActiveRecord.yaml_column_permitted_classes,
40
+ aliases: true,
41
+ )
42
+ end
43
+ end
44
+ else
45
+ def load(payload)
46
+ if @unsafe_load.nil? ? ActiveRecord.use_yaml_unsafe_load : @unsafe_load
47
+ YAML.load(payload)
48
+ else
49
+ YAML.safe_load(
50
+ payload,
51
+ permitted_classes: @permitted_classes + ActiveRecord.yaml_column_permitted_classes,
52
+ aliases: true,
53
+ )
54
+ end
55
+ end
56
+ end
21
57
  end
22
58
 
23
- def load(yaml)
24
- return object_class.new if object_class != Object && yaml.nil?
25
- return yaml unless yaml.is_a?(String) && yaml.start_with?("---")
26
- obj = yaml_load(yaml)
27
-
28
- assert_valid_value(obj, action: "load")
29
- obj ||= object_class.new if object_class != Object
59
+ def initialize(attr_name, object_class = Object, permitted_classes: [], unsafe_load: nil)
60
+ super(
61
+ attr_name,
62
+ SafeCoder.new(permitted_classes: permitted_classes || [], unsafe_load: unsafe_load),
63
+ object_class,
64
+ )
65
+ check_arity_of_constructor
66
+ end
30
67
 
31
- obj
68
+ def init_with(coder) # :nodoc:
69
+ unless coder["coder"]
70
+ permitted_classes = coder["permitted_classes"] || []
71
+ unsafe_load = coder["unsafe_load"] || false
72
+ coder["coder"] = SafeCoder.new(permitted_classes: permitted_classes, unsafe_load: unsafe_load)
73
+ end
74
+ super(coder)
32
75
  end
33
76
 
34
- def assert_valid_value(obj, action:)
35
- unless obj.nil? || obj.is_a?(object_class)
36
- raise SerializationTypeMismatch,
37
- "can't #{action} `#{@attr_name}`: was supposed to be a #{object_class}, but was a #{obj.class}. -- #{obj.inspect}"
77
+ def coder
78
+ # This is to retain forward compatibility when loading records serialized with Marshal
79
+ # from a previous version of Rails.
80
+ @coder ||= begin
81
+ permitted_classes = defined?(@permitted_classes) ? @permitted_classes : []
82
+ unsafe_load = defined?(@unsafe_load) && @unsafe_load.nil?
83
+ SafeCoder.new(permitted_classes: permitted_classes, unsafe_load: unsafe_load)
38
84
  end
39
85
  end
40
86
 
@@ -44,24 +90,6 @@ module ActiveRecord
44
90
  rescue ArgumentError
45
91
  raise ArgumentError, "Cannot serialize #{object_class}. Classes passed to `serialize` must have a 0 argument constructor."
46
92
  end
47
-
48
- if YAML.respond_to?(:unsafe_load)
49
- def yaml_load(payload)
50
- if ActiveRecord.use_yaml_unsafe_load
51
- YAML.unsafe_load(payload)
52
- else
53
- YAML.safe_load(payload, permitted_classes: ActiveRecord.yaml_column_permitted_classes, aliases: true)
54
- end
55
- end
56
- else
57
- def yaml_load(payload)
58
- if ActiveRecord.use_yaml_unsafe_load
59
- YAML.load(payload)
60
- else
61
- YAML.safe_load(payload, permitted_classes: ActiveRecord.yaml_column_permitted_classes, aliases: true)
62
- end
63
- end
64
- end
65
93
  end
66
94
  end
67
95
  end