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
@@ -146,6 +146,11 @@ module ActiveRecord
146
146
  # your own model for something else, you can set +inheritance_column+:
147
147
  #
148
148
  # self.inheritance_column = 'zoink'
149
+ #
150
+ # If you wish to disable single-table inheritance altogether you can set
151
+ # +inheritance_column+ to +nil+
152
+ #
153
+ # self.inheritance_column = nil
149
154
 
150
155
  ##
151
156
  # :singleton-method: inheritance_column=
@@ -271,15 +276,14 @@ module ActiveRecord
271
276
  end
272
277
 
273
278
  @table_name = value
274
- @quoted_table_name = nil
275
279
  @arel_table = nil
276
- @sequence_name = nil unless defined?(@explicit_sequence_name) && @explicit_sequence_name
280
+ @sequence_name = nil unless @explicit_sequence_name
277
281
  @predicate_builder = nil
278
282
  end
279
283
 
280
- # Returns a quoted version of the table name, used to construct SQL statements.
284
+ # Returns a quoted version of the table name.
281
285
  def quoted_table_name
282
- @quoted_table_name ||= connection.quote_table_name(table_name)
286
+ adapter_class.quote_table_name(table_name)
283
287
  end
284
288
 
285
289
  # Computes the table name, (re)sets it internally, and returns it.
@@ -374,7 +378,7 @@ module ActiveRecord
374
378
 
375
379
  def reset_sequence_name # :nodoc:
376
380
  @explicit_sequence_name = false
377
- @sequence_name = connection.default_sequence_name(table_name, primary_key)
381
+ @sequence_name = with_connection { |c| c.default_sequence_name(table_name, primary_key) }
378
382
  end
379
383
 
380
384
  # Sets the name of the sequence to use when generating ids to the given
@@ -399,39 +403,37 @@ module ActiveRecord
399
403
  # Determines if the primary key values should be selected from their
400
404
  # corresponding sequence before the insert statement.
401
405
  def prefetch_primary_key?
402
- connection.prefetch_primary_key?(table_name)
406
+ with_connection { |c| c.prefetch_primary_key?(table_name) }
403
407
  end
404
408
 
405
409
  # Returns the next value that will be used as the primary key on
406
410
  # an insert statement.
407
411
  def next_sequence_value
408
- connection.next_sequence_value(sequence_name)
412
+ with_connection { |c| c.next_sequence_value(sequence_name) }
409
413
  end
410
414
 
411
415
  # Indicates whether the table associated with this class exists
412
416
  def table_exists?
413
- connection.schema_cache.data_source_exists?(table_name)
417
+ schema_cache.data_source_exists?(table_name)
414
418
  end
415
419
 
416
420
  def attributes_builder # :nodoc:
417
- unless defined?(@attributes_builder) && @attributes_builder
421
+ @attributes_builder ||= begin
418
422
  defaults = _default_attributes.except(*(column_names - [primary_key]))
419
- @attributes_builder = ActiveModel::AttributeSet::Builder.new(attribute_types, defaults)
423
+ ActiveModel::AttributeSet::Builder.new(attribute_types, defaults)
420
424
  end
421
- @attributes_builder
422
425
  end
423
426
 
424
427
  def columns_hash # :nodoc:
425
- load_schema
428
+ load_schema unless @columns_hash
426
429
  @columns_hash
427
430
  end
428
431
 
429
432
  def columns
430
- load_schema
431
433
  @columns ||= columns_hash.values.freeze
432
434
  end
433
435
 
434
- def _returning_columns_for_insert # :nodoc:
436
+ def _returning_columns_for_insert(connection) # :nodoc:
435
437
  @_returning_columns_for_insert ||= begin
436
438
  auto_populated_columns = columns.filter_map do |c|
437
439
  c.name if connection.return_value_after_insert?(c)
@@ -441,37 +443,10 @@ module ActiveRecord
441
443
  end
442
444
  end
443
445
 
444
- def attribute_types # :nodoc:
445
- load_schema
446
- @attribute_types ||= Hash.new(Type.default_value)
447
- end
448
-
449
446
  def yaml_encoder # :nodoc:
450
447
  @yaml_encoder ||= ActiveModel::AttributeSet::YAMLEncoder.new(attribute_types)
451
448
  end
452
449
 
453
- # Returns the type of the attribute with the given name, after applying
454
- # all modifiers. This method is the only valid source of information for
455
- # anything related to the types of a model's attributes. This method will
456
- # access the database and load the model's schema if it is required.
457
- #
458
- # The return value of this method will implement the interface described
459
- # by ActiveModel::Type::Value (though the object itself may not subclass
460
- # it).
461
- #
462
- # +attr_name+ The name of the attribute to retrieve the type for. Must be
463
- # a string or a symbol.
464
- def type_for_attribute(attr_name, &block)
465
- attr_name = attr_name.to_s
466
- attr_name = attribute_aliases[attr_name] || attr_name
467
-
468
- if block
469
- attribute_types.fetch(attr_name, &block)
470
- else
471
- attribute_types[attr_name]
472
- end
473
- end
474
-
475
450
  # Returns the column object for the named attribute.
476
451
  # Returns an ActiveRecord::ConnectionAdapters::NullColumn if the
477
452
  # named attribute does not exist.
@@ -499,11 +474,6 @@ module ActiveRecord
499
474
  @column_defaults ||= _default_attributes.deep_dup.to_hash.freeze
500
475
  end
501
476
 
502
- def _default_attributes # :nodoc:
503
- load_schema
504
- @default_attributes ||= ActiveModel::AttributeSet.new({})
505
- end
506
-
507
477
  # Returns an array of column names as strings.
508
478
  def column_names
509
479
  @column_names ||= columns.map(&:name).freeze
@@ -531,7 +501,7 @@ module ActiveRecord
531
501
  # when just after creating a table you want to populate it with some default
532
502
  # values, e.g.:
533
503
  #
534
- # class CreateJobLevels < ActiveRecord::Migration[7.1]
504
+ # class CreateJobLevels < ActiveRecord::Migration[8.0]
535
505
  # def up
536
506
  # create_table :job_levels do |t|
537
507
  # t.integer :id
@@ -551,18 +521,20 @@ module ActiveRecord
551
521
  # end
552
522
  # end
553
523
  def reset_column_information
554
- connection.clear_cache!
524
+ connection_pool.active_connection&.clear_cache!
555
525
  ([self] + descendants).each(&:undefine_attribute_methods)
556
- connection.schema_cache.clear_data_source_cache!(table_name)
526
+ schema_cache.clear_data_source_cache!(table_name)
557
527
 
558
528
  reload_schema_from_cache
559
529
  initialize_find_by_cache
560
530
  end
561
531
 
562
- def load_schema # :nodoc:
532
+ # Load the model's schema information either from the schema cache
533
+ # or directly from the database.
534
+ def load_schema
563
535
  return if schema_loaded?
564
536
  @load_schema_monitor.synchronize do
565
- return if @columns_hash
537
+ return if schema_loaded?
566
538
 
567
539
  load_schema!
568
540
 
@@ -583,9 +555,7 @@ module ActiveRecord
583
555
  @arel_table = nil
584
556
  @column_names = nil
585
557
  @symbol_column_to_string_name_hash = nil
586
- @attribute_types = nil
587
558
  @content_columns = nil
588
- @default_attributes = nil
589
559
  @column_defaults = nil
590
560
  @attributes_builder = nil
591
561
  @columns = nil
@@ -611,7 +581,7 @@ module ActiveRecord
611
581
  end
612
582
 
613
583
  def schema_loaded?
614
- defined?(@schema_loaded) && @schema_loaded
584
+ @schema_loaded
615
585
  end
616
586
 
617
587
  def load_schema!
@@ -619,20 +589,10 @@ module ActiveRecord
619
589
  raise ActiveRecord::TableNotSpecified, "#{self} has no table configured. Set one with #{self}.table_name="
620
590
  end
621
591
 
622
- columns_hash = connection.schema_cache.columns_hash(table_name)
592
+ columns_hash = schema_cache.columns_hash(table_name)
623
593
  columns_hash = columns_hash.except(*ignored_columns) unless ignored_columns.empty?
624
594
  @columns_hash = columns_hash.freeze
625
- @columns_hash.each do |name, column|
626
- type = connection.lookup_cast_type_from_column(column)
627
- type = _convert_type_from_options(type)
628
- define_attribute(
629
- name,
630
- type,
631
- default: column.default,
632
- user_provided_default: false
633
- )
634
- alias_attribute :id_value, :id if name == "id"
635
- end
595
+
636
596
  _default_attributes # Precompute to cache DB-dependent attribute types
637
597
  end
638
598
 
@@ -659,12 +619,14 @@ module ActiveRecord
659
619
  end
660
620
  end
661
621
 
662
- def _convert_type_from_options(type)
622
+ def type_for_column(connection, column)
623
+ type = connection.lookup_cast_type_from_column(column)
624
+
663
625
  if immutable_strings_by_default && type.respond_to?(:to_immutable_string)
664
- type.to_immutable_string
665
- else
666
- type
626
+ type = type.to_immutable_string
667
627
  end
628
+
629
+ type
668
630
  end
669
631
  end
670
632
  end
@@ -421,10 +421,15 @@ module ActiveRecord
421
421
  # update_only is true, and a <tt>:_destroy</tt> key set to a truthy value,
422
422
  # then the existing record will be marked for destruction.
423
423
  def assign_nested_attributes_for_one_to_one_association(association_name, attributes)
424
- options = nested_attributes_options[association_name]
425
424
  if attributes.respond_to?(:permitted?)
426
425
  attributes = attributes.to_h
427
426
  end
427
+
428
+ unless attributes.is_a?(Hash)
429
+ raise ArgumentError, "Hash expected for `#{association_name}` attributes, got #{attributes.class.name}"
430
+ end
431
+
432
+ options = nested_attributes_options[association_name]
428
433
  attributes = attributes.with_indifferent_access
429
434
  existing_record = send(association_name)
430
435
 
@@ -486,7 +491,7 @@ module ActiveRecord
486
491
  end
487
492
 
488
493
  unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array)
489
- raise ArgumentError, "Hash or Array expected for attribute `#{association_name}`, got #{attributes_collection.class.name} (#{attributes_collection.inspect})"
494
+ raise ArgumentError, "Hash or Array expected for `#{association_name}` attributes, got #{attributes_collection.class.name}"
490
495
  end
491
496
 
492
497
  check_record_limit!(options[:limit], attributes_collection)
@@ -509,7 +514,7 @@ module ActiveRecord
509
514
  attribute_ids.empty? ? [] : association.scope.where(association.klass.primary_key => attribute_ids)
510
515
  end
511
516
 
512
- attributes_collection.each do |attributes|
517
+ records = attributes_collection.map do |attributes|
513
518
  if attributes.respond_to?(:permitted?)
514
519
  attributes = attributes.to_h
515
520
  end
@@ -519,12 +524,12 @@ module ActiveRecord
519
524
  unless reject_new_record?(association_name, attributes)
520
525
  association.reader.build(attributes.except(*UNASSIGNABLE_KEYS))
521
526
  end
522
- elsif existing_record = find_record_by_id(existing_records, attributes["id"])
527
+ elsif existing_record = find_record_by_id(association.klass, existing_records, attributes["id"])
523
528
  unless call_reject_if(association_name, attributes)
524
529
  # Make sure we are operating on the actual object which is in the association's
525
530
  # proxy_target array (either by finding it, or adding it if not found)
526
531
  # Take into account that the proxy_target may have changed due to callbacks
527
- target_record = find_record_by_id(association.target, attributes["id"])
532
+ target_record = find_record_by_id(association.klass, association.target, attributes["id"])
528
533
  if target_record
529
534
  existing_record = target_record
530
535
  else
@@ -532,11 +537,14 @@ module ActiveRecord
532
537
  end
533
538
 
534
539
  assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
540
+ existing_record
535
541
  end
536
542
  else
537
543
  raise_nested_attributes_record_not_found!(association_name, attributes["id"])
538
544
  end
539
545
  end
546
+
547
+ association.nested_attributes_target = records
540
548
  end
541
549
 
542
550
  # Takes in a limit and checks if the attributes_collection has too many
@@ -613,10 +621,8 @@ module ActiveRecord
613
621
  model, "id", record_id)
614
622
  end
615
623
 
616
- def find_record_by_id(records, id)
617
- return if records.empty?
618
-
619
- if records.first.class.composite_primary_key?
624
+ def find_record_by_id(klass, records, id)
625
+ if klass.composite_primary_key?
620
626
  id = Array(id).map(&:to_s)
621
627
  records.find { |record| Array(record.id).map(&:to_s) == id }
622
628
  else
@@ -86,10 +86,8 @@ module ActiveRecord # :nodoc:
86
86
  #
87
87
  # User.normalize_value_for(:phone, "+1 (555) 867-5309") # => "5558675309"
88
88
  def normalizes(*names, with:, apply_to_nil: false)
89
- names.each do |name|
90
- attribute(name) do |cast_type|
91
- NormalizedValueType.new(cast_type: cast_type, normalizer: with, normalize_nil: apply_to_nil)
92
- end
89
+ decorate_attributes(names) do |name, cast_type|
90
+ NormalizedValueType.new(cast_type: cast_type, normalizer: with, normalize_nil: apply_to_nil)
93
91
  end
94
92
 
95
93
  self.normalized_attributes += names.map(&:to_sym)
@@ -154,9 +152,7 @@ module ActiveRecord # :nodoc:
154
152
  [self.class, cast_type, normalizer, normalize_nil?].hash
155
153
  end
156
154
 
157
- def inspect
158
- Kernel.instance_method(:inspect).bind_call(self)
159
- end
155
+ define_method(:inspect, Kernel.instance_method(:inspect))
160
156
 
161
157
  private
162
158
  def normalize(value)