activerecord 7.1.5.1 → 8.0.2

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 (206) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +369 -2484
  3. data/README.rdoc +15 -15
  4. data/examples/performance.rb +2 -2
  5. data/lib/active_record/association_relation.rb +2 -1
  6. data/lib/active_record/associations/alias_tracker.rb +31 -23
  7. data/lib/active_record/associations/association.rb +43 -12
  8. data/lib/active_record/associations/belongs_to_association.rb +21 -8
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  10. data/lib/active_record/associations/builder/association.rb +7 -6
  11. data/lib/active_record/associations/builder/belongs_to.rb +1 -0
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
  13. data/lib/active_record/associations/builder/has_many.rb +3 -4
  14. data/lib/active_record/associations/builder/has_one.rb +3 -4
  15. data/lib/active_record/associations/collection_association.rb +17 -9
  16. data/lib/active_record/associations/collection_proxy.rb +14 -1
  17. data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
  18. data/lib/active_record/associations/errors.rb +265 -0
  19. data/lib/active_record/associations/has_many_association.rb +1 -1
  20. data/lib/active_record/associations/has_many_through_association.rb +10 -3
  21. data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
  22. data/lib/active_record/associations/nested_error.rb +47 -0
  23. data/lib/active_record/associations/preloader/association.rb +4 -3
  24. data/lib/active_record/associations/preloader/branch.rb +7 -1
  25. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  26. data/lib/active_record/associations/singular_association.rb +14 -3
  27. data/lib/active_record/associations/through_association.rb +1 -1
  28. data/lib/active_record/associations.rb +92 -295
  29. data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
  30. data/lib/active_record/attribute_assignment.rb +0 -2
  31. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  32. data/lib/active_record/attribute_methods/primary_key.rb +25 -61
  33. data/lib/active_record/attribute_methods/read.rb +1 -13
  34. data/lib/active_record/attribute_methods/serialization.rb +4 -24
  35. data/lib/active_record/attribute_methods/time_zone_conversion.rb +9 -18
  36. data/lib/active_record/attribute_methods.rb +71 -75
  37. data/lib/active_record/attributes.rb +63 -49
  38. data/lib/active_record/autosave_association.rb +92 -57
  39. data/lib/active_record/base.rb +2 -3
  40. data/lib/active_record/callbacks.rb +1 -1
  41. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +48 -122
  42. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
  43. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -1
  44. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +286 -77
  45. data/lib/active_record/connection_adapters/abstract/database_statements.rb +119 -55
  46. data/lib/active_record/connection_adapters/abstract/query_cache.rb +197 -76
  47. data/lib/active_record/connection_adapters/abstract/quoting.rb +66 -92
  48. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
  49. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +12 -3
  50. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +48 -12
  51. data/lib/active_record/connection_adapters/abstract/transaction.rb +140 -67
  52. data/lib/active_record/connection_adapters/abstract_adapter.rb +85 -90
  53. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +71 -52
  54. data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
  55. data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -57
  56. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
  57. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +56 -45
  58. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +92 -101
  59. data/lib/active_record/connection_adapters/mysql2_adapter.rb +13 -31
  60. data/lib/active_record/connection_adapters/pool_config.rb +14 -13
  61. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +86 -41
  62. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  63. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  64. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
  65. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  66. data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
  67. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
  68. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -11
  69. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +36 -20
  70. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +3 -2
  71. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +75 -28
  72. data/lib/active_record/connection_adapters/postgresql_adapter.rb +73 -113
  73. data/lib/active_record/connection_adapters/schema_cache.rb +124 -131
  74. data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
  75. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +81 -97
  76. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -46
  77. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +16 -0
  78. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
  79. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +29 -0
  80. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +35 -3
  81. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +183 -87
  82. data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
  83. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +39 -69
  84. data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -65
  85. data/lib/active_record/connection_adapters.rb +65 -0
  86. data/lib/active_record/connection_handling.rb +74 -37
  87. data/lib/active_record/core.rb +132 -51
  88. data/lib/active_record/counter_cache.rb +19 -10
  89. data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
  90. data/lib/active_record/database_configurations/database_config.rb +23 -4
  91. data/lib/active_record/database_configurations/hash_config.rb +46 -34
  92. data/lib/active_record/database_configurations/url_config.rb +20 -1
  93. data/lib/active_record/database_configurations.rb +1 -1
  94. data/lib/active_record/delegated_type.rb +41 -17
  95. data/lib/active_record/dynamic_matchers.rb +2 -2
  96. data/lib/active_record/encryption/config.rb +3 -1
  97. data/lib/active_record/encryption/encryptable_record.rb +7 -7
  98. data/lib/active_record/encryption/encrypted_attribute_type.rb +33 -4
  99. data/lib/active_record/encryption/encryptor.rb +28 -6
  100. data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
  101. data/lib/active_record/encryption/key_provider.rb +1 -1
  102. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  103. data/lib/active_record/encryption/message_serializer.rb +4 -0
  104. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  105. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  106. data/lib/active_record/encryption/scheme.rb +8 -1
  107. data/lib/active_record/enum.rb +20 -16
  108. data/lib/active_record/errors.rb +54 -20
  109. data/lib/active_record/explain.rb +13 -24
  110. data/lib/active_record/fixtures.rb +37 -33
  111. data/lib/active_record/future_result.rb +21 -13
  112. data/lib/active_record/gem_version.rb +4 -4
  113. data/lib/active_record/inheritance.rb +4 -2
  114. data/lib/active_record/insert_all.rb +19 -16
  115. data/lib/active_record/integration.rb +4 -1
  116. data/lib/active_record/internal_metadata.rb +48 -34
  117. data/lib/active_record/locking/optimistic.rb +8 -7
  118. data/lib/active_record/log_subscriber.rb +5 -32
  119. data/lib/active_record/message_pack.rb +1 -1
  120. data/lib/active_record/migration/command_recorder.rb +33 -14
  121. data/lib/active_record/migration/compatibility.rb +8 -3
  122. data/lib/active_record/migration/default_strategy.rb +4 -5
  123. data/lib/active_record/migration/pending_migration_connection.rb +2 -2
  124. data/lib/active_record/migration.rb +104 -98
  125. data/lib/active_record/model_schema.rb +32 -70
  126. data/lib/active_record/nested_attributes.rb +15 -9
  127. data/lib/active_record/normalization.rb +3 -7
  128. data/lib/active_record/persistence.rb +127 -451
  129. data/lib/active_record/query_cache.rb +19 -8
  130. data/lib/active_record/query_logs.rb +104 -37
  131. data/lib/active_record/query_logs_formatter.rb +17 -28
  132. data/lib/active_record/querying.rb +24 -12
  133. data/lib/active_record/railtie.rb +26 -68
  134. data/lib/active_record/railties/controller_runtime.rb +13 -4
  135. data/lib/active_record/railties/databases.rake +43 -61
  136. data/lib/active_record/reflection.rb +112 -53
  137. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  138. data/lib/active_record/relation/batches.rb +138 -72
  139. data/lib/active_record/relation/calculations.rb +122 -82
  140. data/lib/active_record/relation/delegation.rb +30 -22
  141. data/lib/active_record/relation/finder_methods.rb +32 -18
  142. data/lib/active_record/relation/merger.rb +12 -14
  143. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  144. data/lib/active_record/relation/predicate_builder/association_query_value.rb +10 -2
  145. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
  146. data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
  147. data/lib/active_record/relation/predicate_builder.rb +16 -3
  148. data/lib/active_record/relation/query_attribute.rb +1 -1
  149. data/lib/active_record/relation/query_methods.rb +317 -101
  150. data/lib/active_record/relation/spawn_methods.rb +3 -19
  151. data/lib/active_record/relation/where_clause.rb +7 -19
  152. data/lib/active_record/relation.rb +561 -119
  153. data/lib/active_record/result.rb +95 -46
  154. data/lib/active_record/runtime_registry.rb +39 -0
  155. data/lib/active_record/sanitization.rb +31 -25
  156. data/lib/active_record/schema.rb +8 -6
  157. data/lib/active_record/schema_dumper.rb +53 -20
  158. data/lib/active_record/schema_migration.rb +31 -14
  159. data/lib/active_record/scoping/named.rb +6 -2
  160. data/lib/active_record/signed_id.rb +24 -4
  161. data/lib/active_record/statement_cache.rb +19 -19
  162. data/lib/active_record/store.rb +7 -3
  163. data/lib/active_record/table_metadata.rb +2 -13
  164. data/lib/active_record/tasks/database_tasks.rb +87 -58
  165. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -3
  166. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  167. data/lib/active_record/tasks/sqlite_database_tasks.rb +4 -3
  168. data/lib/active_record/test_fixtures.rb +98 -89
  169. data/lib/active_record/testing/query_assertions.rb +121 -0
  170. data/lib/active_record/timestamp.rb +2 -2
  171. data/lib/active_record/token_for.rb +22 -12
  172. data/lib/active_record/touch_later.rb +1 -1
  173. data/lib/active_record/transaction.rb +132 -0
  174. data/lib/active_record/transactions.rb +72 -17
  175. data/lib/active_record/translation.rb +0 -2
  176. data/lib/active_record/type/serialized.rb +1 -3
  177. data/lib/active_record/type_caster/connection.rb +4 -4
  178. data/lib/active_record/validations/associated.rb +9 -3
  179. data/lib/active_record/validations/uniqueness.rb +23 -18
  180. data/lib/active_record/validations.rb +4 -1
  181. data/lib/active_record.rb +138 -57
  182. data/lib/arel/alias_predication.rb +1 -1
  183. data/lib/arel/collectors/bind.rb +4 -2
  184. data/lib/arel/collectors/composite.rb +7 -0
  185. data/lib/arel/collectors/sql_string.rb +2 -2
  186. data/lib/arel/collectors/substitute_binds.rb +3 -3
  187. data/lib/arel/nodes/binary.rb +1 -7
  188. data/lib/arel/nodes/bound_sql_literal.rb +9 -5
  189. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  190. data/lib/arel/nodes/node.rb +5 -4
  191. data/lib/arel/nodes/sql_literal.rb +8 -1
  192. data/lib/arel/nodes.rb +2 -2
  193. data/lib/arel/predications.rb +1 -1
  194. data/lib/arel/select_manager.rb +1 -1
  195. data/lib/arel/table.rb +3 -7
  196. data/lib/arel/tree_manager.rb +3 -2
  197. data/lib/arel/update_manager.rb +2 -1
  198. data/lib/arel/visitors/dot.rb +1 -0
  199. data/lib/arel/visitors/mysql.rb +9 -4
  200. data/lib/arel/visitors/postgresql.rb +1 -12
  201. data/lib/arel/visitors/sqlite.rb +25 -0
  202. data/lib/arel/visitors/to_sql.rb +29 -16
  203. data/lib/arel.rb +7 -3
  204. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  205. metadata +18 -16
  206. data/lib/active_record/relation/record_fetch_warning.rb +0 -49
@@ -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
@@ -641,21 +379,43 @@ module ActiveRecord
641
379
  # after_add: :congratulate_client,
642
380
  # after_remove: :log_after_remove
643
381
  #
644
- # def congratulate_client(record)
382
+ # def congratulate_client(client)
645
383
  # # ...
646
384
  # end
647
385
  #
648
- # def log_after_remove(record)
386
+ # def log_after_remove(client)
649
387
  # # ...
650
388
  # end
651
389
  # end
652
390
  #
391
+ # Callbacks can be defined in three ways:
392
+ #
393
+ # 1. A symbol that references a method defined on the class with the
394
+ # associated collection. For example, <tt>after_add: :congratulate_client</tt>
395
+ # invokes <tt>Firm#congratulate_client(client)</tt>.
396
+ # 2. A callable with a signature that accepts both the record with the
397
+ # associated collection and the record being added or removed. For
398
+ # example, <tt>after_add: ->(firm, client) { ... }</tt>.
399
+ # 3. An object that responds to the callback name. For example, passing
400
+ # <tt>after_add: CallbackObject.new</tt> invokes <tt>CallbackObject#after_add(firm,
401
+ # client)</tt>.
402
+ #
653
403
  # It's possible to stack callbacks by passing them as an array. Example:
654
404
  #
405
+ # class CallbackObject
406
+ # def after_add(firm, client)
407
+ # firm.log << "after_adding #{client.id}"
408
+ # end
409
+ # end
410
+ #
655
411
  # class Firm < ActiveRecord::Base
656
412
  # has_many :clients,
657
413
  # dependent: :destroy,
658
- # after_add: [:congratulate_client, -> (firm, record) { firm.log << "after_adding#{record.id}" }],
414
+ # after_add: [
415
+ # :congratulate_client,
416
+ # -> (firm, client) { firm.log << "after_adding #{client.id}" },
417
+ # CallbackObject.new
418
+ # ],
659
419
  # after_remove: :log_after_remove
660
420
  # end
661
421
  #
@@ -799,7 +559,7 @@ module ActiveRecord
799
559
  # @group.avatars << Avatar.new # this would work if User belonged_to Avatar rather than the other way around
800
560
  # @group.avatars.delete(@group.avatars.last) # so would this
801
561
  #
802
- # == Setting Inverses
562
+ # === Setting Inverses
803
563
  #
804
564
  # If you are using a #belongs_to on the join model, it is a good idea to set the
805
565
  # <tt>:inverse_of</tt> option on the #belongs_to, which will mean that the following example
@@ -1461,8 +1221,11 @@ module ActiveRecord
1461
1221
  # If you are going to modify the association (rather than just read from it), then it is
1462
1222
  # a good idea to set the <tt>:inverse_of</tt> option on the source association on the
1463
1223
  # join model. This allows associated records to be built which will automatically create
1464
- # the appropriate join model records when they are saved. (See the 'Association Join Models'
1465
- # and 'Setting Inverses' sections above.)
1224
+ # the appropriate join model records when they are saved. See
1225
+ # {Association Join Models}[rdoc-ref:Associations::ClassMethods@Association+Join+Models]
1226
+ # and {Setting Inverses}[rdoc-ref:Associations::ClassMethods@Setting+Inverses] for
1227
+ # more detail.
1228
+ #
1466
1229
  # [+:disable_joins+]
1467
1230
  # Specifies whether joins should be skipped for an association. If set to true, two or more queries
1468
1231
  # will be generated. Note that in some cases, if order or limit is applied, it will be done in-memory
@@ -1491,7 +1254,8 @@ module ActiveRecord
1491
1254
  # [+:inverse_of+]
1492
1255
  # Specifies the name of the #belongs_to association on the associated object
1493
1256
  # that is the inverse of this #has_many association.
1494
- # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
1257
+ # See {Bi-directional associations}[rdoc-ref:Associations::ClassMethods@Bi-directional+associations]
1258
+ # for more detail.
1495
1259
  # [+:extend+]
1496
1260
  # Specifies a module or array of modules that will be extended into the association object returned.
1497
1261
  # Useful for defining methods on associations, especially when they should be shared between multiple
@@ -1507,10 +1271,20 @@ module ActiveRecord
1507
1271
  # This is an optional option. By default Rails will attempt to derive the value automatically.
1508
1272
  # When the value is set the Array size must match associated model's primary key or +query_constraints+ size.
1509
1273
  # [+:index_errors+]
1510
- # Enables differentiation of multiple validation errors from the association records, by including
1274
+ # Allows differentiation of multiple validation errors from the association records, by including
1511
1275
  # an index in the error attribute name, e.g. +roles[2].level+.
1512
- # The index is based on association order, i.e. database order, with yet to be
1276
+ # When set to +true+, the index is based on association order, i.e. database order, with yet to be
1513
1277
  # persisted new records placed at the end.
1278
+ # When set to +:nested_attributes_order+, the index is based on the record order received by
1279
+ # nested attributes setter, when accepts_nested_attributes_for is used.
1280
+ # [:before_add]
1281
+ # Defines an {association callback}[rdoc-ref:Associations::ClassMethods@Association+callbacks] that gets triggered <b>before an object is added</b> to the association collection.
1282
+ # [:after_add]
1283
+ # Defines an {association callback}[rdoc-ref:Associations::ClassMethods@Association+callbacks] that gets triggered <b>after an object is added</b> to the association collection.
1284
+ # [:before_remove]
1285
+ # Defines an {association callback}[rdoc-ref:Associations::ClassMethods@Association+callbacks] that gets triggered <b>before an object is removed</b> from the association collection.
1286
+ # [:after_remove]
1287
+ # Defines an {association callback}[rdoc-ref:Associations::ClassMethods@Association+callbacks] that gets triggered <b>after an object is removed</b> from the association collection.
1514
1288
  #
1515
1289
  # Option examples:
1516
1290
  # has_many :comments, -> { order("posted_on") }
@@ -1524,16 +1298,18 @@ module ActiveRecord
1524
1298
  # has_many :subscribers, through: :subscriptions, disable_joins: true
1525
1299
  # has_many :comments, strict_loading: true
1526
1300
  # has_many :comments, query_constraints: [:blog_id, :post_id]
1527
- # has_many :comments, index_errors: true
1301
+ # has_many :comments, index_errors: :nested_attributes_order
1528
1302
  def has_many(name, scope = nil, **options, &extension)
1529
1303
  reflection = Builder::HasMany.build(self, name, scope, options, &extension)
1530
1304
  Reflection.add_reflection self, name, reflection
1531
1305
  end
1532
1306
 
1533
- # Specifies a one-to-one association with another class. This method should only be used
1534
- # if the other class contains the foreign key. If the current class contains the foreign key,
1535
- # then you should use #belongs_to instead. See also ActiveRecord::Associations::ClassMethods's overview
1536
- # on when to use #has_one and when to use #belongs_to.
1307
+ # Specifies a one-to-one association with another class. This method
1308
+ # should only be used if the other class contains the foreign key. If
1309
+ # the current class contains the foreign key, then you should use
1310
+ # #belongs_to instead. See {Is it a belongs_to or has_one
1311
+ # association?}[rdoc-ref:Associations::ClassMethods@Is+it+a+-23belongs_to+or+-23has_one+association-3F]
1312
+ # for more detail on when to use #has_one and when to use #belongs_to.
1537
1313
  #
1538
1314
  # The following methods for retrieval and query of a single associated object will be added:
1539
1315
  #
@@ -1648,8 +1424,10 @@ module ActiveRecord
1648
1424
  # If you are going to modify the association (rather than just read from it), then it is
1649
1425
  # a good idea to set the <tt>:inverse_of</tt> option on the source association on the
1650
1426
  # join model. This allows associated records to be built which will automatically create
1651
- # the appropriate join model records when they are saved. (See the 'Association Join Models'
1652
- # and 'Setting Inverses' sections above.)
1427
+ # the appropriate join model records when they are saved. See
1428
+ # {Association Join Models}[rdoc-ref:Associations::ClassMethods@Association+Join+Models]
1429
+ # and {Setting Inverses}[rdoc-ref:Associations::ClassMethods@Setting+Inverses] for
1430
+ # more detail.
1653
1431
  # [+:disable_joins+]
1654
1432
  # Specifies whether joins should be skipped for an association. If set to true, two or more queries
1655
1433
  # will be generated. Note that in some cases, if order or limit is applied, it will be done in-memory
@@ -1667,9 +1445,14 @@ module ActiveRecord
1667
1445
  # When set to +true+, validates new objects added to association when saving the parent object. +false+ by default.
1668
1446
  # If you want to ensure associated objects are revalidated on every update, use +validates_associated+.
1669
1447
  # [+:autosave+]
1670
- # If true, always save the associated object or destroy it if marked for destruction,
1671
- # when saving the parent object. If false, never save or destroy the associated object.
1672
- # By default, only save the associated object if it's a new record.
1448
+ # If +true+, always saves the associated object or destroys it if marked for destruction,
1449
+ # when saving the parent object.
1450
+ # If +false+, never save or destroy the associated object.
1451
+ #
1452
+ # By default, only saves the associated object if it's a new record. Setting this option
1453
+ # to +true+ also enables validations on the associated object unless explicitly disabled
1454
+ # with <tt>validate: false</tt>. This is because saving an object with invalid associated
1455
+ # objects would fail, so any associated objects will go through validation checks.
1673
1456
  #
1674
1457
  # Note that NestedAttributes::ClassMethods#accepts_nested_attributes_for sets
1675
1458
  # <tt>:autosave</tt> to <tt>true</tt>.
@@ -1682,7 +1465,8 @@ module ActiveRecord
1682
1465
  # [+:inverse_of+]
1683
1466
  # Specifies the name of the #belongs_to association on the associated object
1684
1467
  # that is the inverse of this #has_one association.
1685
- # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
1468
+ # See {Bi-directional associations}[rdoc-ref:Associations::ClassMethods@Bi-directional+associations]
1469
+ # for more detail.
1686
1470
  # [+:required+]
1687
1471
  # When set to +true+, the association will also have its presence validated.
1688
1472
  # This will validate the association itself, not the id. You can use
@@ -1716,10 +1500,12 @@ module ActiveRecord
1716
1500
  Reflection.add_reflection self, name, reflection
1717
1501
  end
1718
1502
 
1719
- # Specifies a one-to-one association with another class. This method should only be used
1720
- # if this class contains the foreign key. If the other class contains the foreign key,
1721
- # then you should use #has_one instead. See also ActiveRecord::Associations::ClassMethods's overview
1722
- # on when to use #has_one and when to use #belongs_to.
1503
+ # Specifies a one-to-one association with another class. This method
1504
+ # should only be used if this class contains the foreign key. If the
1505
+ # other class contains the foreign key, then you should use #has_one
1506
+ # instead. See {Is it a belongs_to or has_one
1507
+ # association?}[rdoc-ref:Associations::ClassMethods@Is+it+a+-23belongs_to+or+-23has_one+association-3F]
1508
+ # for more detail on when to use #has_one and when to use #belongs_to.
1723
1509
  #
1724
1510
  # Methods will be added for retrieval and query for a single associated object, for which
1725
1511
  # this object holds an id:
@@ -1822,15 +1608,25 @@ module ActiveRecord
1822
1608
  # named <tt>#{table_name}_count</tt> (such as +comments_count+ for a belonging Comment class)
1823
1609
  # is used on the associate class (such as a Post class) - that is the migration for
1824
1610
  # <tt>#{table_name}_count</tt> is created on the associate class (such that <tt>Post.comments_count</tt> will
1825
- # return the count cached, see note below). You can also specify a custom counter
1611
+ # return the count cached). You can also specify a custom counter
1826
1612
  # cache column by providing a column name instead of a +true+/+false+ value to this
1827
1613
  # option (e.g., <tt>counter_cache: :my_custom_counter</tt>.)
1828
- # Note: Specifying a counter cache will add it to that model's list of readonly attributes
1829
- # using +attr_readonly+.
1830
- # [+:polymorphic+]
1831
- # Specify this association is a polymorphic association by passing +true+.
1614
+ #
1615
+ # Starting to use counter caches on existing large tables can be troublesome, because the column
1616
+ # values must be backfilled separately of the column addition (to not lock the table for too long)
1617
+ # and before the use of +:counter_cache+ (otherwise methods like +size+/+any?+/etc, which use
1618
+ # counter caches internally, can produce incorrect results). To safely backfill the values while keeping
1619
+ # counter cache columns updated with the child records creation/removal and to avoid the mentioned methods
1620
+ # use the possibly incorrect counter cache column values and always get the results from the database,
1621
+ # use <tt>counter_cache: { active: false }</tt>.
1622
+ # If you also need to specify a custom column name, use <tt>counter_cache: { active: false, column: :my_custom_counter }</tt>.
1623
+ #
1832
1624
  # Note: If you've enabled the counter cache, then you may want to add the counter cache attribute
1833
1625
  # to the +attr_readonly+ list in the associated classes (e.g. <tt>class Post; attr_readonly :comments_count; end</tt>).
1626
+ # [+:polymorphic+]
1627
+ # Specify this association is a polymorphic association by passing +true+.
1628
+ # Note: Since polymorphic associations rely on storing class names in the database, make sure to update the class names in the
1629
+ # <tt>*_type</tt> polymorphic type column of the corresponding rows.
1834
1630
  # [+:validate+]
1835
1631
  # When set to +true+, validates new objects added to association when saving the parent object. +false+ by default.
1836
1632
  # If you want to ensure associated objects are revalidated on every update, use +validates_associated+.
@@ -1851,7 +1647,8 @@ module ActiveRecord
1851
1647
  # [+:inverse_of+]
1852
1648
  # Specifies the name of the #has_one or #has_many association on the associated
1853
1649
  # object that is the inverse of this #belongs_to association.
1854
- # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
1650
+ # See {Bi-directional associations}[rdoc-ref:Associations::ClassMethods@Bi-directional+associations]
1651
+ # for more detail.
1855
1652
  # [+:optional+]
1856
1653
  # When set to +true+, the association will not have its presence validated.
1857
1654
  # [+:required+]
@@ -1888,7 +1685,7 @@ module ActiveRecord
1888
1685
  # belongs_to :user, optional: true
1889
1686
  # belongs_to :account, default: -> { company.account }
1890
1687
  # belongs_to :account, strict_loading: true
1891
- # belong_to :note, query_constraints: [:organization_id, :note_id]
1688
+ # belongs_to :note, query_constraints: [:organization_id, :note_id]
1892
1689
  def belongs_to(name, scope = nil, **options)
1893
1690
  reflection = Builder::BelongsTo.build(self, name, scope, options)
1894
1691
  Reflection.add_reflection self, name, reflection
@@ -1911,7 +1708,7 @@ module ActiveRecord
1911
1708
  # The join table should not have a primary key or a model associated with it. You must manually generate the
1912
1709
  # join table with a migration such as this:
1913
1710
  #
1914
- # class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration[7.1]
1711
+ # class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration[8.0]
1915
1712
  # def change
1916
1713
  # create_join_table :developers, :projects
1917
1714
  # end
@@ -2105,7 +1902,7 @@ module ActiveRecord
2105
1902
  end
2106
1903
 
2107
1904
  has_many name, scope, **hm_options, &extension
2108
- _reflections[name.to_s].parent_reflection = habtm_reflection
1905
+ _reflections[name].parent_reflection = habtm_reflection
2109
1906
  end
2110
1907
  end
2111
1908
  end
@@ -1,29 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "concurrent/atomic/atomic_boolean"
4
+ require "concurrent/atomic/read_write_lock"
5
+
3
6
  module ActiveRecord
4
7
  class AsynchronousQueriesTracker # :nodoc:
5
- module NullSession # :nodoc:
6
- class << self
7
- def active?
8
- true
9
- end
10
-
11
- def finalize
12
- end
13
- end
14
- end
15
-
16
8
  class Session # :nodoc:
17
9
  def initialize
18
- @active = true
10
+ @active = Concurrent::AtomicBoolean.new(true)
11
+ @lock = Concurrent::ReadWriteLock.new
19
12
  end
20
13
 
21
14
  def active?
22
- @active
15
+ @active.true?
23
16
  end
24
17
 
25
- def finalize
26
- @active = false
18
+ def synchronize(&block)
19
+ @lock.with_read_lock(&block)
20
+ end
21
+
22
+ def finalize(wait = false)
23
+ @active.make_false
24
+ if wait
25
+ # Wait until all thread with a read lock are done
26
+ @lock.with_write_lock { }
27
+ end
27
28
  end
28
29
  end
29
30
 
@@ -33,7 +34,7 @@ module ActiveRecord
33
34
  end
34
35
 
35
36
  def run
36
- ActiveRecord::Base.asynchronous_queries_tracker.start_session
37
+ ActiveRecord::Base.asynchronous_queries_tracker.tap(&:start_session)
37
38
  end
38
39
 
39
40
  def complete(asynchronous_queries_tracker)
@@ -41,20 +42,23 @@ module ActiveRecord
41
42
  end
42
43
  end
43
44
 
44
- attr_reader :current_session
45
-
46
45
  def initialize
47
- @current_session = NullSession
46
+ @stack = []
47
+ end
48
+
49
+ def current_session
50
+ @stack.last or raise ActiveRecordError, "Can't perform asynchronous queries without a query session"
48
51
  end
49
52
 
50
53
  def start_session
51
- @current_session = Session.new
52
- self
54
+ session = Session.new
55
+ @stack << session
53
56
  end
54
57
 
55
- def finalize_session
56
- @current_session.finalize
57
- @current_session = NullSession
58
+ def finalize_session(wait = false)
59
+ session = @stack.pop
60
+ session&.finalize(wait)
61
+ self
58
62
  end
59
63
  end
60
64
  end
@@ -2,8 +2,6 @@
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
7
  multi_parameter_attributes = nested_parameter_attributes = nil