activerecord 7.1.3.2 → 7.2.1

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 (191) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +570 -2094
  3. data/README.rdoc +15 -15
  4. data/examples/performance.rb +2 -2
  5. data/lib/active_record/association_relation.rb +1 -1
  6. data/lib/active_record/associations/alias_tracker.rb +25 -19
  7. data/lib/active_record/associations/association.rb +15 -8
  8. data/lib/active_record/associations/belongs_to_association.rb +18 -11
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  10. data/lib/active_record/associations/builder/belongs_to.rb +1 -0
  11. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
  12. data/lib/active_record/associations/builder/has_many.rb +3 -4
  13. data/lib/active_record/associations/builder/has_one.rb +3 -4
  14. data/lib/active_record/associations/collection_association.rb +11 -5
  15. data/lib/active_record/associations/collection_proxy.rb +14 -1
  16. data/lib/active_record/associations/errors.rb +265 -0
  17. data/lib/active_record/associations/has_many_association.rb +3 -3
  18. data/lib/active_record/associations/has_one_association.rb +2 -2
  19. data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
  20. data/lib/active_record/associations/join_dependency.rb +6 -8
  21. data/lib/active_record/associations/nested_error.rb +47 -0
  22. data/lib/active_record/associations/preloader/association.rb +2 -1
  23. data/lib/active_record/associations/preloader/branch.rb +7 -1
  24. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  25. data/lib/active_record/associations/singular_association.rb +6 -0
  26. data/lib/active_record/associations/through_association.rb +1 -1
  27. data/lib/active_record/associations.rb +34 -273
  28. data/lib/active_record/attribute_assignment.rb +1 -11
  29. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  30. data/lib/active_record/attribute_methods/dirty.rb +2 -2
  31. data/lib/active_record/attribute_methods/primary_key.rb +23 -55
  32. data/lib/active_record/attribute_methods/read.rb +4 -16
  33. data/lib/active_record/attribute_methods/serialization.rb +4 -24
  34. data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -6
  35. data/lib/active_record/attribute_methods/write.rb +3 -3
  36. data/lib/active_record/attribute_methods.rb +89 -58
  37. data/lib/active_record/attributes.rb +60 -45
  38. data/lib/active_record/autosave_association.rb +17 -31
  39. data/lib/active_record/base.rb +2 -3
  40. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +24 -107
  41. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -0
  42. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +244 -58
  43. data/lib/active_record/connection_adapters/abstract/database_statements.rb +35 -18
  44. data/lib/active_record/connection_adapters/abstract/query_cache.rb +188 -75
  45. data/lib/active_record/connection_adapters/abstract/quoting.rb +65 -91
  46. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
  47. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +22 -9
  48. data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -62
  49. data/lib/active_record/connection_adapters/abstract_adapter.rb +38 -59
  50. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +69 -19
  51. data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
  52. data/lib/active_record/connection_adapters/mysql/quoting.rb +43 -48
  53. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +8 -1
  54. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +16 -15
  55. data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -32
  56. data/lib/active_record/connection_adapters/pool_config.rb +7 -6
  57. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +27 -4
  58. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  59. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  60. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  61. data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
  62. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +20 -0
  63. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +16 -12
  64. data/lib/active_record/connection_adapters/postgresql_adapter.rb +26 -21
  65. data/lib/active_record/connection_adapters/schema_cache.rb +123 -128
  66. data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
  67. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -6
  68. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +44 -46
  69. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  70. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
  71. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  72. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +25 -2
  73. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +127 -77
  74. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +15 -15
  75. data/lib/active_record/connection_adapters/trilogy_adapter.rb +32 -65
  76. data/lib/active_record/connection_adapters.rb +121 -0
  77. data/lib/active_record/connection_handling.rb +56 -41
  78. data/lib/active_record/core.rb +60 -39
  79. data/lib/active_record/counter_cache.rb +23 -10
  80. data/lib/active_record/database_configurations/connection_url_resolver.rb +7 -2
  81. data/lib/active_record/database_configurations/database_config.rb +19 -4
  82. data/lib/active_record/database_configurations/hash_config.rb +44 -36
  83. data/lib/active_record/database_configurations/url_config.rb +20 -1
  84. data/lib/active_record/database_configurations.rb +1 -1
  85. data/lib/active_record/delegated_type.rb +30 -6
  86. data/lib/active_record/destroy_association_async_job.rb +1 -1
  87. data/lib/active_record/dynamic_matchers.rb +2 -2
  88. data/lib/active_record/encryption/encryptable_record.rb +3 -3
  89. data/lib/active_record/encryption/encrypted_attribute_type.rb +26 -6
  90. data/lib/active_record/encryption/encryptor.rb +18 -3
  91. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  92. data/lib/active_record/encryption/message_serializer.rb +4 -0
  93. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  94. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  95. data/lib/active_record/encryption/scheme.rb +8 -4
  96. data/lib/active_record/enum.rb +26 -6
  97. data/lib/active_record/errors.rb +46 -20
  98. data/lib/active_record/explain.rb +13 -24
  99. data/lib/active_record/fixtures.rb +37 -31
  100. data/lib/active_record/future_result.rb +17 -4
  101. data/lib/active_record/gem_version.rb +3 -3
  102. data/lib/active_record/inheritance.rb +4 -2
  103. data/lib/active_record/insert_all.rb +18 -15
  104. data/lib/active_record/integration.rb +4 -1
  105. data/lib/active_record/internal_metadata.rb +48 -34
  106. data/lib/active_record/locking/optimistic.rb +8 -7
  107. data/lib/active_record/log_subscriber.rb +0 -21
  108. data/lib/active_record/marshalling.rb +1 -1
  109. data/lib/active_record/message_pack.rb +2 -2
  110. data/lib/active_record/migration/command_recorder.rb +2 -3
  111. data/lib/active_record/migration/compatibility.rb +11 -3
  112. data/lib/active_record/migration/default_strategy.rb +4 -5
  113. data/lib/active_record/migration/pending_migration_connection.rb +2 -2
  114. data/lib/active_record/migration.rb +85 -76
  115. data/lib/active_record/model_schema.rb +39 -70
  116. data/lib/active_record/nested_attributes.rb +11 -3
  117. data/lib/active_record/normalization.rb +3 -7
  118. data/lib/active_record/persistence.rb +32 -354
  119. data/lib/active_record/query_cache.rb +18 -6
  120. data/lib/active_record/query_logs.rb +15 -0
  121. data/lib/active_record/query_logs_formatter.rb +1 -1
  122. data/lib/active_record/querying.rb +21 -9
  123. data/lib/active_record/railtie.rb +54 -67
  124. data/lib/active_record/railties/controller_runtime.rb +13 -4
  125. data/lib/active_record/railties/databases.rake +42 -45
  126. data/lib/active_record/reflection.rb +102 -37
  127. data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
  128. data/lib/active_record/relation/batches.rb +14 -8
  129. data/lib/active_record/relation/calculations.rb +95 -62
  130. data/lib/active_record/relation/delegation.rb +8 -11
  131. data/lib/active_record/relation/finder_methods.rb +16 -2
  132. data/lib/active_record/relation/merger.rb +4 -6
  133. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  134. data/lib/active_record/relation/predicate_builder/association_query_value.rb +9 -3
  135. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +6 -1
  136. data/lib/active_record/relation/predicate_builder.rb +3 -3
  137. data/lib/active_record/relation/query_methods.rb +212 -47
  138. data/lib/active_record/relation/record_fetch_warning.rb +3 -0
  139. data/lib/active_record/relation/spawn_methods.rb +2 -18
  140. data/lib/active_record/relation/where_clause.rb +7 -19
  141. data/lib/active_record/relation.rb +500 -66
  142. data/lib/active_record/result.rb +32 -45
  143. data/lib/active_record/runtime_registry.rb +39 -0
  144. data/lib/active_record/sanitization.rb +24 -19
  145. data/lib/active_record/schema.rb +8 -6
  146. data/lib/active_record/schema_dumper.rb +19 -9
  147. data/lib/active_record/schema_migration.rb +30 -14
  148. data/lib/active_record/scoping/named.rb +1 -0
  149. data/lib/active_record/signed_id.rb +20 -1
  150. data/lib/active_record/statement_cache.rb +7 -7
  151. data/lib/active_record/table_metadata.rb +1 -10
  152. data/lib/active_record/tasks/database_tasks.rb +87 -48
  153. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  154. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  155. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -1
  156. data/lib/active_record/test_fixtures.rb +87 -89
  157. data/lib/active_record/testing/query_assertions.rb +121 -0
  158. data/lib/active_record/timestamp.rb +5 -3
  159. data/lib/active_record/token_for.rb +22 -12
  160. data/lib/active_record/touch_later.rb +1 -1
  161. data/lib/active_record/transaction.rb +132 -0
  162. data/lib/active_record/transactions.rb +70 -14
  163. data/lib/active_record/translation.rb +0 -2
  164. data/lib/active_record/type/serialized.rb +1 -3
  165. data/lib/active_record/type_caster/connection.rb +4 -4
  166. data/lib/active_record/validations/associated.rb +9 -3
  167. data/lib/active_record/validations/uniqueness.rb +14 -10
  168. data/lib/active_record/validations.rb +4 -1
  169. data/lib/active_record.rb +150 -41
  170. data/lib/arel/alias_predication.rb +1 -1
  171. data/lib/arel/collectors/bind.rb +2 -0
  172. data/lib/arel/collectors/composite.rb +7 -0
  173. data/lib/arel/collectors/sql_string.rb +1 -1
  174. data/lib/arel/collectors/substitute_binds.rb +1 -1
  175. data/lib/arel/nodes/binary.rb +0 -6
  176. data/lib/arel/nodes/bound_sql_literal.rb +9 -5
  177. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  178. data/lib/arel/nodes/node.rb +4 -3
  179. data/lib/arel/nodes/sql_literal.rb +7 -0
  180. data/lib/arel/nodes.rb +2 -2
  181. data/lib/arel/predications.rb +1 -1
  182. data/lib/arel/select_manager.rb +1 -1
  183. data/lib/arel/tree_manager.rb +8 -3
  184. data/lib/arel/update_manager.rb +2 -1
  185. data/lib/arel/visitors/dot.rb +1 -0
  186. data/lib/arel/visitors/mysql.rb +9 -4
  187. data/lib/arel/visitors/postgresql.rb +1 -12
  188. data/lib/arel/visitors/to_sql.rb +31 -17
  189. data/lib/arel.rb +7 -3
  190. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  191. metadata +18 -12
@@ -1,268 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecord
4
- class AssociationNotFoundError < ConfigurationError # :nodoc:
5
- attr_reader :record, :association_name
6
-
7
- def initialize(record = nil, association_name = nil)
8
- @record = record
9
- @association_name = association_name
10
- if record && association_name
11
- super("Association named '#{association_name}' was not found on #{record.class.name}; perhaps you misspelled it?")
12
- else
13
- super("Association was not found.")
14
- end
15
- end
16
-
17
- if defined?(DidYouMean::Correctable) && defined?(DidYouMean::SpellChecker)
18
- include DidYouMean::Correctable
19
-
20
- def corrections
21
- if record && association_name
22
- @corrections ||= begin
23
- maybe_these = record.class.reflections.keys
24
- DidYouMean::SpellChecker.new(dictionary: maybe_these).correct(association_name)
25
- end
26
- else
27
- []
28
- end
29
- end
30
- end
31
- end
32
-
33
- class InverseOfAssociationNotFoundError < ActiveRecordError # :nodoc:
34
- attr_reader :reflection, :associated_class
35
-
36
- def initialize(reflection = nil, associated_class = nil)
37
- if reflection
38
- @reflection = reflection
39
- @associated_class = associated_class.nil? ? reflection.klass : associated_class
40
- super("Could not find the inverse association for #{reflection.name} (#{reflection.options[:inverse_of].inspect} in #{associated_class.nil? ? reflection.class_name : associated_class.name})")
41
- else
42
- super("Could not find the inverse association.")
43
- end
44
- end
45
-
46
- if defined?(DidYouMean::Correctable) && defined?(DidYouMean::SpellChecker)
47
- include DidYouMean::Correctable
48
-
49
- def corrections
50
- if reflection && associated_class
51
- @corrections ||= begin
52
- maybe_these = associated_class.reflections.keys
53
- DidYouMean::SpellChecker.new(dictionary: maybe_these).correct(reflection.options[:inverse_of].to_s)
54
- end
55
- else
56
- []
57
- end
58
- end
59
- end
60
- end
61
-
62
- class InverseOfAssociationRecursiveError < ActiveRecordError # :nodoc:
63
- attr_reader :reflection
64
- def initialize(reflection = nil)
65
- if reflection
66
- @reflection = reflection
67
- super("Inverse association #{reflection.name} (#{reflection.options[:inverse_of].inspect} in #{reflection.class_name}) is recursive.")
68
- else
69
- super("Inverse association is recursive.")
70
- end
71
- end
72
- end
73
-
74
- class HasManyThroughAssociationNotFoundError < ActiveRecordError # :nodoc:
75
- attr_reader :owner_class, :reflection
76
-
77
- def initialize(owner_class = nil, reflection = nil)
78
- if owner_class && reflection
79
- @owner_class = owner_class
80
- @reflection = reflection
81
- super("Could not find the association #{reflection.options[:through].inspect} in model #{owner_class.name}")
82
- else
83
- super("Could not find the association.")
84
- end
85
- end
86
-
87
- if defined?(DidYouMean::Correctable) && defined?(DidYouMean::SpellChecker)
88
- include DidYouMean::Correctable
89
-
90
- def corrections
91
- if owner_class && reflection
92
- @corrections ||= begin
93
- maybe_these = owner_class.reflections.keys
94
- maybe_these -= [reflection.name.to_s] # remove failing reflection
95
- DidYouMean::SpellChecker.new(dictionary: maybe_these).correct(reflection.options[:through].to_s)
96
- end
97
- else
98
- []
99
- end
100
- end
101
- end
102
- end
103
-
104
- class HasManyThroughAssociationPolymorphicSourceError < ActiveRecordError # :nodoc:
105
- def initialize(owner_class_name = nil, reflection = nil, source_reflection = nil)
106
- if owner_class_name && reflection && source_reflection
107
- super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' on the polymorphic object '#{source_reflection.class_name}##{source_reflection.name}' without 'source_type'. Try adding 'source_type: \"#{reflection.name.to_s.classify}\"' to 'has_many :through' definition.")
108
- else
109
- super("Cannot have a has_many :through association.")
110
- end
111
- end
112
- end
113
-
114
- class HasManyThroughAssociationPolymorphicThroughError < ActiveRecordError # :nodoc:
115
- def initialize(owner_class_name = nil, reflection = nil)
116
- if owner_class_name && reflection
117
- super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' which goes through the polymorphic association '#{owner_class_name}##{reflection.through_reflection.name}'.")
118
- else
119
- super("Cannot have a has_many :through association.")
120
- end
121
- end
122
- end
123
-
124
- class HasManyThroughAssociationPointlessSourceTypeError < ActiveRecordError # :nodoc:
125
- def initialize(owner_class_name = nil, reflection = nil, source_reflection = nil)
126
- if owner_class_name && reflection && source_reflection
127
- super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' with a :source_type option if the '#{reflection.through_reflection.class_name}##{source_reflection.name}' is not polymorphic. Try removing :source_type on your association.")
128
- else
129
- super("Cannot have a has_many :through association.")
130
- end
131
- end
132
- end
133
-
134
- class HasOneThroughCantAssociateThroughCollection < ActiveRecordError # :nodoc:
135
- def initialize(owner_class_name = nil, reflection = nil, through_reflection = nil)
136
- if owner_class_name && reflection && through_reflection
137
- super("Cannot have a has_one :through association '#{owner_class_name}##{reflection.name}' where the :through association '#{owner_class_name}##{through_reflection.name}' is a collection. Specify a has_one or belongs_to association in the :through option instead.")
138
- else
139
- super("Cannot have a has_one :through association.")
140
- end
141
- end
142
- end
143
-
144
- class HasOneAssociationPolymorphicThroughError < ActiveRecordError # :nodoc:
145
- def initialize(owner_class_name = nil, reflection = nil)
146
- if owner_class_name && reflection
147
- super("Cannot have a has_one :through association '#{owner_class_name}##{reflection.name}' which goes through the polymorphic association '#{owner_class_name}##{reflection.through_reflection.name}'.")
148
- else
149
- super("Cannot have a has_one :through association.")
150
- end
151
- end
152
- end
153
-
154
- class HasManyThroughSourceAssociationNotFoundError < ActiveRecordError # :nodoc:
155
- def initialize(reflection = nil)
156
- if reflection
157
- through_reflection = reflection.through_reflection
158
- source_reflection_names = reflection.source_reflection_names
159
- source_associations = reflection.through_reflection.klass._reflections.keys
160
- super("Could not find the source association(s) #{source_reflection_names.collect(&:inspect).to_sentence(two_words_connector: ' or ', last_word_connector: ', or ')} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'. Is it one of #{source_associations.to_sentence(two_words_connector: ' or ', last_word_connector: ', or ')}?")
161
- else
162
- super("Could not find the source association(s).")
163
- end
164
- end
165
- end
166
-
167
- class HasManyThroughOrderError < ActiveRecordError # :nodoc:
168
- def initialize(owner_class_name = nil, reflection = nil, through_reflection = nil)
169
- if owner_class_name && reflection && through_reflection
170
- super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' which goes through '#{owner_class_name}##{through_reflection.name}' before the through association is defined.")
171
- else
172
- super("Cannot have a has_many :through association before the through association is defined.")
173
- end
174
- end
175
- end
176
-
177
- class ThroughCantAssociateThroughHasOneOrManyReflection < ActiveRecordError # :nodoc:
178
- def initialize(owner = nil, reflection = nil)
179
- if owner && reflection
180
- super("Cannot modify association '#{owner.class.name}##{reflection.name}' because the source reflection class '#{reflection.source_reflection.class_name}' is associated to '#{reflection.through_reflection.class_name}' via :#{reflection.source_reflection.macro}.")
181
- else
182
- super("Cannot modify association.")
183
- end
184
- end
185
- end
186
-
187
- class CompositePrimaryKeyMismatchError < ActiveRecordError # :nodoc:
188
- attr_reader :reflection
189
-
190
- def initialize(reflection = nil)
191
- if reflection
192
- if reflection.has_one? || reflection.collection?
193
- super("Association #{reflection.active_record}##{reflection.name} primary key #{reflection.active_record_primary_key} doesn't match with foreign key #{reflection.foreign_key}. Please specify query_constraints, or primary_key and foreign_key values.")
194
- else
195
- super("Association #{reflection.active_record}##{reflection.name} primary key #{reflection.association_primary_key} doesn't match with foreign key #{reflection.foreign_key}. Please specify query_constraints, or primary_key and foreign_key values.")
196
- end
197
- else
198
- super("Association primary key doesn't match with foreign key.")
199
- end
200
- end
201
- end
202
-
203
- class AmbiguousSourceReflectionForThroughAssociation < ActiveRecordError # :nodoc:
204
- def initialize(klass, macro, association_name, options, possible_sources)
205
- example_options = options.dup
206
- example_options[:source] = possible_sources.first
207
-
208
- super("Ambiguous source reflection for through association. Please " \
209
- "specify a :source directive on your declaration like:\n" \
210
- "\n" \
211
- " class #{klass} < ActiveRecord::Base\n" \
212
- " #{macro} :#{association_name}, #{example_options}\n" \
213
- " end"
214
- )
215
- end
216
- end
217
-
218
- class HasManyThroughCantAssociateThroughHasOneOrManyReflection < ThroughCantAssociateThroughHasOneOrManyReflection # :nodoc:
219
- end
220
-
221
- class HasOneThroughCantAssociateThroughHasOneOrManyReflection < ThroughCantAssociateThroughHasOneOrManyReflection # :nodoc:
222
- end
223
-
224
- class ThroughNestedAssociationsAreReadonly < ActiveRecordError # :nodoc:
225
- def initialize(owner = nil, reflection = nil)
226
- if owner && reflection
227
- super("Cannot modify association '#{owner.class.name}##{reflection.name}' because it goes through more than one other association.")
228
- else
229
- super("Through nested associations are read-only.")
230
- end
231
- end
232
- end
233
-
234
- class HasManyThroughNestedAssociationsAreReadonly < ThroughNestedAssociationsAreReadonly # :nodoc:
235
- end
236
-
237
- class HasOneThroughNestedAssociationsAreReadonly < ThroughNestedAssociationsAreReadonly # :nodoc:
238
- end
239
-
240
- # This error is raised when trying to eager load a polymorphic association using a JOIN.
241
- # Eager loading polymorphic associations is only possible with
242
- # {ActiveRecord::Relation#preload}[rdoc-ref:QueryMethods#preload].
243
- class EagerLoadPolymorphicError < ActiveRecordError
244
- def initialize(reflection = nil)
245
- if reflection
246
- super("Cannot eagerly load the polymorphic association #{reflection.name.inspect}")
247
- else
248
- super("Eager load polymorphic error.")
249
- end
250
- end
251
- end
252
-
253
- # This error is raised when trying to destroy a parent instance in N:1 or 1:1 associations
254
- # (has_many, has_one) when there is at least 1 child associated instance.
255
- # ex: if @project.tasks.size > 0, DeleteRestrictionError will be raised when trying to destroy @project
256
- class DeleteRestrictionError < ActiveRecordError # :nodoc:
257
- def initialize(name = nil)
258
- if name
259
- super("Cannot delete record because of dependent #{name}")
260
- else
261
- super("Delete restriction error.")
262
- end
263
- end
264
- end
265
-
266
4
  # See ActiveRecord::Associations::ClassMethods for documentation.
267
5
  module Associations # :nodoc:
268
6
  extend ActiveSupport::Autoload
@@ -1506,6 +1244,13 @@ module ActiveRecord
1506
1244
  # Serves as a composite foreign key. Defines the list of columns to be used to query the associated object.
1507
1245
  # This is an optional option. By default Rails will attempt to derive the value automatically.
1508
1246
  # When the value is set the Array size must match associated model's primary key or +query_constraints+ size.
1247
+ # [+:index_errors+]
1248
+ # Allows differentiation of multiple validation errors from the association records, by including
1249
+ # an index in the error attribute name, e.g. +roles[2].level+.
1250
+ # When set to +true+, the index is based on association order, i.e. database order, with yet to be
1251
+ # persisted new records placed at the end.
1252
+ # When set to +:nested_attributes_order+, the index is based on the record order received by
1253
+ # nested attributes setter, when accepts_nested_attributes_for is used.
1509
1254
  #
1510
1255
  # Option examples:
1511
1256
  # has_many :comments, -> { order("posted_on") }
@@ -1519,6 +1264,7 @@ module ActiveRecord
1519
1264
  # has_many :subscribers, through: :subscriptions, disable_joins: true
1520
1265
  # has_many :comments, strict_loading: true
1521
1266
  # has_many :comments, query_constraints: [:blog_id, :post_id]
1267
+ # has_many :comments, index_errors: :nested_attributes_order
1522
1268
  def has_many(name, scope = nil, **options, &extension)
1523
1269
  reflection = Builder::HasMany.build(self, name, scope, options, &extension)
1524
1270
  Reflection.add_reflection self, name, reflection
@@ -1661,9 +1407,14 @@ module ActiveRecord
1661
1407
  # When set to +true+, validates new objects added to association when saving the parent object. +false+ by default.
1662
1408
  # If you want to ensure associated objects are revalidated on every update, use +validates_associated+.
1663
1409
  # [+:autosave+]
1664
- # If true, always save the associated object or destroy it if marked for destruction,
1665
- # when saving the parent object. If false, never save or destroy the associated object.
1666
- # By default, only save the associated object if it's a new record.
1410
+ # If +true+, always saves the associated object or destroys it if marked for destruction,
1411
+ # when saving the parent object.
1412
+ # If +false+, never save or destroy the associated object.
1413
+ #
1414
+ # By default, only saves the associated object if it's a new record. Setting this option
1415
+ # to +true+ also enables validations on the associated object unless explicitly disabled
1416
+ # with <tt>validate: false</tt>. This is because saving an object with invalid associated
1417
+ # objects would fail, so any associated objects will go through validation checks.
1667
1418
  #
1668
1419
  # Note that NestedAttributes::ClassMethods#accepts_nested_attributes_for sets
1669
1420
  # <tt>:autosave</tt> to <tt>true</tt>.
@@ -1816,15 +1567,25 @@ module ActiveRecord
1816
1567
  # named <tt>#{table_name}_count</tt> (such as +comments_count+ for a belonging Comment class)
1817
1568
  # is used on the associate class (such as a Post class) - that is the migration for
1818
1569
  # <tt>#{table_name}_count</tt> is created on the associate class (such that <tt>Post.comments_count</tt> will
1819
- # return the count cached, see note below). You can also specify a custom counter
1570
+ # return the count cached). You can also specify a custom counter
1820
1571
  # cache column by providing a column name instead of a +true+/+false+ value to this
1821
1572
  # option (e.g., <tt>counter_cache: :my_custom_counter</tt>.)
1822
- # Note: Specifying a counter cache will add it to that model's list of readonly attributes
1823
- # using +attr_readonly+.
1824
- # [+:polymorphic+]
1825
- # Specify this association is a polymorphic association by passing +true+.
1573
+ #
1574
+ # Starting to use counter caches on existing large tables can be troublesome, because the column
1575
+ # values must be backfilled separately of the column addition (to not lock the table for too long)
1576
+ # and before the use of +:counter_cache+ (otherwise methods like +size+/+any?+/etc, which use
1577
+ # counter caches internally, can produce incorrect results). To safely backfill the values while keeping
1578
+ # counter cache columns updated with the child records creation/removal and to avoid the mentioned methods
1579
+ # use the possibly incorrect counter cache column values and always get the results from the database,
1580
+ # use <tt>counter_cache: { active: false }</tt>.
1581
+ # If you also need to specify a custom column name, use <tt>counter_cache: { active: false, column: :my_custom_counter }</tt>.
1582
+ #
1826
1583
  # Note: If you've enabled the counter cache, then you may want to add the counter cache attribute
1827
1584
  # to the +attr_readonly+ list in the associated classes (e.g. <tt>class Post; attr_readonly :comments_count; end</tt>).
1585
+ # [+:polymorphic+]
1586
+ # Specify this association is a polymorphic association by passing +true+.
1587
+ # Note: Since polymorphic associations rely on storing class names in the database, make sure to update the class names in the
1588
+ # <tt>*_type</tt> polymorphic type column of the corresponding rows.
1828
1589
  # [+:validate+]
1829
1590
  # When set to +true+, validates new objects added to association when saving the parent object. +false+ by default.
1830
1591
  # If you want to ensure associated objects are revalidated on every update, use +validates_associated+.
@@ -1882,7 +1643,7 @@ module ActiveRecord
1882
1643
  # belongs_to :user, optional: true
1883
1644
  # belongs_to :account, default: -> { company.account }
1884
1645
  # belongs_to :account, strict_loading: true
1885
- # belong_to :note, query_constraints: [:organization_id, :note_id]
1646
+ # belongs_to :note, query_constraints: [:organization_id, :note_id]
1886
1647
  def belongs_to(name, scope = nil, **options)
1887
1648
  reflection = Builder::BelongsTo.build(self, name, scope, options)
1888
1649
  Reflection.add_reflection self, name, reflection
@@ -1905,7 +1666,7 @@ module ActiveRecord
1905
1666
  # The join table should not have a primary key or a model associated with it. You must manually generate the
1906
1667
  # join table with a migration such as this:
1907
1668
  #
1908
- # class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration[7.1]
1669
+ # class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration[7.2]
1909
1670
  # def change
1910
1671
  # create_join_table :developers, :projects
1911
1672
  # end
@@ -2099,7 +1860,7 @@ module ActiveRecord
2099
1860
  end
2100
1861
 
2101
1862
  has_many name, scope, **hm_options, &extension
2102
- _reflections[name.to_s].parent_reflection = habtm_reflection
1863
+ _reflections[name].parent_reflection = habtm_reflection
2103
1864
  end
2104
1865
  end
2105
1866
  end
@@ -2,33 +2,23 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module AttributeAssignment
5
- include ActiveModel::AttributeAssignment
6
-
7
5
  private
8
6
  def _assign_attributes(attributes)
9
- multi_parameter_attributes = nested_parameter_attributes = nil
7
+ multi_parameter_attributes = nil
10
8
 
11
9
  attributes.each do |k, v|
12
10
  key = k.to_s
13
11
 
14
12
  if key.include?("(")
15
13
  (multi_parameter_attributes ||= {})[key] = v
16
- elsif v.is_a?(Hash)
17
- (nested_parameter_attributes ||= {})[key] = v
18
14
  else
19
15
  _assign_attribute(key, v)
20
16
  end
21
17
  end
22
18
 
23
- assign_nested_parameter_attributes(nested_parameter_attributes) if nested_parameter_attributes
24
19
  assign_multiparameter_attributes(multi_parameter_attributes) if multi_parameter_attributes
25
20
  end
26
21
 
27
- # Assign any deferred nested attributes after the base attributes have been set.
28
- def assign_nested_parameter_attributes(pairs)
29
- pairs.each { |k, v| _assign_attribute(k, v) }
30
- end
31
-
32
22
  # Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
33
23
  # by calling new on the column type or aggregation type (through composed_of) object with these parameters.
34
24
  # So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module AttributeMethods
5
+ module CompositePrimaryKey # :nodoc:
6
+ # Returns the primary key column's value. If the primary key is composite,
7
+ # returns an array of the primary key column values.
8
+ def id
9
+ if self.class.composite_primary_key?
10
+ @primary_key.map { |pk| _read_attribute(pk) }
11
+ else
12
+ super
13
+ end
14
+ end
15
+
16
+ def primary_key_values_present? # :nodoc:
17
+ if self.class.composite_primary_key?
18
+ id.all?
19
+ else
20
+ super
21
+ end
22
+ end
23
+
24
+ # Sets the primary key column's value. If the primary key is composite,
25
+ # raises TypeError when the set value not enumerable.
26
+ def id=(value)
27
+ if self.class.composite_primary_key?
28
+ raise TypeError, "Expected value matching #{self.class.primary_key.inspect}, got #{value.inspect}." unless value.is_a?(Enumerable)
29
+ @primary_key.zip(value) { |attr, value| _write_attribute(attr, value) }
30
+ else
31
+ super
32
+ end
33
+ end
34
+
35
+ # Queries the primary key column's value. If the primary key is composite,
36
+ # all primary key column values must be queryable.
37
+ def id?
38
+ if self.class.composite_primary_key?
39
+ @primary_key.all? { |col| _query_attribute(col) }
40
+ else
41
+ super
42
+ end
43
+ end
44
+
45
+ # Returns the primary key column's value before type cast. If the primary key is composite,
46
+ # returns an array of primary key column values before type cast.
47
+ def id_before_type_cast
48
+ if self.class.composite_primary_key?
49
+ @primary_key.map { |col| attribute_before_type_cast(col) }
50
+ else
51
+ super
52
+ end
53
+ end
54
+
55
+ # Returns the primary key column's previous value. If the primary key is composite,
56
+ # returns an array of primary key column previous values.
57
+ def id_was
58
+ if self.class.composite_primary_key?
59
+ @primary_key.map { |col| attribute_was(col) }
60
+ else
61
+ super
62
+ end
63
+ end
64
+
65
+ # Returns the primary key column's value from the database. If the primary key is composite,
66
+ # returns an array of primary key column values from database.
67
+ def id_in_database
68
+ if self.class.composite_primary_key?
69
+ @primary_key.map { |col| attribute_in_database(col) }
70
+ else
71
+ super
72
+ end
73
+ end
74
+
75
+ def id_for_database # :nodoc:
76
+ if self.class.composite_primary_key?
77
+ @primary_key.map { |col| @attributes[col].value_for_database }
78
+ else
79
+ super
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -31,7 +31,7 @@ module ActiveRecord
31
31
  # person.name_in_database # => "Alice"
32
32
  # person.saved_change_to_name? # => true
33
33
  # person.saved_change_to_name # => ["Allison", "Alice"]
34
- # person.name_before_last_change # => "Allison"
34
+ # person.name_before_last_save # => "Allison"
35
35
  #
36
36
  # Similar to ActiveModel::Dirty, methods can be invoked as
37
37
  # +saved_change_to_name?+ or by passing an argument to the generic method
@@ -251,7 +251,7 @@ module ActiveRecord
251
251
  changed_attribute_names_to_save
252
252
  else
253
253
  attribute_names.reject do |attr_name|
254
- if column_for_attribute(attr_name).default_function
254
+ if column_for_attribute(attr_name).auto_populated?
255
255
  !attribute_changed?(attr_name)
256
256
  end
257
257
  end
@@ -18,74 +18,45 @@ module ActiveRecord
18
18
  # Returns the primary key column's value. If the primary key is composite,
19
19
  # returns an array of the primary key column values.
20
20
  def id
21
- return _read_attribute(@primary_key) unless @primary_key.is_a?(Array)
22
-
23
- @primary_key.map { |pk| _read_attribute(pk) }
21
+ _read_attribute(@primary_key)
24
22
  end
25
23
 
26
24
  def primary_key_values_present? # :nodoc:
27
- return id.all? if self.class.composite_primary_key?
28
-
29
25
  !!id
30
26
  end
31
27
 
32
28
  # Sets the primary key column's value. If the primary key is composite,
33
29
  # raises TypeError when the set value not enumerable.
34
30
  def id=(value)
35
- if self.class.composite_primary_key?
36
- raise TypeError, "Expected value matching #{self.class.primary_key.inspect}, got #{value.inspect}." unless value.is_a?(Enumerable)
37
- @primary_key.zip(value) { |attr, value| _write_attribute(attr, value) }
38
- else
39
- _write_attribute(@primary_key, value)
40
- end
31
+ _write_attribute(@primary_key, value)
41
32
  end
42
33
 
43
34
  # Queries the primary key column's value. If the primary key is composite,
44
35
  # all primary key column values must be queryable.
45
36
  def id?
46
- if self.class.composite_primary_key?
47
- @primary_key.all? { |col| _query_attribute(col) }
48
- else
49
- _query_attribute(@primary_key)
50
- end
37
+ _query_attribute(@primary_key)
51
38
  end
52
39
 
53
40
  # Returns the primary key column's value before type cast. If the primary key is composite,
54
41
  # returns an array of primary key column values before type cast.
55
42
  def id_before_type_cast
56
- if self.class.composite_primary_key?
57
- @primary_key.map { |col| attribute_before_type_cast(col) }
58
- else
59
- attribute_before_type_cast(@primary_key)
60
- end
43
+ attribute_before_type_cast(@primary_key)
61
44
  end
62
45
 
63
46
  # Returns the primary key column's previous value. If the primary key is composite,
64
47
  # returns an array of primary key column previous values.
65
48
  def id_was
66
- if self.class.composite_primary_key?
67
- @primary_key.map { |col| attribute_was(col) }
68
- else
69
- attribute_was(@primary_key)
70
- end
49
+ attribute_was(@primary_key)
71
50
  end
72
51
 
73
52
  # Returns the primary key column's value from the database. If the primary key is composite,
74
53
  # returns an array of primary key column values from database.
75
54
  def id_in_database
76
- if self.class.composite_primary_key?
77
- @primary_key.map { |col| attribute_in_database(col) }
78
- else
79
- attribute_in_database(@primary_key)
80
- end
55
+ attribute_in_database(@primary_key)
81
56
  end
82
57
 
83
58
  def id_for_database # :nodoc:
84
- if self.class.composite_primary_key?
85
- @primary_key.map { |col| @attributes[col].value_for_database }
86
- else
87
- @attributes[@primary_key].value_for_database
88
- end
59
+ @attributes[@primary_key].value_for_database
89
60
  end
90
61
 
91
62
  private
@@ -109,20 +80,19 @@ module ActiveRecord
109
80
  # Overwriting will negate any effect of the +primary_key_prefix_type+
110
81
  # setting, though.
111
82
  def primary_key
112
- if PRIMARY_KEY_NOT_SET.equal?(@primary_key)
113
- @primary_key = reset_primary_key
114
- end
83
+ reset_primary_key if PRIMARY_KEY_NOT_SET.equal?(@primary_key)
115
84
  @primary_key
116
85
  end
117
86
 
118
87
  def composite_primary_key? # :nodoc:
119
- primary_key.is_a?(Array)
88
+ reset_primary_key if PRIMARY_KEY_NOT_SET.equal?(@primary_key)
89
+ @composite_primary_key
120
90
  end
121
91
 
122
92
  # Returns a quoted version of the primary key name, used to construct
123
93
  # SQL statements.
124
94
  def quoted_primary_key
125
- @quoted_primary_key ||= connection.quote_column_name(primary_key)
95
+ @quoted_primary_key ||= adapter_class.quote_column_name(primary_key)
126
96
  end
127
97
 
128
98
  def reset_primary_key # :nodoc:
@@ -138,12 +108,10 @@ module ActiveRecord
138
108
  base_name.foreign_key(false)
139
109
  elsif base_name && primary_key_prefix_type == :table_name_with_underscore
140
110
  base_name.foreign_key
111
+ elsif ActiveRecord::Base != self && table_exists?
112
+ schema_cache.primary_keys(table_name)
141
113
  else
142
- if ActiveRecord::Base != self && table_exists?
143
- connection.schema_cache.primary_keys(table_name)
144
- else
145
- "id"
146
- end
114
+ "id"
147
115
  end
148
116
  end
149
117
 
@@ -163,25 +131,25 @@ module ActiveRecord
163
131
  #
164
132
  # Project.primary_key # => "foo_id"
165
133
  def primary_key=(value)
166
- @primary_key = derive_primary_key(value)
134
+ @primary_key = if value.is_a?(Array)
135
+ @composite_primary_key = true
136
+ include CompositePrimaryKey
137
+ @primary_key = value.map { |v| -v.to_s }.freeze
138
+ elsif value
139
+ -value.to_s
140
+ end
167
141
  @quoted_primary_key = nil
168
142
  @attributes_builder = nil
169
143
  end
170
144
 
171
145
  private
172
- def derive_primary_key(value)
173
- return unless value
174
-
175
- return -value.to_s unless value.is_a?(Array)
176
-
177
- value.map { |v| -v.to_s }.freeze
178
- end
179
-
180
146
  def inherited(base)
181
147
  super
182
148
  base.class_eval do
183
149
  @primary_key = PRIMARY_KEY_NOT_SET
150
+ @composite_primary_key = false
184
151
  @quoted_primary_key = nil
152
+ @attributes_builder = nil
185
153
  end
186
154
  end
187
155
  end