activerecord 5.0.7.2 → 6.0.3.4

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