activerecord 6.1.7 → 7.2.0

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 (332) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +520 -1385
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +31 -31
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +17 -14
  7. data/lib/active_record/association_relation.rb +2 -12
  8. data/lib/active_record/associations/alias_tracker.rb +25 -19
  9. data/lib/active_record/associations/association.rb +60 -21
  10. data/lib/active_record/associations/association_scope.rb +17 -12
  11. data/lib/active_record/associations/belongs_to_association.rb +37 -11
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -4
  13. data/lib/active_record/associations/builder/association.rb +11 -5
  14. data/lib/active_record/associations/builder/belongs_to.rb +41 -14
  15. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
  17. data/lib/active_record/associations/builder/has_many.rb +4 -4
  18. data/lib/active_record/associations/builder/has_one.rb +4 -4
  19. data/lib/active_record/associations/builder/singular_association.rb +6 -2
  20. data/lib/active_record/associations/collection_association.rb +46 -36
  21. data/lib/active_record/associations/collection_proxy.rb +44 -16
  22. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  23. data/lib/active_record/associations/errors.rb +265 -0
  24. data/lib/active_record/associations/foreign_association.rb +10 -3
  25. data/lib/active_record/associations/has_many_association.rb +29 -19
  26. data/lib/active_record/associations/has_many_through_association.rb +12 -7
  27. data/lib/active_record/associations/has_one_association.rb +20 -10
  28. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  29. data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
  30. data/lib/active_record/associations/join_dependency.rb +23 -15
  31. data/lib/active_record/associations/nested_error.rb +47 -0
  32. data/lib/active_record/associations/preloader/association.rb +212 -53
  33. data/lib/active_record/associations/preloader/batch.rb +48 -0
  34. data/lib/active_record/associations/preloader/branch.rb +153 -0
  35. data/lib/active_record/associations/preloader/through_association.rb +50 -16
  36. data/lib/active_record/associations/preloader.rb +50 -121
  37. data/lib/active_record/associations/singular_association.rb +15 -3
  38. data/lib/active_record/associations/through_association.rb +25 -14
  39. data/lib/active_record/associations.rb +404 -509
  40. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  41. data/lib/active_record/attribute_assignment.rb +2 -14
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
  43. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  44. data/lib/active_record/attribute_methods/dirty.rb +73 -22
  45. data/lib/active_record/attribute_methods/primary_key.rb +47 -27
  46. data/lib/active_record/attribute_methods/query.rb +31 -19
  47. data/lib/active_record/attribute_methods/read.rb +14 -11
  48. data/lib/active_record/attribute_methods/serialization.rb +174 -37
  49. data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -9
  50. data/lib/active_record/attribute_methods/write.rb +12 -15
  51. data/lib/active_record/attribute_methods.rb +164 -52
  52. data/lib/active_record/attributes.rb +51 -49
  53. data/lib/active_record/autosave_association.rb +74 -57
  54. data/lib/active_record/base.rb +27 -5
  55. data/lib/active_record/callbacks.rb +18 -34
  56. data/lib/active_record/coders/column_serializer.rb +61 -0
  57. data/lib/active_record/coders/json.rb +1 -1
  58. data/lib/active_record/coders/yaml_column.rb +70 -46
  59. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  61. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
  62. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +327 -612
  63. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
  64. data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -60
  65. data/lib/active_record/connection_adapters/abstract/query_cache.rb +201 -64
  66. data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -131
  67. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  68. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
  69. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
  70. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  71. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +377 -142
  72. data/lib/active_record/connection_adapters/abstract/transaction.rb +361 -76
  73. data/lib/active_record/connection_adapters/abstract_adapter.rb +624 -163
  74. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +345 -166
  75. data/lib/active_record/connection_adapters/column.rb +13 -0
  76. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  77. data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -130
  78. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -55
  79. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  80. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  81. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  82. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +45 -14
  83. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  84. data/lib/active_record/connection_adapters/mysql2_adapter.rb +107 -68
  85. data/lib/active_record/connection_adapters/pool_config.rb +26 -16
  86. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  87. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
  88. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +114 -54
  89. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  94. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  96. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
  97. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  100. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  101. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  102. data/lib/active_record/connection_adapters/postgresql/quoting.rb +137 -104
  103. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
  104. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
  105. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +173 -3
  106. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
  107. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +401 -77
  108. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  109. data/lib/active_record/connection_adapters/postgresql_adapter.rb +518 -251
  110. data/lib/active_record/connection_adapters/schema_cache.rb +326 -102
  111. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  112. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +78 -55
  113. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +68 -54
  114. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  115. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
  116. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  117. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +66 -22
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +372 -130
  119. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  120. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  121. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  122. data/lib/active_record/connection_adapters.rb +130 -6
  123. data/lib/active_record/connection_handling.rb +132 -146
  124. data/lib/active_record/core.rb +276 -251
  125. data/lib/active_record/counter_cache.rb +68 -34
  126. data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -3
  127. data/lib/active_record/database_configurations/database_config.rb +34 -10
  128. data/lib/active_record/database_configurations/hash_config.rb +107 -31
  129. data/lib/active_record/database_configurations/url_config.rb +38 -13
  130. data/lib/active_record/database_configurations.rb +96 -60
  131. data/lib/active_record/delegated_type.rb +90 -20
  132. data/lib/active_record/deprecator.rb +7 -0
  133. data/lib/active_record/destroy_association_async_job.rb +4 -2
  134. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  135. data/lib/active_record/dynamic_matchers.rb +3 -3
  136. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  137. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  138. data/lib/active_record/encryption/cipher.rb +53 -0
  139. data/lib/active_record/encryption/config.rb +68 -0
  140. data/lib/active_record/encryption/configurable.rb +60 -0
  141. data/lib/active_record/encryption/context.rb +42 -0
  142. data/lib/active_record/encryption/contexts.rb +76 -0
  143. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  144. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  145. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  146. data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -0
  147. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  148. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  149. data/lib/active_record/encryption/encryptor.rb +170 -0
  150. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  151. data/lib/active_record/encryption/errors.rb +15 -0
  152. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  153. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  154. data/lib/active_record/encryption/key.rb +28 -0
  155. data/lib/active_record/encryption/key_generator.rb +53 -0
  156. data/lib/active_record/encryption/key_provider.rb +46 -0
  157. data/lib/active_record/encryption/message.rb +33 -0
  158. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  159. data/lib/active_record/encryption/message_serializer.rb +96 -0
  160. data/lib/active_record/encryption/null_encryptor.rb +25 -0
  161. data/lib/active_record/encryption/properties.rb +76 -0
  162. data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
  163. data/lib/active_record/encryption/scheme.rb +100 -0
  164. data/lib/active_record/encryption.rb +56 -0
  165. data/lib/active_record/enum.rb +163 -63
  166. data/lib/active_record/errors.rb +210 -27
  167. data/lib/active_record/explain.rb +21 -12
  168. data/lib/active_record/explain_registry.rb +11 -6
  169. data/lib/active_record/explain_subscriber.rb +1 -1
  170. data/lib/active_record/fixture_set/file.rb +15 -1
  171. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  172. data/lib/active_record/fixture_set/render_context.rb +2 -0
  173. data/lib/active_record/fixture_set/table_row.rb +70 -14
  174. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  175. data/lib/active_record/fixtures.rb +179 -112
  176. data/lib/active_record/future_result.rb +178 -0
  177. data/lib/active_record/gem_version.rb +4 -4
  178. data/lib/active_record/inheritance.rb +85 -31
  179. data/lib/active_record/insert_all.rb +148 -32
  180. data/lib/active_record/integration.rb +14 -10
  181. data/lib/active_record/internal_metadata.rb +123 -23
  182. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  183. data/lib/active_record/locking/optimistic.rb +43 -27
  184. data/lib/active_record/locking/pessimistic.rb +15 -6
  185. data/lib/active_record/log_subscriber.rb +41 -29
  186. data/lib/active_record/marshalling.rb +56 -0
  187. data/lib/active_record/message_pack.rb +124 -0
  188. data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
  189. data/lib/active_record/middleware/database_selector.rb +23 -13
  190. data/lib/active_record/middleware/shard_selector.rb +62 -0
  191. data/lib/active_record/migration/command_recorder.rb +113 -16
  192. data/lib/active_record/migration/compatibility.rb +235 -46
  193. data/lib/active_record/migration/default_strategy.rb +22 -0
  194. data/lib/active_record/migration/execution_strategy.rb +19 -0
  195. data/lib/active_record/migration/join_table.rb +1 -1
  196. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  197. data/lib/active_record/migration.rb +374 -177
  198. data/lib/active_record/model_schema.rb +143 -159
  199. data/lib/active_record/nested_attributes.rb +48 -21
  200. data/lib/active_record/no_touching.rb +3 -3
  201. data/lib/active_record/normalization.rb +163 -0
  202. data/lib/active_record/persistence.rb +282 -283
  203. data/lib/active_record/promise.rb +84 -0
  204. data/lib/active_record/query_cache.rb +19 -25
  205. data/lib/active_record/query_logs.rb +189 -0
  206. data/lib/active_record/query_logs_formatter.rb +41 -0
  207. data/lib/active_record/querying.rb +44 -9
  208. data/lib/active_record/railtie.rb +234 -71
  209. data/lib/active_record/railties/controller_runtime.rb +25 -11
  210. data/lib/active_record/railties/databases.rake +189 -256
  211. data/lib/active_record/railties/job_runtime.rb +23 -0
  212. data/lib/active_record/readonly_attributes.rb +41 -3
  213. data/lib/active_record/reflection.rb +325 -103
  214. data/lib/active_record/relation/batches/batch_enumerator.rb +38 -9
  215. data/lib/active_record/relation/batches.rb +198 -63
  216. data/lib/active_record/relation/calculations.rb +300 -111
  217. data/lib/active_record/relation/delegation.rb +33 -22
  218. data/lib/active_record/relation/finder_methods.rb +123 -52
  219. data/lib/active_record/relation/merger.rb +26 -19
  220. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  221. data/lib/active_record/relation/predicate_builder/association_query_value.rb +38 -4
  222. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  223. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  224. data/lib/active_record/relation/predicate_builder.rb +29 -22
  225. data/lib/active_record/relation/query_attribute.rb +30 -12
  226. data/lib/active_record/relation/query_methods.rb +842 -150
  227. data/lib/active_record/relation/record_fetch_warning.rb +10 -9
  228. data/lib/active_record/relation/spawn_methods.rb +7 -6
  229. data/lib/active_record/relation/where_clause.rb +15 -36
  230. data/lib/active_record/relation.rb +736 -145
  231. data/lib/active_record/result.rb +67 -54
  232. data/lib/active_record/runtime_registry.rb +71 -13
  233. data/lib/active_record/sanitization.rb +84 -34
  234. data/lib/active_record/schema.rb +39 -23
  235. data/lib/active_record/schema_dumper.rb +90 -31
  236. data/lib/active_record/schema_migration.rb +74 -23
  237. data/lib/active_record/scoping/default.rb +72 -15
  238. data/lib/active_record/scoping/named.rb +5 -13
  239. data/lib/active_record/scoping.rb +65 -34
  240. data/lib/active_record/secure_password.rb +60 -0
  241. data/lib/active_record/secure_token.rb +21 -3
  242. data/lib/active_record/serialization.rb +6 -1
  243. data/lib/active_record/signed_id.rb +30 -9
  244. data/lib/active_record/statement_cache.rb +7 -7
  245. data/lib/active_record/store.rb +10 -10
  246. data/lib/active_record/suppressor.rb +13 -15
  247. data/lib/active_record/table_metadata.rb +7 -3
  248. data/lib/active_record/tasks/database_tasks.rb +277 -149
  249. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
  250. data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
  251. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
  252. data/lib/active_record/test_databases.rb +1 -1
  253. data/lib/active_record/test_fixtures.rb +173 -155
  254. data/lib/active_record/testing/query_assertions.rb +121 -0
  255. data/lib/active_record/timestamp.rb +32 -19
  256. data/lib/active_record/token_for.rb +123 -0
  257. data/lib/active_record/touch_later.rb +12 -7
  258. data/lib/active_record/transaction.rb +132 -0
  259. data/lib/active_record/transactions.rb +118 -41
  260. data/lib/active_record/translation.rb +3 -5
  261. data/lib/active_record/type/adapter_specific_registry.rb +32 -14
  262. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  263. data/lib/active_record/type/internal/timezone.rb +7 -2
  264. data/lib/active_record/type/serialized.rb +9 -7
  265. data/lib/active_record/type/time.rb +4 -0
  266. data/lib/active_record/type/type_map.rb +17 -20
  267. data/lib/active_record/type.rb +1 -2
  268. data/lib/active_record/type_caster/connection.rb +4 -4
  269. data/lib/active_record/validations/absence.rb +1 -1
  270. data/lib/active_record/validations/associated.rb +13 -7
  271. data/lib/active_record/validations/numericality.rb +5 -4
  272. data/lib/active_record/validations/presence.rb +5 -28
  273. data/lib/active_record/validations/uniqueness.rb +64 -15
  274. data/lib/active_record/validations.rb +12 -5
  275. data/lib/active_record/version.rb +1 -1
  276. data/lib/active_record.rb +444 -32
  277. data/lib/arel/alias_predication.rb +1 -1
  278. data/lib/arel/attributes/attribute.rb +0 -8
  279. data/lib/arel/collectors/bind.rb +2 -0
  280. data/lib/arel/collectors/composite.rb +7 -0
  281. data/lib/arel/collectors/sql_string.rb +1 -1
  282. data/lib/arel/collectors/substitute_binds.rb +1 -1
  283. data/lib/arel/crud.rb +28 -22
  284. data/lib/arel/delete_manager.rb +18 -4
  285. data/lib/arel/errors.rb +10 -0
  286. data/lib/arel/factory_methods.rb +4 -0
  287. data/lib/arel/filter_predications.rb +9 -0
  288. data/lib/arel/insert_manager.rb +2 -3
  289. data/lib/arel/nodes/binary.rb +6 -7
  290. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  291. data/lib/arel/nodes/casted.rb +1 -1
  292. data/lib/arel/nodes/cte.rb +36 -0
  293. data/lib/arel/nodes/delete_statement.rb +12 -13
  294. data/lib/arel/nodes/filter.rb +10 -0
  295. data/lib/arel/nodes/fragments.rb +35 -0
  296. data/lib/arel/nodes/function.rb +1 -0
  297. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  298. data/lib/arel/nodes/insert_statement.rb +2 -2
  299. data/lib/arel/nodes/leading_join.rb +8 -0
  300. data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
  301. data/lib/arel/nodes/node.rb +115 -5
  302. data/lib/arel/nodes/select_core.rb +2 -2
  303. data/lib/arel/nodes/select_statement.rb +2 -2
  304. data/lib/arel/nodes/sql_literal.rb +13 -0
  305. data/lib/arel/nodes/table_alias.rb +4 -0
  306. data/lib/arel/nodes/update_statement.rb +8 -3
  307. data/lib/arel/nodes.rb +7 -2
  308. data/lib/arel/predications.rb +14 -4
  309. data/lib/arel/select_manager.rb +11 -5
  310. data/lib/arel/table.rb +9 -6
  311. data/lib/arel/tree_manager.rb +8 -15
  312. data/lib/arel/update_manager.rb +20 -5
  313. data/lib/arel/visitors/dot.rb +81 -90
  314. data/lib/arel/visitors/mysql.rb +23 -5
  315. data/lib/arel/visitors/postgresql.rb +1 -22
  316. data/lib/arel/visitors/to_sql.rb +170 -36
  317. data/lib/arel/visitors/visitor.rb +2 -2
  318. data/lib/arel.rb +23 -4
  319. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  320. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  321. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  322. data/lib/rails/generators/active_record/migration.rb +3 -1
  323. data/lib/rails/generators/active_record/model/USAGE +113 -0
  324. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  325. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  326. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  327. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  328. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  329. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  330. metadata +100 -14
  331. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  332. data/lib/active_record/null_relation.rb +0 -67
@@ -6,6 +6,13 @@ module ActiveRecord
6
6
  module ModelSchema
7
7
  extend ActiveSupport::Concern
8
8
 
9
+ ##
10
+ # :method: id_value
11
+ # :call-seq: id_value
12
+ #
13
+ # Returns the underlying column value for a column named "id". Useful when defining
14
+ # a composite primary key including an "id" column so that the value is readable.
15
+
9
16
  ##
10
17
  # :singleton-method: primary_key_prefix_type
11
18
  # :call-seq: primary_key_prefix_type
@@ -126,9 +133,34 @@ module ActiveRecord
126
133
  # +:immutable_string+. This setting does not affect the behavior of
127
134
  # <tt>attribute :foo, :string</tt>. Defaults to false.
128
135
 
129
- included do
130
- mattr_accessor :primary_key_prefix_type, instance_writer: false
136
+ ##
137
+ # :singleton-method: inheritance_column
138
+ # :call-seq: inheritance_column
139
+ #
140
+ # The name of the table column which stores the class name on single-table
141
+ # inheritance situations.
142
+ #
143
+ # The default inheritance column name is +type+, which means it's a
144
+ # reserved word inside Active Record. To be able to use single-table
145
+ # inheritance with another column name, or to use the column +type+ in
146
+ # your own model for something else, you can set +inheritance_column+:
147
+ #
148
+ # self.inheritance_column = 'zoink'
149
+ #
150
+ # If you wish to disable single-table inheritance altogether you can set
151
+ # +inheritance_column+ to +nil+
152
+ #
153
+ # self.inheritance_column = nil
154
+
155
+ ##
156
+ # :singleton-method: inheritance_column=
157
+ # :call-seq: inheritance_column=(column)
158
+ #
159
+ # Defines the name of the table column which will store the class name on single-table
160
+ # inheritance situations.
131
161
 
162
+ included do
163
+ class_attribute :primary_key_prefix_type, instance_writer: false
132
164
  class_attribute :table_name_prefix, instance_writer: false, default: ""
133
165
  class_attribute :table_name_suffix, instance_writer: false, default: ""
134
166
  class_attribute :schema_migrations_table_name, instance_accessor: false, default: "schema_migrations"
@@ -137,8 +169,15 @@ module ActiveRecord
137
169
  class_attribute :implicit_order_column, instance_accessor: false
138
170
  class_attribute :immutable_strings_by_default, instance_accessor: false
139
171
 
172
+ class_attribute :inheritance_column, instance_accessor: false, default: "type"
173
+ singleton_class.class_eval do
174
+ alias_method :_inheritance_column=, :inheritance_column=
175
+ private :_inheritance_column=
176
+ alias_method :inheritance_column=, :real_inheritance_column=
177
+ end
178
+
140
179
  self.protected_environments = ["production"]
141
- self.inheritance_column = "type"
180
+
142
181
  self.ignored_columns = [].freeze
143
182
 
144
183
  delegate :type_for_attribute, :column_for_attribute, to: :class
@@ -153,8 +192,9 @@ module ActiveRecord
153
192
  # artists, records => artists_records
154
193
  # records, artists => artists_records
155
194
  # music_artists, music_records => music_artists_records
195
+ # music.artists, music.records => music.artists_records
156
196
  def self.derive_join_table_name(first_table, second_table) # :nodoc:
157
- [first_table.to_s, second_table.to_s].sort.join("\0").gsub(/^(.*_)(.+)\0\1(.+)/, '\1\2_\3').tr("\0", "_")
197
+ [first_table.to_s, second_table.to_s].sort.join("\0").gsub(/^(.*[_.])(.+)\0\1(.+)/, '\1\2_\3').tr("\0", "_")
158
198
  end
159
199
 
160
200
  module ClassMethods
@@ -197,6 +237,21 @@ module ActiveRecord
197
237
  # the table name guess for an Invoice class becomes "myapp_invoices".
198
238
  # Invoice::Lineitem becomes "myapp_invoice_lineitems".
199
239
  #
240
+ # Active Model Naming's +model_name+ is the base name used to guess the
241
+ # table name. In case a custom Active Model Name is defined, it will be
242
+ # used for the table name as well:
243
+ #
244
+ # class PostRecord < ActiveRecord::Base
245
+ # class << self
246
+ # def model_name
247
+ # ActiveModel::Name.new(self, nil, "Post")
248
+ # end
249
+ # end
250
+ # end
251
+ #
252
+ # PostRecord.table_name
253
+ # # => "posts"
254
+ #
200
255
  # You can also set your own table name explicitly:
201
256
  #
202
257
  # class Mouse < ActiveRecord::Base
@@ -223,19 +278,21 @@ module ActiveRecord
223
278
  @table_name = value
224
279
  @quoted_table_name = nil
225
280
  @arel_table = nil
226
- @sequence_name = nil unless defined?(@explicit_sequence_name) && @explicit_sequence_name
281
+ @sequence_name = nil unless @explicit_sequence_name
227
282
  @predicate_builder = nil
228
283
  end
229
284
 
230
285
  # Returns a quoted version of the table name, used to construct SQL statements.
231
286
  def quoted_table_name
232
- @quoted_table_name ||= connection.quote_table_name(table_name)
287
+ @quoted_table_name ||= adapter_class.quote_table_name(table_name)
233
288
  end
234
289
 
235
290
  # Computes the table name, (re)sets it internally, and returns it.
236
- def reset_table_name #:nodoc:
237
- self.table_name = if abstract_class?
238
- superclass == Base ? nil : superclass.table_name
291
+ def reset_table_name # :nodoc:
292
+ self.table_name = if self == Base
293
+ nil
294
+ elsif abstract_class?
295
+ superclass.table_name
239
296
  elsif superclass.abstract_class?
240
297
  superclass.table_name || compute_table_name
241
298
  else
@@ -243,11 +300,11 @@ module ActiveRecord
243
300
  end
244
301
  end
245
302
 
246
- def full_table_name_prefix #:nodoc:
303
+ def full_table_name_prefix # :nodoc:
247
304
  (module_parents.detect { |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix
248
305
  end
249
306
 
250
- def full_table_name_suffix #:nodoc:
307
+ def full_table_name_suffix # :nodoc:
251
308
  (module_parents.detect { |p| p.respond_to?(:table_name_suffix) } || self).table_name_suffix
252
309
  end
253
310
 
@@ -266,33 +323,14 @@ module ActiveRecord
266
323
  @protected_environments = environments.map(&:to_s)
267
324
  end
268
325
 
269
- # Defines the name of the table column which will store the class name on single-table
270
- # inheritance situations.
271
- #
272
- # The default inheritance column name is +type+, which means it's a
273
- # reserved word inside Active Record. To be able to use single-table
274
- # inheritance with another column name, or to use the column +type+ in
275
- # your own model for something else, you can set +inheritance_column+:
276
- #
277
- # self.inheritance_column = 'zoink'
278
- def inheritance_column
279
- (@inheritance_column ||= nil) || superclass.inheritance_column
280
- end
281
-
282
- # Sets the value of inheritance_column
283
- def inheritance_column=(value)
284
- @inheritance_column = value.to_s
285
- @explicit_inheritance_column = true
326
+ def real_inheritance_column=(value) # :nodoc:
327
+ self._inheritance_column = value.to_s
286
328
  end
287
329
 
288
330
  # The list of columns names the model should ignore. Ignored columns won't have attribute
289
331
  # accessors defined, and won't be referenced in SQL queries.
290
332
  def ignored_columns
291
- if defined?(@ignored_columns)
292
- @ignored_columns
293
- else
294
- superclass.ignored_columns
295
- end
333
+ @ignored_columns || superclass.ignored_columns
296
334
  end
297
335
 
298
336
  # Sets the columns names the model should ignore. Ignored columns won't have attribute
@@ -313,7 +351,7 @@ module ActiveRecord
313
351
  # # name :string, limit: 255
314
352
  # # category :string, limit: 255
315
353
  #
316
- # self.ignored_columns = [:category]
354
+ # self.ignored_columns += [:category]
317
355
  # end
318
356
  #
319
357
  # The schema still contains "category", but now the model omits it, so any meta-driven code or
@@ -339,9 +377,9 @@ module ActiveRecord
339
377
  end
340
378
  end
341
379
 
342
- def reset_sequence_name #:nodoc:
380
+ def reset_sequence_name # :nodoc:
343
381
  @explicit_sequence_name = false
344
- @sequence_name = connection.default_sequence_name(table_name, primary_key)
382
+ @sequence_name = with_connection { |c| c.default_sequence_name(table_name, primary_key) }
345
383
  end
346
384
 
347
385
  # Sets the name of the sequence to use when generating ids to the given
@@ -366,71 +404,53 @@ module ActiveRecord
366
404
  # Determines if the primary key values should be selected from their
367
405
  # corresponding sequence before the insert statement.
368
406
  def prefetch_primary_key?
369
- connection.prefetch_primary_key?(table_name)
407
+ with_connection { |c| c.prefetch_primary_key?(table_name) }
370
408
  end
371
409
 
372
410
  # Returns the next value that will be used as the primary key on
373
411
  # an insert statement.
374
412
  def next_sequence_value
375
- connection.next_sequence_value(sequence_name)
413
+ with_connection { |c| c.next_sequence_value(sequence_name) }
376
414
  end
377
415
 
378
416
  # Indicates whether the table associated with this class exists
379
417
  def table_exists?
380
- connection.schema_cache.data_source_exists?(table_name)
418
+ schema_cache.data_source_exists?(table_name)
381
419
  end
382
420
 
383
421
  def attributes_builder # :nodoc:
384
- unless defined?(@attributes_builder) && @attributes_builder
422
+ @attributes_builder ||= begin
385
423
  defaults = _default_attributes.except(*(column_names - [primary_key]))
386
- @attributes_builder = ActiveModel::AttributeSet::Builder.new(attribute_types, defaults)
424
+ ActiveModel::AttributeSet::Builder.new(attribute_types, defaults)
387
425
  end
388
- @attributes_builder
389
426
  end
390
427
 
391
428
  def columns_hash # :nodoc:
392
- load_schema
429
+ load_schema unless @columns_hash
393
430
  @columns_hash
394
431
  end
395
432
 
396
433
  def columns
397
- load_schema
434
+ load_schema unless @columns
398
435
  @columns ||= columns_hash.values.freeze
399
436
  end
400
437
 
401
- def attribute_types # :nodoc:
402
- load_schema
403
- @attribute_types ||= Hash.new(Type.default_value)
438
+ def _returning_columns_for_insert(connection) # :nodoc:
439
+ @_returning_columns_for_insert ||= begin
440
+ auto_populated_columns = columns.filter_map do |c|
441
+ c.name if connection.return_value_after_insert?(c)
442
+ end
443
+
444
+ auto_populated_columns.empty? ? Array(primary_key) : auto_populated_columns
445
+ end
404
446
  end
405
447
 
406
448
  def yaml_encoder # :nodoc:
407
449
  @yaml_encoder ||= ActiveModel::AttributeSet::YAMLEncoder.new(attribute_types)
408
450
  end
409
451
 
410
- # Returns the type of the attribute with the given name, after applying
411
- # all modifiers. This method is the only valid source of information for
412
- # anything related to the types of a model's attributes. This method will
413
- # access the database and load the model's schema if it is required.
414
- #
415
- # The return value of this method will implement the interface described
416
- # by ActiveModel::Type::Value (though the object itself may not subclass
417
- # it).
418
- #
419
- # +attr_name+ The name of the attribute to retrieve the type for. Must be
420
- # a string or a symbol.
421
- def type_for_attribute(attr_name, &block)
422
- attr_name = attr_name.to_s
423
- attr_name = attribute_aliases[attr_name] || attr_name
424
-
425
- if block
426
- attribute_types.fetch(attr_name, &block)
427
- else
428
- attribute_types[attr_name]
429
- end
430
- end
431
-
432
452
  # Returns the column object for the named attribute.
433
- # Returns an +ActiveRecord::ConnectionAdapters::NullColumn+ if the
453
+ # Returns an ActiveRecord::ConnectionAdapters::NullColumn if the
434
454
  # named attribute does not exist.
435
455
  #
436
456
  # class Person < ActiveRecord::Base
@@ -456,11 +476,6 @@ module ActiveRecord
456
476
  @column_defaults ||= _default_attributes.deep_dup.to_hash.freeze
457
477
  end
458
478
 
459
- def _default_attributes # :nodoc:
460
- load_schema
461
- @default_attributes ||= ActiveModel::AttributeSet.new({})
462
- end
463
-
464
479
  # Returns an array of column names as strings.
465
480
  def column_names
466
481
  @column_names ||= columns.map(&:name).freeze
@@ -486,9 +501,9 @@ module ActiveRecord
486
501
  #
487
502
  # The most common usage pattern for this method is probably in a migration,
488
503
  # when just after creating a table you want to populate it with some default
489
- # values, eg:
504
+ # values, e.g.:
490
505
  #
491
- # class CreateJobLevels < ActiveRecord::Migration[6.0]
506
+ # class CreateJobLevels < ActiveRecord::Migration[7.2]
492
507
  # def up
493
508
  # create_table :job_levels do |t|
494
509
  # t.integer :id
@@ -508,41 +523,65 @@ module ActiveRecord
508
523
  # end
509
524
  # end
510
525
  def reset_column_information
511
- connection.clear_cache!
526
+ connection_pool.active_connection&.clear_cache!
512
527
  ([self] + descendants).each(&:undefine_attribute_methods)
513
- connection.schema_cache.clear_data_source_cache!(table_name)
528
+ schema_cache.clear_data_source_cache!(table_name)
514
529
 
515
530
  reload_schema_from_cache
516
531
  initialize_find_by_cache
517
532
  end
518
533
 
534
+ def load_schema # :nodoc:
535
+ return if schema_loaded?
536
+ @load_schema_monitor.synchronize do
537
+ return if schema_loaded?
538
+
539
+ load_schema!
540
+
541
+ @schema_loaded = true
542
+ rescue
543
+ reload_schema_from_cache # If the schema loading failed half way through, we must reset the state.
544
+ raise
545
+ end
546
+ end
547
+
519
548
  protected
520
549
  def initialize_load_schema_monitor
521
550
  @load_schema_monitor = Monitor.new
522
551
  end
523
552
 
553
+ def reload_schema_from_cache(recursive = true)
554
+ @_returning_columns_for_insert = nil
555
+ @arel_table = nil
556
+ @column_names = nil
557
+ @symbol_column_to_string_name_hash = nil
558
+ @content_columns = nil
559
+ @column_defaults = nil
560
+ @attributes_builder = nil
561
+ @columns = nil
562
+ @columns_hash = nil
563
+ @schema_loaded = false
564
+ @attribute_names = nil
565
+ @yaml_encoder = nil
566
+ if recursive
567
+ subclasses.each do |descendant|
568
+ descendant.send(:reload_schema_from_cache)
569
+ end
570
+ end
571
+ end
572
+
524
573
  private
525
574
  def inherited(child_class)
526
575
  super
527
576
  child_class.initialize_load_schema_monitor
577
+ child_class.reload_schema_from_cache(false)
578
+ child_class.class_eval do
579
+ @ignored_columns = nil
580
+ end
528
581
  end
529
582
 
530
583
  def schema_loaded?
531
- defined?(@schema_loaded) && @schema_loaded
532
- end
533
-
534
- def load_schema
535
- return if schema_loaded?
536
- @load_schema_monitor.synchronize do
537
- return if defined?(@columns_hash) && @columns_hash
538
-
539
- load_schema!
540
-
541
- @schema_loaded = true
542
- rescue
543
- reload_schema_from_cache # If the schema loading failed half way through, we must reset the state.
544
- raise
545
- end
584
+ @schema_loaded
546
585
  end
547
586
 
548
587
  def load_schema!
@@ -550,45 +589,14 @@ module ActiveRecord
550
589
  raise ActiveRecord::TableNotSpecified, "#{self} has no table configured. Set one with #{self}.table_name="
551
590
  end
552
591
 
553
- columns_hash = connection.schema_cache.columns_hash(table_name)
592
+ columns_hash = schema_cache.columns_hash(table_name)
554
593
  columns_hash = columns_hash.except(*ignored_columns) unless ignored_columns.empty?
555
594
  @columns_hash = columns_hash.freeze
556
- @columns_hash.each do |name, column|
557
- type = connection.lookup_cast_type_from_column(column)
558
- type = _convert_type_from_options(type)
559
- warn_if_deprecated_type(column)
560
- define_attribute(
561
- name,
562
- type,
563
- default: column.default,
564
- user_provided_default: false
565
- )
566
- end
567
- end
568
-
569
- def reload_schema_from_cache
570
- @arel_table = nil
571
- @column_names = nil
572
- @symbol_column_to_string_name_hash = nil
573
- @attribute_types = nil
574
- @content_columns = nil
575
- @default_attributes = nil
576
- @column_defaults = nil
577
- @inheritance_column = nil unless defined?(@explicit_inheritance_column) && @explicit_inheritance_column
578
- @attributes_builder = nil
579
- @columns = nil
580
- @columns_hash = nil
581
- @schema_loaded = false
582
- @attribute_names = nil
583
- @yaml_encoder = nil
584
- direct_descendants.each do |descendant|
585
- descendant.send(:reload_schema_from_cache)
586
- end
587
595
  end
588
596
 
589
597
  # Guesses the table name, but does not decorate it with prefix and suffix information.
590
- def undecorated_table_name(class_name = base_class.name)
591
- table_name = class_name.to_s.demodulize.underscore
598
+ def undecorated_table_name(model_name)
599
+ table_name = model_name.to_s.demodulize.underscore
592
600
  pluralize_table_names ? table_name.pluralize : table_name
593
601
  end
594
602
 
@@ -602,45 +610,21 @@ module ActiveRecord
602
610
  contained += "_"
603
611
  end
604
612
 
605
- "#{full_table_name_prefix}#{contained}#{undecorated_table_name(name)}#{full_table_name_suffix}"
613
+ "#{full_table_name_prefix}#{contained}#{undecorated_table_name(model_name)}#{full_table_name_suffix}"
606
614
  else
607
- # STI subclasses always use their superclass' table.
615
+ # STI subclasses always use their superclass's table.
608
616
  base_class.table_name
609
617
  end
610
618
  end
611
619
 
612
- def _convert_type_from_options(type)
613
- if immutable_strings_by_default && type.respond_to?(:to_immutable_string)
614
- type.to_immutable_string
615
- else
616
- type
617
- end
618
- end
619
-
620
- def warn_if_deprecated_type(column)
621
- return if attributes_to_define_after_schema_loads.key?(column.name)
622
- return unless column.respond_to?(:oid)
620
+ def type_for_column(connection, column)
621
+ type = connection.lookup_cast_type_from_column(column)
623
622
 
624
- if column.array?
625
- array_arguments = ", array: true"
626
- else
627
- array_arguments = ""
623
+ if immutable_strings_by_default && type.respond_to?(:to_immutable_string)
624
+ type = type.to_immutable_string
628
625
  end
629
626
 
630
- if column.sql_type.start_with?("interval")
631
- precision_arguments = column.precision.presence && ", precision: #{column.precision}"
632
- ActiveSupport::Deprecation.warn(<<~WARNING)
633
- The behavior of the `:interval` type will be changing in Rails 7.0
634
- to return an `ActiveSupport::Duration` object. If you'd like to keep
635
- the old behavior, you can add this line to #{self.name} model:
636
-
637
- attribute :#{column.name}, :string#{precision_arguments}#{array_arguments}
638
-
639
- If you'd like the new behavior today, you can add this line:
640
-
641
- attribute :#{column.name}, :interval#{precision_arguments}#{array_arguments}
642
- WARNING
643
- end
627
+ type
644
628
  end
645
629
  end
646
630
  end
@@ -5,7 +5,7 @@ require "active_support/core_ext/module/redefine_method"
5
5
  require "active_support/core_ext/hash/indifferent_access"
6
6
 
7
7
  module ActiveRecord
8
- module NestedAttributes #:nodoc:
8
+ module NestedAttributes # :nodoc:
9
9
  class TooManyRecords < ActiveRecordError
10
10
  end
11
11
 
@@ -15,7 +15,7 @@ module ActiveRecord
15
15
  class_attribute :nested_attributes_options, instance_writer: false, default: {}
16
16
  end
17
17
 
18
- # = Active Record Nested Attributes
18
+ # = Active Record Nested \Attributes
19
19
  #
20
20
  # Nested attributes allow you to save attributes on associated records
21
21
  # through the parent. By default nested attribute updating is turned off
@@ -180,7 +180,7 @@ module ActiveRecord
180
180
  # member.posts.second.title # => '[UPDATED] other post'
181
181
  #
182
182
  # However, the above applies if the parent model is being updated as well.
183
- # For example, If you wanted to create a +member+ named _joe_ and wanted to
183
+ # For example, if you wanted to create a +member+ named _joe_ and wanted to
184
184
  # update the +posts+ at the same time, that would give an
185
185
  # ActiveRecord::RecordNotFound error.
186
186
  #
@@ -245,18 +245,19 @@ module ActiveRecord
245
245
  #
246
246
  # === Validating the presence of a parent model
247
247
  #
248
- # If you want to validate that a child record is associated with a parent
249
- # record, you can use the +validates_presence_of+ method and the +:inverse_of+
250
- # key as this example illustrates:
248
+ # The +belongs_to+ association validates the presence of the parent model
249
+ # by default. You can disable this behavior by specifying <code>optional: true</code>.
250
+ # This can be used, for example, when conditionally validating the presence
251
+ # of the parent model:
251
252
  #
252
- # class Member < ActiveRecord::Base
253
- # has_many :posts, inverse_of: :member
254
- # accepts_nested_attributes_for :posts
253
+ # class Veterinarian < ActiveRecord::Base
254
+ # has_many :patients, inverse_of: :veterinarian
255
+ # accepts_nested_attributes_for :patients
255
256
  # end
256
257
  #
257
- # class Post < ActiveRecord::Base
258
- # belongs_to :member, inverse_of: :posts
259
- # validates_presence_of :member
258
+ # class Patient < ActiveRecord::Base
259
+ # belongs_to :veterinarian, inverse_of: :patients, optional: true
260
+ # validates :veterinarian, presence: true, unless: -> { awaiting_intake }
260
261
  # end
261
262
  #
262
263
  # Note that if you do not specify the +:inverse_of+ option, then
@@ -279,6 +280,24 @@ module ActiveRecord
279
280
  # member = Member.new
280
281
  # member.avatar_attributes = {icon: 'sad'}
281
282
  # member.avatar.width # => 200
283
+ #
284
+ # === Creating forms with nested attributes
285
+ #
286
+ # Use ActionView::Helpers::FormHelper#fields_for to create form elements for
287
+ # nested attributes.
288
+ #
289
+ # Integration test params should reflect the structure of the form. For
290
+ # example:
291
+ #
292
+ # post members_path, params: {
293
+ # member: {
294
+ # name: 'joe',
295
+ # posts_attributes: {
296
+ # '0' => { title: 'Foo' },
297
+ # '1' => { title: 'Bar' }
298
+ # }
299
+ # }
300
+ # }
282
301
  module ClassMethods
283
302
  REJECT_ALL_BLANK_PROC = proc { |attributes| attributes.all? { |key, value| key == "_destroy" || value.blank? } }
284
303
 
@@ -288,7 +307,7 @@ module ActiveRecord
288
307
  # [:allow_destroy]
289
308
  # If true, destroys any members from the attributes hash with a
290
309
  # <tt>_destroy</tt> key and a value that evaluates to +true+
291
- # (e.g. 1, '1', true, or 'true'). This option is off by default.
310
+ # (e.g. 1, '1', true, or 'true'). This option is false by default.
292
311
  # [:reject_if]
293
312
  # Allows you to specify a Proc or a Symbol pointing to a method
294
313
  # that checks whether a record should be built for a certain attribute
@@ -313,11 +332,11 @@ module ActiveRecord
313
332
  # nested attributes are going to be used when an associated record already
314
333
  # exists. In general, an existing record may either be updated with the
315
334
  # new set of attribute values or be replaced by a wholly new record
316
- # containing those values. By default the +:update_only+ option is +false+
335
+ # containing those values. By default the +:update_only+ option is false
317
336
  # and the nested attributes are used to update the existing record only
318
337
  # if they include the record's <tt>:id</tt> value. Otherwise a new
319
338
  # record will be instantiated and used to replace the existing one.
320
- # However if the +:update_only+ option is +true+, the nested attributes
339
+ # However if the +:update_only+ option is true, the nested attributes
321
340
  # are used to update the record's attributes always, regardless of
322
341
  # whether the <tt>:id</tt> is present. The option is ignored for collection
323
342
  # associations.
@@ -374,11 +393,11 @@ module ActiveRecord
374
393
  end
375
394
  end
376
395
 
377
- # Returns ActiveRecord::AutosaveAssociation::marked_for_destruction? It's
396
+ # Returns ActiveRecord::AutosaveAssociation#marked_for_destruction? It's
378
397
  # used in conjunction with fields_for to build a form element for the
379
398
  # destruction of this association.
380
399
  #
381
- # See ActionView::Helpers::FormHelper::fields_for for more info.
400
+ # See ActionView::Helpers::FormHelper#fields_for for more info.
382
401
  def _destroy
383
402
  marked_for_destruction?
384
403
  end
@@ -402,10 +421,15 @@ module ActiveRecord
402
421
  # update_only is true, and a <tt>:_destroy</tt> key set to a truthy value,
403
422
  # then the existing record will be marked for destruction.
404
423
  def assign_nested_attributes_for_one_to_one_association(association_name, attributes)
405
- options = nested_attributes_options[association_name]
406
424
  if attributes.respond_to?(:permitted?)
407
425
  attributes = attributes.to_h
408
426
  end
427
+
428
+ unless attributes.is_a?(Hash)
429
+ raise ArgumentError, "Hash expected for `#{association_name}` attributes, got #{attributes.class.name}"
430
+ end
431
+
432
+ options = nested_attributes_options[association_name]
409
433
  attributes = attributes.with_indifferent_access
410
434
  existing_record = send(association_name)
411
435
 
@@ -467,7 +491,7 @@ module ActiveRecord
467
491
  end
468
492
 
469
493
  unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array)
470
- raise ArgumentError, "Hash or Array expected for attribute `#{association_name}`, got #{attributes_collection.class.name} (#{attributes_collection.inspect})"
494
+ raise ArgumentError, "Hash or Array expected for `#{association_name}` attributes, got #{attributes_collection.class.name}"
471
495
  end
472
496
 
473
497
  check_record_limit!(options[:limit], attributes_collection)
@@ -486,11 +510,11 @@ module ActiveRecord
486
510
  existing_records = if association.loaded?
487
511
  association.target
488
512
  else
489
- attribute_ids = attributes_collection.map { |a| a["id"] || a[:id] }.compact
513
+ attribute_ids = attributes_collection.filter_map { |a| a["id"] || a[:id] }
490
514
  attribute_ids.empty? ? [] : association.scope.where(association.klass.primary_key => attribute_ids)
491
515
  end
492
516
 
493
- attributes_collection.each do |attributes|
517
+ records = attributes_collection.map do |attributes|
494
518
  if attributes.respond_to?(:permitted?)
495
519
  attributes = attributes.to_h
496
520
  end
@@ -513,11 +537,14 @@ module ActiveRecord
513
537
  end
514
538
 
515
539
  assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
540
+ existing_record
516
541
  end
517
542
  else
518
543
  raise_nested_attributes_record_not_found!(association_name, attributes["id"])
519
544
  end
520
545
  end
546
+
547
+ association.nested_attributes_target = records
521
548
  end
522
549
 
523
550
  # Takes in a limit and checks if the attributes_collection has too many
@@ -26,20 +26,20 @@ module ActiveRecord
26
26
  end
27
27
 
28
28
  class << self
29
- def apply_to(klass) #:nodoc:
29
+ def apply_to(klass) # :nodoc:
30
30
  klasses.push(klass)
31
31
  yield
32
32
  ensure
33
33
  klasses.pop
34
34
  end
35
35
 
36
- def applied_to?(klass) #:nodoc:
36
+ def applied_to?(klass) # :nodoc:
37
37
  klasses.any? { |k| k >= klass }
38
38
  end
39
39
 
40
40
  private
41
41
  def klasses
42
- Thread.current[:no_touching_classes] ||= []
42
+ ActiveSupport::IsolatedExecutionState[:active_record_no_touching_classes] ||= []
43
43
  end
44
44
  end
45
45