activerecord 7.0.8.7 → 7.2.3

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 (283) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +781 -1777
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +30 -30
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +16 -13
  7. data/lib/active_record/association_relation.rb +2 -2
  8. data/lib/active_record/associations/alias_tracker.rb +31 -23
  9. data/lib/active_record/associations/association.rb +35 -12
  10. data/lib/active_record/associations/association_scope.rb +16 -9
  11. data/lib/active_record/associations/belongs_to_association.rb +40 -9
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  13. data/lib/active_record/associations/builder/association.rb +3 -3
  14. data/lib/active_record/associations/builder/belongs_to.rb +22 -8
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
  16. data/lib/active_record/associations/builder/has_many.rb +3 -4
  17. data/lib/active_record/associations/builder/has_one.rb +3 -4
  18. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  19. data/lib/active_record/associations/collection_association.rb +35 -21
  20. data/lib/active_record/associations/collection_proxy.rb +29 -11
  21. data/lib/active_record/associations/errors.rb +265 -0
  22. data/lib/active_record/associations/foreign_association.rb +10 -3
  23. data/lib/active_record/associations/has_many_association.rb +21 -14
  24. data/lib/active_record/associations/has_many_through_association.rb +17 -7
  25. data/lib/active_record/associations/has_one_association.rb +10 -3
  26. data/lib/active_record/associations/join_dependency/join_association.rb +4 -3
  27. data/lib/active_record/associations/join_dependency.rb +10 -10
  28. data/lib/active_record/associations/nested_error.rb +47 -0
  29. data/lib/active_record/associations/preloader/association.rb +33 -8
  30. data/lib/active_record/associations/preloader/branch.rb +7 -1
  31. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  32. data/lib/active_record/associations/preloader.rb +13 -10
  33. data/lib/active_record/associations/singular_association.rb +7 -1
  34. data/lib/active_record/associations/through_association.rb +22 -11
  35. data/lib/active_record/associations.rb +354 -485
  36. data/lib/active_record/attribute_assignment.rb +0 -4
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  38. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  39. data/lib/active_record/attribute_methods/dirty.rb +53 -35
  40. data/lib/active_record/attribute_methods/primary_key.rb +45 -25
  41. data/lib/active_record/attribute_methods/query.rb +28 -16
  42. data/lib/active_record/attribute_methods/read.rb +8 -7
  43. data/lib/active_record/attribute_methods/serialization.rb +131 -32
  44. data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -6
  45. data/lib/active_record/attribute_methods/write.rb +6 -6
  46. data/lib/active_record/attribute_methods.rb +153 -33
  47. data/lib/active_record/attributes.rb +96 -71
  48. data/lib/active_record/autosave_association.rb +81 -39
  49. data/lib/active_record/base.rb +11 -7
  50. data/lib/active_record/callbacks.rb +11 -25
  51. data/lib/active_record/coders/column_serializer.rb +61 -0
  52. data/lib/active_record/coders/json.rb +1 -1
  53. data/lib/active_record/coders/yaml_column.rb +70 -42
  54. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +123 -131
  55. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +4 -1
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +343 -91
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +160 -45
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +229 -64
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -63
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +142 -12
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +310 -129
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +539 -111
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +289 -128
  69. data/lib/active_record/connection_adapters/column.rb +9 -0
  70. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  71. data/lib/active_record/connection_adapters/mysql/database_statements.rb +26 -139
  72. data/lib/active_record/connection_adapters/mysql/quoting.rb +60 -55
  73. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  76. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +25 -13
  77. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  78. data/lib/active_record/connection_adapters/mysql2_adapter.rb +108 -68
  79. data/lib/active_record/connection_adapters/pool_config.rb +20 -10
  80. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +14 -3
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +100 -43
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  87. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
  88. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  89. data/lib/active_record/connection_adapters/postgresql/quoting.rb +65 -61
  90. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  91. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  92. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -2
  93. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +54 -1
  94. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +371 -64
  95. data/lib/active_record/connection_adapters/postgresql_adapter.rb +374 -203
  96. data/lib/active_record/connection_adapters/schema_cache.rb +302 -79
  97. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  98. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +60 -43
  99. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -45
  100. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  101. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +14 -0
  102. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  103. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +51 -8
  104. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +298 -113
  105. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  106. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  107. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  108. data/lib/active_record/connection_adapters.rb +124 -1
  109. data/lib/active_record/connection_handling.rb +101 -105
  110. data/lib/active_record/core.rb +273 -178
  111. data/lib/active_record/counter_cache.rb +69 -35
  112. data/lib/active_record/database_configurations/connection_url_resolver.rb +10 -3
  113. data/lib/active_record/database_configurations/database_config.rb +26 -5
  114. data/lib/active_record/database_configurations/hash_config.rb +52 -34
  115. data/lib/active_record/database_configurations/url_config.rb +37 -12
  116. data/lib/active_record/database_configurations.rb +87 -34
  117. data/lib/active_record/delegated_type.rb +56 -27
  118. data/lib/active_record/deprecator.rb +7 -0
  119. data/lib/active_record/destroy_association_async_job.rb +3 -1
  120. data/lib/active_record/dynamic_matchers.rb +2 -2
  121. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  122. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  123. data/lib/active_record/encryption/config.rb +25 -1
  124. data/lib/active_record/encryption/configurable.rb +12 -19
  125. data/lib/active_record/encryption/context.rb +10 -3
  126. data/lib/active_record/encryption/contexts.rb +5 -1
  127. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  128. data/lib/active_record/encryption/encryptable_record.rb +46 -22
  129. data/lib/active_record/encryption/encrypted_attribute_type.rb +48 -13
  130. data/lib/active_record/encryption/encryptor.rb +35 -19
  131. data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
  132. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  133. data/lib/active_record/encryption/key_generator.rb +12 -1
  134. data/lib/active_record/encryption/key_provider.rb +1 -1
  135. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  136. data/lib/active_record/encryption/message_serializer.rb +6 -0
  137. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  138. data/lib/active_record/encryption/properties.rb +3 -3
  139. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  140. data/lib/active_record/encryption/scheme.rb +22 -21
  141. data/lib/active_record/encryption.rb +3 -0
  142. data/lib/active_record/enum.rb +130 -28
  143. data/lib/active_record/errors.rb +154 -34
  144. data/lib/active_record/explain.rb +21 -12
  145. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  146. data/lib/active_record/fixture_set/render_context.rb +2 -0
  147. data/lib/active_record/fixture_set/table_row.rb +48 -10
  148. data/lib/active_record/fixtures.rb +167 -97
  149. data/lib/active_record/future_result.rb +47 -8
  150. data/lib/active_record/gem_version.rb +4 -4
  151. data/lib/active_record/inheritance.rb +34 -18
  152. data/lib/active_record/insert_all.rb +72 -22
  153. data/lib/active_record/integration.rb +11 -8
  154. data/lib/active_record/internal_metadata.rb +124 -20
  155. data/lib/active_record/locking/optimistic.rb +8 -7
  156. data/lib/active_record/locking/pessimistic.rb +5 -2
  157. data/lib/active_record/log_subscriber.rb +18 -22
  158. data/lib/active_record/marshalling.rb +59 -0
  159. data/lib/active_record/message_pack.rb +124 -0
  160. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  161. data/lib/active_record/middleware/database_selector.rb +6 -8
  162. data/lib/active_record/middleware/shard_selector.rb +3 -1
  163. data/lib/active_record/migration/command_recorder.rb +106 -8
  164. data/lib/active_record/migration/compatibility.rb +147 -5
  165. data/lib/active_record/migration/default_strategy.rb +22 -0
  166. data/lib/active_record/migration/execution_strategy.rb +19 -0
  167. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  168. data/lib/active_record/migration.rb +236 -118
  169. data/lib/active_record/model_schema.rb +90 -102
  170. data/lib/active_record/nested_attributes.rb +48 -11
  171. data/lib/active_record/normalization.rb +163 -0
  172. data/lib/active_record/persistence.rb +168 -339
  173. data/lib/active_record/promise.rb +84 -0
  174. data/lib/active_record/query_cache.rb +18 -25
  175. data/lib/active_record/query_logs.rb +96 -52
  176. data/lib/active_record/query_logs_formatter.rb +41 -0
  177. data/lib/active_record/querying.rb +35 -10
  178. data/lib/active_record/railtie.rb +131 -87
  179. data/lib/active_record/railties/controller_runtime.rb +22 -7
  180. data/lib/active_record/railties/databases.rake +147 -155
  181. data/lib/active_record/railties/job_runtime.rb +23 -0
  182. data/lib/active_record/readonly_attributes.rb +32 -5
  183. data/lib/active_record/reflection.rb +267 -69
  184. data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
  185. data/lib/active_record/relation/batches.rb +198 -63
  186. data/lib/active_record/relation/calculations.rb +270 -108
  187. data/lib/active_record/relation/delegation.rb +30 -19
  188. data/lib/active_record/relation/finder_methods.rb +97 -21
  189. data/lib/active_record/relation/merger.rb +6 -6
  190. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  191. data/lib/active_record/relation/predicate_builder/association_query_value.rb +20 -3
  192. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  193. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  194. data/lib/active_record/relation/predicate_builder.rb +28 -16
  195. data/lib/active_record/relation/query_attribute.rb +3 -2
  196. data/lib/active_record/relation/query_methods.rb +585 -109
  197. data/lib/active_record/relation/record_fetch_warning.rb +3 -0
  198. data/lib/active_record/relation/spawn_methods.rb +5 -4
  199. data/lib/active_record/relation/where_clause.rb +15 -21
  200. data/lib/active_record/relation.rb +592 -92
  201. data/lib/active_record/result.rb +49 -48
  202. data/lib/active_record/runtime_registry.rb +63 -1
  203. data/lib/active_record/sanitization.rb +70 -25
  204. data/lib/active_record/schema.rb +8 -7
  205. data/lib/active_record/schema_dumper.rb +90 -23
  206. data/lib/active_record/schema_migration.rb +75 -24
  207. data/lib/active_record/scoping/default.rb +15 -5
  208. data/lib/active_record/scoping/named.rb +3 -2
  209. data/lib/active_record/scoping.rb +2 -1
  210. data/lib/active_record/secure_password.rb +60 -0
  211. data/lib/active_record/secure_token.rb +21 -3
  212. data/lib/active_record/signed_id.rb +33 -11
  213. data/lib/active_record/statement_cache.rb +7 -7
  214. data/lib/active_record/store.rb +8 -8
  215. data/lib/active_record/suppressor.rb +3 -1
  216. data/lib/active_record/table_metadata.rb +1 -1
  217. data/lib/active_record/tasks/database_tasks.rb +190 -118
  218. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  219. data/lib/active_record/tasks/postgresql_database_tasks.rb +23 -13
  220. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
  221. data/lib/active_record/test_fixtures.rb +170 -155
  222. data/lib/active_record/testing/query_assertions.rb +121 -0
  223. data/lib/active_record/timestamp.rb +31 -17
  224. data/lib/active_record/token_for.rb +123 -0
  225. data/lib/active_record/touch_later.rb +12 -7
  226. data/lib/active_record/transaction.rb +132 -0
  227. data/lib/active_record/transactions.rb +108 -24
  228. data/lib/active_record/translation.rb +0 -2
  229. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  230. data/lib/active_record/type/internal/timezone.rb +7 -2
  231. data/lib/active_record/type/serialized.rb +1 -3
  232. data/lib/active_record/type/time.rb +4 -0
  233. data/lib/active_record/type_caster/connection.rb +4 -4
  234. data/lib/active_record/validations/absence.rb +1 -1
  235. data/lib/active_record/validations/associated.rb +9 -3
  236. data/lib/active_record/validations/numericality.rb +5 -4
  237. data/lib/active_record/validations/presence.rb +5 -28
  238. data/lib/active_record/validations/uniqueness.rb +61 -11
  239. data/lib/active_record/validations.rb +12 -5
  240. data/lib/active_record/version.rb +1 -1
  241. data/lib/active_record.rb +247 -33
  242. data/lib/arel/alias_predication.rb +1 -1
  243. data/lib/arel/collectors/bind.rb +3 -1
  244. data/lib/arel/collectors/composite.rb +7 -0
  245. data/lib/arel/collectors/sql_string.rb +1 -1
  246. data/lib/arel/collectors/substitute_binds.rb +1 -1
  247. data/lib/arel/crud.rb +2 -0
  248. data/lib/arel/delete_manager.rb +5 -0
  249. data/lib/arel/errors.rb +10 -0
  250. data/lib/arel/factory_methods.rb +4 -0
  251. data/lib/arel/nodes/binary.rb +6 -7
  252. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  253. data/lib/arel/nodes/cte.rb +36 -0
  254. data/lib/arel/nodes/delete_statement.rb +4 -2
  255. data/lib/arel/nodes/fragments.rb +35 -0
  256. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  257. data/lib/arel/nodes/leading_join.rb +8 -0
  258. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  259. data/lib/arel/nodes/node.rb +115 -5
  260. data/lib/arel/nodes/sql_literal.rb +13 -0
  261. data/lib/arel/nodes/table_alias.rb +4 -0
  262. data/lib/arel/nodes/update_statement.rb +4 -2
  263. data/lib/arel/nodes.rb +6 -2
  264. data/lib/arel/predications.rb +3 -1
  265. data/lib/arel/select_manager.rb +7 -3
  266. data/lib/arel/table.rb +9 -5
  267. data/lib/arel/tree_manager.rb +8 -3
  268. data/lib/arel/update_manager.rb +7 -1
  269. data/lib/arel/visitors/dot.rb +3 -0
  270. data/lib/arel/visitors/mysql.rb +17 -5
  271. data/lib/arel/visitors/postgresql.rb +1 -12
  272. data/lib/arel/visitors/sqlite.rb +25 -0
  273. data/lib/arel/visitors/to_sql.rb +114 -34
  274. data/lib/arel/visitors/visitor.rb +2 -2
  275. data/lib/arel.rb +21 -3
  276. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  277. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  278. data/lib/rails/generators/active_record/migration.rb +3 -1
  279. data/lib/rails/generators/active_record/model/USAGE +113 -0
  280. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  281. metadata +56 -17
  282. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  283. data/lib/active_record/null_relation.rb +0 -63
@@ -6,12 +6,13 @@ module ActiveRecord
6
6
  # See ActiveRecord::Attributes::ClassMethods for documentation
7
7
  module Attributes
8
8
  extend ActiveSupport::Concern
9
+ include ActiveModel::AttributeRegistration
9
10
 
10
- included do
11
- class_attribute :attributes_to_define_after_schema_loads, instance_accessor: false, default: {} # :internal:
12
- end
13
-
11
+ # = Active Record \Attributes
14
12
  module ClassMethods
13
+ # :method: attribute
14
+ # :call-seq: attribute(name, cast_type = nil, **options)
15
+ #
15
16
  # Defines an attribute with a type on this model. It will override the
16
17
  # type of existing attributes if needed. This allows control over how
17
18
  # values are converted to and from SQL when assigned to a model. It also
@@ -20,26 +21,34 @@ module ActiveRecord
20
21
  # your domain objects across much of Active Record, without having to
21
22
  # rely on implementation details or monkey patching.
22
23
  #
23
- # +name+ The name of the methods to define attribute methods for, and the
24
- # column which this will persist to.
24
+ # ==== Parameters
25
25
  #
26
- # +cast_type+ A symbol such as +:string+ or +:integer+, or a type object
27
- # to be used for this attribute. See the examples below for more
28
- # information about providing custom type objects.
26
+ # [+name+]
27
+ # The name of the methods to define attribute methods for, and the
28
+ # column which this will persist to.
29
29
  #
30
- # ==== Options
30
+ # [+cast_type+]
31
+ # A symbol such as +:string+ or +:integer+, or a type object to be used
32
+ # for this attribute. If this parameter is not passed, the previously
33
+ # defined type (if any) will be used. Otherwise, the type will be
34
+ # ActiveModel::Type::Value. See the examples below for more information
35
+ # about providing custom type objects.
31
36
  #
32
- # The following options are accepted:
37
+ # ==== Options
33
38
  #
34
- # +default+ The default value to use when no value is provided. If this option
35
- # is not passed, the previous default value (if any) will be used.
36
- # Otherwise, the default will be +nil+.
39
+ # [+:default+]
40
+ # The default value to use when no value is provided. If this option is
41
+ # not passed, the previously defined default value (if any) on the
42
+ # superclass or in the schema will be used. Otherwise, the default will
43
+ # be +nil+.
37
44
  #
38
- # +array+ (PostgreSQL only) specifies that the type should be an array (see the
39
- # examples below).
45
+ # [+:array+]
46
+ # (PostgreSQL only) Specifies that the type should be an array. See the
47
+ # examples below.
40
48
  #
41
- # +range+ (PostgreSQL only) specifies that the type should be a range (see the
42
- # examples below).
49
+ # [+:range+]
50
+ # (PostgreSQL only) Specifies that the type should be a range. See the
51
+ # examples below.
43
52
  #
44
53
  # When using a symbol for +cast_type+, extra options are forwarded to the
45
54
  # constructor of the type object.
@@ -134,7 +143,7 @@ module ActiveRecord
134
143
  # expected API. It is recommended that your type objects inherit from an
135
144
  # existing type, or from ActiveRecord::Type::Value
136
145
  #
137
- # class MoneyType < ActiveRecord::Type::Integer
146
+ # class PriceType < ActiveRecord::Type::Integer
138
147
  # def cast(value)
139
148
  # if !value.kind_of?(Numeric) && value.include?('$')
140
149
  # price_in_dollars = value.gsub(/\$/, '').to_f
@@ -146,11 +155,11 @@ module ActiveRecord
146
155
  # end
147
156
  #
148
157
  # # config/initializers/types.rb
149
- # ActiveRecord::Type.register(:money, MoneyType)
158
+ # ActiveRecord::Type.register(:price, PriceType)
150
159
  #
151
160
  # # app/models/store_listing.rb
152
161
  # class StoreListing < ActiveRecord::Base
153
- # attribute :price_in_cents, :money
162
+ # attribute :price_in_cents, :price
154
163
  # end
155
164
  #
156
165
  # store_listing = StoreListing.new(price_in_cents: '$10.00')
@@ -170,13 +179,13 @@ module ActiveRecord
170
179
  # class Money < Struct.new(:amount, :currency)
171
180
  # end
172
181
  #
173
- # class MoneyType < ActiveRecord::Type::Value
182
+ # class PriceType < ActiveRecord::Type::Value
174
183
  # def initialize(currency_converter:)
175
184
  # @currency_converter = currency_converter
176
185
  # end
177
186
  #
178
- # # value will be the result of +deserialize+ or
179
- # # +cast+. Assumed to be an instance of +Money+ in
187
+ # # value will be the result of #deserialize or
188
+ # # #cast. Assumed to be an instance of Money in
180
189
  # # this case.
181
190
  # def serialize(value)
182
191
  # value_in_bitcoins = @currency_converter.convert_to_bitcoins(value)
@@ -185,19 +194,19 @@ module ActiveRecord
185
194
  # end
186
195
  #
187
196
  # # config/initializers/types.rb
188
- # ActiveRecord::Type.register(:money, MoneyType)
197
+ # ActiveRecord::Type.register(:price, PriceType)
189
198
  #
190
199
  # # app/models/product.rb
191
200
  # class Product < ActiveRecord::Base
192
201
  # currency_converter = ConversionRatesFromTheInternet.new
193
- # attribute :price_in_bitcoins, :money, currency_converter: currency_converter
202
+ # attribute :price_in_bitcoins, :price, currency_converter: currency_converter
194
203
  # end
195
204
  #
196
205
  # Product.where(price_in_bitcoins: Money.new(5, "USD"))
197
- # # => SELECT * FROM products WHERE price_in_bitcoins = 0.02230
206
+ # # SELECT * FROM products WHERE price_in_bitcoins = 0.02230
198
207
  #
199
208
  # Product.where(price_in_bitcoins: Money.new(5, "GBP"))
200
- # # => SELECT * FROM products WHERE price_in_bitcoins = 0.03412
209
+ # # SELECT * FROM products WHERE price_in_bitcoins = 0.03412
201
210
  #
202
211
  # ==== Dirty Tracking
203
212
  #
@@ -205,51 +214,31 @@ module ActiveRecord
205
214
  # tracking is performed. The methods +changed?+ and +changed_in_place?+
206
215
  # will be called from ActiveModel::Dirty. See the documentation for those
207
216
  # methods in ActiveModel::Type::Value for more details.
208
- def attribute(name, cast_type = nil, default: NO_DEFAULT_PROVIDED, **options)
209
- name = name.to_s
210
- name = attribute_aliases[name] || name
211
-
212
- reload_schema_from_cache
213
-
214
- case cast_type
215
- when Symbol
216
- cast_type = Type.lookup(cast_type, **options, adapter: Type.adapter_name_from(self))
217
- when nil
218
- if (prev_cast_type, prev_default = attributes_to_define_after_schema_loads[name])
219
- default = prev_default if default == NO_DEFAULT_PROVIDED
220
- else
221
- prev_cast_type = -> subtype { subtype }
222
- end
223
-
224
- cast_type = if block_given?
225
- -> subtype { yield Proc === prev_cast_type ? prev_cast_type[subtype] : prev_cast_type }
226
- else
227
- prev_cast_type
228
- end
229
- end
230
-
231
- self.attributes_to_define_after_schema_loads =
232
- attributes_to_define_after_schema_loads.merge(name => [cast_type, default])
233
- end
217
+ #
218
+ #--
219
+ # Implemented by ActiveModel::AttributeRegistration#attribute.
234
220
 
235
- # This is the low level API which sits beneath +attribute+. It only
236
- # accepts type objects, and will do its work immediately instead of
237
- # waiting for the schema to load. Automatic schema detection and
238
- # ClassMethods#attribute both call this under the hood. While this method
221
+ # This API only accepts type objects, and will do its work immediately instead of
222
+ # waiting for the schema to load. While this method
239
223
  # is provided so it can be used by plugin authors, application code
240
224
  # should probably use ClassMethods#attribute.
241
225
  #
242
- # +name+ The name of the attribute being defined. Expected to be a +String+.
226
+ # ==== Parameters
227
+ #
228
+ # [+name+]
229
+ # The name of the attribute being defined. Expected to be a +String+.
243
230
  #
244
- # +cast_type+ The type object to use for this attribute.
231
+ # [+cast_type+]
232
+ # The type object to use for this attribute.
245
233
  #
246
- # +default+ The default value to use when no value is provided. If this option
247
- # is not passed, the previous default value (if any) will be used.
248
- # Otherwise, the default will be +nil+. A proc can also be passed, and
249
- # will be called once each time a new value is needed.
234
+ # [+default+]
235
+ # The default value to use when no value is provided. If this option
236
+ # is not passed, the previous default value (if any) will be used.
237
+ # Otherwise, the default will be +nil+. A proc can also be passed, and
238
+ # will be called once each time a new value is needed.
250
239
  #
251
- # +user_provided_default+ Whether the default value should be cast using
252
- # +cast+ or +deserialize+.
240
+ # [+user_provided_default+]
241
+ # Whether the default value should be cast using +cast+ or +deserialize+.
253
242
  def define_attribute(
254
243
  name,
255
244
  cast_type,
@@ -260,14 +249,38 @@ module ActiveRecord
260
249
  define_default_attribute(name, default, cast_type, from_user: user_provided_default)
261
250
  end
262
251
 
263
- def load_schema! # :nodoc:
264
- super
265
- attributes_to_define_after_schema_loads.each do |name, (cast_type, default)|
266
- cast_type = cast_type[type_for_attribute(name)] if Proc === cast_type
267
- define_attribute(name, cast_type, default: default)
252
+ def _default_attributes # :nodoc:
253
+ @default_attributes ||= begin
254
+ attributes_hash = with_connection do |connection|
255
+ columns_hash.transform_values do |column|
256
+ ActiveModel::Attribute.from_database(column.name, column.default, type_for_column(connection, column))
257
+ end
258
+ end
259
+
260
+ attribute_set = ActiveModel::AttributeSet.new(attributes_hash)
261
+ apply_pending_attribute_modifications(attribute_set)
262
+ attribute_set
268
263
  end
269
264
  end
270
265
 
266
+ ##
267
+ # :method: type_for_attribute
268
+ # :call-seq: type_for_attribute(attribute_name, &block)
269
+ #
270
+ # See ActiveModel::Attributes::ClassMethods#type_for_attribute.
271
+ #
272
+ # This method will access the database and load the model's schema if
273
+ # necessary.
274
+ #--
275
+ # Implemented by ActiveModel::AttributeRegistration::ClassMethods#type_for_attribute.
276
+
277
+ ##
278
+ protected
279
+ def reload_schema_from_cache(*)
280
+ reset_default_attributes!
281
+ super
282
+ end
283
+
271
284
  private
272
285
  NO_DEFAULT_PROVIDED = Object.new # :nodoc:
273
286
  private_constant :NO_DEFAULT_PROVIDED
@@ -287,6 +300,18 @@ module ActiveRecord
287
300
  end
288
301
  _default_attributes[name] = default_attribute
289
302
  end
303
+
304
+ def reset_default_attributes
305
+ reload_schema_from_cache
306
+ end
307
+
308
+ def resolve_type_name(name, **options)
309
+ Type.lookup(name, **options, adapter: Type.adapter_name_from(self))
310
+ end
311
+
312
+ def type_for_column(connection, column)
313
+ hook_attribute_type(column.name, super)
314
+ end
290
315
  end
291
316
  end
292
317
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_record/associations/nested_error"
4
+
3
5
  module ActiveRecord
4
6
  # = Active Record Autosave Association
5
7
  #
@@ -26,7 +28,7 @@ module ActiveRecord
26
28
  #
27
29
  # Child records are validated unless <tt>:validate</tt> is +false+.
28
30
  #
29
- # == Callbacks
31
+ # == \Callbacks
30
32
  #
31
33
  # Association with autosave option defines several callbacks on your
32
34
  # model (around_save, before_save, after_create, after_update). Please note that
@@ -273,6 +275,11 @@ module ActiveRecord
273
275
  end
274
276
 
275
277
  private
278
+ def init_internals
279
+ super
280
+ @_already_called = nil
281
+ end
282
+
276
283
  # Returns the record for an association collection that should be validated
277
284
  # or saved. If +autosave+ is +false+ only new records will be returned,
278
285
  # unless the parent is/was a new record itself.
@@ -310,7 +317,7 @@ module ActiveRecord
310
317
  def validate_single_association(reflection)
311
318
  association = association_instance_get(reflection.name)
312
319
  record = association && association.reader
313
- association_valid?(reflection, record) if record && (record.changed_for_autosave? || custom_validation_context?)
320
+ association_valid?(association, record) if record && (record.changed_for_autosave? || custom_validation_context?)
314
321
  end
315
322
 
316
323
  # Validate the associated records if <tt>:validate</tt> or
@@ -319,7 +326,7 @@ module ActiveRecord
319
326
  def validate_collection_association(reflection)
320
327
  if association = association_instance_get(reflection.name)
321
328
  if records = associated_records_to_validate_or_save(association, new_record?, reflection.options[:autosave])
322
- records.each_with_index { |record, index| association_valid?(reflection, record, index) }
329
+ records.each { |record| association_valid?(association, record) }
323
330
  end
324
331
  end
325
332
  end
@@ -327,38 +334,33 @@ module ActiveRecord
327
334
  # Returns whether or not the association is valid and applies any errors to
328
335
  # the parent, <tt>self</tt>, if it wasn't. Skips any <tt>:autosave</tt>
329
336
  # enabled records if they're marked_for_destruction? or destroyed.
330
- def association_valid?(reflection, record, index = nil)
331
- return true if record.destroyed? || (reflection.options[:autosave] && record.marked_for_destruction?)
337
+ def association_valid?(association, record)
338
+ return true if record.destroyed? || (association.options[:autosave] && record.marked_for_destruction?)
332
339
 
333
340
  context = validation_context if custom_validation_context?
341
+ return true if record.valid?(context)
334
342
 
335
- unless valid = record.valid?(context)
336
- if reflection.options[:autosave]
337
- indexed_attribute = !index.nil? && (reflection.options[:index_errors] || ActiveRecord.index_nested_attribute_errors)
343
+ if context || record.changed_for_autosave?
344
+ associated_errors = record.errors.objects
345
+ else
346
+ # If there are existing invalid records in the DB, we should still be able to reference them.
347
+ # Unless a record (no matter where in the association chain) is invalid and is being changed.
348
+ associated_errors = record.errors.objects.select { |error| error.is_a?(Associations::NestedError) }
349
+ end
338
350
 
339
- record.errors.group_by_attribute.each { |attribute, errors|
340
- attribute = normalize_reflection_attribute(indexed_attribute, reflection, index, attribute)
351
+ if association.options[:autosave]
352
+ return if equal?(record)
341
353
 
342
- errors.each { |error|
343
- self.errors.import(
344
- error,
345
- attribute: attribute
346
- )
347
- }
348
- }
349
- else
350
- errors.add(reflection.name)
351
- end
354
+ associated_errors.each { |error|
355
+ errors.objects.append(
356
+ Associations::NestedError.new(association, error)
357
+ )
358
+ }
359
+ elsif associated_errors.any?
360
+ errors.add(association.reflection.name)
352
361
  end
353
- valid
354
- end
355
362
 
356
- def normalize_reflection_attribute(indexed_attribute, reflection, index, attribute)
357
- if indexed_attribute
358
- "#{reflection.name}[#{index}].#{attribute}"
359
- else
360
- "#{reflection.name}.#{attribute}"
361
- end
363
+ errors.any?
362
364
  end
363
365
 
364
366
  # Is used as an around_save callback to check while saving a collection
@@ -436,7 +438,9 @@ module ActiveRecord
436
438
  # ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
437
439
  def save_has_one_association(reflection)
438
440
  association = association_instance_get(reflection.name)
439
- record = association && association.load_target
441
+ return unless association && association.loaded?
442
+
443
+ record = association.load_target
440
444
 
441
445
  if record && !record.destroyed?
442
446
  autosave = reflection.options[:autosave]
@@ -444,11 +448,18 @@ module ActiveRecord
444
448
  if autosave && record.marked_for_destruction?
445
449
  record.destroy
446
450
  elsif autosave != false
447
- key = reflection.options[:primary_key] ? public_send(reflection.options[:primary_key]) : id
451
+ primary_key = Array(compute_primary_key(reflection, self)).map(&:to_s)
452
+ primary_key_value = primary_key.map { |key| _read_attribute(key) }
448
453
 
449
- if (autosave && record.changed_for_autosave?) || _record_changed?(reflection, record, key)
454
+ if (autosave && record.changed_for_autosave?) || _record_changed?(reflection, record, primary_key_value)
450
455
  unless reflection.through_reflection
451
- record[reflection.foreign_key] = key
456
+ foreign_key = Array(reflection.foreign_key)
457
+ primary_key_foreign_key_pairs = primary_key.zip(foreign_key)
458
+
459
+ primary_key_foreign_key_pairs.each do |primary_key, foreign_key|
460
+ association_id = _read_attribute(primary_key)
461
+ record[foreign_key] = association_id unless record[foreign_key] == association_id
462
+ end
452
463
  association.set_inverse_instance(record)
453
464
  end
454
465
 
@@ -463,14 +474,26 @@ module ActiveRecord
463
474
  # If the record is new or it has changed, returns true.
464
475
  def _record_changed?(reflection, record, key)
465
476
  record.new_record? ||
466
- association_foreign_key_changed?(reflection, record, key) ||
477
+ (association_foreign_key_changed?(reflection, record, key) ||
478
+ inverse_polymorphic_association_changed?(reflection, record)) ||
467
479
  record.will_save_change_to_attribute?(reflection.foreign_key)
468
480
  end
469
481
 
470
482
  def association_foreign_key_changed?(reflection, record, key)
471
483
  return false if reflection.through_reflection?
472
484
 
473
- record._has_attribute?(reflection.foreign_key) && record._read_attribute(reflection.foreign_key) != key
485
+ foreign_key = Array(reflection.foreign_key)
486
+ return false unless foreign_key.all? { |key| record._has_attribute?(key) }
487
+
488
+ foreign_key.map { |key| record._read_attribute(key) } != Array(key)
489
+ end
490
+
491
+ def inverse_polymorphic_association_changed?(reflection, record)
492
+ return false unless reflection.inverse_of&.polymorphic?
493
+
494
+ class_name = record._read_attribute(reflection.inverse_of.foreign_type)
495
+
496
+ reflection.active_record.polymorphic_name != class_name
474
497
  end
475
498
 
476
499
  # Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
@@ -485,14 +508,21 @@ module ActiveRecord
485
508
  autosave = reflection.options[:autosave]
486
509
 
487
510
  if autosave && record.marked_for_destruction?
488
- self[reflection.foreign_key] = nil
511
+ foreign_key = Array(reflection.foreign_key)
512
+ foreign_key.each { |key| self[key] = nil }
489
513
  record.destroy
490
514
  elsif autosave != false
491
515
  saved = record.save(validate: !autosave) if record.new_record? || (autosave && record.changed_for_autosave?)
492
516
 
493
517
  if association.updated?
494
- association_id = record.public_send(reflection.options[:primary_key] || :id)
495
- self[reflection.foreign_key] = association_id
518
+ primary_key = Array(compute_primary_key(reflection, record)).map(&:to_s)
519
+ foreign_key = Array(reflection.foreign_key)
520
+
521
+ primary_key_foreign_key_pairs = primary_key.zip(foreign_key)
522
+ primary_key_foreign_key_pairs.each do |primary_key, foreign_key|
523
+ association_id = record._read_attribute(primary_key)
524
+ self[foreign_key] = association_id unless self[foreign_key] == association_id
525
+ end
496
526
  association.loaded!
497
527
  end
498
528
 
@@ -501,8 +531,20 @@ module ActiveRecord
501
531
  end
502
532
  end
503
533
 
504
- def custom_validation_context?
505
- validation_context && [:create, :update].exclude?(validation_context)
534
+ def compute_primary_key(reflection, record)
535
+ if primary_key_options = reflection.options[:primary_key]
536
+ primary_key_options
537
+ elsif reflection.options[:query_constraints] && (query_constraints = record.class.query_constraints_list)
538
+ query_constraints
539
+ elsif record.class.has_query_constraints? && !reflection.options[:foreign_key]
540
+ record.class.query_constraints_list
541
+ elsif record.class.composite_primary_key?
542
+ # If record has composite primary key of shape [:<tenant_key>, :id], infer primary_key as :id
543
+ primary_key = record.class.primary_key
544
+ primary_key.include?("id") ? "id" : primary_key
545
+ else
546
+ record.class.primary_key
547
+ end
506
548
  end
507
549
 
508
550
  def _ensure_no_duplicate_errors
@@ -233,7 +233,7 @@ module ActiveRecord # :nodoc:
233
233
  #
234
234
  # Connections are usually created through
235
235
  # {ActiveRecord::Base.establish_connection}[rdoc-ref:ConnectionHandling#establish_connection] and retrieved
236
- # by ActiveRecord::Base.connection. All classes inheriting from ActiveRecord::Base will use this
236
+ # by ActiveRecord::Base.lease_connection. All classes inheriting from ActiveRecord::Base will use this
237
237
  # connection. But you can also set a class-specific connection. For example, if Course is an
238
238
  # ActiveRecord::Base, but resides in a different database, you can just say <tt>Course.establish_connection</tt>
239
239
  # and Course and all of its subclasses will use this connection instead.
@@ -256,13 +256,13 @@ module ActiveRecord # :nodoc:
256
256
  # * AssociationTypeMismatch - The object assigned to the association wasn't of the type
257
257
  # specified in the association definition.
258
258
  # * AttributeAssignmentError - An error occurred while doing a mass assignment through the
259
- # {ActiveRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=] method.
259
+ # {ActiveRecord::Base#attributes=}[rdoc-ref:ActiveModel::AttributeAssignment#attributes=] method.
260
260
  # You can inspect the +attribute+ property of the exception object to determine which attribute
261
261
  # triggered the error.
262
262
  # * ConnectionNotEstablished - No connection has been established.
263
263
  # Use {ActiveRecord::Base.establish_connection}[rdoc-ref:ConnectionHandling#establish_connection] before querying.
264
264
  # * MultiparameterAssignmentErrors - Collection of errors that occurred during a mass assignment using the
265
- # {ActiveRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=] method.
265
+ # {ActiveRecord::Base#attributes=}[rdoc-ref:ActiveModel::AttributeAssignment#attributes=] method.
266
266
  # The +errors+ property of this exception contains an array of
267
267
  # AttributeAssignmentError
268
268
  # objects that should be inspected to determine which attributes triggered the errors.
@@ -280,7 +280,7 @@ module ActiveRecord # :nodoc:
280
280
  # So it's possible to assign a logger to the class through <tt>Base.logger=</tt> which will then be used by all
281
281
  # instances in the current object space.
282
282
  class Base
283
- extend ActiveModel::Naming
283
+ include ActiveModel::API
284
284
 
285
285
  extend ActiveSupport::Benchmarkable
286
286
  extend ActiveSupport::DescendantsTracker
@@ -304,18 +304,18 @@ module ActiveRecord # :nodoc:
304
304
  include Scoping
305
305
  include Sanitization
306
306
  include AttributeAssignment
307
- include ActiveModel::Conversion
308
307
  include Integration
309
308
  include Validations
310
309
  include CounterCache
311
310
  include Attributes
312
311
  include Locking::Optimistic
313
312
  include Locking::Pessimistic
313
+ include Encryption::EncryptableRecord
314
314
  include AttributeMethods
315
315
  include Callbacks
316
316
  include Timestamp
317
317
  include Associations
318
- include ActiveModel::SecurePassword
318
+ include SecurePassword
319
319
  include AutosaveAssociation
320
320
  include NestedAttributes
321
321
  include Transactions
@@ -325,9 +325,13 @@ module ActiveRecord # :nodoc:
325
325
  include Serialization
326
326
  include Store
327
327
  include SecureToken
328
+ include TokenFor
328
329
  include SignedId
329
330
  include Suppressor
330
- include Encryption::EncryptableRecord
331
+ include Normalization
332
+ include Marshalling::Methods
333
+
334
+ self.param_delimiter = "_"
331
335
  end
332
336
 
333
337
  ActiveSupport.run_load_hooks(:active_record, Base)
@@ -84,7 +84,7 @@ module ActiveRecord
84
84
  # == Types of callbacks
85
85
  #
86
86
  # There are three types of callbacks accepted by the callback macros: method references (symbol), callback objects,
87
- # inline methods (using a proc). Method references and callback objects are the recommended approaches,
87
+ # inline methods (using a proc). \Method references and callback objects are the recommended approaches,
88
88
  # inline methods using a proc are sometimes appropriate (such as for creating mix-ins).
89
89
  #
90
90
  # The method reference callbacks work by specifying a protected or private method available in the object, like this:
@@ -173,7 +173,7 @@ module ActiveRecord
173
173
  #
174
174
  # If a <tt>before_*</tt> callback throws +:abort+, all the later callbacks and
175
175
  # the associated action are cancelled.
176
- # Callbacks are generally run in the order they are defined, with the exception of callbacks defined as
176
+ # \Callbacks are generally run in the order they are defined, with the exception of callbacks defined as
177
177
  # methods on the model, which are called last.
178
178
  #
179
179
  # == Ordering callbacks
@@ -234,30 +234,16 @@ module ActiveRecord
234
234
  # end
235
235
  #
236
236
  # In this case the +log_children+ is executed before +do_something_else+.
237
- # The same applies to all non-transactional callbacks.
237
+ # This applies to all non-transactional callbacks, and to +before_commit+.
238
238
  #
239
- # As seen below, in case there are multiple transactional callbacks the order
240
- # is reversed.
239
+ # For transactional +after_+ callbacks (+after_commit+, +after_rollback+, etc), the order
240
+ # can be set via configuration.
241
241
  #
242
- # For example:
243
- #
244
- # class Topic < ActiveRecord::Base
245
- # has_many :children
246
- #
247
- # after_commit :log_children
248
- # after_commit :do_something_else
249
- #
250
- # private
251
- # def log_children
252
- # # Child processing
253
- # end
254
- #
255
- # def do_something_else
256
- # # Something else
257
- # end
258
- # end
242
+ # config.active_record.run_after_transaction_callbacks_in_order_defined = false
259
243
  #
260
- # In this case the +do_something_else+ is executed before +log_children+.
244
+ # When set to +true+ (the default from \Rails 7.1), callbacks are executed in the order they
245
+ # are defined, just like the example above. When set to +false+, the order is reversed, so
246
+ # +do_something_else+ is executed before +log_children+.
261
247
  #
262
248
  # == \Transactions
263
249
  #
@@ -432,7 +418,7 @@ module ActiveRecord
432
418
 
433
419
  def destroy # :nodoc:
434
420
  @_destroy_callback_already_called ||= false
435
- return if @_destroy_callback_already_called
421
+ return true if @_destroy_callback_already_called
436
422
  @_destroy_callback_already_called = true
437
423
  _run_destroy_callbacks { super }
438
424
  rescue RecordNotDestroyed => e
@@ -460,7 +446,7 @@ module ActiveRecord
460
446
  end
461
447
 
462
448
  def _update_record
463
- _run_update_callbacks { super }
449
+ _run_update_callbacks { record_update_timestamps { super } }
464
450
  end
465
451
  end
466
452
  end