activerecord 6.1.7 → 7.1.5

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 (311) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +2030 -1020
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +18 -18
  5. data/lib/active_record/aggregations.rb +17 -14
  6. data/lib/active_record/association_relation.rb +1 -11
  7. data/lib/active_record/associations/association.rb +51 -19
  8. data/lib/active_record/associations/association_scope.rb +17 -12
  9. data/lib/active_record/associations/belongs_to_association.rb +28 -9
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  11. data/lib/active_record/associations/builder/association.rb +11 -5
  12. data/lib/active_record/associations/builder/belongs_to.rb +40 -14
  13. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  14. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
  15. data/lib/active_record/associations/builder/has_many.rb +3 -2
  16. data/lib/active_record/associations/builder/has_one.rb +2 -1
  17. data/lib/active_record/associations/builder/singular_association.rb +6 -2
  18. data/lib/active_record/associations/collection_association.rb +39 -35
  19. data/lib/active_record/associations/collection_proxy.rb +30 -15
  20. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  21. data/lib/active_record/associations/foreign_association.rb +10 -3
  22. data/lib/active_record/associations/has_many_association.rb +28 -18
  23. data/lib/active_record/associations/has_many_through_association.rb +12 -7
  24. data/lib/active_record/associations/has_one_association.rb +20 -10
  25. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  26. data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
  27. data/lib/active_record/associations/join_dependency.rb +28 -20
  28. data/lib/active_record/associations/preloader/association.rb +210 -52
  29. data/lib/active_record/associations/preloader/batch.rb +48 -0
  30. data/lib/active_record/associations/preloader/branch.rb +147 -0
  31. data/lib/active_record/associations/preloader/through_association.rb +50 -14
  32. data/lib/active_record/associations/preloader.rb +50 -121
  33. data/lib/active_record/associations/singular_association.rb +9 -3
  34. data/lib/active_record/associations/through_association.rb +25 -14
  35. data/lib/active_record/associations.rb +446 -306
  36. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  37. data/lib/active_record/attribute_assignment.rb +1 -3
  38. data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
  39. data/lib/active_record/attribute_methods/dirty.rb +73 -22
  40. data/lib/active_record/attribute_methods/primary_key.rb +78 -26
  41. data/lib/active_record/attribute_methods/query.rb +31 -19
  42. data/lib/active_record/attribute_methods/read.rb +27 -12
  43. data/lib/active_record/attribute_methods/serialization.rb +194 -37
  44. data/lib/active_record/attribute_methods/time_zone_conversion.rb +8 -3
  45. data/lib/active_record/attribute_methods/write.rb +12 -15
  46. data/lib/active_record/attribute_methods.rb +161 -40
  47. data/lib/active_record/attributes.rb +27 -38
  48. data/lib/active_record/autosave_association.rb +65 -31
  49. data/lib/active_record/base.rb +25 -2
  50. data/lib/active_record/callbacks.rb +18 -34
  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 -46
  54. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +367 -0
  55. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +113 -597
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +172 -50
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +78 -27
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +87 -73
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +367 -141
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +281 -59
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +631 -150
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +317 -164
  70. data/lib/active_record/connection_adapters/column.rb +13 -0
  71. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  72. data/lib/active_record/connection_adapters/mysql/database_statements.rb +25 -134
  73. data/lib/active_record/connection_adapters/mysql/quoting.rb +56 -25
  74. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  76. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  77. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +39 -14
  78. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
  79. data/lib/active_record/connection_adapters/mysql2_adapter.rb +112 -55
  80. data/lib/active_record/connection_adapters/pool_config.rb +20 -11
  81. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +89 -52
  84. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  89. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
  91. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  94. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -56
  96. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
  97. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
  98. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -3
  99. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
  100. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +397 -75
  101. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  102. data/lib/active_record/connection_adapters/postgresql_adapter.rb +508 -246
  103. data/lib/active_record/connection_adapters/schema_cache.rb +319 -90
  104. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  105. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +72 -53
  106. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +37 -21
  107. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
  108. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -22
  109. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +296 -104
  110. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  111. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  112. data/lib/active_record/connection_adapters/trilogy_adapter.rb +258 -0
  113. data/lib/active_record/connection_adapters.rb +9 -6
  114. data/lib/active_record/connection_handling.rb +108 -137
  115. data/lib/active_record/core.rb +242 -233
  116. data/lib/active_record/counter_cache.rb +52 -27
  117. data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -2
  118. data/lib/active_record/database_configurations/database_config.rb +21 -12
  119. data/lib/active_record/database_configurations/hash_config.rb +88 -16
  120. data/lib/active_record/database_configurations/url_config.rb +18 -12
  121. data/lib/active_record/database_configurations.rb +95 -59
  122. data/lib/active_record/delegated_type.rb +66 -20
  123. data/lib/active_record/deprecator.rb +7 -0
  124. data/lib/active_record/destroy_association_async_job.rb +4 -2
  125. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  126. data/lib/active_record/dynamic_matchers.rb +1 -1
  127. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  128. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  129. data/lib/active_record/encryption/cipher.rb +53 -0
  130. data/lib/active_record/encryption/config.rb +68 -0
  131. data/lib/active_record/encryption/configurable.rb +60 -0
  132. data/lib/active_record/encryption/context.rb +42 -0
  133. data/lib/active_record/encryption/contexts.rb +76 -0
  134. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  135. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  136. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  137. data/lib/active_record/encryption/encrypted_attribute_type.rb +155 -0
  138. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  139. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  140. data/lib/active_record/encryption/encryptor.rb +155 -0
  141. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  142. data/lib/active_record/encryption/errors.rb +15 -0
  143. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  144. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  145. data/lib/active_record/encryption/key.rb +28 -0
  146. data/lib/active_record/encryption/key_generator.rb +53 -0
  147. data/lib/active_record/encryption/key_provider.rb +46 -0
  148. data/lib/active_record/encryption/message.rb +33 -0
  149. data/lib/active_record/encryption/message_serializer.rb +92 -0
  150. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  151. data/lib/active_record/encryption/properties.rb +76 -0
  152. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  153. data/lib/active_record/encryption/scheme.rb +100 -0
  154. data/lib/active_record/encryption.rb +58 -0
  155. data/lib/active_record/enum.rb +154 -63
  156. data/lib/active_record/errors.rb +172 -15
  157. data/lib/active_record/explain.rb +23 -3
  158. data/lib/active_record/explain_registry.rb +11 -6
  159. data/lib/active_record/explain_subscriber.rb +1 -1
  160. data/lib/active_record/fixture_set/file.rb +15 -1
  161. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  162. data/lib/active_record/fixture_set/render_context.rb +2 -0
  163. data/lib/active_record/fixture_set/table_row.rb +70 -14
  164. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  165. data/lib/active_record/fixtures.rb +147 -86
  166. data/lib/active_record/future_result.rb +174 -0
  167. data/lib/active_record/gem_version.rb +3 -3
  168. data/lib/active_record/inheritance.rb +81 -29
  169. data/lib/active_record/insert_all.rb +135 -22
  170. data/lib/active_record/integration.rb +11 -10
  171. data/lib/active_record/internal_metadata.rb +119 -33
  172. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  173. data/lib/active_record/locking/optimistic.rb +37 -22
  174. data/lib/active_record/locking/pessimistic.rb +15 -6
  175. data/lib/active_record/log_subscriber.rb +52 -19
  176. data/lib/active_record/marshalling.rb +59 -0
  177. data/lib/active_record/message_pack.rb +124 -0
  178. data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
  179. data/lib/active_record/middleware/database_selector.rb +23 -13
  180. data/lib/active_record/middleware/shard_selector.rb +62 -0
  181. data/lib/active_record/migration/command_recorder.rb +112 -14
  182. data/lib/active_record/migration/compatibility.rb +233 -46
  183. data/lib/active_record/migration/default_strategy.rb +23 -0
  184. data/lib/active_record/migration/execution_strategy.rb +19 -0
  185. data/lib/active_record/migration/join_table.rb +1 -1
  186. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  187. data/lib/active_record/migration.rb +361 -173
  188. data/lib/active_record/model_schema.rb +125 -101
  189. data/lib/active_record/nested_attributes.rb +50 -20
  190. data/lib/active_record/no_touching.rb +3 -3
  191. data/lib/active_record/normalization.rb +167 -0
  192. data/lib/active_record/persistence.rb +409 -88
  193. data/lib/active_record/promise.rb +84 -0
  194. data/lib/active_record/query_cache.rb +4 -22
  195. data/lib/active_record/query_logs.rb +174 -0
  196. data/lib/active_record/query_logs_formatter.rb +41 -0
  197. data/lib/active_record/querying.rb +29 -6
  198. data/lib/active_record/railtie.rb +220 -44
  199. data/lib/active_record/railties/controller_runtime.rb +15 -10
  200. data/lib/active_record/railties/databases.rake +188 -252
  201. data/lib/active_record/railties/job_runtime.rb +23 -0
  202. data/lib/active_record/readonly_attributes.rb +41 -3
  203. data/lib/active_record/reflection.rb +248 -81
  204. data/lib/active_record/relation/batches/batch_enumerator.rb +23 -7
  205. data/lib/active_record/relation/batches.rb +192 -63
  206. data/lib/active_record/relation/calculations.rb +246 -90
  207. data/lib/active_record/relation/delegation.rb +28 -14
  208. data/lib/active_record/relation/finder_methods.rb +108 -51
  209. data/lib/active_record/relation/merger.rb +22 -13
  210. data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
  211. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  212. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  213. data/lib/active_record/relation/predicate_builder.rb +27 -20
  214. data/lib/active_record/relation/query_attribute.rb +30 -12
  215. data/lib/active_record/relation/query_methods.rb +670 -129
  216. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  217. data/lib/active_record/relation/spawn_methods.rb +20 -3
  218. data/lib/active_record/relation/where_clause.rb +10 -19
  219. data/lib/active_record/relation.rb +287 -120
  220. data/lib/active_record/result.rb +37 -11
  221. data/lib/active_record/runtime_registry.rb +32 -13
  222. data/lib/active_record/sanitization.rb +65 -20
  223. data/lib/active_record/schema.rb +36 -22
  224. data/lib/active_record/schema_dumper.rb +73 -24
  225. data/lib/active_record/schema_migration.rb +68 -33
  226. data/lib/active_record/scoping/default.rb +72 -15
  227. data/lib/active_record/scoping/named.rb +5 -13
  228. data/lib/active_record/scoping.rb +65 -34
  229. data/lib/active_record/secure_password.rb +60 -0
  230. data/lib/active_record/secure_token.rb +21 -3
  231. data/lib/active_record/serialization.rb +6 -1
  232. data/lib/active_record/signed_id.rb +10 -8
  233. data/lib/active_record/store.rb +10 -10
  234. data/lib/active_record/suppressor.rb +13 -15
  235. data/lib/active_record/table_metadata.rb +16 -3
  236. data/lib/active_record/tasks/database_tasks.rb +251 -140
  237. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
  238. data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
  239. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  240. data/lib/active_record/test_databases.rb +1 -1
  241. data/lib/active_record/test_fixtures.rb +117 -96
  242. data/lib/active_record/timestamp.rb +32 -19
  243. data/lib/active_record/token_for.rb +113 -0
  244. data/lib/active_record/touch_later.rb +11 -6
  245. data/lib/active_record/transactions.rb +48 -27
  246. data/lib/active_record/translation.rb +3 -3
  247. data/lib/active_record/type/adapter_specific_registry.rb +32 -14
  248. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  249. data/lib/active_record/type/internal/timezone.rb +7 -2
  250. data/lib/active_record/type/serialized.rb +9 -5
  251. data/lib/active_record/type/time.rb +4 -0
  252. data/lib/active_record/type/type_map.rb +17 -20
  253. data/lib/active_record/type.rb +1 -2
  254. data/lib/active_record/validations/absence.rb +1 -1
  255. data/lib/active_record/validations/associated.rb +4 -4
  256. data/lib/active_record/validations/numericality.rb +5 -4
  257. data/lib/active_record/validations/presence.rb +5 -28
  258. data/lib/active_record/validations/uniqueness.rb +51 -6
  259. data/lib/active_record/validations.rb +8 -4
  260. data/lib/active_record/version.rb +1 -1
  261. data/lib/active_record.rb +335 -32
  262. data/lib/arel/attributes/attribute.rb +0 -8
  263. data/lib/arel/crud.rb +28 -22
  264. data/lib/arel/delete_manager.rb +18 -4
  265. data/lib/arel/errors.rb +10 -0
  266. data/lib/arel/factory_methods.rb +4 -0
  267. data/lib/arel/filter_predications.rb +9 -0
  268. data/lib/arel/insert_manager.rb +2 -3
  269. data/lib/arel/nodes/and.rb +4 -0
  270. data/lib/arel/nodes/binary.rb +6 -1
  271. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  272. data/lib/arel/nodes/casted.rb +1 -1
  273. data/lib/arel/nodes/cte.rb +36 -0
  274. data/lib/arel/nodes/delete_statement.rb +12 -13
  275. data/lib/arel/nodes/filter.rb +10 -0
  276. data/lib/arel/nodes/fragments.rb +35 -0
  277. data/lib/arel/nodes/function.rb +1 -0
  278. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  279. data/lib/arel/nodes/insert_statement.rb +2 -2
  280. data/lib/arel/nodes/leading_join.rb +8 -0
  281. data/lib/arel/nodes/node.rb +111 -2
  282. data/lib/arel/nodes/select_core.rb +2 -2
  283. data/lib/arel/nodes/select_statement.rb +2 -2
  284. data/lib/arel/nodes/sql_literal.rb +6 -0
  285. data/lib/arel/nodes/table_alias.rb +4 -0
  286. data/lib/arel/nodes/update_statement.rb +8 -3
  287. data/lib/arel/nodes.rb +5 -0
  288. data/lib/arel/predications.rb +13 -3
  289. data/lib/arel/select_manager.rb +10 -4
  290. data/lib/arel/table.rb +9 -6
  291. data/lib/arel/tree_manager.rb +5 -13
  292. data/lib/arel/update_manager.rb +18 -4
  293. data/lib/arel/visitors/dot.rb +80 -90
  294. data/lib/arel/visitors/mysql.rb +16 -3
  295. data/lib/arel/visitors/postgresql.rb +0 -10
  296. data/lib/arel/visitors/to_sql.rb +141 -20
  297. data/lib/arel/visitors/visitor.rb +2 -2
  298. data/lib/arel.rb +18 -3
  299. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  300. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  301. data/lib/rails/generators/active_record/migration.rb +3 -1
  302. data/lib/rails/generators/active_record/model/USAGE +113 -0
  303. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  304. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  305. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  306. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  307. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  308. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  309. metadata +96 -16
  310. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  311. data/lib/active_record/null_relation.rb +0 -67
@@ -23,7 +23,7 @@ module ActiveRecord
23
23
 
24
24
  RESTRICTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
25
25
 
26
- class GeneratedAttributeMethods < Module #:nodoc:
26
+ class GeneratedAttributeMethods < Module # :nodoc:
27
27
  include Mutex_m
28
28
  end
29
29
 
@@ -33,26 +33,99 @@ module ActiveRecord
33
33
  Base.instance_methods +
34
34
  Base.private_instance_methods -
35
35
  Base.superclass.instance_methods -
36
- Base.superclass.private_instance_methods
36
+ Base.superclass.private_instance_methods +
37
+ %i[__id__ dup freeze frozen? hash class clone]
37
38
  ).map { |m| -m.to_s }.to_set.freeze
38
39
  end
39
40
  end
40
41
 
41
42
  module ClassMethods
42
- def inherited(child_class) #:nodoc:
43
- child_class.initialize_generated_modules
44
- super
45
- end
46
-
47
43
  def initialize_generated_modules # :nodoc:
48
44
  @generated_attribute_methods = const_set(:GeneratedAttributeMethods, GeneratedAttributeMethods.new)
49
45
  private_constant :GeneratedAttributeMethods
50
46
  @attribute_methods_generated = false
47
+ @alias_attributes_mass_generated = false
51
48
  include @generated_attribute_methods
52
49
 
53
50
  super
54
51
  end
55
52
 
53
+ def alias_attribute(new_name, old_name)
54
+ super
55
+
56
+ if @alias_attributes_mass_generated
57
+ ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |code_generator|
58
+ generate_alias_attribute_methods(code_generator, new_name, old_name)
59
+ end
60
+ end
61
+ end
62
+
63
+ def eagerly_generate_alias_attribute_methods(_new_name, _old_name) # :nodoc:
64
+ # alias attributes in Active Record are lazily generated
65
+ end
66
+
67
+ def generate_alias_attributes # :nodoc:
68
+ superclass.generate_alias_attributes unless superclass == Base
69
+ return false if @alias_attributes_mass_generated
70
+
71
+ generated_attribute_methods.synchronize do
72
+ return if @alias_attributes_mass_generated
73
+ ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |code_generator|
74
+ aliases_by_attribute_name.each do |old_name, new_names|
75
+ new_names.each do |new_name|
76
+ generate_alias_attribute_methods(code_generator, new_name, old_name)
77
+ end
78
+ end
79
+ end
80
+
81
+ @alias_attributes_mass_generated = true
82
+ end
83
+ true
84
+ end
85
+
86
+ def generate_alias_attribute_methods(code_generator, new_name, old_name) # :nodoc:
87
+ attribute_method_patterns.each do |pattern|
88
+ alias_attribute_method_definition(code_generator, pattern, new_name, old_name)
89
+ end
90
+ attribute_method_patterns_cache.clear
91
+ end
92
+
93
+ def alias_attribute_method_definition(code_generator, pattern, new_name, old_name) # :nodoc:
94
+ method_name = pattern.method_name(new_name).to_s
95
+ target_name = pattern.method_name(old_name).to_s
96
+ old_name = old_name.to_s
97
+
98
+ method_defined = method_defined?(target_name) || private_method_defined?(target_name)
99
+ manually_defined = method_defined &&
100
+ !self.instance_method(target_name).owner.is_a?(GeneratedAttributeMethods)
101
+ reserved_method_name = ::ActiveRecord::AttributeMethods.dangerous_attribute_methods.include?(target_name)
102
+
103
+ if !abstract_class? && !has_attribute?(old_name)
104
+ # We only need to issue this deprecation warning once, so we issue it when defining the original reader method.
105
+ should_warn = target_name == old_name
106
+ if should_warn
107
+ ActiveRecord.deprecator.warn(
108
+ "#{self} model aliases `#{old_name}`, but `#{old_name}` is not an attribute. " \
109
+ "Starting in Rails 7.2, alias_attribute with non-attribute targets will raise. " \
110
+ "Use `alias_method :#{new_name}, :#{old_name}` or define the method manually."
111
+ )
112
+ end
113
+ super
114
+ elsif manually_defined && !reserved_method_name
115
+ aliased_method_redefined_as_well = method_defined_within?(method_name, self)
116
+ return if aliased_method_redefined_as_well
117
+
118
+ ActiveRecord.deprecator.warn(
119
+ "#{self} model aliases `#{old_name}` and has a method called `#{target_name}` defined. " \
120
+ "Starting in Rails 7.2 `#{method_name}` will not be calling `#{target_name}` anymore. " \
121
+ "You may want to additionally define `#{method_name}` to preserve the current behavior."
122
+ )
123
+ super
124
+ else
125
+ define_attribute_method_pattern(pattern, old_name, owner: code_generator, as: new_name, override: true)
126
+ end
127
+ end
128
+
56
129
  # Generates all the attribute related methods for columns in the database
57
130
  # accessors, mutators and query methods.
58
131
  def define_attribute_methods # :nodoc:
@@ -65,12 +138,18 @@ module ActiveRecord
65
138
  super(attribute_names)
66
139
  @attribute_methods_generated = true
67
140
  end
141
+ true
142
+ end
143
+
144
+ def attribute_methods_generated? # :nodoc:
145
+ @attribute_methods_generated && @alias_attributes_mass_generated
68
146
  end
69
147
 
70
148
  def undefine_attribute_methods # :nodoc:
71
149
  generated_attribute_methods.synchronize do
72
150
  super if defined?(@attribute_methods_generated) && @attribute_methods_generated
73
151
  @attribute_methods_generated = false
152
+ @alias_attributes_mass_generated = false
74
153
  end
75
154
  end
76
155
 
@@ -97,7 +176,7 @@ module ActiveRecord
97
176
  super
98
177
  else
99
178
  # If ThisClass < ... < SomeSuperClass < ... < Base and SomeSuperClass
100
- # defines its own attribute method, then we don't want to overwrite that.
179
+ # defines its own attribute method, then we don't want to override that.
101
180
  defined = method_defined_within?(method_name, superclass, Base) &&
102
181
  ! superclass.instance_method(method_name).owner.is_a?(GeneratedAttributeMethods)
103
182
  defined || super
@@ -186,6 +265,16 @@ module ActiveRecord
186
265
  def _has_attribute?(attr_name) # :nodoc:
187
266
  attribute_types.key?(attr_name)
188
267
  end
268
+
269
+ private
270
+ def inherited(child_class)
271
+ super
272
+ child_class.initialize_generated_modules
273
+ child_class.class_eval do
274
+ @alias_attributes_mass_generated = false
275
+ @attribute_names = nil
276
+ end
277
+ end
189
278
  end
190
279
 
191
280
  # A Person object with a name attribute can ask <tt>person.respond_to?(:name)</tt>,
@@ -267,9 +356,8 @@ module ActiveRecord
267
356
 
268
357
  # Returns an <tt>#inspect</tt>-like string for the value of the
269
358
  # attribute +attr_name+. String attributes are truncated up to 50
270
- # characters, Date and Time attributes are returned in the
271
- # <tt>:db</tt> format. Other attributes return the value of
272
- # <tt>#inspect</tt> without modification.
359
+ # characters. Other attributes return the value of <tt>#inspect</tt>
360
+ # without modification.
273
361
  #
274
362
  # person = Person.create!(name: 'David Heinemeier Hansson ' * 3)
275
363
  #
@@ -277,7 +365,7 @@ module ActiveRecord
277
365
  # # => "\"David Heinemeier Hansson David Heinemeier Hansson ...\""
278
366
  #
279
367
  # person.attribute_for_inspect(:created_at)
280
- # # => "\"2012-10-22 00:15:07\""
368
+ # # => "\"2012-10-22 00:15:07.000000000 +0000\""
281
369
  #
282
370
  # person.attribute_for_inspect(:tag_ids)
283
371
  # # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]"
@@ -310,37 +398,40 @@ module ActiveRecord
310
398
  !value.nil? && !(value.respond_to?(:empty?) && value.empty?)
311
399
  end
312
400
 
313
- # Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
314
- # "2004-12-12" in a date column is cast to a date object, like Date.new(2004, 12, 12)). It raises
315
- # <tt>ActiveModel::MissingAttributeError</tt> if the identified attribute is missing.
316
- #
317
- # Note: +:id+ is always present.
401
+ # Returns the value of the attribute identified by +attr_name+ after it has
402
+ # been type cast. (For information about specific type casting behavior, see
403
+ # the types under ActiveModel::Type.)
318
404
  #
319
405
  # class Person < ActiveRecord::Base
320
406
  # belongs_to :organization
321
407
  # end
322
408
  #
323
- # person = Person.new(name: 'Francesco', age: '22')
324
- # person[:name] # => "Francesco"
325
- # person[:age] # => 22
409
+ # person = Person.new(name: "Francesco", date_of_birth: "2004-12-12")
410
+ # person[:name] # => "Francesco"
411
+ # person[:date_of_birth] # => Date.new(2004, 12, 12)
412
+ # person[:organization_id] # => nil
326
413
  #
327
- # person = Person.select('id').first
328
- # person[:name] # => ActiveModel::MissingAttributeError: missing attribute: name
329
- # person[:organization_id] # => ActiveModel::MissingAttributeError: missing attribute: organization_id
414
+ # Raises ActiveModel::MissingAttributeError if the attribute is missing.
415
+ # Note, however, that the +id+ attribute will never be considered missing.
416
+ #
417
+ # person = Person.select(:name).first
418
+ # person[:name] # => "Francesco"
419
+ # person[:date_of_birth] # => ActiveModel::MissingAttributeError: missing attribute 'date_of_birth' for Person
420
+ # person[:organization_id] # => ActiveModel::MissingAttributeError: missing attribute 'organization_id' for Person
421
+ # person[:id] # => nil
330
422
  def [](attr_name)
331
423
  read_attribute(attr_name) { |n| missing_attribute(n, caller) }
332
424
  end
333
425
 
334
- # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
335
- # (Alias for the protected #write_attribute method).
426
+ # Updates the attribute identified by +attr_name+ using the specified
427
+ # +value+. The attribute value will be type cast upon being read.
336
428
  #
337
429
  # class Person < ActiveRecord::Base
338
430
  # end
339
431
  #
340
432
  # person = Person.new
341
- # person[:age] = '22'
342
- # person[:age] # => 22
343
- # person[:age].class # => Integer
433
+ # person[:date_of_birth] = "2004-12-12"
434
+ # person[:date_of_birth] # => Date.new(2004, 12, 12)
344
435
  def []=(attr_name, value)
345
436
  write_attribute(attr_name, value)
346
437
  end
@@ -361,10 +452,9 @@ module ActiveRecord
361
452
  # end
362
453
  #
363
454
  # private
364
- #
365
- # def print_accessed_fields
366
- # p @posts.first.accessed_fields
367
- # end
455
+ # def print_accessed_fields
456
+ # p @posts.first.accessed_fields
457
+ # end
368
458
  # end
369
459
  #
370
460
  # Which allows you to quickly change your code to:
@@ -379,31 +469,62 @@ module ActiveRecord
379
469
  end
380
470
 
381
471
  private
472
+ def respond_to_missing?(name, include_private = false)
473
+ if self.class.define_attribute_methods
474
+ # Some methods weren't defined yet.
475
+ return true if self.class.method_defined?(name)
476
+ return true if include_private && self.class.private_method_defined?(name)
477
+ end
478
+
479
+ super
480
+ end
481
+
482
+ def method_missing(name, ...)
483
+ unless self.class.attribute_methods_generated?
484
+ if self.class.method_defined?(name)
485
+ # The method is explicitly defined in the model, but calls a generated
486
+ # method with super. So we must resume the call chain at the right setp.
487
+ last_method = method(name)
488
+ last_method = last_method.super_method while last_method.super_method
489
+ self.class.define_attribute_methods
490
+ if last_method.super_method
491
+ return last_method.super_method.call(...)
492
+ end
493
+ elsif self.class.define_attribute_methods | self.class.generate_alias_attributes
494
+ # Some attribute methods weren't generated yet, we retry the call
495
+ return public_send(name, ...)
496
+ end
497
+ end
498
+
499
+ super
500
+ end
501
+
382
502
  def attribute_method?(attr_name)
383
503
  # We check defined? because Syck calls respond_to? before actually calling initialize.
384
504
  defined?(@attributes) && @attributes.key?(attr_name)
385
505
  end
386
506
 
387
507
  def attributes_with_values(attribute_names)
388
- attribute_names.index_with do |name|
389
- _read_attribute(name)
390
- end
508
+ attribute_names.index_with { |name| @attributes[name] }
391
509
  end
392
510
 
393
- # Filters the primary keys and readonly attributes from the attribute names.
511
+ # Filters the primary keys, readonly attributes and virtual columns from the attribute names.
394
512
  def attributes_for_update(attribute_names)
395
513
  attribute_names &= self.class.column_names
396
514
  attribute_names.delete_if do |name|
397
- self.class.readonly_attribute?(name)
515
+ self.class.readonly_attribute?(name) ||
516
+ self.class.counter_cache_column?(name) ||
517
+ column_for_attribute(name).virtual?
398
518
  end
399
519
  end
400
520
 
401
- # Filters out the primary keys, from the attribute names, when the primary
521
+ # Filters out the virtual columns and also primary keys, from the attribute names, when the primary
402
522
  # key is to be generated (e.g. the id attribute has no value).
403
523
  def attributes_for_create(attribute_names)
404
524
  attribute_names &= self.class.column_names
405
525
  attribute_names.delete_if do |name|
406
- pk_attribute?(name) && id.nil?
526
+ (pk_attribute?(name) && id.nil?) ||
527
+ column_for_attribute(name).virtual?
407
528
  end
408
529
  end
409
530
 
@@ -414,7 +535,7 @@ module ActiveRecord
414
535
  inspected_value = if value.is_a?(String) && value.length > 50
415
536
  "#{value[0, 50]}...".inspect
416
537
  elsif value.is_a?(Date) || value.is_a?(Time)
417
- %("#{value.to_s(:inspect)}")
538
+ %("#{value.to_fs(:inspect)}")
418
539
  else
419
540
  value.inspect
420
541
  end
@@ -10,11 +10,8 @@ module ActiveRecord
10
10
  included do
11
11
  class_attribute :attributes_to_define_after_schema_loads, instance_accessor: false, default: {} # :internal:
12
12
  end
13
-
13
+ # = Active Record \Attributes
14
14
  module ClassMethods
15
- ##
16
- # :call-seq: attribute(name, cast_type = nil, **options)
17
- #
18
15
  # Defines an attribute with a type on this model. It will override the
19
16
  # type of existing attributes if needed. This allows control over how
20
17
  # values are converted to and from SQL when assigned to a model. It also
@@ -197,10 +194,10 @@ module ActiveRecord
197
194
  # end
198
195
  #
199
196
  # Product.where(price_in_bitcoins: Money.new(5, "USD"))
200
- # # => SELECT * FROM products WHERE price_in_bitcoins = 0.02230
197
+ # # SELECT * FROM products WHERE price_in_bitcoins = 0.02230
201
198
  #
202
199
  # Product.where(price_in_bitcoins: Money.new(5, "GBP"))
203
- # # => SELECT * FROM products WHERE price_in_bitcoins = 0.03412
200
+ # # SELECT * FROM products WHERE price_in_bitcoins = 0.03412
204
201
  #
205
202
  # ==== Dirty Tracking
206
203
  #
@@ -208,14 +205,31 @@ module ActiveRecord
208
205
  # tracking is performed. The methods +changed?+ and +changed_in_place?+
209
206
  # will be called from ActiveModel::Dirty. See the documentation for those
210
207
  # methods in ActiveModel::Type::Value for more details.
211
- def attribute(name, cast_type = nil, **options, &block)
208
+ def attribute(name, cast_type = nil, default: NO_DEFAULT_PROVIDED, **options)
212
209
  name = name.to_s
210
+ name = attribute_aliases[name] || name
211
+
213
212
  reload_schema_from_cache
214
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
+
215
231
  self.attributes_to_define_after_schema_loads =
216
- attributes_to_define_after_schema_loads.merge(
217
- name => [cast_type || block, options]
218
- )
232
+ attributes_to_define_after_schema_loads.merge(name => [cast_type, default])
219
233
  end
220
234
 
221
235
  # This is the low level API which sits beneath +attribute+. It only
@@ -248,8 +262,9 @@ module ActiveRecord
248
262
 
249
263
  def load_schema! # :nodoc:
250
264
  super
251
- attributes_to_define_after_schema_loads.each do |name, (type, options)|
252
- define_attribute(name, _lookup_cast_type(name, type, options), **options.slice(:default))
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)
253
268
  end
254
269
  end
255
270
 
@@ -272,32 +287,6 @@ module ActiveRecord
272
287
  end
273
288
  _default_attributes[name] = default_attribute
274
289
  end
275
-
276
- def decorate_attribute_type(attr_name, **default)
277
- type, options = attributes_to_define_after_schema_loads[attr_name]
278
-
279
- default.with_defaults!(default: options[:default]) if options&.key?(:default)
280
-
281
- attribute(attr_name, **default) do |cast_type|
282
- if type && !type.is_a?(Proc)
283
- cast_type = _lookup_cast_type(attr_name, type, options)
284
- end
285
-
286
- yield cast_type
287
- end
288
- end
289
-
290
- def _lookup_cast_type(name, type, options)
291
- case type
292
- when Symbol
293
- adapter_name = ActiveRecord::Type.adapter_name_from(self)
294
- ActiveRecord::Type.lookup(type, **options.except(:default), adapter: adapter_name)
295
- when Proc
296
- type[type_for_attribute(name)]
297
- else
298
- type || type_for_attribute(name)
299
- end
300
- end
301
290
  end
302
291
  end
303
292
  end
@@ -26,7 +26,7 @@ module ActiveRecord
26
26
  #
27
27
  # Child records are validated unless <tt>:validate</tt> is +false+.
28
28
  #
29
- # == Callbacks
29
+ # == \Callbacks
30
30
  #
31
31
  # Association with autosave option defines several callbacks on your
32
32
  # model (around_save, before_save, after_create, after_update). Please note that
@@ -138,7 +138,7 @@ module ActiveRecord
138
138
  module AutosaveAssociation
139
139
  extend ActiveSupport::Concern
140
140
 
141
- module AssociationBuilderExtension #:nodoc:
141
+ module AssociationBuilderExtension # :nodoc:
142
142
  def self.build(model, reflection)
143
143
  model.send(:add_autosave_association_callbacks, reflection)
144
144
  end
@@ -150,25 +150,10 @@ module ActiveRecord
150
150
 
151
151
  included do
152
152
  Associations::Builder::Association.extensions << AssociationBuilderExtension
153
- mattr_accessor :index_nested_attribute_errors, instance_writer: false, default: false
154
153
  end
155
154
 
156
155
  module ClassMethods # :nodoc:
157
156
  private
158
- if Module.method(:method_defined?).arity == 1 # MRI 2.5 and older
159
- using Module.new {
160
- refine Module do
161
- def method_defined?(method, inherit = true)
162
- if inherit
163
- super(method)
164
- else
165
- instance_methods(false).include?(method.to_sym)
166
- end
167
- end
168
- end
169
- }
170
- end
171
-
172
157
  def define_non_cyclic_method(name, &block)
173
158
  return if method_defined?(name, false)
174
159
 
@@ -210,7 +195,7 @@ module ActiveRecord
210
195
  after_create save_method
211
196
  after_update save_method
212
197
  elsif reflection.has_one?
213
- define_method(save_method) { save_has_one_association(reflection) } unless method_defined?(save_method)
198
+ define_non_cyclic_method(save_method) { save_has_one_association(reflection) }
214
199
  # Configures two callbacks instead of a single after_save so that
215
200
  # the model may rely on their execution order relative to its
216
201
  # own callbacks.
@@ -288,6 +273,11 @@ module ActiveRecord
288
273
  end
289
274
 
290
275
  private
276
+ def init_internals
277
+ super
278
+ @_already_called = nil
279
+ end
280
+
291
281
  # Returns the record for an association collection that should be validated
292
282
  # or saved. If +autosave+ is +false+ only new records will be returned,
293
283
  # unless the parent is/was a new record itself.
@@ -349,7 +339,7 @@ module ActiveRecord
349
339
 
350
340
  unless valid = record.valid?(context)
351
341
  if reflection.options[:autosave]
352
- indexed_attribute = !index.nil? && (reflection.options[:index_errors] || ActiveRecord::Base.index_nested_attribute_errors)
342
+ indexed_attribute = !index.nil? && (reflection.options[:index_errors] || ActiveRecord.index_nested_attribute_errors)
353
343
 
354
344
  record.errors.group_by_attribute.each { |attribute, errors|
355
345
  attribute = normalize_reflection_attribute(indexed_attribute, reflection, index, attribute)
@@ -419,6 +409,8 @@ module ActiveRecord
419
409
  saved = true
420
410
 
421
411
  if autosave != false && (new_record_before_save || record.new_record?)
412
+ association.set_inverse_instance(record)
413
+
422
414
  if autosave
423
415
  saved = association.insert_record(record, false)
424
416
  elsif !reflection.nested?
@@ -449,7 +441,9 @@ module ActiveRecord
449
441
  # ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
450
442
  def save_has_one_association(reflection)
451
443
  association = association_instance_get(reflection.name)
452
- record = association && association.load_target
444
+ return unless association && association.loaded?
445
+
446
+ record = association.load_target
453
447
 
454
448
  if record && !record.destroyed?
455
449
  autosave = reflection.options[:autosave]
@@ -457,14 +451,19 @@ module ActiveRecord
457
451
  if autosave && record.marked_for_destruction?
458
452
  record.destroy
459
453
  elsif autosave != false
460
- key = reflection.options[:primary_key] ? public_send(reflection.options[:primary_key]) : id
454
+ primary_key = Array(compute_primary_key(reflection, self)).map(&:to_s)
455
+ primary_key_value = primary_key.map { |key| _read_attribute(key) }
461
456
 
462
- if (autosave && record.changed_for_autosave?) || record_changed?(reflection, record, key)
457
+ if (autosave && record.changed_for_autosave?) || _record_changed?(reflection, record, primary_key_value)
463
458
  unless reflection.through_reflection
464
- record[reflection.foreign_key] = key
465
- if inverse_reflection = reflection.inverse_of
466
- record.association(inverse_reflection.name).inversed_from(self)
459
+ foreign_key = Array(reflection.foreign_key)
460
+ primary_key_foreign_key_pairs = primary_key.zip(foreign_key)
461
+
462
+ primary_key_foreign_key_pairs.each do |primary_key, foreign_key|
463
+ association_id = _read_attribute(primary_key)
464
+ record[foreign_key] = association_id unless record[foreign_key] == association_id
467
465
  end
466
+ association.set_inverse_instance(record)
468
467
  end
469
468
 
470
469
  saved = record.save(validate: !autosave)
@@ -476,16 +475,28 @@ module ActiveRecord
476
475
  end
477
476
 
478
477
  # If the record is new or it has changed, returns true.
479
- def record_changed?(reflection, record, key)
478
+ def _record_changed?(reflection, record, key)
480
479
  record.new_record? ||
481
- association_foreign_key_changed?(reflection, record, key) ||
480
+ (association_foreign_key_changed?(reflection, record, key) ||
481
+ inverse_polymorphic_association_changed?(reflection, record)) ||
482
482
  record.will_save_change_to_attribute?(reflection.foreign_key)
483
483
  end
484
484
 
485
485
  def association_foreign_key_changed?(reflection, record, key)
486
486
  return false if reflection.through_reflection?
487
487
 
488
- record._has_attribute?(reflection.foreign_key) && record._read_attribute(reflection.foreign_key) != key
488
+ foreign_key = Array(reflection.foreign_key)
489
+ return false unless foreign_key.all? { |key| record._has_attribute?(key) }
490
+
491
+ foreign_key.map { |key| record._read_attribute(key) } != Array(key)
492
+ end
493
+
494
+ def inverse_polymorphic_association_changed?(reflection, record)
495
+ return false unless reflection.inverse_of&.polymorphic?
496
+
497
+ class_name = record._read_attribute(reflection.inverse_of.foreign_type)
498
+
499
+ reflection.active_record != record.class.polymorphic_class_for(class_name)
489
500
  end
490
501
 
491
502
  # Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
@@ -500,14 +511,21 @@ module ActiveRecord
500
511
  autosave = reflection.options[:autosave]
501
512
 
502
513
  if autosave && record.marked_for_destruction?
503
- self[reflection.foreign_key] = nil
514
+ foreign_key = Array(reflection.foreign_key)
515
+ foreign_key.each { |key| self[key] = nil }
504
516
  record.destroy
505
517
  elsif autosave != false
506
518
  saved = record.save(validate: !autosave) if record.new_record? || (autosave && record.changed_for_autosave?)
507
519
 
508
520
  if association.updated?
509
- association_id = record.public_send(reflection.options[:primary_key] || :id)
510
- self[reflection.foreign_key] = association_id
521
+ primary_key = Array(compute_primary_key(reflection, record)).map(&:to_s)
522
+ foreign_key = Array(reflection.foreign_key)
523
+
524
+ primary_key_foreign_key_pairs = primary_key.zip(foreign_key)
525
+ primary_key_foreign_key_pairs.each do |primary_key, foreign_key|
526
+ association_id = record._read_attribute(primary_key)
527
+ self[foreign_key] = association_id unless self[foreign_key] == association_id
528
+ end
511
529
  association.loaded!
512
530
  end
513
531
 
@@ -516,6 +534,22 @@ module ActiveRecord
516
534
  end
517
535
  end
518
536
 
537
+ def compute_primary_key(reflection, record)
538
+ if primary_key_options = reflection.options[:primary_key]
539
+ primary_key_options
540
+ elsif reflection.options[:query_constraints] && (query_constraints = record.class.query_constraints_list)
541
+ query_constraints
542
+ elsif record.class.has_query_constraints? && !reflection.options[:foreign_key]
543
+ record.class.query_constraints_list
544
+ elsif record.class.composite_primary_key?
545
+ # If record has composite primary key of shape [:<tenant_key>, :id], infer primary_key as :id
546
+ primary_key = record.class.primary_key
547
+ primary_key.include?("id") ? "id" : primary_key
548
+ else
549
+ record.class.primary_key
550
+ end
551
+ end
552
+
519
553
  def custom_validation_context?
520
554
  validation_context && [:create, :update].exclude?(validation_context)
521
555
  end