activerecord 7.1.5.1 → 7.2.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +515 -2445
  3. data/README.rdoc +15 -15
  4. data/examples/performance.rb +2 -2
  5. data/lib/active_record/association_relation.rb +1 -1
  6. data/lib/active_record/associations/alias_tracker.rb +25 -19
  7. data/lib/active_record/associations/association.rb +9 -8
  8. data/lib/active_record/associations/belongs_to_association.rb +14 -7
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  10. data/lib/active_record/associations/builder/belongs_to.rb +1 -0
  11. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
  12. data/lib/active_record/associations/builder/has_many.rb +3 -4
  13. data/lib/active_record/associations/builder/has_one.rb +3 -4
  14. data/lib/active_record/associations/collection_association.rb +6 -4
  15. data/lib/active_record/associations/collection_proxy.rb +14 -1
  16. data/lib/active_record/associations/has_many_association.rb +1 -1
  17. data/lib/active_record/associations/join_dependency/join_association.rb +29 -28
  18. data/lib/active_record/associations/join_dependency.rb +5 -5
  19. data/lib/active_record/associations/nested_error.rb +47 -0
  20. data/lib/active_record/associations/preloader/association.rb +2 -1
  21. data/lib/active_record/associations/preloader/branch.rb +7 -1
  22. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  23. data/lib/active_record/associations/singular_association.rb +6 -0
  24. data/lib/active_record/associations/through_association.rb +1 -1
  25. data/lib/active_record/associations.rb +33 -16
  26. data/lib/active_record/attribute_assignment.rb +1 -11
  27. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  28. data/lib/active_record/attribute_methods/dirty.rb +1 -1
  29. data/lib/active_record/attribute_methods/primary_key.rb +23 -55
  30. data/lib/active_record/attribute_methods/read.rb +4 -16
  31. data/lib/active_record/attribute_methods/serialization.rb +4 -24
  32. data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -10
  33. data/lib/active_record/attribute_methods/write.rb +3 -3
  34. data/lib/active_record/attribute_methods.rb +60 -71
  35. data/lib/active_record/attributes.rb +55 -42
  36. data/lib/active_record/autosave_association.rb +13 -32
  37. data/lib/active_record/base.rb +2 -3
  38. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +24 -107
  39. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -0
  40. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +248 -65
  41. data/lib/active_record/connection_adapters/abstract/database_statements.rb +34 -17
  42. data/lib/active_record/connection_adapters/abstract/query_cache.rb +159 -74
  43. data/lib/active_record/connection_adapters/abstract/quoting.rb +65 -91
  44. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
  45. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +14 -5
  46. data/lib/active_record/connection_adapters/abstract/transaction.rb +60 -57
  47. data/lib/active_record/connection_adapters/abstract_adapter.rb +18 -46
  48. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +32 -6
  49. data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
  50. data/lib/active_record/connection_adapters/mysql/quoting.rb +43 -48
  51. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +7 -1
  52. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +11 -5
  53. data/lib/active_record/connection_adapters/mysql2_adapter.rb +5 -23
  54. data/lib/active_record/connection_adapters/pool_config.rb +7 -6
  55. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +27 -4
  56. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
  57. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  58. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  59. data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
  60. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +20 -0
  61. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +15 -13
  62. data/lib/active_record/connection_adapters/postgresql_adapter.rb +26 -21
  63. data/lib/active_record/connection_adapters/schema_cache.rb +123 -128
  64. data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
  65. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -6
  66. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +44 -46
  67. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  68. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
  69. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  70. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +25 -2
  71. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +107 -75
  72. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +12 -6
  73. data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -48
  74. data/lib/active_record/connection_adapters.rb +121 -0
  75. data/lib/active_record/connection_handling.rb +56 -41
  76. data/lib/active_record/core.rb +53 -37
  77. data/lib/active_record/counter_cache.rb +18 -9
  78. data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
  79. data/lib/active_record/database_configurations/database_config.rb +15 -4
  80. data/lib/active_record/database_configurations/hash_config.rb +38 -34
  81. data/lib/active_record/database_configurations/url_config.rb +20 -1
  82. data/lib/active_record/database_configurations.rb +1 -1
  83. data/lib/active_record/delegated_type.rb +24 -0
  84. data/lib/active_record/dynamic_matchers.rb +2 -2
  85. data/lib/active_record/encryption/encryptable_record.rb +2 -2
  86. data/lib/active_record/encryption/encrypted_attribute_type.rb +22 -2
  87. data/lib/active_record/encryption/encryptor.rb +17 -2
  88. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  89. data/lib/active_record/encryption/message_serializer.rb +4 -0
  90. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  91. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  92. data/lib/active_record/encryption.rb +0 -2
  93. data/lib/active_record/enum.rb +10 -1
  94. data/lib/active_record/errors.rb +16 -11
  95. data/lib/active_record/explain.rb +13 -24
  96. data/lib/active_record/fixtures.rb +37 -31
  97. data/lib/active_record/future_result.rb +8 -4
  98. data/lib/active_record/gem_version.rb +3 -3
  99. data/lib/active_record/inheritance.rb +4 -2
  100. data/lib/active_record/insert_all.rb +18 -15
  101. data/lib/active_record/integration.rb +4 -1
  102. data/lib/active_record/internal_metadata.rb +48 -34
  103. data/lib/active_record/locking/optimistic.rb +7 -6
  104. data/lib/active_record/log_subscriber.rb +0 -21
  105. data/lib/active_record/marshalling.rb +1 -4
  106. data/lib/active_record/message_pack.rb +1 -1
  107. data/lib/active_record/migration/command_recorder.rb +2 -3
  108. data/lib/active_record/migration/compatibility.rb +5 -3
  109. data/lib/active_record/migration/default_strategy.rb +4 -5
  110. data/lib/active_record/migration/pending_migration_connection.rb +2 -2
  111. data/lib/active_record/migration.rb +85 -76
  112. data/lib/active_record/model_schema.rb +28 -68
  113. data/lib/active_record/nested_attributes.rb +13 -16
  114. data/lib/active_record/normalization.rb +3 -7
  115. data/lib/active_record/persistence.rb +30 -352
  116. data/lib/active_record/query_cache.rb +18 -6
  117. data/lib/active_record/query_logs.rb +15 -0
  118. data/lib/active_record/querying.rb +21 -9
  119. data/lib/active_record/railtie.rb +50 -62
  120. data/lib/active_record/railties/controller_runtime.rb +13 -4
  121. data/lib/active_record/railties/databases.rake +41 -44
  122. data/lib/active_record/reflection.rb +90 -35
  123. data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
  124. data/lib/active_record/relation/batches.rb +3 -3
  125. data/lib/active_record/relation/calculations.rb +94 -61
  126. data/lib/active_record/relation/delegation.rb +8 -11
  127. data/lib/active_record/relation/finder_methods.rb +16 -2
  128. data/lib/active_record/relation/merger.rb +4 -6
  129. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  130. data/lib/active_record/relation/predicate_builder.rb +3 -3
  131. data/lib/active_record/relation/query_methods.rb +196 -57
  132. data/lib/active_record/relation/record_fetch_warning.rb +3 -0
  133. data/lib/active_record/relation/spawn_methods.rb +2 -18
  134. data/lib/active_record/relation/where_clause.rb +7 -19
  135. data/lib/active_record/relation.rb +496 -72
  136. data/lib/active_record/result.rb +31 -44
  137. data/lib/active_record/runtime_registry.rb +39 -0
  138. data/lib/active_record/sanitization.rb +24 -19
  139. data/lib/active_record/schema.rb +8 -6
  140. data/lib/active_record/schema_dumper.rb +19 -9
  141. data/lib/active_record/schema_migration.rb +30 -14
  142. data/lib/active_record/signed_id.rb +11 -1
  143. data/lib/active_record/statement_cache.rb +7 -7
  144. data/lib/active_record/table_metadata.rb +1 -10
  145. data/lib/active_record/tasks/database_tasks.rb +76 -70
  146. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  147. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  148. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -1
  149. data/lib/active_record/test_fixtures.rb +81 -91
  150. data/lib/active_record/testing/query_assertions.rb +121 -0
  151. data/lib/active_record/timestamp.rb +1 -1
  152. data/lib/active_record/token_for.rb +22 -12
  153. data/lib/active_record/touch_later.rb +1 -1
  154. data/lib/active_record/transaction.rb +68 -0
  155. data/lib/active_record/transactions.rb +43 -14
  156. data/lib/active_record/translation.rb +0 -2
  157. data/lib/active_record/type/serialized.rb +1 -3
  158. data/lib/active_record/type_caster/connection.rb +4 -4
  159. data/lib/active_record/validations/associated.rb +9 -3
  160. data/lib/active_record/validations/uniqueness.rb +14 -10
  161. data/lib/active_record/validations.rb +4 -1
  162. data/lib/active_record.rb +149 -40
  163. data/lib/arel/alias_predication.rb +1 -1
  164. data/lib/arel/collectors/bind.rb +2 -0
  165. data/lib/arel/collectors/composite.rb +7 -0
  166. data/lib/arel/collectors/sql_string.rb +1 -1
  167. data/lib/arel/collectors/substitute_binds.rb +1 -1
  168. data/lib/arel/nodes/binary.rb +0 -6
  169. data/lib/arel/nodes/bound_sql_literal.rb +9 -5
  170. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  171. data/lib/arel/nodes/node.rb +4 -3
  172. data/lib/arel/nodes/sql_literal.rb +7 -0
  173. data/lib/arel/nodes.rb +2 -2
  174. data/lib/arel/predications.rb +1 -1
  175. data/lib/arel/select_manager.rb +1 -1
  176. data/lib/arel/tree_manager.rb +3 -2
  177. data/lib/arel/update_manager.rb +2 -1
  178. data/lib/arel/visitors/dot.rb +1 -0
  179. data/lib/arel/visitors/mysql.rb +9 -4
  180. data/lib/arel/visitors/postgresql.rb +1 -12
  181. data/lib/arel/visitors/to_sql.rb +29 -16
  182. data/lib/arel.rb +7 -3
  183. metadata +20 -15
@@ -1506,11 +1506,13 @@ 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
1509
+ # [:index_errors]
1510
+ # Allows 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
+ # When set to +true+, the index is based on association order, i.e. database order, with yet to be
1513
1513
  # persisted new records placed at the end.
1514
+ # When set to +:nested_attributes_order+, the index is based on the record order received by
1515
+ # nested attributes setter, when accepts_nested_attributes_for is used.
1514
1516
  #
1515
1517
  # Option examples:
1516
1518
  # has_many :comments, -> { order("posted_on") }
@@ -1524,7 +1526,7 @@ module ActiveRecord
1524
1526
  # has_many :subscribers, through: :subscriptions, disable_joins: true
1525
1527
  # has_many :comments, strict_loading: true
1526
1528
  # has_many :comments, query_constraints: [:blog_id, :post_id]
1527
- # has_many :comments, index_errors: true
1529
+ # has_many :comments, index_errors: :nested_attributes_order
1528
1530
  def has_many(name, scope = nil, **options, &extension)
1529
1531
  reflection = Builder::HasMany.build(self, name, scope, options, &extension)
1530
1532
  Reflection.add_reflection self, name, reflection
@@ -1667,9 +1669,14 @@ module ActiveRecord
1667
1669
  # When set to +true+, validates new objects added to association when saving the parent object. +false+ by default.
1668
1670
  # If you want to ensure associated objects are revalidated on every update, use +validates_associated+.
1669
1671
  # [+: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.
1672
+ # If +true+, always saves the associated object or destroys it if marked for destruction,
1673
+ # when saving the parent object.
1674
+ # If +false+, never save or destroy the associated object.
1675
+ #
1676
+ # By default, only saves the associated object if it's a new record. Setting this option
1677
+ # to +true+ also enables validations on the associated object unless explicitly disabled
1678
+ # with <tt>validate: false</tt>. This is because saving an object with invalid associated
1679
+ # objects would fail, so any associated objects will go through validation checks.
1673
1680
  #
1674
1681
  # Note that NestedAttributes::ClassMethods#accepts_nested_attributes_for sets
1675
1682
  # <tt>:autosave</tt> to <tt>true</tt>.
@@ -1822,15 +1829,25 @@ module ActiveRecord
1822
1829
  # named <tt>#{table_name}_count</tt> (such as +comments_count+ for a belonging Comment class)
1823
1830
  # is used on the associate class (such as a Post class) - that is the migration for
1824
1831
  # <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
1832
+ # return the count cached). You can also specify a custom counter
1826
1833
  # cache column by providing a column name instead of a +true+/+false+ value to this
1827
1834
  # 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+.
1835
+ #
1836
+ # Starting to use counter caches on existing large tables can be troublesome, because the column
1837
+ # values must be backfilled separately of the column addition (to not lock the table for too long)
1838
+ # and before the use of +:counter_cache+ (otherwise methods like +size+/+any?+/etc, which use
1839
+ # counter caches internally, can produce incorrect results). To safely backfill the values while keeping
1840
+ # counter cache columns updated with the child records creation/removal and to avoid the mentioned methods
1841
+ # use the possibly incorrect counter cache column values and always get the results from the database,
1842
+ # use <tt>counter_cache: { active: false }</tt>.
1843
+ # If you also need to specify a custom column name, use <tt>counter_cache: { active: false, column: :my_custom_counter }</tt>.
1844
+ #
1832
1845
  # Note: If you've enabled the counter cache, then you may want to add the counter cache attribute
1833
1846
  # to the +attr_readonly+ list in the associated classes (e.g. <tt>class Post; attr_readonly :comments_count; end</tt>).
1847
+ # [+:polymorphic+]
1848
+ # Specify this association is a polymorphic association by passing +true+.
1849
+ # Note: Since polymorphic associations rely on storing class names in the database, make sure to update the class names in the
1850
+ # <tt>*_type</tt> polymorphic type column of the corresponding rows.
1834
1851
  # [+:validate+]
1835
1852
  # When set to +true+, validates new objects added to association when saving the parent object. +false+ by default.
1836
1853
  # If you want to ensure associated objects are revalidated on every update, use +validates_associated+.
@@ -1888,7 +1905,7 @@ module ActiveRecord
1888
1905
  # belongs_to :user, optional: true
1889
1906
  # belongs_to :account, default: -> { company.account }
1890
1907
  # belongs_to :account, strict_loading: true
1891
- # belong_to :note, query_constraints: [:organization_id, :note_id]
1908
+ # belongs_to :note, query_constraints: [:organization_id, :note_id]
1892
1909
  def belongs_to(name, scope = nil, **options)
1893
1910
  reflection = Builder::BelongsTo.build(self, name, scope, options)
1894
1911
  Reflection.add_reflection self, name, reflection
@@ -1911,7 +1928,7 @@ module ActiveRecord
1911
1928
  # The join table should not have a primary key or a model associated with it. You must manually generate the
1912
1929
  # join table with a migration such as this:
1913
1930
  #
1914
- # class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration[7.1]
1931
+ # class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration[7.2]
1915
1932
  # def change
1916
1933
  # create_join_table :developers, :projects
1917
1934
  # end
@@ -2105,7 +2122,7 @@ module ActiveRecord
2105
2122
  end
2106
2123
 
2107
2124
  has_many name, scope, **hm_options, &extension
2108
- _reflections[name.to_s].parent_reflection = habtm_reflection
2125
+ _reflections[name].parent_reflection = habtm_reflection
2109
2126
  end
2110
2127
  end
2111
2128
  end
@@ -2,33 +2,23 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module AttributeAssignment
5
- include ActiveModel::AttributeAssignment
6
-
7
5
  private
8
6
  def _assign_attributes(attributes)
9
- multi_parameter_attributes = nested_parameter_attributes = nil
7
+ multi_parameter_attributes = nil
10
8
 
11
9
  attributes.each do |k, v|
12
10
  key = k.to_s
13
11
 
14
12
  if key.include?("(")
15
13
  (multi_parameter_attributes ||= {})[key] = v
16
- elsif v.is_a?(Hash)
17
- (nested_parameter_attributes ||= {})[key] = v
18
14
  else
19
15
  _assign_attribute(key, v)
20
16
  end
21
17
  end
22
18
 
23
- assign_nested_parameter_attributes(nested_parameter_attributes) if nested_parameter_attributes
24
19
  assign_multiparameter_attributes(multi_parameter_attributes) if multi_parameter_attributes
25
20
  end
26
21
 
27
- # Assign any deferred nested attributes after the base attributes have been set.
28
- def assign_nested_parameter_attributes(pairs)
29
- pairs.each { |k, v| _assign_attribute(k, v) }
30
- end
31
-
32
22
  # Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
33
23
  # by calling new on the column type or aggregation type (through composed_of) object with these parameters.
34
24
  # So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module AttributeMethods
5
+ module CompositePrimaryKey # :nodoc:
6
+ # Returns the primary key column's value. If the primary key is composite,
7
+ # returns an array of the primary key column values.
8
+ def id
9
+ if self.class.composite_primary_key?
10
+ @primary_key.map { |pk| _read_attribute(pk) }
11
+ else
12
+ super
13
+ end
14
+ end
15
+
16
+ def primary_key_values_present? # :nodoc:
17
+ if self.class.composite_primary_key?
18
+ id.all?
19
+ else
20
+ super
21
+ end
22
+ end
23
+
24
+ # Sets the primary key column's value. If the primary key is composite,
25
+ # raises TypeError when the set value not enumerable.
26
+ def id=(value)
27
+ if self.class.composite_primary_key?
28
+ raise TypeError, "Expected value matching #{self.class.primary_key.inspect}, got #{value.inspect}." unless value.is_a?(Enumerable)
29
+ @primary_key.zip(value) { |attr, value| _write_attribute(attr, value) }
30
+ else
31
+ super
32
+ end
33
+ end
34
+
35
+ # Queries the primary key column's value. If the primary key is composite,
36
+ # all primary key column values must be queryable.
37
+ def id?
38
+ if self.class.composite_primary_key?
39
+ @primary_key.all? { |col| _query_attribute(col) }
40
+ else
41
+ super
42
+ end
43
+ end
44
+
45
+ # Returns the primary key column's value before type cast. If the primary key is composite,
46
+ # returns an array of primary key column values before type cast.
47
+ def id_before_type_cast
48
+ if self.class.composite_primary_key?
49
+ @primary_key.map { |col| attribute_before_type_cast(col) }
50
+ else
51
+ super
52
+ end
53
+ end
54
+
55
+ # Returns the primary key column's previous value. If the primary key is composite,
56
+ # returns an array of primary key column previous values.
57
+ def id_was
58
+ if self.class.composite_primary_key?
59
+ @primary_key.map { |col| attribute_was(col) }
60
+ else
61
+ super
62
+ end
63
+ end
64
+
65
+ # Returns the primary key column's value from the database. If the primary key is composite,
66
+ # returns an array of primary key column values from database.
67
+ def id_in_database
68
+ if self.class.composite_primary_key?
69
+ @primary_key.map { |col| attribute_in_database(col) }
70
+ else
71
+ super
72
+ end
73
+ end
74
+
75
+ def id_for_database # :nodoc:
76
+ if self.class.composite_primary_key?
77
+ @primary_key.map { |col| @attributes[col].value_for_database }
78
+ else
79
+ super
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -31,7 +31,7 @@ module ActiveRecord
31
31
  # person.name_in_database # => "Alice"
32
32
  # person.saved_change_to_name? # => true
33
33
  # person.saved_change_to_name # => ["Allison", "Alice"]
34
- # person.name_before_last_save # => "Allison"
34
+ # person.name_before_last_change # => "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
@@ -18,74 +18,45 @@ module ActiveRecord
18
18
  # Returns the primary key column's value. If the primary key is composite,
19
19
  # returns an array of the primary key column values.
20
20
  def id
21
- return _read_attribute(@primary_key) unless @primary_key.is_a?(Array)
22
-
23
- @primary_key.map { |pk| _read_attribute(pk) }
21
+ _read_attribute(@primary_key)
24
22
  end
25
23
 
26
24
  def primary_key_values_present? # :nodoc:
27
- return id.all? if self.class.composite_primary_key?
28
-
29
25
  !!id
30
26
  end
31
27
 
32
28
  # Sets the primary key column's value. If the primary key is composite,
33
29
  # raises TypeError when the set value not enumerable.
34
30
  def id=(value)
35
- if self.class.composite_primary_key?
36
- raise TypeError, "Expected value matching #{self.class.primary_key.inspect}, got #{value.inspect}." unless value.is_a?(Enumerable)
37
- @primary_key.zip(value) { |attr, value| _write_attribute(attr, value) }
38
- else
39
- _write_attribute(@primary_key, value)
40
- end
31
+ _write_attribute(@primary_key, value)
41
32
  end
42
33
 
43
34
  # Queries the primary key column's value. If the primary key is composite,
44
35
  # all primary key column values must be queryable.
45
36
  def id?
46
- if self.class.composite_primary_key?
47
- @primary_key.all? { |col| _query_attribute(col) }
48
- else
49
- _query_attribute(@primary_key)
50
- end
37
+ _query_attribute(@primary_key)
51
38
  end
52
39
 
53
40
  # Returns the primary key column's value before type cast. If the primary key is composite,
54
41
  # returns an array of primary key column values before type cast.
55
42
  def id_before_type_cast
56
- if self.class.composite_primary_key?
57
- @primary_key.map { |col| attribute_before_type_cast(col) }
58
- else
59
- attribute_before_type_cast(@primary_key)
60
- end
43
+ attribute_before_type_cast(@primary_key)
61
44
  end
62
45
 
63
46
  # Returns the primary key column's previous value. If the primary key is composite,
64
47
  # returns an array of primary key column previous values.
65
48
  def id_was
66
- if self.class.composite_primary_key?
67
- @primary_key.map { |col| attribute_was(col) }
68
- else
69
- attribute_was(@primary_key)
70
- end
49
+ attribute_was(@primary_key)
71
50
  end
72
51
 
73
52
  # Returns the primary key column's value from the database. If the primary key is composite,
74
53
  # returns an array of primary key column values from database.
75
54
  def id_in_database
76
- if self.class.composite_primary_key?
77
- @primary_key.map { |col| attribute_in_database(col) }
78
- else
79
- attribute_in_database(@primary_key)
80
- end
55
+ attribute_in_database(@primary_key)
81
56
  end
82
57
 
83
58
  def id_for_database # :nodoc:
84
- if self.class.composite_primary_key?
85
- @primary_key.map { |col| @attributes[col].value_for_database }
86
- else
87
- @attributes[@primary_key].value_for_database
88
- end
59
+ @attributes[@primary_key].value_for_database
89
60
  end
90
61
 
91
62
  private
@@ -109,20 +80,19 @@ module ActiveRecord
109
80
  # Overwriting will negate any effect of the +primary_key_prefix_type+
110
81
  # setting, though.
111
82
  def primary_key
112
- if PRIMARY_KEY_NOT_SET.equal?(@primary_key)
113
- @primary_key = reset_primary_key
114
- end
83
+ reset_primary_key if PRIMARY_KEY_NOT_SET.equal?(@primary_key)
115
84
  @primary_key
116
85
  end
117
86
 
118
87
  def composite_primary_key? # :nodoc:
119
- primary_key.is_a?(Array)
88
+ reset_primary_key if PRIMARY_KEY_NOT_SET.equal?(@primary_key)
89
+ @composite_primary_key
120
90
  end
121
91
 
122
92
  # Returns a quoted version of the primary key name, used to construct
123
93
  # SQL statements.
124
94
  def quoted_primary_key
125
- @quoted_primary_key ||= connection.quote_column_name(primary_key)
95
+ @quoted_primary_key ||= adapter_class.quote_column_name(primary_key)
126
96
  end
127
97
 
128
98
  def reset_primary_key # :nodoc:
@@ -138,12 +108,10 @@ module ActiveRecord
138
108
  base_name.foreign_key(false)
139
109
  elsif base_name && primary_key_prefix_type == :table_name_with_underscore
140
110
  base_name.foreign_key
111
+ elsif ActiveRecord::Base != self && table_exists?
112
+ schema_cache.primary_keys(table_name)
141
113
  else
142
- if ActiveRecord::Base != self && table_exists?
143
- connection.schema_cache.primary_keys(table_name)
144
- else
145
- "id"
146
- end
114
+ "id"
147
115
  end
148
116
  end
149
117
 
@@ -163,25 +131,25 @@ module ActiveRecord
163
131
  #
164
132
  # Project.primary_key # => "foo_id"
165
133
  def primary_key=(value)
166
- @primary_key = derive_primary_key(value)
134
+ @primary_key = if value.is_a?(Array)
135
+ @composite_primary_key = true
136
+ include CompositePrimaryKey
137
+ @primary_key = value.map { |v| -v.to_s }.freeze
138
+ elsif value
139
+ -value.to_s
140
+ end
167
141
  @quoted_primary_key = nil
168
142
  @attributes_builder = nil
169
143
  end
170
144
 
171
145
  private
172
- def derive_primary_key(value)
173
- return unless value
174
-
175
- return -value.to_s unless value.is_a?(Array)
176
-
177
- value.map { |v| -v.to_s }.freeze
178
- end
179
-
180
146
  def inherited(base)
181
147
  super
182
148
  base.class_eval do
183
149
  @primary_key = PRIMARY_KEY_NOT_SET
150
+ @composite_primary_key = false
184
151
  @quoted_primary_key = nil
152
+ @attributes_builder = nil
185
153
  end
186
154
  end
187
155
  end
@@ -8,11 +8,11 @@ module ActiveRecord
8
8
 
9
9
  module ClassMethods # :nodoc:
10
10
  private
11
- def define_method_attribute(canonical_name, owner:, as: canonical_name)
11
+ def define_method_attribute(name, owner:)
12
12
  ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
13
- owner, canonical_name
13
+ owner, name
14
14
  ) do |temp_method_name, attr_name_expr|
15
- owner.define_cached_method(temp_method_name, as: as, namespace: :active_record) do |batch|
15
+ owner.define_cached_method(name, as: temp_method_name, 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) }" <<
@@ -30,19 +30,7 @@ module ActiveRecord
30
30
  name = attr_name.to_s
31
31
  name = self.class.attribute_aliases[name] || name
32
32
 
33
- return @attributes.fetch_value(name, &block) unless name == "id" && @primary_key
34
-
35
- if self.class.composite_primary_key?
36
- @attributes.fetch_value("id", &block)
37
- else
38
- if @primary_key != "id"
39
- ActiveRecord.deprecator.warn(<<-MSG.squish)
40
- Using read_attribute(:id) to read the primary key value is deprecated.
41
- Use #id instead.
42
- MSG
43
- end
44
- @attributes.fetch_value(@primary_key, &block)
45
- end
33
+ @attributes.fetch_value(name, &block)
46
34
  end
47
35
 
48
36
  # This method exists to avoid the expensive primary_key check internally, without
@@ -180,29 +180,7 @@ module ActiveRecord
180
180
  # serialize :preferences, coder: Rot13JSON
181
181
  # end
182
182
  #
183
- def serialize(attr_name, class_name_or_coder = nil, coder: nil, type: Object, yaml: {}, **options)
184
- unless class_name_or_coder.nil?
185
- if class_name_or_coder == ::JSON || [:load, :dump].all? { |x| class_name_or_coder.respond_to?(x) }
186
- ActiveRecord.deprecator.warn(<<~MSG)
187
- Passing the coder as positional argument is deprecated and will be removed in Rails 7.2.
188
-
189
- Please pass the coder as a keyword argument:
190
-
191
- serialize #{attr_name.inspect}, coder: #{class_name_or_coder}
192
- MSG
193
- coder = class_name_or_coder
194
- else
195
- ActiveRecord.deprecator.warn(<<~MSG)
196
- Passing the class as positional argument is deprecated and will be removed in Rails 7.2.
197
-
198
- Please pass the class as a keyword argument:
199
-
200
- serialize #{attr_name.inspect}, type: #{class_name_or_coder.name}
201
- MSG
202
- type = class_name_or_coder
203
- end
204
- end
205
-
183
+ def serialize(attr_name, coder: nil, type: Object, yaml: {}, **options)
206
184
  coder ||= default_column_serializer
207
185
  unless coder
208
186
  raise ArgumentError, <<~MSG.squish
@@ -214,7 +192,9 @@ module ActiveRecord
214
192
 
215
193
  column_serializer = build_column_serializer(attr_name, coder, type, yaml)
216
194
 
217
- attribute(attr_name, **options) do |cast_type|
195
+ attribute(attr_name, **options)
196
+
197
+ decorate_attributes([attr_name]) do |attr_name, cast_type|
218
198
  if type_incompatible_with_serialize?(cast_type, coder, type)
219
199
  raise ColumnNotSerializableError.new(attr_name, cast_type)
220
200
  end
@@ -32,10 +32,6 @@ 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
-
39
35
  private
40
36
  def convert_time_to_time_zone(value)
41
37
  return if value.nil?
@@ -73,14 +69,15 @@ module ActiveRecord
73
69
  end
74
70
 
75
71
  module ClassMethods # :nodoc:
76
- def define_attribute(name, cast_type, **)
77
- if create_time_zone_conversion_attribute?(name, cast_type)
78
- cast_type = TimeZoneConverter.new(cast_type)
72
+ private
73
+ def hook_attribute_type(name, cast_type)
74
+ if create_time_zone_conversion_attribute?(name, cast_type)
75
+ cast_type = TimeZoneConverter.new(cast_type)
76
+ end
77
+
78
+ super
79
79
  end
80
- super
81
- end
82
80
 
83
- private
84
81
  def create_time_zone_conversion_attribute?(name, cast_type)
85
82
  enabled_for_column = time_zone_aware_attributes &&
86
83
  !skip_time_zone_conversion_for_attributes.include?(name.to_sym)
@@ -12,11 +12,11 @@ module ActiveRecord
12
12
 
13
13
  module ClassMethods # :nodoc:
14
14
  private
15
- def define_method_attribute=(canonical_name, owner:, as: canonical_name)
15
+ def define_method_attribute=(name, owner:)
16
16
  ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
17
- owner, canonical_name, writer: true,
17
+ owner, name, writer: true,
18
18
  ) do |temp_method_name, attr_name_expr|
19
- owner.define_cached_method(temp_method_name, as: "#{as}=", namespace: :active_record) do |batch|
19
+ owner.define_cached_method("#{name}=", as: temp_method_name, namespace: :active_record) do |batch|
20
20
  batch <<
21
21
  "def #{temp_method_name}(value)" <<
22
22
  " _write_attribute(#{attr_name_expr}, value)" <<