activerecord 7.1.3.4 → 7.2.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (186) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +507 -2133
  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 +18 -11
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  10. data/lib/active_record/associations/builder/belongs_to.rb +1 -0
  11. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
  12. data/lib/active_record/associations/builder/has_many.rb +3 -4
  13. data/lib/active_record/associations/builder/has_one.rb +3 -4
  14. data/lib/active_record/associations/collection_association.rb +4 -2
  15. data/lib/active_record/associations/collection_proxy.rb +14 -1
  16. data/lib/active_record/associations/has_many_association.rb +3 -3
  17. data/lib/active_record/associations/has_one_association.rb +2 -2
  18. data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
  19. data/lib/active_record/associations/join_dependency.rb +5 -7
  20. data/lib/active_record/associations/nested_error.rb +47 -0
  21. data/lib/active_record/associations/preloader/association.rb +2 -1
  22. data/lib/active_record/associations/preloader/branch.rb +7 -1
  23. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  24. data/lib/active_record/associations/singular_association.rb +6 -0
  25. data/lib/active_record/associations/through_association.rb +1 -1
  26. data/lib/active_record/associations.rb +34 -11
  27. data/lib/active_record/attribute_assignment.rb +1 -11
  28. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  29. data/lib/active_record/attribute_methods/dirty.rb +1 -1
  30. data/lib/active_record/attribute_methods/primary_key.rb +23 -55
  31. data/lib/active_record/attribute_methods/read.rb +1 -13
  32. data/lib/active_record/attribute_methods/serialization.rb +4 -24
  33. data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -6
  34. data/lib/active_record/attribute_methods.rb +87 -58
  35. data/lib/active_record/attributes.rb +55 -42
  36. data/lib/active_record/autosave_association.rb +14 -30
  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 -58
  41. data/lib/active_record/connection_adapters/abstract/database_statements.rb +35 -18
  42. data/lib/active_record/connection_adapters/abstract/query_cache.rb +160 -75
  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 +22 -9
  46. data/lib/active_record/connection_adapters/abstract/transaction.rb +60 -57
  47. data/lib/active_record/connection_adapters/abstract_adapter.rb +32 -61
  48. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +69 -19
  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 -0
  52. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +11 -5
  53. data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -32
  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 +6 -0
  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 +16 -12
  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 +109 -77
  72. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +12 -6
  73. data/lib/active_record/connection_adapters/trilogy_adapter.rb +32 -65
  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 +59 -38
  77. data/lib/active_record/counter_cache.rb +23 -10
  78. data/lib/active_record/database_configurations/connection_url_resolver.rb +7 -2
  79. data/lib/active_record/database_configurations/database_config.rb +15 -4
  80. data/lib/active_record/database_configurations/hash_config.rb +44 -36
  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 +30 -6
  84. data/lib/active_record/destroy_association_async_job.rb +1 -1
  85. data/lib/active_record/dynamic_matchers.rb +2 -2
  86. data/lib/active_record/encryption/encryptable_record.rb +2 -2
  87. data/lib/active_record/encryption/encrypted_attribute_type.rb +24 -4
  88. data/lib/active_record/encryption/encryptor.rb +17 -2
  89. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  90. data/lib/active_record/encryption/message_serializer.rb +4 -0
  91. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  92. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  93. data/lib/active_record/encryption/scheme.rb +8 -4
  94. data/lib/active_record/enum.rb +11 -2
  95. data/lib/active_record/errors.rb +16 -11
  96. data/lib/active_record/explain.rb +13 -24
  97. data/lib/active_record/fixtures.rb +37 -31
  98. data/lib/active_record/future_result.rb +17 -4
  99. data/lib/active_record/gem_version.rb +3 -3
  100. data/lib/active_record/inheritance.rb +4 -2
  101. data/lib/active_record/insert_all.rb +18 -15
  102. data/lib/active_record/integration.rb +4 -1
  103. data/lib/active_record/internal_metadata.rb +48 -34
  104. data/lib/active_record/locking/optimistic.rb +8 -7
  105. data/lib/active_record/log_subscriber.rb +0 -21
  106. data/lib/active_record/marshalling.rb +1 -1
  107. data/lib/active_record/message_pack.rb +2 -2
  108. data/lib/active_record/migration/command_recorder.rb +2 -3
  109. data/lib/active_record/migration/compatibility.rb +11 -3
  110. data/lib/active_record/migration/default_strategy.rb +4 -5
  111. data/lib/active_record/migration/pending_migration_connection.rb +2 -2
  112. data/lib/active_record/migration.rb +85 -76
  113. data/lib/active_record/model_schema.rb +34 -69
  114. data/lib/active_record/nested_attributes.rb +11 -3
  115. data/lib/active_record/normalization.rb +3 -7
  116. data/lib/active_record/persistence.rb +32 -354
  117. data/lib/active_record/query_cache.rb +18 -6
  118. data/lib/active_record/query_logs.rb +15 -0
  119. data/lib/active_record/query_logs_formatter.rb +1 -1
  120. data/lib/active_record/querying.rb +21 -9
  121. data/lib/active_record/railtie.rb +52 -64
  122. data/lib/active_record/railties/controller_runtime.rb +13 -4
  123. data/lib/active_record/railties/databases.rake +41 -44
  124. data/lib/active_record/reflection.rb +98 -37
  125. data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
  126. data/lib/active_record/relation/batches.rb +3 -3
  127. data/lib/active_record/relation/calculations.rb +94 -61
  128. data/lib/active_record/relation/delegation.rb +8 -11
  129. data/lib/active_record/relation/finder_methods.rb +16 -2
  130. data/lib/active_record/relation/merger.rb +4 -6
  131. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  132. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +6 -1
  133. data/lib/active_record/relation/predicate_builder.rb +3 -3
  134. data/lib/active_record/relation/query_methods.rb +196 -43
  135. data/lib/active_record/relation/record_fetch_warning.rb +3 -0
  136. data/lib/active_record/relation/spawn_methods.rb +2 -18
  137. data/lib/active_record/relation/where_clause.rb +7 -19
  138. data/lib/active_record/relation.rb +500 -66
  139. data/lib/active_record/result.rb +32 -45
  140. data/lib/active_record/runtime_registry.rb +39 -0
  141. data/lib/active_record/sanitization.rb +24 -19
  142. data/lib/active_record/schema.rb +8 -6
  143. data/lib/active_record/schema_dumper.rb +19 -9
  144. data/lib/active_record/schema_migration.rb +30 -14
  145. data/lib/active_record/signed_id.rb +11 -1
  146. data/lib/active_record/statement_cache.rb +7 -7
  147. data/lib/active_record/table_metadata.rb +1 -10
  148. data/lib/active_record/tasks/database_tasks.rb +70 -42
  149. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  150. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  151. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -1
  152. data/lib/active_record/test_fixtures.rb +82 -91
  153. data/lib/active_record/testing/query_assertions.rb +121 -0
  154. data/lib/active_record/timestamp.rb +4 -2
  155. data/lib/active_record/token_for.rb +22 -12
  156. data/lib/active_record/touch_later.rb +1 -1
  157. data/lib/active_record/transaction.rb +68 -0
  158. data/lib/active_record/transactions.rb +43 -14
  159. data/lib/active_record/translation.rb +0 -2
  160. data/lib/active_record/type/serialized.rb +1 -3
  161. data/lib/active_record/type_caster/connection.rb +4 -4
  162. data/lib/active_record/validations/associated.rb +9 -3
  163. data/lib/active_record/validations/uniqueness.rb +14 -10
  164. data/lib/active_record/validations.rb +4 -1
  165. data/lib/active_record.rb +149 -40
  166. data/lib/arel/alias_predication.rb +1 -1
  167. data/lib/arel/collectors/bind.rb +2 -0
  168. data/lib/arel/collectors/composite.rb +7 -0
  169. data/lib/arel/collectors/sql_string.rb +1 -1
  170. data/lib/arel/collectors/substitute_binds.rb +1 -1
  171. data/lib/arel/nodes/binary.rb +0 -6
  172. data/lib/arel/nodes/bound_sql_literal.rb +9 -5
  173. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  174. data/lib/arel/nodes/node.rb +4 -3
  175. data/lib/arel/nodes/sql_literal.rb +7 -0
  176. data/lib/arel/nodes.rb +2 -2
  177. data/lib/arel/predications.rb +1 -1
  178. data/lib/arel/select_manager.rb +1 -1
  179. data/lib/arel/tree_manager.rb +8 -3
  180. data/lib/arel/update_manager.rb +2 -1
  181. data/lib/arel/visitors/dot.rb +1 -0
  182. data/lib/arel/visitors/mysql.rb +9 -4
  183. data/lib/arel/visitors/postgresql.rb +1 -12
  184. data/lib/arel/visitors/to_sql.rb +31 -17
  185. data/lib/arel.rb +7 -3
  186. metadata +17 -12
@@ -82,7 +82,7 @@ module ActiveRecord
82
82
  def stale_state
83
83
  if through_reflection.belongs_to?
84
84
  Array(through_reflection.foreign_key).filter_map do |foreign_key_column|
85
- owner[foreign_key_column] && owner[foreign_key_column].to_s
85
+ owner[foreign_key_column]
86
86
  end.presence
87
87
  end
88
88
  end
@@ -1506,6 +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
+ # 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
+ # 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.
1509
1516
  #
1510
1517
  # Option examples:
1511
1518
  # has_many :comments, -> { order("posted_on") }
@@ -1519,6 +1526,7 @@ module ActiveRecord
1519
1526
  # has_many :subscribers, through: :subscriptions, disable_joins: true
1520
1527
  # has_many :comments, strict_loading: true
1521
1528
  # has_many :comments, query_constraints: [:blog_id, :post_id]
1529
+ # has_many :comments, index_errors: :nested_attributes_order
1522
1530
  def has_many(name, scope = nil, **options, &extension)
1523
1531
  reflection = Builder::HasMany.build(self, name, scope, options, &extension)
1524
1532
  Reflection.add_reflection self, name, reflection
@@ -1661,9 +1669,14 @@ module ActiveRecord
1661
1669
  # When set to +true+, validates new objects added to association when saving the parent object. +false+ by default.
1662
1670
  # If you want to ensure associated objects are revalidated on every update, use +validates_associated+.
1663
1671
  # [+:autosave+]
1664
- # If true, always save the associated object or destroy it if marked for destruction,
1665
- # when saving the parent object. If false, never save or destroy the associated object.
1666
- # By default, only save the associated object if it's a new record.
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.
1667
1680
  #
1668
1681
  # Note that NestedAttributes::ClassMethods#accepts_nested_attributes_for sets
1669
1682
  # <tt>:autosave</tt> to <tt>true</tt>.
@@ -1816,15 +1829,25 @@ module ActiveRecord
1816
1829
  # named <tt>#{table_name}_count</tt> (such as +comments_count+ for a belonging Comment class)
1817
1830
  # is used on the associate class (such as a Post class) - that is the migration for
1818
1831
  # <tt>#{table_name}_count</tt> is created on the associate class (such that <tt>Post.comments_count</tt> will
1819
- # return the count cached, see note below). You can also specify a custom counter
1832
+ # return the count cached). You can also specify a custom counter
1820
1833
  # cache column by providing a column name instead of a +true+/+false+ value to this
1821
1834
  # option (e.g., <tt>counter_cache: :my_custom_counter</tt>.)
1822
- # Note: Specifying a counter cache will add it to that model's list of readonly attributes
1823
- # using +attr_readonly+.
1824
- # [+:polymorphic+]
1825
- # Specify this association is a polymorphic association by passing +true+.
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
+ #
1826
1845
  # Note: If you've enabled the counter cache, then you may want to add the counter cache attribute
1827
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.
1828
1851
  # [+:validate+]
1829
1852
  # When set to +true+, validates new objects added to association when saving the parent object. +false+ by default.
1830
1853
  # If you want to ensure associated objects are revalidated on every update, use +validates_associated+.
@@ -1882,7 +1905,7 @@ module ActiveRecord
1882
1905
  # belongs_to :user, optional: true
1883
1906
  # belongs_to :account, default: -> { company.account }
1884
1907
  # belongs_to :account, strict_loading: true
1885
- # belong_to :note, query_constraints: [:organization_id, :note_id]
1908
+ # belongs_to :note, query_constraints: [:organization_id, :note_id]
1886
1909
  def belongs_to(name, scope = nil, **options)
1887
1910
  reflection = Builder::BelongsTo.build(self, name, scope, options)
1888
1911
  Reflection.add_reflection self, name, reflection
@@ -1905,7 +1928,7 @@ module ActiveRecord
1905
1928
  # The join table should not have a primary key or a model associated with it. You must manually generate the
1906
1929
  # join table with a migration such as this:
1907
1930
  #
1908
- # class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration[7.1]
1931
+ # class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration[7.2]
1909
1932
  # def change
1910
1933
  # create_join_table :developers, :projects
1911
1934
  # end
@@ -2099,7 +2122,7 @@ module ActiveRecord
2099
2122
  end
2100
2123
 
2101
2124
  has_many name, scope, **hm_options, &extension
2102
- _reflections[name.to_s].parent_reflection = habtm_reflection
2125
+ _reflections[name].parent_reflection = habtm_reflection
2103
2126
  end
2104
2127
  end
2105
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
@@ -251,7 +251,7 @@ module ActiveRecord
251
251
  changed_attribute_names_to_save
252
252
  else
253
253
  attribute_names.reject do |attr_name|
254
- if column_for_attribute(attr_name).default_function
254
+ if column_for_attribute(attr_name).auto_populated?
255
255
  !attribute_changed?(attr_name)
256
256
  end
257
257
  end
@@ -18,74 +18,45 @@ module ActiveRecord
18
18
  # Returns the primary key column's value. If the primary key is composite,
19
19
  # returns an array of the primary key column values.
20
20
  def id
21
- return _read_attribute(@primary_key) unless @primary_key.is_a?(Array)
22
-
23
- @primary_key.map { |pk| _read_attribute(pk) }
21
+ _read_attribute(@primary_key)
24
22
  end
25
23
 
26
24
  def primary_key_values_present? # :nodoc:
27
- return id.all? if self.class.composite_primary_key?
28
-
29
25
  !!id
30
26
  end
31
27
 
32
28
  # Sets the primary key column's value. If the primary key is composite,
33
29
  # raises TypeError when the set value not enumerable.
34
30
  def id=(value)
35
- if self.class.composite_primary_key?
36
- raise TypeError, "Expected value matching #{self.class.primary_key.inspect}, got #{value.inspect}." unless value.is_a?(Enumerable)
37
- @primary_key.zip(value) { |attr, value| _write_attribute(attr, value) }
38
- else
39
- _write_attribute(@primary_key, value)
40
- end
31
+ _write_attribute(@primary_key, value)
41
32
  end
42
33
 
43
34
  # Queries the primary key column's value. If the primary key is composite,
44
35
  # all primary key column values must be queryable.
45
36
  def id?
46
- if self.class.composite_primary_key?
47
- @primary_key.all? { |col| _query_attribute(col) }
48
- else
49
- _query_attribute(@primary_key)
50
- end
37
+ _query_attribute(@primary_key)
51
38
  end
52
39
 
53
40
  # Returns the primary key column's value before type cast. If the primary key is composite,
54
41
  # returns an array of primary key column values before type cast.
55
42
  def id_before_type_cast
56
- if self.class.composite_primary_key?
57
- @primary_key.map { |col| attribute_before_type_cast(col) }
58
- else
59
- attribute_before_type_cast(@primary_key)
60
- end
43
+ attribute_before_type_cast(@primary_key)
61
44
  end
62
45
 
63
46
  # Returns the primary key column's previous value. If the primary key is composite,
64
47
  # returns an array of primary key column previous values.
65
48
  def id_was
66
- if self.class.composite_primary_key?
67
- @primary_key.map { |col| attribute_was(col) }
68
- else
69
- attribute_was(@primary_key)
70
- end
49
+ attribute_was(@primary_key)
71
50
  end
72
51
 
73
52
  # Returns the primary key column's value from the database. If the primary key is composite,
74
53
  # returns an array of primary key column values from database.
75
54
  def id_in_database
76
- if self.class.composite_primary_key?
77
- @primary_key.map { |col| attribute_in_database(col) }
78
- else
79
- attribute_in_database(@primary_key)
80
- end
55
+ attribute_in_database(@primary_key)
81
56
  end
82
57
 
83
58
  def id_for_database # :nodoc:
84
- if self.class.composite_primary_key?
85
- @primary_key.map { |col| @attributes[col].value_for_database }
86
- else
87
- @attributes[@primary_key].value_for_database
88
- end
59
+ @attributes[@primary_key].value_for_database
89
60
  end
90
61
 
91
62
  private
@@ -109,20 +80,19 @@ module ActiveRecord
109
80
  # Overwriting will negate any effect of the +primary_key_prefix_type+
110
81
  # setting, though.
111
82
  def primary_key
112
- if PRIMARY_KEY_NOT_SET.equal?(@primary_key)
113
- @primary_key = reset_primary_key
114
- end
83
+ reset_primary_key if PRIMARY_KEY_NOT_SET.equal?(@primary_key)
115
84
  @primary_key
116
85
  end
117
86
 
118
87
  def composite_primary_key? # :nodoc:
119
- primary_key.is_a?(Array)
88
+ reset_primary_key if PRIMARY_KEY_NOT_SET.equal?(@primary_key)
89
+ @composite_primary_key
120
90
  end
121
91
 
122
92
  # Returns a quoted version of the primary key name, used to construct
123
93
  # SQL statements.
124
94
  def quoted_primary_key
125
- @quoted_primary_key ||= connection.quote_column_name(primary_key)
95
+ @quoted_primary_key ||= adapter_class.quote_column_name(primary_key)
126
96
  end
127
97
 
128
98
  def reset_primary_key # :nodoc:
@@ -138,12 +108,10 @@ module ActiveRecord
138
108
  base_name.foreign_key(false)
139
109
  elsif base_name && primary_key_prefix_type == :table_name_with_underscore
140
110
  base_name.foreign_key
111
+ elsif ActiveRecord::Base != self && table_exists?
112
+ schema_cache.primary_keys(table_name)
141
113
  else
142
- if ActiveRecord::Base != self && table_exists?
143
- connection.schema_cache.primary_keys(table_name)
144
- else
145
- "id"
146
- end
114
+ "id"
147
115
  end
148
116
  end
149
117
 
@@ -163,25 +131,25 @@ module ActiveRecord
163
131
  #
164
132
  # Project.primary_key # => "foo_id"
165
133
  def primary_key=(value)
166
- @primary_key = derive_primary_key(value)
134
+ @primary_key = if value.is_a?(Array)
135
+ @composite_primary_key = true
136
+ include CompositePrimaryKey
137
+ @primary_key = value.map { |v| -v.to_s }.freeze
138
+ elsif value
139
+ -value.to_s
140
+ end
167
141
  @quoted_primary_key = nil
168
142
  @attributes_builder = nil
169
143
  end
170
144
 
171
145
  private
172
- def derive_primary_key(value)
173
- return unless value
174
-
175
- return -value.to_s unless value.is_a?(Array)
176
-
177
- value.map { |v| -v.to_s }.freeze
178
- end
179
-
180
146
  def inherited(base)
181
147
  super
182
148
  base.class_eval do
183
149
  @primary_key = PRIMARY_KEY_NOT_SET
150
+ @composite_primary_key = false
184
151
  @quoted_primary_key = nil
152
+ @attributes_builder = nil
185
153
  end
186
154
  end
187
155
  end
@@ -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
@@ -69,14 +69,15 @@ module ActiveRecord
69
69
  end
70
70
 
71
71
  module ClassMethods # :nodoc:
72
- def define_attribute(name, cast_type, **)
73
- if create_time_zone_conversion_attribute?(name, cast_type)
74
- 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
75
79
  end
76
- super
77
- end
78
80
 
79
- private
80
81
  def create_time_zone_conversion_attribute?(name, cast_type)
81
82
  enabled_for_column = time_zone_aware_attributes &&
82
83
  !skip_time_zone_conversion_for_attributes.include?(name.to_sym)