activerecord 5.0.7.2 → 6.1.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (363) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +829 -2015
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +11 -9
  5. data/examples/performance.rb +31 -29
  6. data/examples/simple.rb +5 -3
  7. data/lib/active_record.rb +37 -29
  8. data/lib/active_record/aggregations.rb +249 -247
  9. data/lib/active_record/association_relation.rb +30 -18
  10. data/lib/active_record/associations.rb +1714 -1596
  11. data/lib/active_record/associations/alias_tracker.rb +36 -42
  12. data/lib/active_record/associations/association.rb +143 -68
  13. data/lib/active_record/associations/association_scope.rb +98 -94
  14. data/lib/active_record/associations/belongs_to_association.rb +76 -46
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -12
  16. data/lib/active_record/associations/builder/association.rb +27 -28
  17. data/lib/active_record/associations/builder/belongs_to.rb +52 -60
  18. data/lib/active_record/associations/builder/collection_association.rb +12 -22
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +40 -62
  20. data/lib/active_record/associations/builder/has_many.rb +10 -2
  21. data/lib/active_record/associations/builder/has_one.rb +35 -2
  22. data/lib/active_record/associations/builder/singular_association.rb +5 -1
  23. data/lib/active_record/associations/collection_association.rb +104 -259
  24. data/lib/active_record/associations/collection_proxy.rb +169 -125
  25. data/lib/active_record/associations/foreign_association.rb +22 -0
  26. data/lib/active_record/associations/has_many_association.rb +46 -31
  27. data/lib/active_record/associations/has_many_through_association.rb +66 -46
  28. data/lib/active_record/associations/has_one_association.rb +71 -52
  29. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  30. data/lib/active_record/associations/join_dependency.rb +169 -180
  31. data/lib/active_record/associations/join_dependency/join_association.rb +53 -79
  32. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  33. data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
  34. data/lib/active_record/associations/preloader.rb +97 -104
  35. data/lib/active_record/associations/preloader/association.rb +109 -97
  36. data/lib/active_record/associations/preloader/through_association.rb +77 -76
  37. data/lib/active_record/associations/singular_association.rb +12 -45
  38. data/lib/active_record/associations/through_association.rb +27 -15
  39. data/lib/active_record/attribute_assignment.rb +55 -60
  40. data/lib/active_record/attribute_methods.rb +111 -141
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -9
  42. data/lib/active_record/attribute_methods/dirty.rb +172 -112
  43. data/lib/active_record/attribute_methods/primary_key.rb +88 -91
  44. data/lib/active_record/attribute_methods/query.rb +6 -8
  45. data/lib/active_record/attribute_methods/read.rb +18 -50
  46. data/lib/active_record/attribute_methods/serialization.rb +38 -10
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -66
  48. data/lib/active_record/attribute_methods/write.rb +25 -32
  49. data/lib/active_record/attributes.rb +69 -31
  50. data/lib/active_record/autosave_association.rb +102 -66
  51. data/lib/active_record/base.rb +16 -25
  52. data/lib/active_record/callbacks.rb +202 -43
  53. data/lib/active_record/coders/json.rb +2 -0
  54. data/lib/active_record/coders/yaml_column.rb +11 -12
  55. data/lib/active_record/connection_adapters.rb +50 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +661 -375
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +14 -38
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +269 -105
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +54 -35
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +137 -93
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +155 -113
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +328 -162
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +68 -80
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +591 -259
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +229 -91
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +392 -244
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +457 -582
  69. data/lib/active_record/connection_adapters/column.rb +55 -13
  70. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  71. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +8 -31
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +135 -49
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +24 -23
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -20
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +79 -49
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +66 -56
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +70 -36
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +268 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +20 -12
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +74 -37
  82. data/lib/active_record/connection_adapters/pool_config.rb +63 -0
  83. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  84. data/lib/active_record/connection_adapters/postgresql/column.rb +39 -28
  85. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +70 -101
  86. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +5 -3
  87. data/lib/active_record/connection_adapters/postgresql/oid.rb +26 -21
  88. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +22 -11
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +6 -5
  90. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -6
  93. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +14 -4
  95. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  96. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -4
  97. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +19 -18
  98. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  101. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -5
  104. data/lib/active_record/connection_adapters/postgresql/oid/{json.rb → oid.rb} +6 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +30 -9
  106. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -30
  107. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
  108. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
  109. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +18 -4
  110. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
  111. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
  112. data/lib/active_record/connection_adapters/postgresql/quoting.rb +98 -38
  113. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +21 -27
  114. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +80 -0
  115. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +147 -105
  116. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +34 -32
  117. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +426 -324
  118. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +32 -23
  119. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -6
  120. data/lib/active_record/connection_adapters/postgresql_adapter.rb +418 -293
  121. data/lib/active_record/connection_adapters/schema_cache.rb +135 -18
  122. data/lib/active_record/connection_adapters/sql_type_metadata.rb +22 -7
  123. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
  124. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +3 -1
  125. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +72 -18
  126. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -6
  127. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  128. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  129. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +170 -0
  130. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +282 -290
  131. data/lib/active_record/connection_adapters/statement_pool.rb +9 -8
  132. data/lib/active_record/connection_handling.rb +287 -45
  133. data/lib/active_record/core.rb +385 -181
  134. data/lib/active_record/counter_cache.rb +60 -28
  135. data/lib/active_record/database_configurations.rb +272 -0
  136. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  137. data/lib/active_record/database_configurations/database_config.rb +80 -0
  138. data/lib/active_record/database_configurations/hash_config.rb +96 -0
  139. data/lib/active_record/database_configurations/url_config.rb +53 -0
  140. data/lib/active_record/delegated_type.rb +209 -0
  141. data/lib/active_record/destroy_association_async_job.rb +36 -0
  142. data/lib/active_record/dynamic_matchers.rb +87 -87
  143. data/lib/active_record/enum.rb +122 -47
  144. data/lib/active_record/errors.rb +153 -22
  145. data/lib/active_record/explain.rb +13 -8
  146. data/lib/active_record/explain_registry.rb +3 -1
  147. data/lib/active_record/explain_subscriber.rb +9 -4
  148. data/lib/active_record/fixture_set/file.rb +20 -22
  149. data/lib/active_record/fixture_set/model_metadata.rb +32 -0
  150. data/lib/active_record/fixture_set/render_context.rb +17 -0
  151. data/lib/active_record/fixture_set/table_row.rb +152 -0
  152. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  153. data/lib/active_record/fixtures.rb +246 -507
  154. data/lib/active_record/gem_version.rb +6 -4
  155. data/lib/active_record/inheritance.rb +168 -95
  156. data/lib/active_record/insert_all.rb +208 -0
  157. data/lib/active_record/integration.rb +114 -25
  158. data/lib/active_record/internal_metadata.rb +30 -24
  159. data/lib/active_record/legacy_yaml_adapter.rb +11 -5
  160. data/lib/active_record/locking/optimistic.rb +81 -85
  161. data/lib/active_record/locking/pessimistic.rb +22 -6
  162. data/lib/active_record/log_subscriber.rb +68 -31
  163. data/lib/active_record/middleware/database_selector.rb +77 -0
  164. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  165. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  166. data/lib/active_record/migration.rb +439 -342
  167. data/lib/active_record/migration/command_recorder.rb +152 -98
  168. data/lib/active_record/migration/compatibility.rb +229 -60
  169. data/lib/active_record/migration/join_table.rb +8 -7
  170. data/lib/active_record/model_schema.rb +230 -122
  171. data/lib/active_record/nested_attributes.rb +213 -203
  172. data/lib/active_record/no_touching.rb +11 -2
  173. data/lib/active_record/null_relation.rb +12 -34
  174. data/lib/active_record/persistence.rb +471 -97
  175. data/lib/active_record/query_cache.rb +23 -12
  176. data/lib/active_record/querying.rb +43 -25
  177. data/lib/active_record/railtie.rb +155 -43
  178. data/lib/active_record/railties/console_sandbox.rb +2 -0
  179. data/lib/active_record/railties/controller_runtime.rb +34 -33
  180. data/lib/active_record/railties/databases.rake +507 -195
  181. data/lib/active_record/readonly_attributes.rb +9 -4
  182. data/lib/active_record/reflection.rb +245 -269
  183. data/lib/active_record/relation.rb +475 -324
  184. data/lib/active_record/relation/batches.rb +125 -72
  185. data/lib/active_record/relation/batches/batch_enumerator.rb +28 -10
  186. data/lib/active_record/relation/calculations.rb +267 -171
  187. data/lib/active_record/relation/delegation.rb +73 -69
  188. data/lib/active_record/relation/finder_methods.rb +238 -248
  189. data/lib/active_record/relation/from_clause.rb +7 -9
  190. data/lib/active_record/relation/merger.rb +95 -77
  191. data/lib/active_record/relation/predicate_builder.rb +109 -110
  192. data/lib/active_record/relation/predicate_builder/array_handler.rb +22 -17
  193. data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -0
  194. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +6 -4
  195. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +55 -0
  196. data/lib/active_record/relation/predicate_builder/range_handler.rb +7 -18
  197. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  198. data/lib/active_record/relation/query_attribute.rb +33 -2
  199. data/lib/active_record/relation/query_methods.rb +654 -374
  200. data/lib/active_record/relation/record_fetch_warning.rb +8 -6
  201. data/lib/active_record/relation/spawn_methods.rb +15 -14
  202. data/lib/active_record/relation/where_clause.rb +171 -109
  203. data/lib/active_record/result.rb +88 -51
  204. data/lib/active_record/runtime_registry.rb +5 -3
  205. data/lib/active_record/sanitization.rb +73 -100
  206. data/lib/active_record/schema.rb +7 -14
  207. data/lib/active_record/schema_dumper.rb +101 -69
  208. data/lib/active_record/schema_migration.rb +16 -12
  209. data/lib/active_record/scoping.rb +20 -20
  210. data/lib/active_record/scoping/default.rb +92 -95
  211. data/lib/active_record/scoping/named.rb +39 -30
  212. data/lib/active_record/secure_token.rb +19 -9
  213. data/lib/active_record/serialization.rb +7 -3
  214. data/lib/active_record/signed_id.rb +116 -0
  215. data/lib/active_record/statement_cache.rb +80 -29
  216. data/lib/active_record/store.rb +122 -42
  217. data/lib/active_record/suppressor.rb +6 -3
  218. data/lib/active_record/table_metadata.rb +51 -39
  219. data/lib/active_record/tasks/database_tasks.rb +332 -115
  220. data/lib/active_record/tasks/mysql_database_tasks.rb +66 -104
  221. data/lib/active_record/tasks/postgresql_database_tasks.rb +84 -56
  222. data/lib/active_record/tasks/sqlite_database_tasks.rb +40 -19
  223. data/lib/active_record/test_databases.rb +24 -0
  224. data/lib/active_record/test_fixtures.rb +246 -0
  225. data/lib/active_record/timestamp.rb +70 -38
  226. data/lib/active_record/touch_later.rb +26 -24
  227. data/lib/active_record/transactions.rb +121 -184
  228. data/lib/active_record/translation.rb +3 -1
  229. data/lib/active_record/type.rb +29 -17
  230. data/lib/active_record/type/adapter_specific_registry.rb +44 -48
  231. data/lib/active_record/type/date.rb +2 -0
  232. data/lib/active_record/type/date_time.rb +2 -0
  233. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  234. data/lib/active_record/type/hash_lookup_type_map.rb +5 -4
  235. data/lib/active_record/type/internal/timezone.rb +2 -0
  236. data/lib/active_record/type/json.rb +30 -0
  237. data/lib/active_record/type/serialized.rb +20 -9
  238. data/lib/active_record/type/text.rb +11 -0
  239. data/lib/active_record/type/time.rb +12 -1
  240. data/lib/active_record/type/type_map.rb +14 -17
  241. data/lib/active_record/type/unsigned_integer.rb +16 -0
  242. data/lib/active_record/type_caster.rb +4 -2
  243. data/lib/active_record/type_caster/connection.rb +17 -13
  244. data/lib/active_record/type_caster/map.rb +10 -6
  245. data/lib/active_record/validations.rb +8 -5
  246. data/lib/active_record/validations/absence.rb +2 -0
  247. data/lib/active_record/validations/associated.rb +4 -3
  248. data/lib/active_record/validations/length.rb +2 -0
  249. data/lib/active_record/validations/numericality.rb +35 -0
  250. data/lib/active_record/validations/presence.rb +4 -2
  251. data/lib/active_record/validations/uniqueness.rb +52 -45
  252. data/lib/active_record/version.rb +3 -1
  253. data/lib/arel.rb +54 -0
  254. data/lib/arel/alias_predication.rb +9 -0
  255. data/lib/arel/attributes/attribute.rb +41 -0
  256. data/lib/arel/collectors/bind.rb +29 -0
  257. data/lib/arel/collectors/composite.rb +39 -0
  258. data/lib/arel/collectors/plain_string.rb +20 -0
  259. data/lib/arel/collectors/sql_string.rb +27 -0
  260. data/lib/arel/collectors/substitute_binds.rb +35 -0
  261. data/lib/arel/crud.rb +42 -0
  262. data/lib/arel/delete_manager.rb +18 -0
  263. data/lib/arel/errors.rb +9 -0
  264. data/lib/arel/expressions.rb +29 -0
  265. data/lib/arel/factory_methods.rb +49 -0
  266. data/lib/arel/insert_manager.rb +49 -0
  267. data/lib/arel/math.rb +45 -0
  268. data/lib/arel/nodes.rb +70 -0
  269. data/lib/arel/nodes/and.rb +32 -0
  270. data/lib/arel/nodes/ascending.rb +23 -0
  271. data/lib/arel/nodes/binary.rb +126 -0
  272. data/lib/arel/nodes/bind_param.rb +44 -0
  273. data/lib/arel/nodes/case.rb +55 -0
  274. data/lib/arel/nodes/casted.rb +62 -0
  275. data/lib/arel/nodes/comment.rb +29 -0
  276. data/lib/arel/nodes/count.rb +12 -0
  277. data/lib/arel/nodes/delete_statement.rb +45 -0
  278. data/lib/arel/nodes/descending.rb +23 -0
  279. data/lib/arel/nodes/equality.rb +15 -0
  280. data/lib/arel/nodes/extract.rb +24 -0
  281. data/lib/arel/nodes/false.rb +16 -0
  282. data/lib/arel/nodes/full_outer_join.rb +8 -0
  283. data/lib/arel/nodes/function.rb +44 -0
  284. data/lib/arel/nodes/grouping.rb +11 -0
  285. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  286. data/lib/arel/nodes/in.rb +15 -0
  287. data/lib/arel/nodes/infix_operation.rb +92 -0
  288. data/lib/arel/nodes/inner_join.rb +8 -0
  289. data/lib/arel/nodes/insert_statement.rb +37 -0
  290. data/lib/arel/nodes/join_source.rb +20 -0
  291. data/lib/arel/nodes/matches.rb +18 -0
  292. data/lib/arel/nodes/named_function.rb +23 -0
  293. data/lib/arel/nodes/node.rb +51 -0
  294. data/lib/arel/nodes/node_expression.rb +13 -0
  295. data/lib/arel/nodes/ordering.rb +27 -0
  296. data/lib/arel/nodes/outer_join.rb +8 -0
  297. data/lib/arel/nodes/over.rb +15 -0
  298. data/lib/arel/nodes/regexp.rb +16 -0
  299. data/lib/arel/nodes/right_outer_join.rb +8 -0
  300. data/lib/arel/nodes/select_core.rb +67 -0
  301. data/lib/arel/nodes/select_statement.rb +41 -0
  302. data/lib/arel/nodes/sql_literal.rb +19 -0
  303. data/lib/arel/nodes/string_join.rb +11 -0
  304. data/lib/arel/nodes/table_alias.rb +31 -0
  305. data/lib/arel/nodes/terminal.rb +16 -0
  306. data/lib/arel/nodes/true.rb +16 -0
  307. data/lib/arel/nodes/unary.rb +44 -0
  308. data/lib/arel/nodes/unary_operation.rb +20 -0
  309. data/lib/arel/nodes/unqualified_column.rb +22 -0
  310. data/lib/arel/nodes/update_statement.rb +41 -0
  311. data/lib/arel/nodes/values_list.rb +9 -0
  312. data/lib/arel/nodes/window.rb +126 -0
  313. data/lib/arel/nodes/with.rb +11 -0
  314. data/lib/arel/order_predications.rb +13 -0
  315. data/lib/arel/predications.rb +250 -0
  316. data/lib/arel/select_manager.rb +270 -0
  317. data/lib/arel/table.rb +118 -0
  318. data/lib/arel/tree_manager.rb +72 -0
  319. data/lib/arel/update_manager.rb +34 -0
  320. data/lib/arel/visitors.rb +13 -0
  321. data/lib/arel/visitors/dot.rb +308 -0
  322. data/lib/arel/visitors/mysql.rb +93 -0
  323. data/lib/arel/visitors/postgresql.rb +120 -0
  324. data/lib/arel/visitors/sqlite.rb +38 -0
  325. data/lib/arel/visitors/to_sql.rb +899 -0
  326. data/lib/arel/visitors/visitor.rb +45 -0
  327. data/lib/arel/window_predications.rb +9 -0
  328. data/lib/rails/generators/active_record.rb +7 -5
  329. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
  330. data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
  331. data/lib/rails/generators/active_record/migration.rb +22 -3
  332. data/lib/rails/generators/active_record/migration/migration_generator.rb +38 -35
  333. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +3 -1
  334. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +7 -5
  335. data/lib/rails/generators/active_record/model/model_generator.rb +41 -25
  336. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  337. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +10 -1
  338. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  339. metadata +141 -57
  340. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  341. data/lib/active_record/associations/preloader/collection_association.rb +0 -17
  342. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  343. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  344. data/lib/active_record/associations/preloader/has_one.rb +0 -15
  345. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  346. data/lib/active_record/associations/preloader/singular_association.rb +0 -20
  347. data/lib/active_record/attribute.rb +0 -213
  348. data/lib/active_record/attribute/user_provided_default.rb +0 -28
  349. data/lib/active_record/attribute_decorators.rb +0 -67
  350. data/lib/active_record/attribute_mutation_tracker.rb +0 -70
  351. data/lib/active_record/attribute_set.rb +0 -110
  352. data/lib/active_record/attribute_set/builder.rb +0 -132
  353. data/lib/active_record/collection_cache_key.rb +0 -50
  354. data/lib/active_record/connection_adapters/connection_specification.rb +0 -263
  355. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -22
  356. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +0 -50
  357. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  358. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
  359. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -17
  360. data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
  361. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -57
  362. data/lib/active_record/relation/where_clause_factory.rb +0 -38
  363. data/lib/active_record/type/internal/abstract_json.rb +0 -33
@@ -1,6 +1,8 @@
1
- require 'active_support/core_ext/hash/except'
2
- require 'active_support/core_ext/object/try'
3
- require 'active_support/core_ext/hash/indifferent_access'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/hash/except"
4
+ require "active_support/core_ext/module/redefine_method"
5
+ require "active_support/core_ext/hash/indifferent_access"
4
6
 
5
7
  module ActiveRecord
6
8
  module NestedAttributes #:nodoc:
@@ -10,8 +12,7 @@ module ActiveRecord
10
12
  extend ActiveSupport::Concern
11
13
 
12
14
  included do
13
- class_attribute :nested_attributes_options, instance_writer: false
14
- self.nested_attributes_options = {}
15
+ class_attribute :nested_attributes_options, instance_writer: false, default: {}
15
16
  end
16
17
 
17
18
  # = Active Record Nested Attributes
@@ -61,6 +62,18 @@ module ActiveRecord
61
62
  # member.update params[:member]
62
63
  # member.avatar.icon # => 'sad'
63
64
  #
65
+ # If you want to update the current avatar without providing the id, you must add <tt>:update_only</tt> option.
66
+ #
67
+ # class Member < ActiveRecord::Base
68
+ # has_one :avatar
69
+ # accepts_nested_attributes_for :avatar, update_only: true
70
+ # end
71
+ #
72
+ # params = { member: { avatar_attributes: { icon: 'sad' } } }
73
+ # member.update params[:member]
74
+ # member.avatar.id # => 2
75
+ # member.avatar.icon # => 'sad'
76
+ #
64
77
  # By default you will only be able to set and update attributes on the
65
78
  # associated model. If you want to destroy the associated model through the
66
79
  # attributes hash, you have to enable it first using the
@@ -267,7 +280,7 @@ module ActiveRecord
267
280
  # member.avatar_attributes = {icon: 'sad'}
268
281
  # member.avatar.width # => 200
269
282
  module ClassMethods
270
- REJECT_ALL_BLANK_PROC = proc { |attributes| attributes.all? { |key, value| key == '_destroy' || value.blank? } }
283
+ REJECT_ALL_BLANK_PROC = proc { |attributes| attributes.all? { |key, value| key == "_destroy" || value.blank? } }
271
284
 
272
285
  # Defines an attributes writer for the specified association(s).
273
286
  #
@@ -275,7 +288,7 @@ module ActiveRecord
275
288
  # [:allow_destroy]
276
289
  # If true, destroys any members from the attributes hash with a
277
290
  # <tt>_destroy</tt> key and a value that evaluates to +true+
278
- # (eg. 1, '1', true, or 'true'). This option is off by default.
291
+ # (e.g. 1, '1', true, or 'true'). This option is off by default.
279
292
  # [:reject_if]
280
293
  # Allows you to specify a Proc or a Symbol pointing to a method
281
294
  # that checks whether a record should be built for a certain attribute
@@ -317,7 +330,7 @@ module ActiveRecord
317
330
  # # creates avatar_attributes= and posts_attributes=
318
331
  # accepts_nested_attributes_for :avatar, :posts, allow_destroy: true
319
332
  def accepts_nested_attributes_for(*attr_names)
320
- options = { :allow_destroy => false, :update_only => false }
333
+ options = { allow_destroy: false, update_only: false }
321
334
  options.update(attr_names.extract_options!)
322
335
  options.assert_valid_keys(:allow_destroy, :reject_if, :limit, :update_only)
323
336
  options[:reject_if] = REJECT_ALL_BLANK_PROC if options[:reject_if] == :all_blank
@@ -340,28 +353,25 @@ module ActiveRecord
340
353
  end
341
354
 
342
355
  private
343
-
344
- # Generates a writer method for this association. Serves as a point for
345
- # accessing the objects in the association. For example, this method
346
- # could generate the following:
347
- #
348
- # def pirate_attributes=(attributes)
349
- # assign_nested_attributes_for_one_to_one_association(:pirate, attributes)
350
- # end
351
- #
352
- # This redirects the attempts to write objects in an association through
353
- # the helper methods defined below. Makes it seem like the nested
354
- # associations are just regular associations.
355
- def generate_association_writer(association_name, type)
356
- generated_association_methods.module_eval <<-eoruby, __FILE__, __LINE__ + 1
357
- if method_defined?(:#{association_name}_attributes=)
358
- remove_method(:#{association_name}_attributes=)
359
- end
360
- def #{association_name}_attributes=(attributes)
361
- assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes)
362
- end
363
- eoruby
364
- end
356
+ # Generates a writer method for this association. Serves as a point for
357
+ # accessing the objects in the association. For example, this method
358
+ # could generate the following:
359
+ #
360
+ # def pirate_attributes=(attributes)
361
+ # assign_nested_attributes_for_one_to_one_association(:pirate, attributes)
362
+ # end
363
+ #
364
+ # This redirects the attempts to write objects in an association through
365
+ # the helper methods defined below. Makes it seem like the nested
366
+ # associations are just regular associations.
367
+ def generate_association_writer(association_name, type)
368
+ generated_association_methods.module_eval <<-eoruby, __FILE__, __LINE__ + 1
369
+ silence_redefinition_of_method :#{association_name}_attributes=
370
+ def #{association_name}_attributes=(attributes)
371
+ assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes)
372
+ end
373
+ eoruby
374
+ end
365
375
  end
366
376
 
367
377
  # Returns ActiveRecord::AutosaveAssociation::marked_for_destruction? It's
@@ -374,214 +384,214 @@ module ActiveRecord
374
384
  end
375
385
 
376
386
  private
387
+ # Attribute hash keys that should not be assigned as normal attributes.
388
+ # These hash keys are nested attributes implementation details.
389
+ UNASSIGNABLE_KEYS = %w( id _destroy )
377
390
 
378
- # Attribute hash keys that should not be assigned as normal attributes.
379
- # These hash keys are nested attributes implementation details.
380
- UNASSIGNABLE_KEYS = %w( id _destroy )
381
-
382
- # Assigns the given attributes to the association.
383
- #
384
- # If an associated record does not yet exist, one will be instantiated. If
385
- # an associated record already exists, the method's behavior depends on
386
- # the value of the update_only option. If update_only is +false+ and the
387
- # given attributes include an <tt>:id</tt> that matches the existing record's
388
- # id, then the existing record will be modified. If no <tt>:id</tt> is provided
389
- # it will be replaced with a new record. If update_only is +true+ the existing
390
- # record will be modified regardless of whether an <tt>:id</tt> is provided.
391
- #
392
- # If the given attributes include a matching <tt>:id</tt> attribute, or
393
- # update_only is true, and a <tt>:_destroy</tt> key set to a truthy value,
394
- # then the existing record will be marked for destruction.
395
- def assign_nested_attributes_for_one_to_one_association(association_name, attributes)
396
- options = self.nested_attributes_options[association_name]
397
- if attributes.respond_to?(:permitted?)
398
- attributes = attributes.to_h
399
- end
400
- attributes = attributes.with_indifferent_access
401
- existing_record = send(association_name)
391
+ # Assigns the given attributes to the association.
392
+ #
393
+ # If an associated record does not yet exist, one will be instantiated. If
394
+ # an associated record already exists, the method's behavior depends on
395
+ # the value of the update_only option. If update_only is +false+ and the
396
+ # given attributes include an <tt>:id</tt> that matches the existing record's
397
+ # id, then the existing record will be modified. If no <tt>:id</tt> is provided
398
+ # it will be replaced with a new record. If update_only is +true+ the existing
399
+ # record will be modified regardless of whether an <tt>:id</tt> is provided.
400
+ #
401
+ # If the given attributes include a matching <tt>:id</tt> attribute, or
402
+ # update_only is true, and a <tt>:_destroy</tt> key set to a truthy value,
403
+ # then the existing record will be marked for destruction.
404
+ def assign_nested_attributes_for_one_to_one_association(association_name, attributes)
405
+ options = nested_attributes_options[association_name]
406
+ if attributes.respond_to?(:permitted?)
407
+ attributes = attributes.to_h
408
+ end
409
+ attributes = attributes.with_indifferent_access
410
+ existing_record = send(association_name)
402
411
 
403
- if (options[:update_only] || !attributes['id'].blank?) && existing_record &&
404
- (options[:update_only] || existing_record.id.to_s == attributes['id'].to_s)
405
- assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy]) unless call_reject_if(association_name, attributes)
412
+ if (options[:update_only] || !attributes["id"].blank?) && existing_record &&
413
+ (options[:update_only] || existing_record.id.to_s == attributes["id"].to_s)
414
+ assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy]) unless call_reject_if(association_name, attributes)
406
415
 
407
- elsif attributes['id'].present?
408
- raise_nested_attributes_record_not_found!(association_name, attributes['id'])
416
+ elsif attributes["id"].present?
417
+ raise_nested_attributes_record_not_found!(association_name, attributes["id"])
409
418
 
410
- elsif !reject_new_record?(association_name, attributes)
411
- assignable_attributes = attributes.except(*UNASSIGNABLE_KEYS)
419
+ elsif !reject_new_record?(association_name, attributes)
420
+ assignable_attributes = attributes.except(*UNASSIGNABLE_KEYS)
412
421
 
413
- if existing_record && existing_record.new_record?
414
- existing_record.assign_attributes(assignable_attributes)
415
- association(association_name).initialize_attributes(existing_record)
416
- else
417
- method = "build_#{association_name}"
418
- if respond_to?(method)
419
- send(method, assignable_attributes)
422
+ if existing_record && existing_record.new_record?
423
+ existing_record.assign_attributes(assignable_attributes)
424
+ association(association_name).initialize_attributes(existing_record)
420
425
  else
421
- raise ArgumentError, "Cannot build association `#{association_name}'. Are you trying to build a polymorphic one-to-one association?"
426
+ method = :"build_#{association_name}"
427
+ if respond_to?(method)
428
+ send(method, assignable_attributes)
429
+ else
430
+ raise ArgumentError, "Cannot build association `#{association_name}'. Are you trying to build a polymorphic one-to-one association?"
431
+ end
422
432
  end
423
433
  end
424
434
  end
425
- end
426
435
 
427
- # Assigns the given attributes to the collection association.
428
- #
429
- # Hashes with an <tt>:id</tt> value matching an existing associated record
430
- # will update that record. Hashes without an <tt>:id</tt> value will build
431
- # a new record for the association. Hashes with a matching <tt>:id</tt>
432
- # value and a <tt>:_destroy</tt> key set to a truthy value will mark the
433
- # matched record for destruction.
434
- #
435
- # For example:
436
- #
437
- # assign_nested_attributes_for_collection_association(:people, {
438
- # '1' => { id: '1', name: 'Peter' },
439
- # '2' => { name: 'John' },
440
- # '3' => { id: '2', _destroy: true }
441
- # })
442
- #
443
- # Will update the name of the Person with ID 1, build a new associated
444
- # person with the name 'John', and mark the associated Person with ID 2
445
- # for destruction.
446
- #
447
- # Also accepts an Array of attribute hashes:
448
- #
449
- # assign_nested_attributes_for_collection_association(:people, [
450
- # { id: '1', name: 'Peter' },
451
- # { name: 'John' },
452
- # { id: '2', _destroy: true }
453
- # ])
454
- def assign_nested_attributes_for_collection_association(association_name, attributes_collection)
455
- options = self.nested_attributes_options[association_name]
456
- if attributes_collection.respond_to?(:permitted?)
457
- attributes_collection = attributes_collection.to_h
458
- end
436
+ # Assigns the given attributes to the collection association.
437
+ #
438
+ # Hashes with an <tt>:id</tt> value matching an existing associated record
439
+ # will update that record. Hashes without an <tt>:id</tt> value will build
440
+ # a new record for the association. Hashes with a matching <tt>:id</tt>
441
+ # value and a <tt>:_destroy</tt> key set to a truthy value will mark the
442
+ # matched record for destruction.
443
+ #
444
+ # For example:
445
+ #
446
+ # assign_nested_attributes_for_collection_association(:people, {
447
+ # '1' => { id: '1', name: 'Peter' },
448
+ # '2' => { name: 'John' },
449
+ # '3' => { id: '2', _destroy: true }
450
+ # })
451
+ #
452
+ # Will update the name of the Person with ID 1, build a new associated
453
+ # person with the name 'John', and mark the associated Person with ID 2
454
+ # for destruction.
455
+ #
456
+ # Also accepts an Array of attribute hashes:
457
+ #
458
+ # assign_nested_attributes_for_collection_association(:people, [
459
+ # { id: '1', name: 'Peter' },
460
+ # { name: 'John' },
461
+ # { id: '2', _destroy: true }
462
+ # ])
463
+ def assign_nested_attributes_for_collection_association(association_name, attributes_collection)
464
+ options = nested_attributes_options[association_name]
465
+ if attributes_collection.respond_to?(:permitted?)
466
+ attributes_collection = attributes_collection.to_h
467
+ end
459
468
 
460
- unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array)
461
- raise ArgumentError, "Hash or Array expected, got #{attributes_collection.class.name} (#{attributes_collection.inspect})"
462
- end
469
+ 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})"
471
+ end
463
472
 
464
- check_record_limit!(options[:limit], attributes_collection)
473
+ check_record_limit!(options[:limit], attributes_collection)
465
474
 
466
- if attributes_collection.is_a? Hash
467
- keys = attributes_collection.keys
468
- attributes_collection = if keys.include?('id') || keys.include?(:id)
469
- [attributes_collection]
470
- else
471
- attributes_collection.values
475
+ if attributes_collection.is_a? Hash
476
+ keys = attributes_collection.keys
477
+ attributes_collection = if keys.include?("id") || keys.include?(:id)
478
+ [attributes_collection]
479
+ else
480
+ attributes_collection.values
481
+ end
472
482
  end
473
- end
474
-
475
- association = association(association_name)
476
483
 
477
- existing_records = if association.loaded?
478
- association.target
479
- else
480
- attribute_ids = attributes_collection.map {|a| a['id'] || a[:id] }.compact
481
- attribute_ids.empty? ? [] : association.scope.where(association.klass.primary_key => attribute_ids)
482
- end
484
+ association = association(association_name)
483
485
 
484
- attributes_collection.each do |attributes|
485
- if attributes.respond_to?(:permitted?)
486
- attributes = attributes.to_h
486
+ existing_records = if association.loaded?
487
+ association.target
488
+ else
489
+ attribute_ids = attributes_collection.map { |a| a["id"] || a[:id] }.compact
490
+ attribute_ids.empty? ? [] : association.scope.where(association.klass.primary_key => attribute_ids)
487
491
  end
488
- attributes = attributes.with_indifferent_access
489
492
 
490
- if attributes['id'].blank?
491
- unless reject_new_record?(association_name, attributes)
492
- association.build(attributes.except(*UNASSIGNABLE_KEYS))
493
+ attributes_collection.each do |attributes|
494
+ if attributes.respond_to?(:permitted?)
495
+ attributes = attributes.to_h
493
496
  end
494
- elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes['id'].to_s }
495
- unless call_reject_if(association_name, attributes)
496
- # Make sure we are operating on the actual object which is in the association's
497
- # proxy_target array (either by finding it, or adding it if not found)
498
- # Take into account that the proxy_target may have changed due to callbacks
499
- target_record = association.target.detect { |record| record.id.to_s == attributes['id'].to_s }
500
- if target_record
501
- existing_record = target_record
502
- else
503
- association.add_to_target(existing_record, :skip_callbacks)
504
- end
497
+ attributes = attributes.with_indifferent_access
505
498
 
506
- assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
499
+ if attributes["id"].blank?
500
+ unless reject_new_record?(association_name, attributes)
501
+ association.reader.build(attributes.except(*UNASSIGNABLE_KEYS))
502
+ end
503
+ elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes["id"].to_s }
504
+ unless call_reject_if(association_name, attributes)
505
+ # Make sure we are operating on the actual object which is in the association's
506
+ # proxy_target array (either by finding it, or adding it if not found)
507
+ # Take into account that the proxy_target may have changed due to callbacks
508
+ target_record = association.target.detect { |record| record.id.to_s == attributes["id"].to_s }
509
+ if target_record
510
+ existing_record = target_record
511
+ else
512
+ association.add_to_target(existing_record, skip_callbacks: true)
513
+ end
514
+
515
+ assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
516
+ end
517
+ else
518
+ raise_nested_attributes_record_not_found!(association_name, attributes["id"])
507
519
  end
508
- else
509
- raise_nested_attributes_record_not_found!(association_name, attributes['id'])
510
520
  end
511
521
  end
512
- end
513
522
 
514
- # Takes in a limit and checks if the attributes_collection has too many
515
- # records. It accepts limit in the form of symbol, proc, or
516
- # number-like object (anything that can be compared with an integer).
517
- #
518
- # Raises TooManyRecords error if the attributes_collection is
519
- # larger than the limit.
520
- def check_record_limit!(limit, attributes_collection)
521
- if limit
522
- limit = case limit
523
- when Symbol
524
- send(limit)
525
- when Proc
526
- limit.call
527
- else
528
- limit
529
- end
523
+ # Takes in a limit and checks if the attributes_collection has too many
524
+ # records. It accepts limit in the form of symbol, proc, or
525
+ # number-like object (anything that can be compared with an integer).
526
+ #
527
+ # Raises TooManyRecords error if the attributes_collection is
528
+ # larger than the limit.
529
+ def check_record_limit!(limit, attributes_collection)
530
+ if limit
531
+ limit = \
532
+ case limit
533
+ when Symbol
534
+ send(limit)
535
+ when Proc
536
+ limit.call
537
+ else
538
+ limit
539
+ end
530
540
 
531
- if limit && attributes_collection.size > limit
532
- raise TooManyRecords, "Maximum #{limit} records are allowed. Got #{attributes_collection.size} records instead."
541
+ if limit && attributes_collection.size > limit
542
+ raise TooManyRecords, "Maximum #{limit} records are allowed. Got #{attributes_collection.size} records instead."
543
+ end
533
544
  end
534
545
  end
535
- end
536
546
 
537
- # Updates a record with the +attributes+ or marks it for destruction if
538
- # +allow_destroy+ is +true+ and has_destroy_flag? returns +true+.
539
- def assign_to_or_mark_for_destruction(record, attributes, allow_destroy)
540
- record.assign_attributes(attributes.except(*UNASSIGNABLE_KEYS))
541
- record.mark_for_destruction if has_destroy_flag?(attributes) && allow_destroy
542
- end
547
+ # Updates a record with the +attributes+ or marks it for destruction if
548
+ # +allow_destroy+ is +true+ and has_destroy_flag? returns +true+.
549
+ def assign_to_or_mark_for_destruction(record, attributes, allow_destroy)
550
+ record.assign_attributes(attributes.except(*UNASSIGNABLE_KEYS))
551
+ record.mark_for_destruction if has_destroy_flag?(attributes) && allow_destroy
552
+ end
543
553
 
544
- # Determines if a hash contains a truthy _destroy key.
545
- def has_destroy_flag?(hash)
546
- Type::Boolean.new.cast(hash['_destroy'])
547
- end
554
+ # Determines if a hash contains a truthy _destroy key.
555
+ def has_destroy_flag?(hash)
556
+ Type::Boolean.new.cast(hash["_destroy"])
557
+ end
548
558
 
549
- # Determines if a new record should be rejected by checking
550
- # has_destroy_flag? or if a <tt>:reject_if</tt> proc exists for this
551
- # association and evaluates to +true+.
552
- def reject_new_record?(association_name, attributes)
553
- will_be_destroyed?(association_name, attributes) || call_reject_if(association_name, attributes)
554
- end
559
+ # Determines if a new record should be rejected by checking
560
+ # has_destroy_flag? or if a <tt>:reject_if</tt> proc exists for this
561
+ # association and evaluates to +true+.
562
+ def reject_new_record?(association_name, attributes)
563
+ will_be_destroyed?(association_name, attributes) || call_reject_if(association_name, attributes)
564
+ end
555
565
 
556
- # Determines if a record with the particular +attributes+ should be
557
- # rejected by calling the reject_if Symbol or Proc (if defined).
558
- # The reject_if option is defined by +accepts_nested_attributes_for+.
559
- #
560
- # Returns false if there is a +destroy_flag+ on the attributes.
561
- def call_reject_if(association_name, attributes)
562
- return false if will_be_destroyed?(association_name, attributes)
566
+ # Determines if a record with the particular +attributes+ should be
567
+ # rejected by calling the reject_if Symbol or Proc (if defined).
568
+ # The reject_if option is defined by +accepts_nested_attributes_for+.
569
+ #
570
+ # Returns false if there is a +destroy_flag+ on the attributes.
571
+ def call_reject_if(association_name, attributes)
572
+ return false if will_be_destroyed?(association_name, attributes)
563
573
 
564
- case callback = self.nested_attributes_options[association_name][:reject_if]
565
- when Symbol
566
- method(callback).arity == 0 ? send(callback) : send(callback, attributes)
567
- when Proc
568
- callback.call(attributes)
574
+ case callback = nested_attributes_options[association_name][:reject_if]
575
+ when Symbol
576
+ method(callback).arity == 0 ? send(callback) : send(callback, attributes)
577
+ when Proc
578
+ callback.call(attributes)
579
+ end
569
580
  end
570
- end
571
581
 
572
- # Only take into account the destroy flag if <tt>:allow_destroy</tt> is true
573
- def will_be_destroyed?(association_name, attributes)
574
- allow_destroy?(association_name) && has_destroy_flag?(attributes)
575
- end
582
+ # Only take into account the destroy flag if <tt>:allow_destroy</tt> is true
583
+ def will_be_destroyed?(association_name, attributes)
584
+ allow_destroy?(association_name) && has_destroy_flag?(attributes)
585
+ end
576
586
 
577
- def allow_destroy?(association_name)
578
- self.nested_attributes_options[association_name][:allow_destroy]
579
- end
587
+ def allow_destroy?(association_name)
588
+ nested_attributes_options[association_name][:allow_destroy]
589
+ end
580
590
 
581
- def raise_nested_attributes_record_not_found!(association_name, record_id)
582
- model = self.class._reflect_on_association(association_name).klass.name
583
- raise RecordNotFound.new("Couldn't find #{model} with ID=#{record_id} for #{self.class.name} with ID=#{id}",
584
- model, 'id', record_id)
585
- end
591
+ def raise_nested_attributes_record_not_found!(association_name, record_id)
592
+ model = self.class._reflect_on_association(association_name).klass.name
593
+ raise RecordNotFound.new("Couldn't find #{model} with ID=#{record_id} for #{self.class.name} with ID=#{id}",
594
+ model, "id", record_id)
595
+ end
586
596
  end
587
597
  end