activerecord 7.0.6 → 7.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (229) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1356 -1425
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +15 -16
  5. data/lib/active_record/aggregations.rb +16 -13
  6. data/lib/active_record/association_relation.rb +1 -1
  7. data/lib/active_record/associations/association.rb +18 -3
  8. data/lib/active_record/associations/association_scope.rb +16 -9
  9. data/lib/active_record/associations/belongs_to_association.rb +14 -6
  10. data/lib/active_record/associations/builder/association.rb +3 -3
  11. data/lib/active_record/associations/builder/belongs_to.rb +21 -8
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
  13. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  14. data/lib/active_record/associations/collection_association.rb +18 -10
  15. data/lib/active_record/associations/collection_proxy.rb +21 -11
  16. data/lib/active_record/associations/foreign_association.rb +10 -3
  17. data/lib/active_record/associations/has_many_association.rb +20 -13
  18. data/lib/active_record/associations/has_many_through_association.rb +10 -6
  19. data/lib/active_record/associations/has_one_association.rb +10 -7
  20. data/lib/active_record/associations/join_dependency.rb +10 -8
  21. data/lib/active_record/associations/preloader/association.rb +27 -6
  22. data/lib/active_record/associations/preloader.rb +12 -9
  23. data/lib/active_record/associations/singular_association.rb +6 -8
  24. data/lib/active_record/associations/through_association.rb +22 -11
  25. data/lib/active_record/associations.rb +193 -97
  26. data/lib/active_record/attribute_assignment.rb +0 -2
  27. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  28. data/lib/active_record/attribute_methods/dirty.rb +40 -26
  29. data/lib/active_record/attribute_methods/primary_key.rb +76 -24
  30. data/lib/active_record/attribute_methods/query.rb +28 -16
  31. data/lib/active_record/attribute_methods/read.rb +18 -5
  32. data/lib/active_record/attribute_methods/serialization.rb +150 -31
  33. data/lib/active_record/attribute_methods/write.rb +3 -3
  34. data/lib/active_record/attribute_methods.rb +105 -21
  35. data/lib/active_record/attributes.rb +3 -3
  36. data/lib/active_record/autosave_association.rb +60 -18
  37. data/lib/active_record/base.rb +7 -2
  38. data/lib/active_record/callbacks.rb +10 -24
  39. data/lib/active_record/coders/column_serializer.rb +61 -0
  40. data/lib/active_record/coders/json.rb +1 -1
  41. data/lib/active_record/coders/yaml_column.rb +70 -42
  42. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +163 -88
  43. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  44. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
  45. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +63 -43
  46. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  47. data/lib/active_record/connection_adapters/abstract/database_statements.rb +109 -32
  48. data/lib/active_record/connection_adapters/abstract/query_cache.rb +60 -22
  49. data/lib/active_record/connection_adapters/abstract/quoting.rb +41 -6
  50. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  51. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  52. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
  53. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +289 -122
  54. data/lib/active_record/connection_adapters/abstract/transaction.rb +280 -58
  55. data/lib/active_record/connection_adapters/abstract_adapter.rb +496 -102
  56. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +207 -113
  57. data/lib/active_record/connection_adapters/column.rb +9 -0
  58. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  59. data/lib/active_record/connection_adapters/mysql/database_statements.rb +23 -144
  60. data/lib/active_record/connection_adapters/mysql/quoting.rb +21 -14
  61. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  62. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
  63. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  64. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +17 -12
  65. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
  66. data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
  67. data/lib/active_record/connection_adapters/pool_config.rb +14 -5
  68. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  69. data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
  70. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +76 -29
  71. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  72. data/lib/active_record/connection_adapters/postgresql/quoting.rb +14 -8
  73. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  74. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  75. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
  76. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +42 -0
  77. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +351 -54
  78. data/lib/active_record/connection_adapters/postgresql_adapter.rb +336 -168
  79. data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
  80. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  81. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +42 -36
  82. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +9 -5
  83. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
  84. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +28 -9
  85. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +163 -81
  86. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  87. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
  88. data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
  89. data/lib/active_record/connection_adapters.rb +3 -1
  90. data/lib/active_record/connection_handling.rb +71 -94
  91. data/lib/active_record/core.rb +128 -138
  92. data/lib/active_record/counter_cache.rb +46 -25
  93. data/lib/active_record/database_configurations/database_config.rb +9 -3
  94. data/lib/active_record/database_configurations/hash_config.rb +22 -12
  95. data/lib/active_record/database_configurations/url_config.rb +17 -11
  96. data/lib/active_record/database_configurations.rb +86 -33
  97. data/lib/active_record/delegated_type.rb +8 -3
  98. data/lib/active_record/deprecator.rb +7 -0
  99. data/lib/active_record/destroy_association_async_job.rb +2 -0
  100. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  101. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  102. data/lib/active_record/encryption/config.rb +25 -1
  103. data/lib/active_record/encryption/configurable.rb +12 -19
  104. data/lib/active_record/encryption/context.rb +10 -3
  105. data/lib/active_record/encryption/contexts.rb +5 -1
  106. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  107. data/lib/active_record/encryption/encryptable_record.rb +36 -18
  108. data/lib/active_record/encryption/encrypted_attribute_type.rb +17 -6
  109. data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -54
  110. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +2 -2
  111. data/lib/active_record/encryption/key_generator.rb +12 -1
  112. data/lib/active_record/encryption/message_serializer.rb +2 -0
  113. data/lib/active_record/encryption/properties.rb +3 -3
  114. data/lib/active_record/encryption/scheme.rb +19 -22
  115. data/lib/active_record/encryption.rb +1 -0
  116. data/lib/active_record/enum.rb +113 -26
  117. data/lib/active_record/errors.rb +89 -15
  118. data/lib/active_record/explain.rb +23 -3
  119. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  120. data/lib/active_record/fixture_set/render_context.rb +2 -0
  121. data/lib/active_record/fixture_set/table_row.rb +29 -8
  122. data/lib/active_record/fixtures.rb +119 -71
  123. data/lib/active_record/future_result.rb +30 -5
  124. data/lib/active_record/gem_version.rb +4 -4
  125. data/lib/active_record/inheritance.rb +30 -16
  126. data/lib/active_record/insert_all.rb +55 -8
  127. data/lib/active_record/integration.rb +8 -8
  128. data/lib/active_record/internal_metadata.rb +118 -30
  129. data/lib/active_record/locking/pessimistic.rb +5 -2
  130. data/lib/active_record/log_subscriber.rb +29 -12
  131. data/lib/active_record/marshalling.rb +56 -0
  132. data/lib/active_record/message_pack.rb +124 -0
  133. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  134. data/lib/active_record/middleware/database_selector.rb +5 -7
  135. data/lib/active_record/middleware/shard_selector.rb +3 -1
  136. data/lib/active_record/migration/command_recorder.rb +100 -4
  137. data/lib/active_record/migration/compatibility.rb +142 -58
  138. data/lib/active_record/migration/default_strategy.rb +23 -0
  139. data/lib/active_record/migration/execution_strategy.rb +19 -0
  140. data/lib/active_record/migration.rb +265 -112
  141. data/lib/active_record/model_schema.rb +47 -27
  142. data/lib/active_record/nested_attributes.rb +28 -3
  143. data/lib/active_record/normalization.rb +158 -0
  144. data/lib/active_record/persistence.rb +186 -34
  145. data/lib/active_record/promise.rb +84 -0
  146. data/lib/active_record/query_cache.rb +3 -21
  147. data/lib/active_record/query_logs.rb +77 -52
  148. data/lib/active_record/query_logs_formatter.rb +41 -0
  149. data/lib/active_record/querying.rb +15 -2
  150. data/lib/active_record/railtie.rb +107 -45
  151. data/lib/active_record/railties/controller_runtime.rb +12 -8
  152. data/lib/active_record/railties/databases.rake +139 -145
  153. data/lib/active_record/railties/job_runtime.rb +23 -0
  154. data/lib/active_record/readonly_attributes.rb +32 -5
  155. data/lib/active_record/reflection.rb +169 -45
  156. data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
  157. data/lib/active_record/relation/batches.rb +190 -61
  158. data/lib/active_record/relation/calculations.rb +152 -63
  159. data/lib/active_record/relation/delegation.rb +22 -8
  160. data/lib/active_record/relation/finder_methods.rb +85 -15
  161. data/lib/active_record/relation/merger.rb +2 -0
  162. data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -2
  163. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  164. data/lib/active_record/relation/predicate_builder.rb +27 -16
  165. data/lib/active_record/relation/query_attribute.rb +25 -1
  166. data/lib/active_record/relation/query_methods.rb +377 -69
  167. data/lib/active_record/relation/spawn_methods.rb +18 -1
  168. data/lib/active_record/relation.rb +76 -35
  169. data/lib/active_record/result.rb +19 -5
  170. data/lib/active_record/runtime_registry.rb +10 -1
  171. data/lib/active_record/sanitization.rb +51 -11
  172. data/lib/active_record/schema.rb +2 -3
  173. data/lib/active_record/schema_dumper.rb +41 -7
  174. data/lib/active_record/schema_migration.rb +68 -33
  175. data/lib/active_record/scoping/default.rb +15 -5
  176. data/lib/active_record/scoping/named.rb +2 -2
  177. data/lib/active_record/scoping.rb +2 -1
  178. data/lib/active_record/secure_password.rb +60 -0
  179. data/lib/active_record/secure_token.rb +21 -3
  180. data/lib/active_record/signed_id.rb +7 -5
  181. data/lib/active_record/store.rb +8 -8
  182. data/lib/active_record/suppressor.rb +3 -1
  183. data/lib/active_record/table_metadata.rb +11 -2
  184. data/lib/active_record/tasks/database_tasks.rb +127 -105
  185. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  186. data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
  187. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -7
  188. data/lib/active_record/test_fixtures.rb +113 -96
  189. data/lib/active_record/timestamp.rb +26 -14
  190. data/lib/active_record/token_for.rb +113 -0
  191. data/lib/active_record/touch_later.rb +11 -6
  192. data/lib/active_record/transactions.rb +39 -13
  193. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  194. data/lib/active_record/type/internal/timezone.rb +7 -2
  195. data/lib/active_record/type/serialized.rb +4 -0
  196. data/lib/active_record/type/time.rb +4 -0
  197. data/lib/active_record/validations/absence.rb +1 -1
  198. data/lib/active_record/validations/numericality.rb +5 -4
  199. data/lib/active_record/validations/presence.rb +5 -28
  200. data/lib/active_record/validations/uniqueness.rb +47 -2
  201. data/lib/active_record/validations.rb +8 -4
  202. data/lib/active_record/version.rb +1 -1
  203. data/lib/active_record.rb +121 -16
  204. data/lib/arel/errors.rb +10 -0
  205. data/lib/arel/factory_methods.rb +4 -0
  206. data/lib/arel/nodes/and.rb +4 -0
  207. data/lib/arel/nodes/binary.rb +6 -1
  208. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  209. data/lib/arel/nodes/cte.rb +36 -0
  210. data/lib/arel/nodes/fragments.rb +35 -0
  211. data/lib/arel/nodes/homogeneous_in.rb +0 -8
  212. data/lib/arel/nodes/leading_join.rb +8 -0
  213. data/lib/arel/nodes/node.rb +111 -2
  214. data/lib/arel/nodes/sql_literal.rb +6 -0
  215. data/lib/arel/nodes/table_alias.rb +4 -0
  216. data/lib/arel/nodes.rb +4 -0
  217. data/lib/arel/predications.rb +2 -0
  218. data/lib/arel/table.rb +9 -5
  219. data/lib/arel/visitors/mysql.rb +8 -1
  220. data/lib/arel/visitors/to_sql.rb +81 -17
  221. data/lib/arel/visitors/visitor.rb +2 -2
  222. data/lib/arel.rb +16 -2
  223. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  224. data/lib/rails/generators/active_record/migration.rb +3 -1
  225. data/lib/rails/generators/active_record/model/USAGE +113 -0
  226. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  227. metadata +52 -17
  228. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  229. data/lib/active_record/null_relation.rb +0 -63
@@ -184,6 +184,22 @@ module ActiveRecord
184
184
  end
185
185
  end
186
186
 
187
+ class CompositePrimaryKeyMismatchError < ActiveRecordError # :nodoc:
188
+ attr_reader :reflection
189
+
190
+ def initialize(reflection = nil)
191
+ if reflection
192
+ if reflection.has_one? || reflection.collection?
193
+ super("Association #{reflection.active_record}##{reflection.name} primary key #{reflection.active_record_primary_key} doesn't match with foreign key #{reflection.foreign_key}. Please specify query_constraints, or primary_key and foreign_key values.")
194
+ else
195
+ super("Association #{reflection.active_record}##{reflection.name} primary key #{reflection.association_primary_key} doesn't match with foreign key #{reflection.foreign_key}. Please specify query_constraints, or primary_key and foreign_key values.")
196
+ end
197
+ else
198
+ super("Association primary key doesn't match with foreign key.")
199
+ end
200
+ end
201
+ end
202
+
187
203
  class AmbiguousSourceReflectionForThroughAssociation < ActiveRecordError # :nodoc:
188
204
  def initialize(klass, macro, association_name, options, possible_sources)
189
205
  example_options = options.dup
@@ -319,8 +335,8 @@ module ActiveRecord
319
335
 
320
336
  private
321
337
  def init_internals
322
- @association_cache = {}
323
338
  super
339
+ @association_cache = {}
324
340
  end
325
341
 
326
342
  # Returns the specified association instance if it exists, +nil+ otherwise.
@@ -333,6 +349,8 @@ module ActiveRecord
333
349
  @association_cache[name] = association
334
350
  end
335
351
 
352
+ # = Active Record \Associations
353
+ #
336
354
  # \Associations are a set of macro-like class methods for tying objects together through
337
355
  # foreign keys. They express relationships like "Project has one Project Manager"
338
356
  # or "Project belongs to a Portfolio". Each macro adds a number of methods to the
@@ -349,23 +367,42 @@ module ActiveRecord
349
367
  #
350
368
  # The project class now has the following methods (and more) to ease the traversal and
351
369
  # manipulation of its relationships:
352
- # * <tt>Project#portfolio</tt>, <tt>Project#portfolio=(portfolio)</tt>, <tt>Project#reload_portfolio</tt>
353
- # * <tt>Project#project_manager</tt>, <tt>Project#project_manager=(project_manager)</tt>, <tt>Project#reload_project_manager</tt>
354
- # * <tt>Project#milestones.empty?</tt>, <tt>Project#milestones.size</tt>, <tt>Project#milestones</tt>, <tt>Project#milestones<<(milestone)</tt>,
355
- # <tt>Project#milestones.delete(milestone)</tt>, <tt>Project#milestones.destroy(milestone)</tt>, <tt>Project#milestones.find(milestone_id)</tt>,
356
- # <tt>Project#milestones.build</tt>, <tt>Project#milestones.create</tt>
357
- # * <tt>Project#categories.empty?</tt>, <tt>Project#categories.size</tt>, <tt>Project#categories</tt>, <tt>Project#categories<<(category1)</tt>,
358
- # <tt>Project#categories.delete(category1)</tt>, <tt>Project#categories.destroy(category1)</tt>
370
+ #
371
+ # project = Project.first
372
+ # project.portfolio
373
+ # project.portfolio = Portfolio.first
374
+ # project.reload_portfolio
375
+ #
376
+ # project.project_manager
377
+ # project.project_manager = ProjectManager.first
378
+ # project.reload_project_manager
379
+ #
380
+ # project.milestones.empty?
381
+ # project.milestones.size
382
+ # project.milestones
383
+ # project.milestones << Milestone.first
384
+ # project.milestones.delete(Milestone.first)
385
+ # project.milestones.destroy(Milestone.first)
386
+ # project.milestones.find(Milestone.first.id)
387
+ # project.milestones.build
388
+ # project.milestones.create
389
+ #
390
+ # project.categories.empty?
391
+ # project.categories.size
392
+ # project.categories
393
+ # project.categories << Category.first
394
+ # project.categories.delete(category1)
395
+ # project.categories.destroy(category1)
359
396
  #
360
397
  # === A word of warning
361
398
  #
362
399
  # Don't create associations that have the same name as {instance methods}[rdoc-ref:ActiveRecord::Core] of
363
- # <tt>ActiveRecord::Base</tt>. Since the association adds a method with that name to
364
- # its model, using an association with the same name as one provided by <tt>ActiveRecord::Base</tt> will override the method inherited through <tt>ActiveRecord::Base</tt> and will break things.
365
- # For instance, +attributes+ and +connection+ would be bad choices for association names, because those names already exist in the list of <tt>ActiveRecord::Base</tt> instance methods.
400
+ # +ActiveRecord::Base+. Since the association adds a method with that name to
401
+ # its model, using an association with the same name as one provided by +ActiveRecord::Base+ will override the method inherited through +ActiveRecord::Base+ and will break things.
402
+ # For instance, +attributes+ and +connection+ would be bad choices for association names, because those names already exist in the list of +ActiveRecord::Base+ instance methods.
366
403
  #
367
404
  # == Auto-generated methods
368
- # See also Instance Public methods below for more details.
405
+ # See also "Instance Public methods" below ( from #belongs_to ) for more details.
369
406
  #
370
407
  # === Singular associations (one-to-one)
371
408
  # | | belongs_to |
@@ -611,6 +648,7 @@ module ActiveRecord
611
648
  # def log_after_remove(record)
612
649
  # # ...
613
650
  # end
651
+ # end
614
652
  #
615
653
  # It's possible to stack callbacks by passing them as an array. Example:
616
654
  #
@@ -1025,45 +1063,45 @@ module ActiveRecord
1025
1063
  # Indexes are appended for any more successive uses of the table name.
1026
1064
  #
1027
1065
  # Post.joins(:comments)
1028
- # # => SELECT ... FROM posts INNER JOIN comments ON ...
1066
+ # # SELECT ... FROM posts INNER JOIN comments ON ...
1029
1067
  # Post.joins(:special_comments) # STI
1030
- # # => SELECT ... FROM posts INNER JOIN comments ON ... AND comments.type = 'SpecialComment'
1068
+ # # SELECT ... FROM posts INNER JOIN comments ON ... AND comments.type = 'SpecialComment'
1031
1069
  # Post.joins(:comments, :special_comments) # special_comments is the reflection name, posts is the parent table name
1032
- # # => SELECT ... FROM posts INNER JOIN comments ON ... INNER JOIN comments special_comments_posts
1070
+ # # SELECT ... FROM posts INNER JOIN comments ON ... INNER JOIN comments special_comments_posts
1033
1071
  #
1034
1072
  # Acts as tree example:
1035
1073
  #
1036
1074
  # TreeMixin.joins(:children)
1037
- # # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
1075
+ # # SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
1038
1076
  # TreeMixin.joins(children: :parent)
1039
- # # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
1040
- # INNER JOIN parents_mixins ...
1077
+ # # SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
1078
+ # # INNER JOIN parents_mixins ...
1041
1079
  # TreeMixin.joins(children: {parent: :children})
1042
- # # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
1043
- # INNER JOIN parents_mixins ...
1044
- # INNER JOIN mixins childrens_mixins_2
1080
+ # # SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
1081
+ # # INNER JOIN parents_mixins ...
1082
+ # # INNER JOIN mixins childrens_mixins_2
1045
1083
  #
1046
1084
  # Has and Belongs to Many join tables use the same idea, but add a <tt>_join</tt> suffix:
1047
1085
  #
1048
1086
  # Post.joins(:categories)
1049
- # # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
1087
+ # # SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
1050
1088
  # Post.joins(categories: :posts)
1051
- # # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
1052
- # INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories
1089
+ # # SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
1090
+ # # INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories
1053
1091
  # Post.joins(categories: {posts: :categories})
1054
- # # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
1055
- # INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories
1056
- # INNER JOIN categories_posts categories_posts_join INNER JOIN categories categories_posts_2
1092
+ # # SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
1093
+ # # INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories
1094
+ # # INNER JOIN categories_posts categories_posts_join INNER JOIN categories categories_posts_2
1057
1095
  #
1058
1096
  # If you wish to specify your own custom joins using ActiveRecord::QueryMethods#joins method, those table
1059
1097
  # names will take precedence over the eager associations:
1060
1098
  #
1061
1099
  # Post.joins(:comments).joins("inner join comments ...")
1062
- # # => SELECT ... FROM posts INNER JOIN comments_posts ON ... INNER JOIN comments ...
1100
+ # # SELECT ... FROM posts INNER JOIN comments_posts ON ... INNER JOIN comments ...
1063
1101
  # Post.joins(:comments, :special_comments).joins("inner join comments ...")
1064
- # # => SELECT ... FROM posts INNER JOIN comments comments_posts ON ...
1065
- # INNER JOIN comments special_comments_posts ...
1066
- # INNER JOIN comments ...
1102
+ # # SELECT ... FROM posts INNER JOIN comments comments_posts ON ...
1103
+ # # INNER JOIN comments special_comments_posts ...
1104
+ # # INNER JOIN comments ...
1067
1105
  #
1068
1106
  # Table aliases are automatically truncated according to the maximum length of table identifiers
1069
1107
  # according to the specific database.
@@ -1144,7 +1182,8 @@ module ActiveRecord
1144
1182
  # belongs_to :dungeon, inverse_of: :evil_wizard
1145
1183
  # end
1146
1184
  #
1147
- # For more information, see the documentation for the +:inverse_of+ option.
1185
+ # For more information, see the documentation for the +:inverse_of+ option and the
1186
+ # {Active Record Associations guide}[https://guides.rubyonrails.org/association_basics.html#bi-directional-associations].
1148
1187
  #
1149
1188
  # == Deleting from associations
1150
1189
  #
@@ -1166,7 +1205,7 @@ module ActiveRecord
1166
1205
  # specific association types. When no option is given, the behavior is to do nothing
1167
1206
  # with the associated records when destroying a record.
1168
1207
  #
1169
- # Note that <tt>:dependent</tt> is implemented using Rails' callback
1208
+ # Note that <tt>:dependent</tt> is implemented using \Rails' callback
1170
1209
  # system, which works by processing callbacks in order. Therefore, other
1171
1210
  # callbacks declared either before or after the <tt>:dependent</tt> option
1172
1211
  # can affect what it does.
@@ -1302,23 +1341,32 @@ module ActiveRecord
1302
1341
  #
1303
1342
  # === Example
1304
1343
  #
1305
- # A <tt>Firm</tt> class declares <tt>has_many :clients</tt>, which will add:
1306
- # * <tt>Firm#clients</tt> (similar to <tt>Client.where(firm_id: id)</tt>)
1307
- # * <tt>Firm#clients<<</tt>
1308
- # * <tt>Firm#clients.delete</tt>
1309
- # * <tt>Firm#clients.destroy</tt>
1310
- # * <tt>Firm#clients=</tt>
1311
- # * <tt>Firm#client_ids</tt>
1312
- # * <tt>Firm#client_ids=</tt>
1313
- # * <tt>Firm#clients.clear</tt>
1314
- # * <tt>Firm#clients.empty?</tt> (similar to <tt>firm.clients.size == 0</tt>)
1315
- # * <tt>Firm#clients.size</tt> (similar to <tt>Client.count "firm_id = #{id}"</tt>)
1316
- # * <tt>Firm#clients.find</tt> (similar to <tt>Client.where(firm_id: id).find(id)</tt>)
1317
- # * <tt>Firm#clients.exists?(name: 'ACME')</tt> (similar to <tt>Client.exists?(name: 'ACME', firm_id: firm.id)</tt>)
1318
- # * <tt>Firm#clients.build</tt> (similar to <tt>Client.new(firm_id: id)</tt>)
1319
- # * <tt>Firm#clients.create</tt> (similar to <tt>c = Client.new(firm_id: id); c.save; c</tt>)
1320
- # * <tt>Firm#clients.create!</tt> (similar to <tt>c = Client.new(firm_id: id); c.save!</tt>)
1321
- # * <tt>Firm#clients.reload</tt>
1344
+ # class Firm < ActiveRecord::Base
1345
+ # has_many :clients
1346
+ # end
1347
+ #
1348
+ # Declaring <tt>has_many :clients</tt> adds the following methods (and more):
1349
+ #
1350
+ # firm = Firm.find(2)
1351
+ # client = Client.find(6)
1352
+ #
1353
+ # firm.clients # similar to Client.where(firm_id: 2)
1354
+ # firm.clients << client
1355
+ # firm.clients.delete(client)
1356
+ # firm.clients.destroy(client)
1357
+ # firm.clients = [client]
1358
+ # firm.client_ids
1359
+ # firm.client_ids = [6]
1360
+ # firm.clients.clear
1361
+ # firm.clients.empty? # similar to firm.clients.size == 0
1362
+ # firm.clients.size # similar to Client.count "firm_id = 2"
1363
+ # firm.clients.find # similar to Client.where(firm_id: 2).find(6)
1364
+ # firm.clients.exists?(name: 'ACME') # similar to Client.exists?(name: 'ACME', firm_id: 2)
1365
+ # firm.clients.build # similar to Client.new(firm_id: 2)
1366
+ # firm.clients.create # similar to Client.create(firm_id: 2)
1367
+ # firm.clients.create! # similar to Client.create!(firm_id: 2)
1368
+ # firm.clients.reload
1369
+ #
1322
1370
  # The declaration can also include an +options+ hash to specialize the behavior of the association.
1323
1371
  #
1324
1372
  # === Scopes
@@ -1357,8 +1405,8 @@ module ActiveRecord
1357
1405
  # of this class in lower-case and "_id" suffixed. So a Person class that makes a #has_many
1358
1406
  # association will use "person_id" as the default <tt>:foreign_key</tt>.
1359
1407
  #
1360
- # If you are going to modify the association (rather than just read from it), then it is
1361
- # a good idea to set the <tt>:inverse_of</tt> option.
1408
+ # Setting the <tt>:foreign_key</tt> option prevents automatic detection of the association's
1409
+ # inverse, so it is generally a good idea to set the <tt>:inverse_of</tt> option as well.
1362
1410
  # [:foreign_type]
1363
1411
  # Specify the column used to store the associated object's type, if this is a polymorphic
1364
1412
  # association. By default this is guessed to be the name of the polymorphic association
@@ -1370,7 +1418,7 @@ module ActiveRecord
1370
1418
  # [:dependent]
1371
1419
  # Controls what happens to the associated objects when
1372
1420
  # their owner is destroyed. Note that these are implemented as
1373
- # callbacks, and Rails executes callbacks in order. Therefore, other
1421
+ # callbacks, and \Rails executes callbacks in order. Therefore, other
1374
1422
  # similar callbacks may affect the <tt>:dependent</tt> behavior, and the
1375
1423
  # <tt>:dependent</tt> behavior may affect other callbacks.
1376
1424
  #
@@ -1382,7 +1430,7 @@ module ActiveRecord
1382
1430
  # * <tt>:delete_all</tt> causes all the associated objects to be deleted directly from the database (so callbacks will not be executed).
1383
1431
  # * <tt>:nullify</tt> causes the foreign keys to be set to +NULL+. Polymorphic type will also be nullified
1384
1432
  # on polymorphic associations. Callbacks are not executed.
1385
- # * <tt>:restrict_with_exception</tt> causes an <tt>ActiveRecord::DeleteRestrictionError</tt> exception to be raised if there are any associated records.
1433
+ # * <tt>:restrict_with_exception</tt> causes an ActiveRecord::DeleteRestrictionError exception to be raised if there are any associated records.
1386
1434
  # * <tt>:restrict_with_error</tt> causes an error to be added to the owner if there are any associated objects.
1387
1435
  #
1388
1436
  # If using with the <tt>:through</tt> option, the association on the join model must be
@@ -1414,7 +1462,7 @@ module ActiveRecord
1414
1462
  # a good idea to set the <tt>:inverse_of</tt> option on the source association on the
1415
1463
  # join model. This allows associated records to be built which will automatically create
1416
1464
  # the appropriate join model records when they are saved. (See the 'Association Join Models'
1417
- # section above.)
1465
+ # and 'Setting Inverses' sections above.)
1418
1466
  # [:disable_joins]
1419
1467
  # Specifies whether joins should be skipped for an association. If set to true, two or more queries
1420
1468
  # will be generated. Note that in some cases, if order or limit is applied, it will be done in-memory
@@ -1454,6 +1502,10 @@ module ActiveRecord
1454
1502
  # [:ensuring_owner_was]
1455
1503
  # Specifies an instance method to be called on the owner. The method must return true in order for the
1456
1504
  # associated records to be deleted in a background job.
1505
+ # [:query_constraints]
1506
+ # Serves as a composite foreign key. Defines the list of columns to be used to query the associated object.
1507
+ # This is an optional option. By default Rails will attempt to derive the value automatically.
1508
+ # When the value is set the Array size must match associated model's primary key or `query_constraints` size.
1457
1509
  #
1458
1510
  # Option examples:
1459
1511
  # has_many :comments, -> { order("posted_on") }
@@ -1466,6 +1518,7 @@ module ActiveRecord
1466
1518
  # has_many :subscribers, through: :subscriptions, source: :user
1467
1519
  # has_many :subscribers, through: :subscriptions, disable_joins: true
1468
1520
  # has_many :comments, strict_loading: true
1521
+ # has_many :comments, query_constraints: [:blog_id, :post_id]
1469
1522
  def has_many(name, scope = nil, **options, &extension)
1470
1523
  reflection = Builder::HasMany.build(self, name, scope, options, &extension)
1471
1524
  Reflection.add_reflection self, name, reflection
@@ -1500,16 +1553,27 @@ module ActiveRecord
1500
1553
  # if the record is invalid.
1501
1554
  # [reload_association]
1502
1555
  # Returns the associated object, forcing a database read.
1556
+ # [reset_association]
1557
+ # Unloads the associated object. The next access will query it from the database.
1503
1558
  #
1504
1559
  # === Example
1505
1560
  #
1506
- # An Account class declares <tt>has_one :beneficiary</tt>, which will add:
1507
- # * <tt>Account#beneficiary</tt> (similar to <tt>Beneficiary.where(account_id: id).first</tt>)
1508
- # * <tt>Account#beneficiary=(beneficiary)</tt> (similar to <tt>beneficiary.account_id = account.id; beneficiary.save</tt>)
1509
- # * <tt>Account#build_beneficiary</tt> (similar to <tt>Beneficiary.new(account_id: id)</tt>)
1510
- # * <tt>Account#create_beneficiary</tt> (similar to <tt>b = Beneficiary.new(account_id: id); b.save; b</tt>)
1511
- # * <tt>Account#create_beneficiary!</tt> (similar to <tt>b = Beneficiary.new(account_id: id); b.save!; b</tt>)
1512
- # * <tt>Account#reload_beneficiary</tt>
1561
+ # class Account < ActiveRecord::Base
1562
+ # has_one :beneficiary
1563
+ # end
1564
+ #
1565
+ # Declaring <tt>has_one :beneficiary</tt> adds the following methods (and more):
1566
+ #
1567
+ # account = Account.find(5)
1568
+ # beneficiary = Beneficiary.find(8)
1569
+ #
1570
+ # account.beneficiary # similar to Beneficiary.find_by(account_id: 5)
1571
+ # account.beneficiary = beneficiary # similar to beneficiary.update(account_id: 5)
1572
+ # account.build_beneficiary # similar to Beneficiary.new(account_id: 5)
1573
+ # account.create_beneficiary # similar to Beneficiary.create(account_id: 5)
1574
+ # account.create_beneficiary! # similar to Beneficiary.create!(account_id: 5)
1575
+ # account.reload_beneficiary
1576
+ # account.reset_beneficiary
1513
1577
  #
1514
1578
  # === Scopes
1515
1579
  #
@@ -1543,7 +1607,7 @@ module ActiveRecord
1543
1607
  # * <tt>:delete</tt> causes the associated object to be deleted directly from the database (so callbacks will not execute)
1544
1608
  # * <tt>:nullify</tt> causes the foreign key to be set to +NULL+. Polymorphic type column is also nullified
1545
1609
  # on polymorphic associations. Callbacks are not executed.
1546
- # * <tt>:restrict_with_exception</tt> causes an <tt>ActiveRecord::DeleteRestrictionError</tt> exception to be raised if there is an associated record
1610
+ # * <tt>:restrict_with_exception</tt> causes an ActiveRecord::DeleteRestrictionError exception to be raised if there is an associated record
1547
1611
  # * <tt>:restrict_with_error</tt> causes an error to be added to the owner if there is an associated object
1548
1612
  #
1549
1613
  # Note that <tt>:dependent</tt> option is ignored when using <tt>:through</tt> option.
@@ -1552,8 +1616,8 @@ module ActiveRecord
1552
1616
  # of this class in lower-case and "_id" suffixed. So a Person class that makes a #has_one association
1553
1617
  # will use "person_id" as the default <tt>:foreign_key</tt>.
1554
1618
  #
1555
- # If you are going to modify the association (rather than just read from it), then it is
1556
- # a good idea to set the <tt>:inverse_of</tt> option.
1619
+ # Setting the <tt>:foreign_key</tt> option prevents automatic detection of the association's
1620
+ # inverse, so it is generally a good idea to set the <tt>:inverse_of</tt> option as well.
1557
1621
  # [:foreign_type]
1558
1622
  # Specify the column used to store the associated object's type, if this is a polymorphic
1559
1623
  # association. By default this is guessed to be the name of the polymorphic association
@@ -1579,7 +1643,7 @@ module ActiveRecord
1579
1643
  # a good idea to set the <tt>:inverse_of</tt> option on the source association on the
1580
1644
  # join model. This allows associated records to be built which will automatically create
1581
1645
  # the appropriate join model records when they are saved. (See the 'Association Join Models'
1582
- # section above.)
1646
+ # and 'Setting Inverses' sections above.)
1583
1647
  # [:disable_joins]
1584
1648
  # Specifies whether joins should be skipped for an association. If set to true, two or more queries
1585
1649
  # will be generated. Note that in some cases, if order or limit is applied, it will be done in-memory
@@ -1622,6 +1686,10 @@ module ActiveRecord
1622
1686
  # [:ensuring_owner_was]
1623
1687
  # Specifies an instance method to be called on the owner. The method must return true in order for the
1624
1688
  # associated records to be deleted in a background job.
1689
+ # [:query_constraints]
1690
+ # Serves as a composite foreign key. Defines the list of columns to be used to query the associated object.
1691
+ # This is an optional option. By default Rails will attempt to derive the value automatically.
1692
+ # When the value is set the Array size must match associated model's primary key or `query_constraints` size.
1625
1693
  #
1626
1694
  # Option examples:
1627
1695
  # has_one :credit_card, dependent: :destroy # destroys the associated credit card
@@ -1636,6 +1704,7 @@ module ActiveRecord
1636
1704
  # has_one :primary_address, -> { where(primary: true) }, through: :addressables, source: :addressable
1637
1705
  # has_one :credit_card, required: true
1638
1706
  # has_one :credit_card, strict_loading: true
1707
+ # has_one :employment_record_book, query_constraints: [:organization_id, :employee_id]
1639
1708
  def has_one(name, scope = nil, **options)
1640
1709
  reflection = Builder::HasOne.build(self, name, scope, options)
1641
1710
  Reflection.add_reflection self, name, reflection
@@ -1669,6 +1738,8 @@ module ActiveRecord
1669
1738
  # if the record is invalid.
1670
1739
  # [reload_association]
1671
1740
  # Returns the associated object, forcing a database read.
1741
+ # [reset_association]
1742
+ # Unloads the associated object. The next access will query it from the database.
1672
1743
  # [association_changed?]
1673
1744
  # Returns true if a new associate object has been assigned and the next save will update the foreign key.
1674
1745
  # [association_previously_changed?]
@@ -1676,16 +1747,24 @@ module ActiveRecord
1676
1747
  #
1677
1748
  # === Example
1678
1749
  #
1679
- # A Post class declares <tt>belongs_to :author</tt>, which will add:
1680
- # * <tt>Post#author</tt> (similar to <tt>Author.find(author_id)</tt>)
1681
- # * <tt>Post#author=(author)</tt> (similar to <tt>post.author_id = author.id</tt>)
1682
- # * <tt>Post#build_author</tt> (similar to <tt>post.author = Author.new</tt>)
1683
- # * <tt>Post#create_author</tt> (similar to <tt>post.author = Author.new; post.author.save; post.author</tt>)
1684
- # * <tt>Post#create_author!</tt> (similar to <tt>post.author = Author.new; post.author.save!; post.author</tt>)
1685
- # * <tt>Post#reload_author</tt>
1686
- # * <tt>Post#author_changed?</tt>
1687
- # * <tt>Post#author_previously_changed?</tt>
1688
- # The declaration can also include an +options+ hash to specialize the behavior of the association.
1750
+ # class Post < ActiveRecord::Base
1751
+ # belongs_to :author
1752
+ # end
1753
+ #
1754
+ # Declaring <tt>belongs_to :author</tt> adds the following methods (and more):
1755
+ #
1756
+ # post = Post.find(7)
1757
+ # author = Author.find(19)
1758
+ #
1759
+ # post.author # similar to Author.find(post.author_id)
1760
+ # post.author = author # similar to post.author_id = author.id
1761
+ # post.build_author # similar to post.author = Author.new
1762
+ # post.create_author # similar to post.author = Author.new; post.author.save; post.author
1763
+ # post.create_author! # similar to post.author = Author.new; post.author.save!; post.author
1764
+ # post.reload_author
1765
+ # post.reset_author
1766
+ # post.author_changed?
1767
+ # post.author_previously_changed?
1689
1768
  #
1690
1769
  # === Scopes
1691
1770
  #
@@ -1700,6 +1779,8 @@ module ActiveRecord
1700
1779
  #
1701
1780
  # === Options
1702
1781
  #
1782
+ # The declaration can also include an +options+ hash to specialize the behavior of the association.
1783
+ #
1703
1784
  # [:class_name]
1704
1785
  # Specify the class name of the association. Use it only if that name can't be inferred
1705
1786
  # from the association name. So <tt>belongs_to :author</tt> will by default be linked to the Author class, but
@@ -1711,8 +1792,8 @@ module ActiveRecord
1711
1792
  # <tt>belongs_to :favorite_person, class_name: "Person"</tt> will use a foreign key
1712
1793
  # of "favorite_person_id".
1713
1794
  #
1714
- # If you are going to modify the association (rather than just read from it), then it is
1715
- # a good idea to set the <tt>:inverse_of</tt> option.
1795
+ # Setting the <tt>:foreign_key</tt> option prevents automatic detection of the association's
1796
+ # inverse, so it is generally a good idea to set the <tt>:inverse_of</tt> option as well.
1716
1797
  # [:foreign_type]
1717
1798
  # Specify the column used to store the associated object's type, if this is a polymorphic
1718
1799
  # association. By default this is guessed to be the name of the association with a "_type"
@@ -1776,11 +1857,16 @@ module ActiveRecord
1776
1857
  # [:default]
1777
1858
  # Provide a callable (i.e. proc or lambda) to specify that the association should
1778
1859
  # be initialized with a particular record before validation.
1860
+ # Please note that callable won't be executed if the record exists.
1779
1861
  # [:strict_loading]
1780
1862
  # Enforces strict loading every time the associated record is loaded through this association.
1781
1863
  # [:ensuring_owner_was]
1782
1864
  # Specifies an instance method to be called on the owner. The method must return true in order for the
1783
1865
  # associated records to be deleted in a background job.
1866
+ # [:query_constraints]
1867
+ # Serves as a composite foreign key. Defines the list of columns to be used to query the associated object.
1868
+ # This is an optional option. By default Rails will attempt to derive the value automatically.
1869
+ # When the value is set the Array size must match associated model's primary key or `query_constraints` size.
1784
1870
  #
1785
1871
  # Option examples:
1786
1872
  # belongs_to :firm, foreign_key: "client_of"
@@ -1796,6 +1882,7 @@ module ActiveRecord
1796
1882
  # belongs_to :user, optional: true
1797
1883
  # belongs_to :account, default: -> { company.account }
1798
1884
  # belongs_to :account, strict_loading: true
1885
+ # belong_to :note, query_constraints: [:organization_id, :note_id]
1799
1886
  def belongs_to(name, scope = nil, **options)
1800
1887
  reflection = Builder::BelongsTo.build(self, name, scope, options)
1801
1888
  Reflection.add_reflection self, name, reflection
@@ -1818,7 +1905,7 @@ module ActiveRecord
1818
1905
  # The join table should not have a primary key or a model associated with it. You must manually generate the
1819
1906
  # join table with a migration such as this:
1820
1907
  #
1821
- # class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration[7.0]
1908
+ # class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration[7.1]
1822
1909
  # def change
1823
1910
  # create_join_table :developers, :projects
1824
1911
  # end
@@ -1879,22 +1966,31 @@ module ActiveRecord
1879
1966
  #
1880
1967
  # === Example
1881
1968
  #
1882
- # A Developer class declares <tt>has_and_belongs_to_many :projects</tt>, which will add:
1883
- # * <tt>Developer#projects</tt>
1884
- # * <tt>Developer#projects<<</tt>
1885
- # * <tt>Developer#projects.delete</tt>
1886
- # * <tt>Developer#projects.destroy</tt>
1887
- # * <tt>Developer#projects=</tt>
1888
- # * <tt>Developer#project_ids</tt>
1889
- # * <tt>Developer#project_ids=</tt>
1890
- # * <tt>Developer#projects.clear</tt>
1891
- # * <tt>Developer#projects.empty?</tt>
1892
- # * <tt>Developer#projects.size</tt>
1893
- # * <tt>Developer#projects.find(id)</tt>
1894
- # * <tt>Developer#projects.exists?(...)</tt>
1895
- # * <tt>Developer#projects.build</tt> (similar to <tt>Project.new(developer_id: id)</tt>)
1896
- # * <tt>Developer#projects.create</tt> (similar to <tt>c = Project.new(developer_id: id); c.save; c</tt>)
1897
- # * <tt>Developer#projects.reload</tt>
1969
+ # class Developer < ActiveRecord::Base
1970
+ # has_and_belongs_to_many :projects
1971
+ # end
1972
+ #
1973
+ # Declaring <tt>has_and_belongs_to_many :projects</tt> adds the following methods (and more):
1974
+ #
1975
+ # developer = Developer.find(11)
1976
+ # project = Project.find(9)
1977
+ #
1978
+ # developer.projects
1979
+ # developer.projects << project
1980
+ # developer.projects.delete(project)
1981
+ # developer.projects.destroy(project)
1982
+ # developer.projects = [project]
1983
+ # developer.project_ids
1984
+ # developer.project_ids = [9]
1985
+ # developer.projects.clear
1986
+ # developer.projects.empty?
1987
+ # developer.projects.size
1988
+ # developer.projects.find(9)
1989
+ # developer.projects.exists?(9)
1990
+ # developer.projects.build # similar to Project.new(developer_id: 11)
1991
+ # developer.projects.create # similar to Project.create(developer_id: 11)
1992
+ # developer.projects.reload
1993
+ #
1898
1994
  # The declaration may include an +options+ hash to specialize the behavior of the association.
1899
1995
  #
1900
1996
  # === Scopes
@@ -1940,8 +2036,8 @@ module ActiveRecord
1940
2036
  # a #has_and_belongs_to_many association to Project will use "person_id" as the
1941
2037
  # default <tt>:foreign_key</tt>.
1942
2038
  #
1943
- # If you are going to modify the association (rather than just read from it), then it is
1944
- # a good idea to set the <tt>:inverse_of</tt> option.
2039
+ # Setting the <tt>:foreign_key</tt> option prevents automatic detection of the association's
2040
+ # inverse, so it is generally a good idea to set the <tt>:inverse_of</tt> option as well.
1945
2041
  # [:association_foreign_key]
1946
2042
  # Specify the foreign key used for the association on the receiving side of the association.
1947
2043
  # By default this is guessed to be the name of the associated class in lower-case and "_id" suffixed.
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_model/forbidden_attributes_protection"
4
-
5
3
  module ActiveRecord
6
4
  module AttributeAssignment
7
5
  include ActiveModel::AttributeAssignment
@@ -52,6 +52,23 @@ module ActiveRecord
52
52
  attribute_before_type_cast(name)
53
53
  end
54
54
 
55
+ # Returns the value of the attribute identified by +attr_name+ after
56
+ # serialization.
57
+ #
58
+ # class Book < ActiveRecord::Base
59
+ # enum status: { draft: 1, published: 2 }
60
+ # end
61
+ #
62
+ # book = Book.new(status: "published")
63
+ # book.read_attribute(:status) # => "published"
64
+ # book.read_attribute_for_database(:status) # => 2
65
+ def read_attribute_for_database(attr_name)
66
+ name = attr_name.to_s
67
+ name = self.class.attribute_aliases[name] || name
68
+
69
+ attribute_for_database(name)
70
+ end
71
+
55
72
  # Returns a hash of attributes before typecasting and deserialization.
56
73
  #
57
74
  # class Task < ActiveRecord::Base