activerecord 7.1.3 → 7.1.5.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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +324 -0
  3. data/lib/active_record/associations/belongs_to_association.rb +4 -4
  4. data/lib/active_record/associations/collection_association.rb +4 -4
  5. data/lib/active_record/associations/has_many_association.rb +2 -2
  6. data/lib/active_record/associations/has_one_association.rb +2 -2
  7. data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
  8. data/lib/active_record/associations/join_dependency.rb +10 -12
  9. data/lib/active_record/associations.rb +6 -0
  10. data/lib/active_record/attribute_methods/dirty.rb +2 -2
  11. data/lib/active_record/attribute_methods/read.rb +3 -3
  12. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -0
  13. data/lib/active_record/attribute_methods/write.rb +3 -3
  14. data/lib/active_record/attribute_methods.rb +46 -6
  15. data/lib/active_record/autosave_association.rb +5 -2
  16. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +8 -1
  17. data/lib/active_record/connection_adapters/abstract/database_statements.rb +1 -1
  18. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
  19. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +8 -4
  20. data/lib/active_record/connection_adapters/abstract_adapter.rb +14 -15
  21. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +37 -13
  22. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +2 -1
  23. data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -10
  24. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  25. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +3 -1
  26. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -2
  27. data/lib/active_record/connection_adapters/trilogy_adapter.rb +16 -20
  28. data/lib/active_record/core.rb +8 -3
  29. data/lib/active_record/counter_cache.rb +7 -3
  30. data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -1
  31. data/lib/active_record/database_configurations/hash_config.rb +6 -2
  32. data/lib/active_record/delegated_type.rb +6 -6
  33. data/lib/active_record/destroy_association_async_job.rb +1 -1
  34. data/lib/active_record/encryption/encrypted_attribute_type.rb +2 -2
  35. data/lib/active_record/encryption/scheme.rb +8 -4
  36. data/lib/active_record/encryption.rb +2 -0
  37. data/lib/active_record/enum.rb +1 -1
  38. data/lib/active_record/future_result.rb +9 -0
  39. data/lib/active_record/gem_version.rb +2 -2
  40. data/lib/active_record/locking/optimistic.rb +1 -1
  41. data/lib/active_record/marshalling.rb +4 -1
  42. data/lib/active_record/message_pack.rb +1 -1
  43. data/lib/active_record/migration/compatibility.rb +6 -0
  44. data/lib/active_record/model_schema.rb +7 -2
  45. data/lib/active_record/nested_attributes.rb +13 -2
  46. data/lib/active_record/persistence.rb +2 -2
  47. data/lib/active_record/query_cache.rb +1 -1
  48. data/lib/active_record/query_logs_formatter.rb +1 -1
  49. data/lib/active_record/railtie.rb +14 -14
  50. data/lib/active_record/railties/databases.rake +2 -2
  51. data/lib/active_record/reflection.rb +8 -2
  52. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +6 -1
  53. data/lib/active_record/relation/query_methods.rb +21 -7
  54. data/lib/active_record/relation.rb +13 -3
  55. data/lib/active_record/result.rb +1 -1
  56. data/lib/active_record/tasks/database_tasks.rb +30 -8
  57. data/lib/active_record/test_fixtures.rb +1 -0
  58. data/lib/active_record/timestamp.rb +3 -1
  59. data/lib/active_record.rb +2 -2
  60. data/lib/arel/tree_manager.rb +5 -1
  61. data/lib/arel/visitors/to_sql.rb +2 -1
  62. metadata +13 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9f7c51f040979b7650eb209b0dc856a0877bccc968dd99cde06e3cf1776dd638
4
- data.tar.gz: b81eca56b185f087c95b6691b307ff1c9c63d0d03ff650b729e943888f053152
3
+ metadata.gz: 53c82b47d80060218db2fd5a44483fe30a9ce2a7e235c30e894d4d6a9c0e7d35
4
+ data.tar.gz: b5432955eae8f100ceb96c40759de6932288b1aedf78d6c4b08cfb223b7adb8d
5
5
  SHA512:
6
- metadata.gz: 6f68dc9e6cdccf23f5467810a68083564760601aa3c82b4c85720cfb2c39500439a86bb63c2ce8fea37337de6fc4f157722300c691cfd239e92732569b5c20f6
7
- data.tar.gz: 6f8382df74c2c16b873cab0effeda0a3ef3e578d5087e435d7d2f8c6890dcf0d85e2211f2bc890182af49e836b09e4e345e2b9bc37eb8472814e98bb6636b005
6
+ metadata.gz: 935ee9fbc8c855663b10de47bffa2b7d51328a9cc7575f4aa5c9b26eaf0cc1ed9c4271e3c3f5982ceafbb00a679b0262b18782f165b31454fafc4ce6966b1575
7
+ data.tar.gz: 53dba913a6174f80766bc995dcfad80c1fbdf2f461474ed8b7e13a1cc8e6a321076f1e74a8a5ace878fce08154dd3a6dff51263365d982d22be50bd4c928de9e
data/CHANGELOG.md CHANGED
@@ -1,3 +1,327 @@
1
+ ## Rails 7.1.5.1 (December 10, 2024) ##
2
+
3
+ * No changes.
4
+
5
+
6
+ ## Rails 7.1.5 (October 30, 2024) ##
7
+
8
+ * Fix marshalling of unsaved associated records in 7.1 format.
9
+
10
+ The 7.1 format would only marshal associated records if the association was loaded.
11
+ But associations that would only contain unsaved records would be skipped.
12
+
13
+ *Jean Boussier*
14
+
15
+ * Fix an issue where `.left_outer_joins` used with multiple associations that have
16
+ the same child association but different parents does not join all parents.
17
+
18
+ Previously, using `.left_outer_joins` with the same child association would only join one of the parents.
19
+
20
+ Now it will correctly join both parents.
21
+
22
+ Fixes #41498.
23
+
24
+ *Garrett Blehm*
25
+
26
+ * Ensure `ActiveRecord::Encryption.config` is always ready before access.
27
+
28
+ Previously, `ActiveRecord::Encryption` configuration was deferred until `ActiveRecord::Base`
29
+ was loaded. Therefore, accessing `ActiveRecord::Encryption.config` properties before
30
+ `ActiveRecord::Base` was loaded would give incorrect results.
31
+
32
+ `ActiveRecord::Encryption` now has its own loading hook so that its configuration is set as
33
+ soon as needed.
34
+
35
+ When `ActiveRecord::Base` is loaded, even lazily, it in turn triggers the loading of
36
+ `ActiveRecord::Encryption`, thus preserving the original behavior of having its config ready
37
+ before any use of `ActiveRecord::Base`.
38
+
39
+ *Maxime Réty*
40
+
41
+ * Add `TimeZoneConverter#==` method, so objects will be properly compared by
42
+ their type, scale, limit & precision.
43
+
44
+ Address #52699.
45
+
46
+ *Ruy Rocha*
47
+
48
+
49
+ ## Rails 7.1.4.2 (October 23, 2024) ##
50
+
51
+ * No changes.
52
+
53
+
54
+ ## Rails 7.1.4.1 (October 15, 2024) ##
55
+
56
+ * No changes.
57
+
58
+
59
+ ## Rails 7.1.4 (August 22, 2024) ##
60
+
61
+ * Allow to eager load nested nil associations.
62
+
63
+ *fatkodima*
64
+
65
+ * Fix `create_table` with `:auto_increment` option for MySQL adapter.
66
+
67
+ *fatkodima*
68
+
69
+ * Don't load has_one associations during autosave.
70
+
71
+ *Eugene Kenny*
72
+
73
+ * Fix migration ordering for `bin/rails db:prepare` across databases.
74
+
75
+ *fatkodima*
76
+
77
+ * Fix `alias_attribute` to ignore methods defined in parent classes.
78
+
79
+ *Jean Boussier*
80
+
81
+ * Fix a performance regression in attribute methods.
82
+
83
+ *Jean Boussier*
84
+
85
+ * Fix Active Record configs variable shadowing.
86
+
87
+ *Joel Lubrano*
88
+
89
+ * Fix running migrations on other databases when `database_tasks: false` on primary.
90
+
91
+ *fatkodima*
92
+
93
+ * Fix non-partial inserts for models with composite identity primary keys.
94
+
95
+ *fatkodima*
96
+
97
+ * Fix `ActiveRecord::Relation#touch_all` with custom attribute aliased as attribute for update.
98
+
99
+ *fatkodima*
100
+
101
+ * Fix a crash when an Executor wrapped fork exit.
102
+
103
+ *Joé Dupuis*
104
+
105
+ * Fix `destroy_async` job for owners with composite primary keys.
106
+
107
+ *fatkodima*
108
+
109
+ * Ensure pre-7.1 migrations use legacy index names when using `rename_table`.
110
+
111
+ *fatkodima*
112
+
113
+ * Allow `primary_key:` association option to be composite.
114
+
115
+ *Nikita Vasilevsky*
116
+
117
+ * Do not try to alias on key update when raw SQL is supplied.
118
+
119
+ *Gabriel Amaral*
120
+
121
+ * Memoize `key_provider` from `key` or deterministic `key_provider` if any.
122
+
123
+ *Rosa Gutierrez*
124
+
125
+ * Fix `upsert` warning for MySQL.
126
+
127
+ *fatkodima*
128
+
129
+ * Fix predicate builder for polymorphic models referencing models with composite primary keys.
130
+
131
+ *fatkodima*
132
+
133
+ * Fix `update_all/delete_all` on CPK model relation with join subquery.
134
+
135
+ *Nikita Vasilevsky*
136
+
137
+ * Remove memoization to accept `key_provider` overridden by `with_encryption_context`.
138
+
139
+ *John Hawthorn*
140
+
141
+ * Raise error for Trilogy when prepared_statements is true.
142
+
143
+ Trilogy doesn't currently support prepared statements. The error that
144
+ applications would see is a `StatementInvalid` error. This doesn't quite point
145
+ you to the fact this isn't supported. So raise a more appropriate error
146
+ pointing to what to change.
147
+
148
+ *Eileen M. Uchitelle*
149
+
150
+ * Fix loading schema cache when all databases have disabled database tasks.
151
+
152
+ *fatkodima*
153
+
154
+ * Always request `primary_key` in `RETURNING` if no other columns requested.
155
+
156
+ *Nikita Vasilevsky*
157
+
158
+ * Handle records being loaded with Marshal without triggering schema load
159
+
160
+ When using the old marshalling format for Active Record and loading
161
+ a serialized instance, it didn't trigger loading the schema and defining
162
+ attribute methods.
163
+
164
+ *Jean Boussier*
165
+
166
+ * Prevent some constant redefinition warnings when defining `inherited` on models.
167
+
168
+ *Adrian Hirt*
169
+
170
+ * Fix a memory perfomance regression in attribute methods.
171
+
172
+ Attribute methods used much more memory and were slower to define than
173
+ they should have been.
174
+
175
+ *Jean Boussier*
176
+
177
+ * Fix an issue that could cause database connection leaks.
178
+
179
+ If Active Record successfully connected to the database, but then failed
180
+ to read the server informations, the connection would be leaked until the
181
+ Ruby garbage collector triggers.
182
+
183
+ *Jean Boussier*
184
+
185
+ * Fix an issue where the IDs reader method did not return expected results
186
+ for preloaded associations in models using composite primary keys.
187
+
188
+ *Jay Ang*
189
+
190
+ * PostgreSQL `Cidr#change?` detects the address prefix change.
191
+
192
+ *Taketo Takashima*
193
+
194
+ * Fix Active Record serialization to not include instantiated but not loaded associations
195
+
196
+ *Jean Boussier*, *Ben Kyriakou*
197
+
198
+ * Allow `Sqlite3Adapter` to use `sqlite3` gem version `2.x`
199
+
200
+ *Mike Dalessio*
201
+
202
+ * Strict loading using `:n_plus_one_only` does not eagerly load child associations.
203
+
204
+ With this change, child associations are no longer eagerly loaded, to
205
+ match intended behavior and to prevent non-deterministic order issues caused
206
+ by calling methods like `first` or `last`. As `first` and `last` don't cause
207
+ an N+1 by themselves, calling child associations will no longer raise.
208
+ Fixes #49473.
209
+
210
+ Before:
211
+
212
+ ```ruby
213
+ person = Person.find(1)
214
+ person.strict_loading!(mode: :n_plus_one_only)
215
+ person.posts.first
216
+ # SELECT * FROM posts WHERE person_id = 1; -- non-deterministic order
217
+ person.posts.first.firm # raises ActiveRecord::StrictLoadingViolationError
218
+ ```
219
+
220
+ After:
221
+
222
+ ```ruby
223
+ person = Person.find(1)
224
+ person.strict_loading!(mode: :n_plus_one_only)
225
+ person.posts.first # this is 1+1, not N+1
226
+ # SELECT * FROM posts WHERE person_id = 1 ORDER BY id LIMIT 1;
227
+ person.posts.first.firm # no longer raises
228
+ ```
229
+
230
+ *Reid Lynch*
231
+
232
+ * Using `Model.query_constraints` with a single non-primary-key column used to raise as expected, but with an
233
+ incorrect error message. This has been fixed to raise with a more appropriate error message.
234
+
235
+ *Joshua Young*
236
+
237
+ * Fix `has_one` association autosave setting the foreign key attribute when it is unchanged.
238
+
239
+ This behaviour is also inconsistent with autosaving `belongs_to` and can have unintended side effects like raising
240
+ an `ActiveRecord::ReadonlyAttributeError` when the foreign key attribute is marked as read-only.
241
+
242
+ *Joshua Young*
243
+
244
+ * Fix an issue where `ActiveRecord::Encryption` configurations are not ready before the loading
245
+ of Active Record models, when an application is eager loaded. As a result, encrypted attributes
246
+ could be misconfigured in some cases.
247
+
248
+ *Maxime Réty*
249
+
250
+ * Properly synchronize `Mysql2Adapter#active?` and `TrilogyAdapter#active?`
251
+
252
+ As well as `disconnect!` and `verify!`.
253
+
254
+ This generally isn't a big problem as connections must not be shared between
255
+ threads, but is required when running transactional tests or system tests
256
+ and could lead to a SEGV.
257
+
258
+ *Jean Boussier*
259
+
260
+ * Fix counter caches when the foreign key is composite.
261
+
262
+ If the model holding the counter cache had a composite primary key,
263
+ inserting a dependent record would fail with an `ArgumentError`
264
+ `Expected corresponding value for...`
265
+
266
+ *fatkodima*
267
+
268
+ * Fix loading of schema cache for multiple databases.
269
+
270
+ Before this change, if you have multiple databases configured in your
271
+ application, and had schema cache present, Rails would load the same
272
+ cache to all databases.
273
+
274
+ *Rafael Mendonça França*
275
+
276
+ * Fix eager loading of composite primary key associations.
277
+
278
+ `relation.eager_load(:other_model)` could load the wrong records if `other_model`
279
+ had a composite primary key.
280
+
281
+ *Nikita Vasilevsky*
282
+
283
+ * Fix async queries returning a doubly wrapped result when hitting the query cache.
284
+
285
+ *fatkodima*
286
+
287
+ * Fix single quote escapes on default generated MySQL columns
288
+
289
+ MySQL 5.7.5+ supports generated columns, which can be used to create a column that is computed from an expression.
290
+
291
+ Previously, the schema dump would output a string with double escapes for generated columns with single quotes in the default expression.
292
+
293
+ This would result in issues when importing the schema on a fresh instance of a MySQL database.
294
+
295
+ Now, the string will not be escaped and will be valid Ruby upon importing of the schema.
296
+
297
+ *Yash Kapadia*
298
+
299
+ * Fix Migrations with versions older than 7.1 validating options given to
300
+ `t.references`.
301
+
302
+ *Hartley McGuire*
303
+
304
+
305
+ ## Rails 7.1.3.4 (June 04, 2024) ##
306
+
307
+ * No changes.
308
+
309
+
310
+ ## Rails 7.1.3.3 (May 16, 2024) ##
311
+
312
+ * No changes.
313
+
314
+
315
+ ## Rails 7.1.3.2 (February 21, 2024) ##
316
+
317
+ * No changes.
318
+
319
+
320
+ ## Rails 7.1.3.1 (February 21, 2024) ##
321
+
322
+ * No changes.
323
+
324
+
1
325
  ## Rails 7.1.3 (January 16, 2024) ##
2
326
 
3
327
  * Fix Migrations with versions older than 7.1 validating options given to
@@ -12,11 +12,11 @@ module ActiveRecord
12
12
  raise ActiveRecord::Rollback unless target.destroy
13
13
  when :destroy_async
14
14
  if reflection.foreign_key.is_a?(Array)
15
- primary_key_column = reflection.active_record_primary_key.map(&:to_sym)
16
- id = reflection.foreign_key.map { |col| owner.public_send(col.to_sym) }
15
+ primary_key_column = reflection.active_record_primary_key
16
+ id = reflection.foreign_key.map { |col| owner.public_send(col) }
17
17
  else
18
- primary_key_column = reflection.active_record_primary_key.to_sym
19
- id = owner.public_send(reflection.foreign_key.to_sym)
18
+ primary_key_column = reflection.active_record_primary_key
19
+ id = owner.public_send(reflection.foreign_key)
20
20
  end
21
21
 
22
22
  enqueue_destroy_association(
@@ -48,11 +48,11 @@ module ActiveRecord
48
48
  # Implements the ids reader method, e.g. foo.item_ids for Foo.has_many :items
49
49
  def ids_reader
50
50
  if loaded?
51
- target.pluck(reflection.association_primary_key)
51
+ target.pluck(*reflection.association_primary_key)
52
52
  elsif !target.empty?
53
- load_target.pluck(reflection.association_primary_key)
53
+ load_target.pluck(*reflection.association_primary_key)
54
54
  else
55
- @association_ids ||= scope.pluck(reflection.association_primary_key)
55
+ @association_ids ||= scope.pluck(*reflection.association_primary_key)
56
56
  end
57
57
  end
58
58
 
@@ -303,7 +303,7 @@ module ActiveRecord
303
303
 
304
304
  def find_from_target?
305
305
  loaded? ||
306
- owner.strict_loading? ||
306
+ (owner.strict_loading? && owner.strict_loading_all?) ||
307
307
  reflection.strict_loading? ||
308
308
  owner.new_record? ||
309
309
  target.any? { |record| record.new_record? || record.changed? }
@@ -35,10 +35,10 @@ module ActiveRecord
35
35
  unless target.empty?
36
36
  association_class = target.first.class
37
37
  if association_class.query_constraints_list
38
- primary_key_column = association_class.query_constraints_list.map(&:to_sym)
38
+ primary_key_column = association_class.query_constraints_list
39
39
  ids = target.collect { |assoc| primary_key_column.map { |col| assoc.public_send(col) } }
40
40
  else
41
- primary_key_column = association_class.primary_key.to_sym
41
+ primary_key_column = association_class.primary_key
42
42
  ids = target.collect { |assoc| assoc.public_send(primary_key_column) }
43
43
  end
44
44
 
@@ -34,10 +34,10 @@ module ActiveRecord
34
34
  throw(:abort) unless target.destroyed?
35
35
  when :destroy_async
36
36
  if target.class.query_constraints_list
37
- primary_key_column = target.class.query_constraints_list.map(&:to_sym)
37
+ primary_key_column = target.class.query_constraints_list
38
38
  id = primary_key_column.map { |col| target.public_send(col) }
39
39
  else
40
- primary_key_column = target.class.primary_key.to_sym
40
+ primary_key_column = target.class.primary_key
41
41
  id = target.public_send(primary_key_column)
42
42
  end
43
43
 
@@ -25,8 +25,9 @@ module ActiveRecord
25
25
  joins = []
26
26
  chain = []
27
27
 
28
- reflection.chain.each do |reflection|
29
- table, terminated = yield reflection
28
+ reflection_chain = reflection.chain
29
+ reflection_chain.each_with_index do |reflection, index|
30
+ table, terminated = yield reflection, reflection_chain[index..]
30
31
  @table ||= table
31
32
 
32
33
  if terminated
@@ -61,7 +61,7 @@ module ActiveRecord
61
61
  when Hash
62
62
  associations.each do |k, v|
63
63
  cache = hash[k] ||= {}
64
- walk_tree v, cache
64
+ walk_tree v, cache if v
65
65
  end
66
66
  else
67
67
  raise ConfigurationError, associations.inspect
@@ -190,12 +190,12 @@ module ActiveRecord
190
190
  def make_constraints(parent, child, join_type)
191
191
  foreign_table = parent.table
192
192
  foreign_klass = parent.base_klass
193
- child.join_constraints(foreign_table, foreign_klass, join_type, alias_tracker) do |reflection|
194
- table, terminated = @joined_tables[reflection]
193
+ child.join_constraints(foreign_table, foreign_klass, join_type, alias_tracker) do |reflection, remaining_reflection_chain|
194
+ table, terminated = @joined_tables[remaining_reflection_chain]
195
195
  root = reflection == child.reflection
196
196
 
197
197
  if table && (!root || !terminated)
198
- @joined_tables[reflection] = [table, root] if root
198
+ @joined_tables[remaining_reflection_chain] = [table, root] if root
199
199
  next table, true
200
200
  end
201
201
 
@@ -206,7 +206,7 @@ module ActiveRecord
206
206
  root ? name : "#{name}_join"
207
207
  end
208
208
 
209
- @joined_tables[reflection] ||= [table, root] if join_type == Arel::Nodes::OuterJoin
209
+ @joined_tables[remaining_reflection_chain] ||= [table, root] if join_type == Arel::Nodes::OuterJoin
210
210
  table
211
211
  end.concat child.children.flat_map { |c| make_constraints(child, c, join_type) }
212
212
  end
@@ -254,10 +254,10 @@ module ActiveRecord
254
254
 
255
255
  if node.primary_key
256
256
  keys = Array(node.primary_key).map { |column| aliases.column_alias(node, column) }
257
- ids = keys.map { |key| row[key] }
257
+ id = keys.map { |key| row[key] }
258
258
  else
259
259
  keys = Array(node.reflection.join_primary_key).map { |column| aliases.column_alias(node, column.to_s) }
260
- ids = keys.map { nil } # Avoid id-based model caching.
260
+ id = keys.map { nil } # Avoid id-based model caching.
261
261
  end
262
262
 
263
263
  if keys.any? { |key| row[key].nil? }
@@ -266,11 +266,9 @@ module ActiveRecord
266
266
  next
267
267
  end
268
268
 
269
- ids.each do |id|
270
- unless model = seen[ar_parent][node][id]
271
- model = construct_model(ar_parent, node, row, model_cache, id, strict_loading_value)
272
- seen[ar_parent][node][id] = model if id
273
- end
269
+ unless model = seen[ar_parent][node][id]
270
+ model = construct_model(ar_parent, node, row, model_cache, id, strict_loading_value)
271
+ seen[ar_parent][node][id] = model if id
274
272
  end
275
273
 
276
274
  construct(model, node, row, seen, model_cache, strict_loading_value)
@@ -1506,6 +1506,11 @@ module ActiveRecord
1506
1506
  # Serves as a composite foreign key. Defines the list of columns to be used to query the associated object.
1507
1507
  # This is an optional option. By default Rails will attempt to derive the value automatically.
1508
1508
  # When the value is set the Array size must match associated model's primary key or +query_constraints+ size.
1509
+ # [+:index_errors+]
1510
+ # Enables differentiation of multiple validation errors from the association records, by including
1511
+ # 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
1513
+ # persisted new records placed at the end.
1509
1514
  #
1510
1515
  # Option examples:
1511
1516
  # has_many :comments, -> { order("posted_on") }
@@ -1519,6 +1524,7 @@ module ActiveRecord
1519
1524
  # has_many :subscribers, through: :subscriptions, disable_joins: true
1520
1525
  # has_many :comments, strict_loading: true
1521
1526
  # has_many :comments, query_constraints: [:blog_id, :post_id]
1527
+ # has_many :comments, index_errors: true
1522
1528
  def has_many(name, scope = nil, **options, &extension)
1523
1529
  reflection = Builder::HasMany.build(self, name, scope, options, &extension)
1524
1530
  Reflection.add_reflection self, name, reflection
@@ -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
@@ -8,11 +8,11 @@ module ActiveRecord
8
8
 
9
9
  module ClassMethods # :nodoc:
10
10
  private
11
- def define_method_attribute(name, owner:)
11
+ def define_method_attribute(canonical_name, owner:, as: canonical_name)
12
12
  ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
13
- owner, name
13
+ owner, canonical_name
14
14
  ) do |temp_method_name, attr_name_expr|
15
- owner.define_cached_method(name, as: temp_method_name, namespace: :active_record) do |batch|
15
+ owner.define_cached_method(temp_method_name, as: as, namespace: :active_record) do |batch|
16
16
  batch <<
17
17
  "def #{temp_method_name}" <<
18
18
  " _read_attribute(#{attr_name_expr}) { |n| missing_attribute(n, caller) }" <<
@@ -32,6 +32,10 @@ module ActiveRecord
32
32
  end
33
33
  end
34
34
 
35
+ def ==(other)
36
+ other.is_a?(self.class) && __getobj__ == other.__getobj__
37
+ end
38
+
35
39
  private
36
40
  def convert_time_to_time_zone(value)
37
41
  return if value.nil?
@@ -12,11 +12,11 @@ module ActiveRecord
12
12
 
13
13
  module ClassMethods # :nodoc:
14
14
  private
15
- def define_method_attribute=(name, owner:)
15
+ def define_method_attribute=(canonical_name, owner:, as: canonical_name)
16
16
  ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
17
- owner, name, writer: true,
17
+ owner, canonical_name, writer: true,
18
18
  ) do |temp_method_name, attr_name_expr|
19
- owner.define_cached_method("#{name}=", as: temp_method_name, namespace: :active_record) do |batch|
19
+ owner.define_cached_method(temp_method_name, as: "#{as}=", namespace: :active_record) do |batch|
20
20
  batch <<
21
21
  "def #{temp_method_name}(value)" <<
22
22
  " _write_attribute(#{attr_name_expr}, value)" <<
@@ -66,7 +66,7 @@ module ActiveRecord
66
66
 
67
67
  def generate_alias_attributes # :nodoc:
68
68
  superclass.generate_alias_attributes unless superclass == Base
69
- return if @alias_attributes_mass_generated
69
+ return false if @alias_attributes_mass_generated
70
70
 
71
71
  generated_attribute_methods.synchronize do
72
72
  return if @alias_attributes_mass_generated
@@ -80,12 +80,19 @@ module ActiveRecord
80
80
 
81
81
  @alias_attributes_mass_generated = true
82
82
  end
83
+ true
83
84
  end
84
85
 
85
- def alias_attribute_method_definition(code_generator, pattern, new_name, old_name)
86
+ def generate_alias_attribute_methods(code_generator, new_name, old_name) # :nodoc:
87
+ attribute_method_patterns.each do |pattern|
88
+ alias_attribute_method_definition(code_generator, pattern, new_name, old_name)
89
+ end
90
+ attribute_method_patterns_cache.clear
91
+ end
92
+
93
+ def alias_attribute_method_definition(code_generator, pattern, new_name, old_name) # :nodoc:
86
94
  method_name = pattern.method_name(new_name).to_s
87
95
  target_name = pattern.method_name(old_name).to_s
88
- parameters = pattern.parameters
89
96
  old_name = old_name.to_s
90
97
 
91
98
  method_defined = method_defined?(target_name) || private_method_defined?(target_name)
@@ -115,9 +122,7 @@ module ActiveRecord
115
122
  )
116
123
  super
117
124
  else
118
- define_proxy_call(code_generator, method_name, pattern.proxy_target, parameters, old_name,
119
- namespace: :proxy_alias_attribute
120
- )
125
+ define_attribute_method_pattern(pattern, old_name, owner: code_generator, as: new_name, override: true)
121
126
  end
122
127
  end
123
128
 
@@ -133,6 +138,11 @@ module ActiveRecord
133
138
  super(attribute_names)
134
139
  @attribute_methods_generated = true
135
140
  end
141
+ true
142
+ end
143
+
144
+ def attribute_methods_generated? # :nodoc:
145
+ @attribute_methods_generated && @alias_attributes_mass_generated
136
146
  end
137
147
 
138
148
  def undefine_attribute_methods # :nodoc:
@@ -459,6 +469,36 @@ module ActiveRecord
459
469
  end
460
470
 
461
471
  private
472
+ def respond_to_missing?(name, include_private = false)
473
+ if self.class.define_attribute_methods
474
+ # Some methods weren't defined yet.
475
+ return true if self.class.method_defined?(name)
476
+ return true if include_private && self.class.private_method_defined?(name)
477
+ end
478
+
479
+ super
480
+ end
481
+
482
+ def method_missing(name, ...)
483
+ unless self.class.attribute_methods_generated?
484
+ if self.class.method_defined?(name)
485
+ # The method is explicitly defined in the model, but calls a generated
486
+ # method with super. So we must resume the call chain at the right setp.
487
+ last_method = method(name)
488
+ last_method = last_method.super_method while last_method.super_method
489
+ self.class.define_attribute_methods
490
+ if last_method.super_method
491
+ return last_method.super_method.call(...)
492
+ end
493
+ elsif self.class.define_attribute_methods | self.class.generate_alias_attributes
494
+ # Some attribute methods weren't generated yet, we retry the call
495
+ return public_send(name, ...)
496
+ end
497
+ end
498
+
499
+ super
500
+ end
501
+
462
502
  def attribute_method?(attr_name)
463
503
  # We check defined? because Syck calls respond_to? before actually calling initialize.
464
504
  defined?(@attributes) && @attributes.key?(attr_name)
@@ -441,7 +441,9 @@ module ActiveRecord
441
441
  # ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
442
442
  def save_has_one_association(reflection)
443
443
  association = association_instance_get(reflection.name)
444
- record = association && association.load_target
444
+ return unless association && association.loaded?
445
+
446
+ record = association.load_target
445
447
 
446
448
  if record && !record.destroyed?
447
449
  autosave = reflection.options[:autosave]
@@ -458,7 +460,8 @@ module ActiveRecord
458
460
  primary_key_foreign_key_pairs = primary_key.zip(foreign_key)
459
461
 
460
462
  primary_key_foreign_key_pairs.each do |primary_key, foreign_key|
461
- record[foreign_key] = _read_attribute(primary_key)
463
+ association_id = _read_attribute(primary_key)
464
+ record[foreign_key] = association_id unless record[foreign_key] == association_id
462
465
  end
463
466
  association.set_inverse_instance(record)
464
467
  end