activerecord 7.0.8.6 → 7.2.2.1

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 (279) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +631 -1939
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +29 -29
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +16 -13
  7. data/lib/active_record/association_relation.rb +2 -2
  8. data/lib/active_record/associations/alias_tracker.rb +25 -19
  9. data/lib/active_record/associations/association.rb +35 -12
  10. data/lib/active_record/associations/association_scope.rb +16 -9
  11. data/lib/active_record/associations/belongs_to_association.rb +23 -8
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  13. data/lib/active_record/associations/builder/association.rb +3 -3
  14. data/lib/active_record/associations/builder/belongs_to.rb +22 -8
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
  16. data/lib/active_record/associations/builder/has_many.rb +3 -4
  17. data/lib/active_record/associations/builder/has_one.rb +3 -4
  18. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  19. data/lib/active_record/associations/collection_association.rb +26 -14
  20. data/lib/active_record/associations/collection_proxy.rb +29 -11
  21. data/lib/active_record/associations/errors.rb +265 -0
  22. data/lib/active_record/associations/foreign_association.rb +10 -3
  23. data/lib/active_record/associations/has_many_association.rb +21 -14
  24. data/lib/active_record/associations/has_many_through_association.rb +17 -7
  25. data/lib/active_record/associations/has_one_association.rb +10 -3
  26. data/lib/active_record/associations/join_dependency/join_association.rb +30 -27
  27. data/lib/active_record/associations/join_dependency.rb +10 -10
  28. data/lib/active_record/associations/nested_error.rb +47 -0
  29. data/lib/active_record/associations/preloader/association.rb +33 -8
  30. data/lib/active_record/associations/preloader/branch.rb +7 -1
  31. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  32. data/lib/active_record/associations/preloader.rb +13 -10
  33. data/lib/active_record/associations/singular_association.rb +7 -1
  34. data/lib/active_record/associations/through_association.rb +22 -11
  35. data/lib/active_record/associations.rb +354 -485
  36. data/lib/active_record/attribute_assignment.rb +0 -4
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  38. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  39. data/lib/active_record/attribute_methods/dirty.rb +53 -35
  40. data/lib/active_record/attribute_methods/primary_key.rb +45 -25
  41. data/lib/active_record/attribute_methods/query.rb +28 -16
  42. data/lib/active_record/attribute_methods/read.rb +8 -7
  43. data/lib/active_record/attribute_methods/serialization.rb +131 -32
  44. data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -6
  45. data/lib/active_record/attribute_methods/write.rb +6 -6
  46. data/lib/active_record/attribute_methods.rb +148 -33
  47. data/lib/active_record/attributes.rb +64 -50
  48. data/lib/active_record/autosave_association.rb +69 -37
  49. data/lib/active_record/base.rb +9 -5
  50. data/lib/active_record/callbacks.rb +11 -25
  51. data/lib/active_record/coders/column_serializer.rb +61 -0
  52. data/lib/active_record/coders/json.rb +1 -1
  53. data/lib/active_record/coders/yaml_column.rb +70 -42
  54. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +123 -131
  55. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +4 -1
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +323 -88
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +160 -45
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +217 -63
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -63
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +307 -129
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +510 -111
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +278 -125
  69. data/lib/active_record/connection_adapters/column.rb +9 -0
  70. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  71. data/lib/active_record/connection_adapters/mysql/database_statements.rb +26 -139
  72. data/lib/active_record/connection_adapters/mysql/quoting.rb +53 -54
  73. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  76. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +25 -13
  77. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  78. data/lib/active_record/connection_adapters/mysql2_adapter.rb +101 -68
  79. data/lib/active_record/connection_adapters/pool_config.rb +20 -10
  80. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +14 -3
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +100 -43
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  87. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
  88. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  89. data/lib/active_record/connection_adapters/postgresql/quoting.rb +65 -61
  90. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  91. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  92. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +151 -2
  93. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
  94. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +370 -63
  95. data/lib/active_record/connection_adapters/postgresql_adapter.rb +367 -201
  96. data/lib/active_record/connection_adapters/schema_cache.rb +302 -79
  97. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  98. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +60 -43
  99. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +45 -46
  100. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  101. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +14 -0
  102. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  103. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +50 -8
  104. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +290 -110
  105. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  106. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  107. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  108. data/lib/active_record/connection_adapters.rb +124 -1
  109. data/lib/active_record/connection_handling.rb +96 -104
  110. data/lib/active_record/core.rb +251 -176
  111. data/lib/active_record/counter_cache.rb +68 -34
  112. data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
  113. data/lib/active_record/database_configurations/database_config.rb +26 -5
  114. data/lib/active_record/database_configurations/hash_config.rb +52 -34
  115. data/lib/active_record/database_configurations/url_config.rb +37 -12
  116. data/lib/active_record/database_configurations.rb +87 -34
  117. data/lib/active_record/delegated_type.rb +39 -10
  118. data/lib/active_record/deprecator.rb +7 -0
  119. data/lib/active_record/destroy_association_async_job.rb +3 -1
  120. data/lib/active_record/dynamic_matchers.rb +2 -2
  121. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  122. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  123. data/lib/active_record/encryption/config.rb +25 -1
  124. data/lib/active_record/encryption/configurable.rb +12 -19
  125. data/lib/active_record/encryption/context.rb +10 -3
  126. data/lib/active_record/encryption/contexts.rb +5 -1
  127. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  128. data/lib/active_record/encryption/encryptable_record.rb +45 -21
  129. data/lib/active_record/encryption/encrypted_attribute_type.rb +47 -12
  130. data/lib/active_record/encryption/encryptor.rb +18 -3
  131. data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
  132. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  133. data/lib/active_record/encryption/key_generator.rb +12 -1
  134. data/lib/active_record/encryption/key_provider.rb +1 -1
  135. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  136. data/lib/active_record/encryption/message_serializer.rb +6 -0
  137. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  138. data/lib/active_record/encryption/properties.rb +3 -3
  139. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  140. data/lib/active_record/encryption/scheme.rb +22 -21
  141. data/lib/active_record/encryption.rb +3 -0
  142. data/lib/active_record/enum.rb +129 -28
  143. data/lib/active_record/errors.rb +151 -31
  144. data/lib/active_record/explain.rb +21 -12
  145. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  146. data/lib/active_record/fixture_set/render_context.rb +2 -0
  147. data/lib/active_record/fixture_set/table_row.rb +29 -8
  148. data/lib/active_record/fixtures.rb +167 -97
  149. data/lib/active_record/future_result.rb +47 -8
  150. data/lib/active_record/gem_version.rb +4 -4
  151. data/lib/active_record/inheritance.rb +34 -18
  152. data/lib/active_record/insert_all.rb +72 -22
  153. data/lib/active_record/integration.rb +11 -8
  154. data/lib/active_record/internal_metadata.rb +124 -20
  155. data/lib/active_record/locking/optimistic.rb +8 -7
  156. data/lib/active_record/locking/pessimistic.rb +5 -2
  157. data/lib/active_record/log_subscriber.rb +18 -22
  158. data/lib/active_record/marshalling.rb +59 -0
  159. data/lib/active_record/message_pack.rb +124 -0
  160. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  161. data/lib/active_record/middleware/database_selector.rb +6 -8
  162. data/lib/active_record/middleware/shard_selector.rb +3 -1
  163. data/lib/active_record/migration/command_recorder.rb +106 -8
  164. data/lib/active_record/migration/compatibility.rb +147 -5
  165. data/lib/active_record/migration/default_strategy.rb +22 -0
  166. data/lib/active_record/migration/execution_strategy.rb +19 -0
  167. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  168. data/lib/active_record/migration.rb +234 -117
  169. data/lib/active_record/model_schema.rb +90 -102
  170. data/lib/active_record/nested_attributes.rb +48 -11
  171. data/lib/active_record/normalization.rb +163 -0
  172. data/lib/active_record/persistence.rb +168 -339
  173. data/lib/active_record/promise.rb +84 -0
  174. data/lib/active_record/query_cache.rb +18 -25
  175. data/lib/active_record/query_logs.rb +92 -52
  176. data/lib/active_record/query_logs_formatter.rb +41 -0
  177. data/lib/active_record/querying.rb +33 -8
  178. data/lib/active_record/railtie.rb +129 -85
  179. data/lib/active_record/railties/controller_runtime.rb +22 -7
  180. data/lib/active_record/railties/databases.rake +145 -154
  181. data/lib/active_record/railties/job_runtime.rb +23 -0
  182. data/lib/active_record/readonly_attributes.rb +32 -5
  183. data/lib/active_record/reflection.rb +267 -69
  184. data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
  185. data/lib/active_record/relation/batches.rb +198 -63
  186. data/lib/active_record/relation/calculations.rb +250 -93
  187. data/lib/active_record/relation/delegation.rb +30 -19
  188. data/lib/active_record/relation/finder_methods.rb +93 -18
  189. data/lib/active_record/relation/merger.rb +6 -6
  190. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  191. data/lib/active_record/relation/predicate_builder/association_query_value.rb +18 -3
  192. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  193. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  194. data/lib/active_record/relation/predicate_builder.rb +28 -16
  195. data/lib/active_record/relation/query_attribute.rb +2 -1
  196. data/lib/active_record/relation/query_methods.rb +576 -107
  197. data/lib/active_record/relation/record_fetch_warning.rb +3 -0
  198. data/lib/active_record/relation/spawn_methods.rb +5 -4
  199. data/lib/active_record/relation/where_clause.rb +7 -19
  200. data/lib/active_record/relation.rb +580 -90
  201. data/lib/active_record/result.rb +49 -48
  202. data/lib/active_record/runtime_registry.rb +63 -1
  203. data/lib/active_record/sanitization.rb +70 -25
  204. data/lib/active_record/schema.rb +8 -7
  205. data/lib/active_record/schema_dumper.rb +63 -14
  206. data/lib/active_record/schema_migration.rb +75 -24
  207. data/lib/active_record/scoping/default.rb +15 -5
  208. data/lib/active_record/scoping/named.rb +3 -2
  209. data/lib/active_record/scoping.rb +2 -1
  210. data/lib/active_record/secure_password.rb +60 -0
  211. data/lib/active_record/secure_token.rb +21 -3
  212. data/lib/active_record/signed_id.rb +27 -6
  213. data/lib/active_record/statement_cache.rb +7 -7
  214. data/lib/active_record/store.rb +8 -8
  215. data/lib/active_record/suppressor.rb +3 -1
  216. data/lib/active_record/table_metadata.rb +1 -1
  217. data/lib/active_record/tasks/database_tasks.rb +190 -118
  218. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  219. data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
  220. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
  221. data/lib/active_record/test_fixtures.rb +170 -155
  222. data/lib/active_record/testing/query_assertions.rb +121 -0
  223. data/lib/active_record/timestamp.rb +31 -17
  224. data/lib/active_record/token_for.rb +123 -0
  225. data/lib/active_record/touch_later.rb +12 -7
  226. data/lib/active_record/transaction.rb +132 -0
  227. data/lib/active_record/transactions.rb +106 -24
  228. data/lib/active_record/translation.rb +0 -2
  229. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  230. data/lib/active_record/type/internal/timezone.rb +7 -2
  231. data/lib/active_record/type/serialized.rb +1 -3
  232. data/lib/active_record/type/time.rb +4 -0
  233. data/lib/active_record/type_caster/connection.rb +4 -4
  234. data/lib/active_record/validations/absence.rb +1 -1
  235. data/lib/active_record/validations/associated.rb +9 -3
  236. data/lib/active_record/validations/numericality.rb +5 -4
  237. data/lib/active_record/validations/presence.rb +5 -28
  238. data/lib/active_record/validations/uniqueness.rb +61 -11
  239. data/lib/active_record/validations.rb +12 -5
  240. data/lib/active_record/version.rb +1 -1
  241. data/lib/active_record.rb +247 -33
  242. data/lib/arel/alias_predication.rb +1 -1
  243. data/lib/arel/collectors/bind.rb +2 -0
  244. data/lib/arel/collectors/composite.rb +7 -0
  245. data/lib/arel/collectors/sql_string.rb +1 -1
  246. data/lib/arel/collectors/substitute_binds.rb +1 -1
  247. data/lib/arel/errors.rb +10 -0
  248. data/lib/arel/factory_methods.rb +4 -0
  249. data/lib/arel/nodes/binary.rb +6 -7
  250. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  251. data/lib/arel/nodes/cte.rb +36 -0
  252. data/lib/arel/nodes/fragments.rb +35 -0
  253. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  254. data/lib/arel/nodes/leading_join.rb +8 -0
  255. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  256. data/lib/arel/nodes/node.rb +115 -5
  257. data/lib/arel/nodes/sql_literal.rb +13 -0
  258. data/lib/arel/nodes/table_alias.rb +4 -0
  259. data/lib/arel/nodes.rb +6 -2
  260. data/lib/arel/predications.rb +3 -1
  261. data/lib/arel/select_manager.rb +1 -1
  262. data/lib/arel/table.rb +9 -5
  263. data/lib/arel/tree_manager.rb +8 -3
  264. data/lib/arel/update_manager.rb +2 -1
  265. data/lib/arel/visitors/dot.rb +1 -0
  266. data/lib/arel/visitors/mysql.rb +17 -5
  267. data/lib/arel/visitors/postgresql.rb +1 -12
  268. data/lib/arel/visitors/sqlite.rb +25 -0
  269. data/lib/arel/visitors/to_sql.rb +112 -34
  270. data/lib/arel/visitors/visitor.rb +2 -2
  271. data/lib/arel.rb +21 -3
  272. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  273. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  274. data/lib/rails/generators/active_record/migration.rb +3 -1
  275. data/lib/rails/generators/active_record/model/USAGE +113 -0
  276. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  277. metadata +56 -14
  278. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  279. data/lib/active_record/null_relation.rb +0 -63
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2004-2022 David Heinemeier Hansson
1
+ Copyright (c) David Heinemeier Hansson
2
2
 
3
3
  Arel originally copyright (c) 2007-2016 Nick Kallen, Bryan Helmkamp, Emilio Tagua, Aaron Patterson
4
4
 
data/README.rdoc CHANGED
@@ -1,4 +1,4 @@
1
- = Active Record -- Object-relational mapping in Rails
1
+ = Active Record -- Object-relational mapping in \Rails
2
2
 
3
3
  Active Record connects classes to relational database tables to establish an
4
4
  almost zero-configuration persistence layer for applications. The library
@@ -13,29 +13,28 @@ columns. Although these mappings can be defined explicitly, it's recommended
13
13
  to follow naming conventions, especially when getting started with the
14
14
  library.
15
15
 
16
- You can read more about Active Record in the {Active Record Basics}[https://edgeguides.rubyonrails.org/active_record_basics.html] guide.
16
+ You can read more about Active Record in the {Active Record Basics}[https://guides.rubyonrails.org/active_record_basics.html] guide.
17
17
 
18
18
  A short rundown of some of the major features:
19
19
 
20
20
  * Automated mapping between classes and tables, attributes and columns.
21
21
 
22
- class Product < ActiveRecord::Base
23
- end
24
-
25
- {Learn more}[link:classes/ActiveRecord/Base.html]
22
+ class Product < ActiveRecord::Base
23
+ end
26
24
 
27
- The Product class is automatically mapped to the table named "products",
28
- which might look like this:
25
+ The Product class is automatically mapped to the table named "products",
26
+ which might look like this:
29
27
 
30
- CREATE TABLE products (
31
- id bigint NOT NULL auto_increment,
32
- name varchar(255),
33
- PRIMARY KEY (id)
34
- );
28
+ CREATE TABLE products (
29
+ id bigint NOT NULL auto_increment,
30
+ name varchar(255),
31
+ PRIMARY KEY (id)
32
+ );
35
33
 
36
- This would also define the following accessors: <tt>Product#name</tt> and
37
- <tt>Product#name=(new_name)</tt>.
34
+ This would also define the following accessors: <tt>Product#name</tt> and
35
+ <tt>Product#name=(new_name)</tt>.
38
36
 
37
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Base.html]
39
38
 
40
39
  * Associations between objects defined by simple class methods.
41
40
 
@@ -45,7 +44,7 @@ This would also define the following accessors: <tt>Product#name</tt> and
45
44
  belongs_to :conglomerate
46
45
  end
47
46
 
48
- {Learn more}[link:classes/ActiveRecord/Associations/ClassMethods.html]
47
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html]
49
48
 
50
49
 
51
50
  * Aggregations of value objects.
@@ -57,7 +56,7 @@ This would also define the following accessors: <tt>Product#name</tt> and
57
56
  mapping: [%w(address_street street), %w(address_city city)]
58
57
  end
59
58
 
60
- {Learn more}[link:classes/ActiveRecord/Aggregations/ClassMethods.html]
59
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Aggregations/ClassMethods.html]
61
60
 
62
61
 
63
62
  * Validation rules that can differ for new or existing objects.
@@ -69,7 +68,7 @@ This would also define the following accessors: <tt>Product#name</tt> and
69
68
  validates :password, :email_address, confirmation: true, on: :create
70
69
  end
71
70
 
72
- {Learn more}[link:classes/ActiveRecord/Validations.html]
71
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Validations.html]
73
72
 
74
73
 
75
74
  * Callbacks available for the entire life cycle (instantiation, saving, destroying, validating, etc.).
@@ -79,7 +78,7 @@ This would also define the following accessors: <tt>Product#name</tt> and
79
78
  # the `invalidate_payment_plan` method gets called just before Person#destroy
80
79
  end
81
80
 
82
- {Learn more}[link:classes/ActiveRecord/Callbacks.html]
81
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html]
83
82
 
84
83
 
85
84
  * Inheritance hierarchies.
@@ -89,7 +88,7 @@ This would also define the following accessors: <tt>Product#name</tt> and
89
88
  class Client < Company; end
90
89
  class PriorityClient < Client; end
91
90
 
92
- {Learn more}[link:classes/ActiveRecord/Base.html]
91
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Base.html]
93
92
 
94
93
 
95
94
  * Transactions.
@@ -100,7 +99,7 @@ This would also define the following accessors: <tt>Product#name</tt> and
100
99
  mary.deposit(100)
101
100
  end
102
101
 
103
- {Learn more}[link:classes/ActiveRecord/Transactions/ClassMethods.html]
102
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html]
104
103
 
105
104
 
106
105
  * Reflections on columns, associations, and aggregations.
@@ -109,7 +108,7 @@ This would also define the following accessors: <tt>Product#name</tt> and
109
108
  reflection.klass # => Client (class)
110
109
  Firm.columns # Returns an array of column descriptors for the firms table
111
110
 
112
- {Learn more}[link:classes/ActiveRecord/Reflection/ClassMethods.html]
111
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Reflection/ClassMethods.html]
113
112
 
114
113
 
115
114
  * Database abstraction through simple adapters.
@@ -126,13 +125,13 @@ This would also define the following accessors: <tt>Product#name</tt> and
126
125
  database: 'activerecord'
127
126
  )
128
127
 
129
- {Learn more}[link:classes/ActiveRecord/Base.html] and read about the built-in support for
130
- MySQL[link:classes/ActiveRecord/ConnectionAdapters/Mysql2Adapter.html],
131
- PostgreSQL[link:classes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapter.html], and
132
- SQLite3[link:classes/ActiveRecord/ConnectionAdapters/SQLite3Adapter.html].
128
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Base.html] and read about the built-in support for
129
+ MySQL[https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Mysql2Adapter.html],
130
+ PostgreSQL[https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapter.html], and
131
+ SQLite3[https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SQLite3Adapter.html].
133
132
 
134
133
 
135
- * Logging support for Log4r[https://github.com/colbygk/log4r] and Logger[https://ruby-doc.org/stdlib/libdoc/logger/rdoc/].
134
+ * Logging support for Log4r[https://github.com/colbygk/log4r] and Logger[https://docs.ruby-lang.org/en/master/Logger.html].
136
135
 
137
136
  ActiveRecord::Base.logger = ActiveSupport::Logger.new(STDOUT)
138
137
  ActiveRecord::Base.logger = Log4r::Logger.new('Application Log')
@@ -140,7 +139,7 @@ This would also define the following accessors: <tt>Product#name</tt> and
140
139
 
141
140
  * Database agnostic schema management with Migrations.
142
141
 
143
- class AddSystemSettings < ActiveRecord::Migration[7.0]
142
+ class AddSystemSettings < ActiveRecord::Migration[7.2]
144
143
  def up
145
144
  create_table :system_settings do |t|
146
145
  t.string :name
@@ -158,7 +157,7 @@ This would also define the following accessors: <tt>Product#name</tt> and
158
157
  end
159
158
  end
160
159
 
161
- {Learn more}[link:classes/ActiveRecord/Migration.html]
160
+ {Learn more}[https://api.rubyonrails.org/classes/ActiveRecord/Migration.html]
162
161
 
163
162
 
164
163
  == Philosophy
@@ -167,6 +166,7 @@ Active Record is an implementation of the object-relational mapping (ORM)
167
166
  pattern[https://www.martinfowler.com/eaaCatalog/activeRecord.html] by the same
168
167
  name described by Martin Fowler:
169
168
 
169
+ >>>
170
170
  "An object that wraps a row in a database table or view,
171
171
  encapsulates the database access, and adds domain logic on that data."
172
172
 
@@ -176,10 +176,10 @@ Benchmark.ips(TIME) do |x|
176
176
  end
177
177
 
178
178
  x.report "Model.log" do
179
- Exhibit.connection.send(:log, "hello", "world") { }
179
+ Exhibit.lease_connection.send(:log, "hello", "world") { }
180
180
  end
181
181
 
182
182
  x.report "AR.execute(query)" do
183
- ActiveRecord::Base.connection.execute("SELECT * FROM exhibits WHERE id = #{(rand * 1000 + 1).to_i}")
183
+ ActiveRecord::Base.lease_connection.execute("SELECT * FROM exhibits WHERE id = #{(rand * 1000 + 1).to_i}")
184
184
  end
185
185
  end
@@ -4,7 +4,7 @@ module ActiveRecord
4
4
  # See ActiveRecord::Aggregations::ClassMethods for documentation
5
5
  module Aggregations
6
6
  def initialize_dup(*) # :nodoc:
7
- @aggregation_cache = {}
7
+ @aggregation_cache = @aggregation_cache.dup
8
8
  super
9
9
  end
10
10
 
@@ -19,10 +19,12 @@ module ActiveRecord
19
19
  end
20
20
 
21
21
  def init_internals
22
- @aggregation_cache = {}
23
22
  super
23
+ @aggregation_cache = {}
24
24
  end
25
25
 
26
+ # = Active Record \Aggregations
27
+ #
26
28
  # Active Record implements aggregation through a macro-like class method called #composed_of
27
29
  # for representing attributes as value objects. It expresses relationships like "Account [is]
28
30
  # composed of Money [among other things]" or "Person [is] composed of [an] address". Each call
@@ -32,8 +34,8 @@ module ActiveRecord
32
34
  # the database).
33
35
  #
34
36
  # class Customer < ActiveRecord::Base
35
- # composed_of :balance, class_name: "Money", mapping: %w(balance amount)
36
- # composed_of :address, mapping: [ %w(address_street street), %w(address_city city) ]
37
+ # composed_of :balance, class_name: "Money", mapping: { balance: :amount }
38
+ # composed_of :address, mapping: { address_street: :street, address_city: :city }
37
39
  # end
38
40
  #
39
41
  # The customer class now has the following methods to manipulate the value objects:
@@ -150,7 +152,7 @@ module ActiveRecord
150
152
  # class NetworkResource < ActiveRecord::Base
151
153
  # composed_of :cidr,
152
154
  # class_name: 'NetAddr::CIDR',
153
- # mapping: [ %w(network_address network), %w(cidr_range bits) ],
155
+ # mapping: { network_address: :network, cidr_range: :bits },
154
156
  # allow_nil: true,
155
157
  # constructor: Proc.new { |network_address, cidr_range| NetAddr::CIDR.create("#{network_address}/#{cidr_range}") },
156
158
  # converter: Proc.new { |value| NetAddr::CIDR.create(value.is_a?(Array) ? value.join('/') : value) }
@@ -188,10 +190,10 @@ module ActiveRecord
188
190
  # to the Address class, but if the real class name is +CompanyAddress+, you'll have to specify it
189
191
  # with this option.
190
192
  # * <tt>:mapping</tt> - Specifies the mapping of entity attributes to attributes of the value
191
- # object. Each mapping is represented as an array where the first item is the name of the
192
- # entity attribute and the second item is the name of the attribute in the value object. The
193
+ # object. Each mapping is represented as a key-value pair where the key is the name of the
194
+ # entity attribute and the value is the name of the attribute in the value object. The
193
195
  # order in which mappings are defined determines the order in which attributes are sent to the
194
- # value class constructor.
196
+ # value class constructor. The mapping can be written as a hash or as an array of pairs.
195
197
  # * <tt>:allow_nil</tt> - Specifies that the value object will not be instantiated when all mapped
196
198
  # attributes are +nil+. Setting the value object to +nil+ has the effect of writing +nil+ to all
197
199
  # mapped attributes.
@@ -208,14 +210,15 @@ module ActiveRecord
208
210
  # can return +nil+ to skip the assignment.
209
211
  #
210
212
  # Option examples:
211
- # composed_of :temperature, mapping: %w(reading celsius)
212
- # composed_of :balance, class_name: "Money", mapping: %w(balance amount)
213
+ # composed_of :temperature, mapping: { reading: :celsius }
214
+ # composed_of :balance, class_name: "Money", mapping: { balance: :amount }
215
+ # composed_of :address, mapping: { address_street: :street, address_city: :city }
213
216
  # composed_of :address, mapping: [ %w(address_street street), %w(address_city city) ]
214
217
  # composed_of :gps_location
215
218
  # composed_of :gps_location, allow_nil: true
216
219
  # composed_of :ip_address,
217
220
  # class_name: 'IPAddr',
218
- # mapping: %w(ip to_i),
221
+ # mapping: { ip: :to_i },
219
222
  # constructor: Proc.new { |ip| IPAddr.new(ip, Socket::AF_INET) },
220
223
  # converter: Proc.new { |ip| ip.is_a?(Integer) ? IPAddr.new(ip, Socket::AF_INET) : IPAddr.new(ip.to_s) }
221
224
  #
@@ -249,7 +252,7 @@ module ActiveRecord
249
252
  object = constructor.respond_to?(:call) ?
250
253
  constructor.call(*attrs) :
251
254
  class_name.constantize.send(constructor, *attrs)
252
- @aggregation_cache[name] = object
255
+ @aggregation_cache[name] = object.freeze
253
256
  end
254
257
  @aggregation_cache[name]
255
258
  end
@@ -275,7 +278,7 @@ module ActiveRecord
275
278
  @aggregation_cache[name] = nil
276
279
  else
277
280
  mapping.each { |key, value| write_attribute(key, part.send(value)) }
278
- @aggregation_cache[name] = part.freeze
281
+ @aggregation_cache[name] = part.dup.freeze
279
282
  end
280
283
  end
281
284
  end
@@ -16,13 +16,13 @@ module ActiveRecord
16
16
  end
17
17
 
18
18
  %w(insert insert_all insert! insert_all! upsert upsert_all).each do |method|
19
- class_eval <<~RUBY
19
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
20
20
  def #{method}(attributes, **kwargs)
21
21
  if @association.reflection.through_reflection?
22
22
  raise ArgumentError, "Bulk insert or upsert is currently not supported for has_many through association"
23
23
  end
24
24
 
25
- scoping { klass.#{method}(attributes, **kwargs) }
25
+ super
26
26
  end
27
27
  RUBY
28
28
  end
@@ -6,21 +6,23 @@ module ActiveRecord
6
6
  module Associations
7
7
  # Keeps track of table aliases for ActiveRecord::Associations::JoinDependency
8
8
  class AliasTracker # :nodoc:
9
- def self.create(connection, initial_table, joins, aliases = nil)
10
- if joins.empty?
11
- aliases ||= Hash.new(0)
12
- elsif aliases
13
- default_proc = aliases.default_proc || proc { 0 }
14
- aliases.default_proc = proc { |h, k|
15
- h[k] = initial_count_for(connection, k, joins) + default_proc.call(h, k)
16
- }
17
- else
18
- aliases = Hash.new { |h, k|
19
- h[k] = initial_count_for(connection, k, joins)
20
- }
9
+ def self.create(pool, initial_table, joins, aliases = nil)
10
+ pool.with_connection do |connection|
11
+ if joins.empty?
12
+ aliases ||= Hash.new(0)
13
+ elsif aliases
14
+ default_proc = aliases.default_proc || proc { 0 }
15
+ aliases.default_proc = proc { |h, k|
16
+ h[k] = initial_count_for(connection, k, joins) + default_proc.call(h, k)
17
+ }
18
+ else
19
+ aliases = Hash.new { |h, k|
20
+ h[k] = initial_count_for(connection, k, joins)
21
+ }
22
+ end
23
+ aliases[initial_table] = 1
24
+ new(connection.table_alias_length, aliases)
21
25
  end
22
- aliases[initial_table] = 1
23
- new(connection, aliases)
24
26
  end
25
27
 
26
28
  def self.initial_count_for(connection, name, table_joins)
@@ -46,9 +48,9 @@ module ActiveRecord
46
48
  end
47
49
 
48
50
  # table_joins is an array of arel joins which might conflict with the aliases we assign here
49
- def initialize(connection, aliases)
50
- @aliases = aliases
51
- @connection = connection
51
+ def initialize(table_alias_length, aliases)
52
+ @aliases = aliases
53
+ @table_alias_length = table_alias_length
52
54
  end
53
55
 
54
56
  def aliased_table_for(arel_table, table_name = nil)
@@ -60,7 +62,7 @@ module ActiveRecord
60
62
  arel_table = arel_table.alias(table_name) if arel_table.name != table_name
61
63
  else
62
64
  # Otherwise, we need to use an alias
63
- aliased_name = @connection.table_alias_for(yield)
65
+ aliased_name = table_alias_for(yield)
64
66
 
65
67
  # Update the count
66
68
  count = aliases[aliased_name] += 1
@@ -76,8 +78,12 @@ module ActiveRecord
76
78
  attr_reader :aliases
77
79
 
78
80
  private
81
+ def table_alias_for(table_name)
82
+ table_name[0...@table_alias_length].tr(".", "_")
83
+ end
84
+
79
85
  def truncate(name)
80
- name.slice(0, @connection.table_alias_length - 2)
86
+ name.slice(0, @table_alias_length - 2)
81
87
  end
82
88
  end
83
89
  end
@@ -19,7 +19,7 @@ module ActiveRecord
19
19
  # Associations in Active Record are middlemen between the object that
20
20
  # holds the association, known as the <tt>owner</tt>, and the associated
21
21
  # result set, known as the <tt>target</tt>. Association metadata is available in
22
- # <tt>reflection</tt>, which is an instance of <tt>ActiveRecord::Reflection::AssociationReflection</tt>.
22
+ # <tt>reflection</tt>, which is an instance of +ActiveRecord::Reflection::AssociationReflection+.
23
23
  #
24
24
  # For example, given
25
25
  #
@@ -33,7 +33,8 @@ module ActiveRecord
33
33
  # <tt>owner</tt>, the collection of its posts as <tt>target</tt>, and
34
34
  # the <tt>reflection</tt> object represents a <tt>:has_many</tt> macro.
35
35
  class Association # :nodoc:
36
- attr_reader :owner, :target, :reflection, :disable_joins
36
+ attr_accessor :owner
37
+ attr_reader :target, :reflection, :disable_joins
37
38
 
38
39
  delegate :options, to: :reflection
39
40
 
@@ -45,12 +46,13 @@ module ActiveRecord
45
46
 
46
47
  reset
47
48
  reset_scope
49
+
50
+ @skip_strict_loading = nil
48
51
  end
49
52
 
50
53
  # Resets the \loaded flag to +false+ and sets the \target to +nil+.
51
54
  def reset
52
55
  @loaded = false
53
- @target = nil
54
56
  @stale_state = nil
55
57
  end
56
58
 
@@ -61,7 +63,7 @@ module ActiveRecord
61
63
  # Reloads the \target and returns +self+ on success.
62
64
  # The QueryCache is cleared if +force+ is true.
63
65
  def reload(force = false)
64
- klass.connection.clear_query_cache if force && klass
66
+ klass.connection_pool.clear_query_cache if force && klass
65
67
  reset
66
68
  reset_scope
67
69
  load_target
@@ -208,6 +210,12 @@ module ActiveRecord
208
210
  _create_record(attributes, true, &block)
209
211
  end
210
212
 
213
+ # Whether the association represent a single record
214
+ # or a collection of records.
215
+ def collection?
216
+ false
217
+ end
218
+
211
219
  private
212
220
  # Reader and writer methods call this so that consistent errors are presented
213
221
  # when the association target class does not exist.
@@ -216,7 +224,7 @@ module ActiveRecord
216
224
  end
217
225
 
218
226
  def find_target
219
- if violates_strict_loading? && owner.validation_context.nil?
227
+ if violates_strict_loading?
220
228
  Base.strict_loading_violation!(owner: owner.class, reflection: reflection)
221
229
  end
222
230
 
@@ -229,17 +237,31 @@ module ActiveRecord
229
237
  end
230
238
 
231
239
  binds = AssociationScope.get_bind_values(owner, reflection.chain)
232
- sc.execute(binds, klass.connection) do |record|
233
- set_inverse_instance(record)
234
- if owner.strict_loading_n_plus_one_only? && reflection.macro == :has_many
235
- record.strict_loading!
236
- else
237
- record.strict_loading!(false, mode: owner.strict_loading_mode)
240
+ klass.with_connection do |c|
241
+ sc.execute(binds, c) do |record|
242
+ set_inverse_instance(record)
243
+ if owner.strict_loading_n_plus_one_only? && reflection.macro == :has_many
244
+ record.strict_loading!
245
+ else
246
+ record.strict_loading!(false, mode: owner.strict_loading_mode)
247
+ end
238
248
  end
239
249
  end
240
250
  end
241
251
 
252
+ def skip_strict_loading(&block)
253
+ skip_strict_loading_was = @skip_strict_loading
254
+ @skip_strict_loading = true
255
+ yield
256
+ ensure
257
+ @skip_strict_loading = skip_strict_loading_was
258
+ end
259
+
242
260
  def violates_strict_loading?
261
+ return if @skip_strict_loading
262
+
263
+ return unless owner.validation_context.nil?
264
+
243
265
  return reflection.strict_loading? if reflection.options.key?(:strict_loading)
244
266
 
245
267
  owner.strict_loading? && !owner.strict_loading_n_plus_one_only?
@@ -322,7 +344,8 @@ module ActiveRecord
322
344
 
323
345
  # Returns true if record contains the foreign_key
324
346
  def foreign_key_for?(record)
325
- record._has_attribute?(reflection.foreign_key)
347
+ foreign_key = Array(reflection.foreign_key)
348
+ foreign_key.all? { |key| record._has_attribute?(key) }
326
349
  end
327
350
 
328
351
  # This should be implemented to return the values of the relevant key(s) on the owner,
@@ -35,7 +35,7 @@ module ActiveRecord
35
35
  binds = []
36
36
  last_reflection = chain.last
37
37
 
38
- binds << last_reflection.join_id_for(owner)
38
+ binds.push(*last_reflection.join_id_for(owner))
39
39
  if last_reflection.type
40
40
  binds << owner.class.polymorphic_name
41
41
  end
@@ -56,12 +56,15 @@ module ActiveRecord
56
56
  end
57
57
 
58
58
  def last_chain_scope(scope, reflection, owner)
59
- primary_key = reflection.join_primary_key
60
- foreign_key = reflection.join_foreign_key
59
+ primary_key = Array(reflection.join_primary_key)
60
+ foreign_key = Array(reflection.join_foreign_key)
61
61
 
62
62
  table = reflection.aliased_table
63
- value = transform_value(owner[foreign_key])
64
- scope = apply_scope(scope, table, primary_key, value)
63
+ primary_key_foreign_key_pairs = primary_key.zip(foreign_key)
64
+ primary_key_foreign_key_pairs.each do |join_key, foreign_key|
65
+ value = transform_value(owner._read_attribute(foreign_key))
66
+ scope = apply_scope(scope, table, join_key, value)
67
+ end
65
68
 
66
69
  if reflection.type
67
70
  polymorphic_type = transform_value(owner.class.polymorphic_name)
@@ -76,19 +79,23 @@ module ActiveRecord
76
79
  end
77
80
 
78
81
  def next_chain_scope(scope, reflection, next_reflection)
79
- primary_key = reflection.join_primary_key
80
- foreign_key = reflection.join_foreign_key
82
+ primary_key = Array(reflection.join_primary_key)
83
+ foreign_key = Array(reflection.join_foreign_key)
81
84
 
82
85
  table = reflection.aliased_table
83
86
  foreign_table = next_reflection.aliased_table
84
- constraint = table[primary_key].eq(foreign_table[foreign_key])
87
+
88
+ primary_key_foreign_key_pairs = primary_key.zip(foreign_key)
89
+ constraints = primary_key_foreign_key_pairs.map do |join_primary_key, foreign_key|
90
+ table[join_primary_key].eq(foreign_table[foreign_key])
91
+ end.inject(&:and)
85
92
 
86
93
  if reflection.type
87
94
  value = transform_value(next_reflection.klass.polymorphic_name)
88
95
  scope = apply_scope(scope, table, reflection.type, value)
89
96
  end
90
97
 
91
- scope.joins!(join(foreign_table, constraint))
98
+ scope.joins!(join(foreign_table, constraints))
92
99
  end
93
100
 
94
101
  class ReflectionProxy < SimpleDelegator # :nodoc:
@@ -11,8 +11,13 @@ module ActiveRecord
11
11
  when :destroy
12
12
  raise ActiveRecord::Rollback unless target.destroy
13
13
  when :destroy_async
14
- id = owner.public_send(reflection.foreign_key.to_sym)
15
- primary_key_column = reflection.active_record_primary_key.to_sym
14
+ if reflection.foreign_key.is_a?(Array)
15
+ primary_key_column = reflection.active_record_primary_key
16
+ id = reflection.foreign_key.map { |col| owner.public_send(col) }
17
+ else
18
+ primary_key_column = reflection.active_record_primary_key
19
+ id = owner.public_send(reflection.foreign_key)
20
+ end
16
21
 
17
22
  enqueue_destroy_association(
18
23
  owner_model_name: owner.class.to_s,
@@ -119,10 +124,21 @@ module ActiveRecord
119
124
  end
120
125
 
121
126
  def replace_keys(record, force: false)
122
- target_key = record ? record._read_attribute(primary_key(record.class)) : nil
127
+ reflection_fk = reflection.foreign_key
128
+ if reflection_fk.is_a?(Array)
129
+ target_key_values = record ? Array(primary_key(record.class)).map { |key| record._read_attribute(key) } : []
130
+
131
+ if force || reflection_fk.map { |fk| owner._read_attribute(fk) } != target_key_values
132
+ reflection_fk.each_with_index do |key, index|
133
+ owner[key] = target_key_values[index]
134
+ end
135
+ end
136
+ else
137
+ target_key_value = record ? record._read_attribute(primary_key(record.class)) : nil
123
138
 
124
- if force || owner._read_attribute(reflection.foreign_key) != target_key
125
- owner[reflection.foreign_key] = target_key
139
+ if force || owner._read_attribute(reflection_fk) != target_key_value
140
+ owner[reflection_fk] = target_key_value
141
+ end
126
142
  end
127
143
  end
128
144
 
@@ -131,7 +147,7 @@ module ActiveRecord
131
147
  end
132
148
 
133
149
  def foreign_key_present?
134
- owner._read_attribute(reflection.foreign_key)
150
+ Array(reflection.foreign_key).all? { |fk| owner._read_attribute(fk) }
135
151
  end
136
152
 
137
153
  def invertible_for?(record)
@@ -140,8 +156,7 @@ module ActiveRecord
140
156
  end
141
157
 
142
158
  def stale_state
143
- result = owner._read_attribute(reflection.foreign_key) { |n| owner.send(:missing_attribute, n, caller) }
144
- result && result.to_s
159
+ owner._read_attribute(reflection.foreign_key) { |n| owner.send(:missing_attribute, n, caller) }
145
160
  end
146
161
  end
147
162
  end
@@ -41,8 +41,9 @@ module ActiveRecord
41
41
  end
42
42
 
43
43
  def stale_state
44
- foreign_key = super
45
- foreign_key && [foreign_key.to_s, owner[reflection.foreign_type].to_s]
44
+ if foreign_key = super
45
+ [foreign_key, owner[reflection.foreign_type]]
46
+ end
46
47
  end
47
48
  end
48
49
  end
@@ -19,7 +19,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
19
19
  self.extensions = []
20
20
 
21
21
  VALID_OPTIONS = [
22
- :class_name, :anonymous_class, :primary_key, :foreign_key, :dependent, :validate, :inverse_of, :strict_loading
22
+ :class_name, :anonymous_class, :primary_key, :foreign_key, :dependent, :validate, :inverse_of, :strict_loading, :query_constraints
23
23
  ].freeze # :nodoc:
24
24
 
25
25
  def self.build(model, name, scope, options, &block)
@@ -128,8 +128,8 @@ module ActiveRecord::Associations::Builder # :nodoc:
128
128
 
129
129
  def self.check_dependent_options(dependent, model)
130
130
  if dependent == :destroy_async && !model.destroy_association_async_job
131
- err_message = "ActiveJob is required to use destroy_async on associations"
132
- raise ActiveRecord::ActiveJobRequiredError, err_message
131
+ err_message = "A valid destroy_association_async_job is required to use `dependent: :destroy_async` on associations"
132
+ raise ActiveRecord::ConfigurationError, err_message
133
133
  end
134
134
  unless valid_dependent_options.include? dependent
135
135
  raise ArgumentError, "The :dependent option must be one of #{valid_dependent_options}, but is :#{dependent}"