activerecord 3.2.6 → 6.0.0

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 (371) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +611 -6417
  3. data/MIT-LICENSE +4 -2
  4. data/README.rdoc +44 -47
  5. data/examples/performance.rb +79 -71
  6. data/examples/simple.rb +6 -5
  7. data/lib/active_record/aggregations.rb +268 -238
  8. data/lib/active_record/association_relation.rb +40 -0
  9. data/lib/active_record/associations/alias_tracker.rb +47 -42
  10. data/lib/active_record/associations/association.rb +173 -81
  11. data/lib/active_record/associations/association_scope.rb +124 -92
  12. data/lib/active_record/associations/belongs_to_association.rb +83 -38
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +11 -9
  14. data/lib/active_record/associations/builder/association.rb +113 -32
  15. data/lib/active_record/associations/builder/belongs_to.rb +105 -60
  16. data/lib/active_record/associations/builder/collection_association.rb +53 -56
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +98 -41
  18. data/lib/active_record/associations/builder/has_many.rb +11 -63
  19. data/lib/active_record/associations/builder/has_one.rb +47 -45
  20. data/lib/active_record/associations/builder/singular_association.rb +30 -18
  21. data/lib/active_record/associations/collection_association.rb +217 -295
  22. data/lib/active_record/associations/collection_proxy.rb +1074 -77
  23. data/lib/active_record/associations/foreign_association.rb +20 -0
  24. data/lib/active_record/associations/has_many_association.rb +78 -50
  25. data/lib/active_record/associations/has_many_through_association.rb +99 -61
  26. data/lib/active_record/associations/has_one_association.rb +75 -30
  27. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  28. data/lib/active_record/associations/join_dependency/join_association.rb +45 -119
  29. data/lib/active_record/associations/join_dependency/join_base.rb +11 -12
  30. data/lib/active_record/associations/join_dependency/join_part.rb +35 -42
  31. data/lib/active_record/associations/join_dependency.rb +208 -164
  32. data/lib/active_record/associations/preloader/association.rb +93 -87
  33. data/lib/active_record/associations/preloader/through_association.rb +87 -38
  34. data/lib/active_record/associations/preloader.rb +134 -110
  35. data/lib/active_record/associations/singular_association.rb +19 -24
  36. data/lib/active_record/associations/through_association.rb +61 -27
  37. data/lib/active_record/associations.rb +1766 -1505
  38. data/lib/active_record/attribute_assignment.rb +57 -193
  39. data/lib/active_record/attribute_decorators.rb +90 -0
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +58 -8
  41. data/lib/active_record/attribute_methods/dirty.rb +187 -67
  42. data/lib/active_record/attribute_methods/primary_key.rb +100 -78
  43. data/lib/active_record/attribute_methods/query.rb +10 -8
  44. data/lib/active_record/attribute_methods/read.rb +29 -118
  45. data/lib/active_record/attribute_methods/serialization.rb +60 -72
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +69 -42
  47. data/lib/active_record/attribute_methods/write.rb +36 -44
  48. data/lib/active_record/attribute_methods.rb +306 -161
  49. data/lib/active_record/attributes.rb +279 -0
  50. data/lib/active_record/autosave_association.rb +324 -238
  51. data/lib/active_record/base.rb +114 -507
  52. data/lib/active_record/callbacks.rb +147 -83
  53. data/lib/active_record/coders/json.rb +15 -0
  54. data/lib/active_record/coders/yaml_column.rb +32 -23
  55. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +962 -279
  56. data/lib/active_record/connection_adapters/abstract/database_limits.rb +32 -5
  57. data/lib/active_record/connection_adapters/abstract/database_statements.rb +331 -209
  58. data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -23
  59. data/lib/active_record/connection_adapters/abstract/quoting.rb +201 -65
  60. data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
  61. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +510 -289
  63. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +93 -0
  64. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1182 -313
  65. data/lib/active_record/connection_adapters/abstract/transaction.rb +323 -0
  66. data/lib/active_record/connection_adapters/abstract_adapter.rb +585 -120
  67. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +610 -463
  68. data/lib/active_record/connection_adapters/column.rb +58 -233
  69. data/lib/active_record/connection_adapters/connection_specification.rb +297 -0
  70. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
  71. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  72. data/lib/active_record/connection_adapters/mysql/database_statements.rb +200 -0
  73. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  74. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +72 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
  79. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
  80. data/lib/active_record/connection_adapters/mysql2_adapter.rb +75 -207
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -0
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +182 -0
  83. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +53 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +113 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
  107. data/lib/active_record/connection_adapters/postgresql/quoting.rb +205 -0
  108. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
  109. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +222 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +776 -0
  113. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
  114. data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
  115. data/lib/active_record/connection_adapters/postgresql_adapter.rb +695 -1052
  116. data/lib/active_record/connection_adapters/schema_cache.rb +115 -24
  117. data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
  118. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +103 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +528 -26
  126. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  127. data/lib/active_record/connection_handling.rb +267 -0
  128. data/lib/active_record/core.rb +599 -0
  129. data/lib/active_record/counter_cache.rb +177 -103
  130. data/lib/active_record/database_configurations/database_config.rb +37 -0
  131. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  132. data/lib/active_record/database_configurations/url_config.rb +79 -0
  133. data/lib/active_record/database_configurations.rb +233 -0
  134. data/lib/active_record/define_callbacks.rb +22 -0
  135. data/lib/active_record/dynamic_matchers.rb +107 -64
  136. data/lib/active_record/enum.rb +274 -0
  137. data/lib/active_record/errors.rb +254 -61
  138. data/lib/active_record/explain.rb +35 -70
  139. data/lib/active_record/explain_registry.rb +32 -0
  140. data/lib/active_record/explain_subscriber.rb +18 -8
  141. data/lib/active_record/fixture_set/file.rb +82 -0
  142. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  143. data/lib/active_record/fixture_set/render_context.rb +17 -0
  144. data/lib/active_record/fixture_set/table_row.rb +153 -0
  145. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  146. data/lib/active_record/fixtures.rb +291 -475
  147. data/lib/active_record/gem_version.rb +17 -0
  148. data/lib/active_record/inheritance.rb +219 -100
  149. data/lib/active_record/insert_all.rb +179 -0
  150. data/lib/active_record/integration.rb +175 -17
  151. data/lib/active_record/internal_metadata.rb +53 -0
  152. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  153. data/lib/active_record/locale/en.yml +9 -1
  154. data/lib/active_record/locking/optimistic.rb +106 -92
  155. data/lib/active_record/locking/pessimistic.rb +23 -11
  156. data/lib/active_record/log_subscriber.rb +80 -30
  157. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  158. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  159. data/lib/active_record/middleware/database_selector.rb +75 -0
  160. data/lib/active_record/migration/command_recorder.rb +235 -56
  161. data/lib/active_record/migration/compatibility.rb +244 -0
  162. data/lib/active_record/migration/join_table.rb +17 -0
  163. data/lib/active_record/migration.rb +917 -301
  164. data/lib/active_record/model_schema.rb +351 -175
  165. data/lib/active_record/nested_attributes.rb +366 -235
  166. data/lib/active_record/no_touching.rb +65 -0
  167. data/lib/active_record/null_relation.rb +68 -0
  168. data/lib/active_record/persistence.rb +761 -166
  169. data/lib/active_record/query_cache.rb +22 -44
  170. data/lib/active_record/querying.rb +55 -31
  171. data/lib/active_record/railtie.rb +185 -47
  172. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  173. data/lib/active_record/railties/console_sandbox.rb +5 -4
  174. data/lib/active_record/railties/controller_runtime.rb +35 -33
  175. data/lib/active_record/railties/databases.rake +366 -463
  176. data/lib/active_record/readonly_attributes.rb +4 -6
  177. data/lib/active_record/reflection.rb +736 -228
  178. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  179. data/lib/active_record/relation/batches.rb +252 -52
  180. data/lib/active_record/relation/calculations.rb +340 -270
  181. data/lib/active_record/relation/delegation.rb +117 -36
  182. data/lib/active_record/relation/finder_methods.rb +439 -286
  183. data/lib/active_record/relation/from_clause.rb +26 -0
  184. data/lib/active_record/relation/merger.rb +184 -0
  185. data/lib/active_record/relation/predicate_builder/array_handler.rb +49 -0
  186. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  187. data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
  188. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  189. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  190. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  191. data/lib/active_record/relation/predicate_builder/relation_handler.rb +19 -0
  192. data/lib/active_record/relation/predicate_builder.rb +131 -39
  193. data/lib/active_record/relation/query_attribute.rb +50 -0
  194. data/lib/active_record/relation/query_methods.rb +1163 -221
  195. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  196. data/lib/active_record/relation/spawn_methods.rb +49 -120
  197. data/lib/active_record/relation/where_clause.rb +190 -0
  198. data/lib/active_record/relation/where_clause_factory.rb +33 -0
  199. data/lib/active_record/relation.rb +671 -349
  200. data/lib/active_record/result.rb +149 -15
  201. data/lib/active_record/runtime_registry.rb +24 -0
  202. data/lib/active_record/sanitization.rb +153 -133
  203. data/lib/active_record/schema.rb +22 -19
  204. data/lib/active_record/schema_dumper.rb +178 -112
  205. data/lib/active_record/schema_migration.rb +60 -0
  206. data/lib/active_record/scoping/default.rb +107 -98
  207. data/lib/active_record/scoping/named.rb +130 -115
  208. data/lib/active_record/scoping.rb +77 -123
  209. data/lib/active_record/secure_token.rb +40 -0
  210. data/lib/active_record/serialization.rb +10 -6
  211. data/lib/active_record/statement_cache.rb +148 -0
  212. data/lib/active_record/store.rb +256 -16
  213. data/lib/active_record/suppressor.rb +61 -0
  214. data/lib/active_record/table_metadata.rb +75 -0
  215. data/lib/active_record/tasks/database_tasks.rb +506 -0
  216. data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
  217. data/lib/active_record/tasks/postgresql_database_tasks.rb +141 -0
  218. data/lib/active_record/tasks/sqlite_database_tasks.rb +77 -0
  219. data/lib/active_record/test_databases.rb +23 -0
  220. data/lib/active_record/test_fixtures.rb +224 -0
  221. data/lib/active_record/timestamp.rb +93 -39
  222. data/lib/active_record/touch_later.rb +66 -0
  223. data/lib/active_record/transactions.rb +260 -129
  224. data/lib/active_record/translation.rb +3 -1
  225. data/lib/active_record/type/adapter_specific_registry.rb +129 -0
  226. data/lib/active_record/type/date.rb +9 -0
  227. data/lib/active_record/type/date_time.rb +9 -0
  228. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  229. data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
  230. data/lib/active_record/type/internal/timezone.rb +17 -0
  231. data/lib/active_record/type/json.rb +30 -0
  232. data/lib/active_record/type/serialized.rb +71 -0
  233. data/lib/active_record/type/text.rb +11 -0
  234. data/lib/active_record/type/time.rb +21 -0
  235. data/lib/active_record/type/type_map.rb +62 -0
  236. data/lib/active_record/type/unsigned_integer.rb +17 -0
  237. data/lib/active_record/type.rb +78 -0
  238. data/lib/active_record/type_caster/connection.rb +34 -0
  239. data/lib/active_record/type_caster/map.rb +20 -0
  240. data/lib/active_record/type_caster.rb +9 -0
  241. data/lib/active_record/validations/absence.rb +25 -0
  242. data/lib/active_record/validations/associated.rb +35 -18
  243. data/lib/active_record/validations/length.rb +26 -0
  244. data/lib/active_record/validations/presence.rb +68 -0
  245. data/lib/active_record/validations/uniqueness.rb +123 -77
  246. data/lib/active_record/validations.rb +54 -43
  247. data/lib/active_record/version.rb +7 -7
  248. data/lib/active_record.rb +97 -49
  249. data/lib/arel/alias_predication.rb +9 -0
  250. data/lib/arel/attributes/attribute.rb +37 -0
  251. data/lib/arel/attributes.rb +22 -0
  252. data/lib/arel/collectors/bind.rb +24 -0
  253. data/lib/arel/collectors/composite.rb +31 -0
  254. data/lib/arel/collectors/plain_string.rb +20 -0
  255. data/lib/arel/collectors/sql_string.rb +20 -0
  256. data/lib/arel/collectors/substitute_binds.rb +28 -0
  257. data/lib/arel/crud.rb +42 -0
  258. data/lib/arel/delete_manager.rb +18 -0
  259. data/lib/arel/errors.rb +9 -0
  260. data/lib/arel/expressions.rb +29 -0
  261. data/lib/arel/factory_methods.rb +49 -0
  262. data/lib/arel/insert_manager.rb +49 -0
  263. data/lib/arel/math.rb +45 -0
  264. data/lib/arel/nodes/and.rb +32 -0
  265. data/lib/arel/nodes/ascending.rb +23 -0
  266. data/lib/arel/nodes/binary.rb +52 -0
  267. data/lib/arel/nodes/bind_param.rb +36 -0
  268. data/lib/arel/nodes/case.rb +55 -0
  269. data/lib/arel/nodes/casted.rb +50 -0
  270. data/lib/arel/nodes/comment.rb +29 -0
  271. data/lib/arel/nodes/count.rb +12 -0
  272. data/lib/arel/nodes/delete_statement.rb +45 -0
  273. data/lib/arel/nodes/descending.rb +23 -0
  274. data/lib/arel/nodes/equality.rb +18 -0
  275. data/lib/arel/nodes/extract.rb +24 -0
  276. data/lib/arel/nodes/false.rb +16 -0
  277. data/lib/arel/nodes/full_outer_join.rb +8 -0
  278. data/lib/arel/nodes/function.rb +44 -0
  279. data/lib/arel/nodes/grouping.rb +8 -0
  280. data/lib/arel/nodes/in.rb +8 -0
  281. data/lib/arel/nodes/infix_operation.rb +80 -0
  282. data/lib/arel/nodes/inner_join.rb +8 -0
  283. data/lib/arel/nodes/insert_statement.rb +37 -0
  284. data/lib/arel/nodes/join_source.rb +20 -0
  285. data/lib/arel/nodes/matches.rb +18 -0
  286. data/lib/arel/nodes/named_function.rb +23 -0
  287. data/lib/arel/nodes/node.rb +50 -0
  288. data/lib/arel/nodes/node_expression.rb +13 -0
  289. data/lib/arel/nodes/outer_join.rb +8 -0
  290. data/lib/arel/nodes/over.rb +15 -0
  291. data/lib/arel/nodes/regexp.rb +16 -0
  292. data/lib/arel/nodes/right_outer_join.rb +8 -0
  293. data/lib/arel/nodes/select_core.rb +67 -0
  294. data/lib/arel/nodes/select_statement.rb +41 -0
  295. data/lib/arel/nodes/sql_literal.rb +16 -0
  296. data/lib/arel/nodes/string_join.rb +11 -0
  297. data/lib/arel/nodes/table_alias.rb +27 -0
  298. data/lib/arel/nodes/terminal.rb +16 -0
  299. data/lib/arel/nodes/true.rb +16 -0
  300. data/lib/arel/nodes/unary.rb +45 -0
  301. data/lib/arel/nodes/unary_operation.rb +20 -0
  302. data/lib/arel/nodes/unqualified_column.rb +22 -0
  303. data/lib/arel/nodes/update_statement.rb +41 -0
  304. data/lib/arel/nodes/values_list.rb +9 -0
  305. data/lib/arel/nodes/window.rb +126 -0
  306. data/lib/arel/nodes/with.rb +11 -0
  307. data/lib/arel/nodes.rb +68 -0
  308. data/lib/arel/order_predications.rb +13 -0
  309. data/lib/arel/predications.rb +257 -0
  310. data/lib/arel/select_manager.rb +271 -0
  311. data/lib/arel/table.rb +110 -0
  312. data/lib/arel/tree_manager.rb +72 -0
  313. data/lib/arel/update_manager.rb +34 -0
  314. data/lib/arel/visitors/depth_first.rb +204 -0
  315. data/lib/arel/visitors/dot.rb +297 -0
  316. data/lib/arel/visitors/ibm_db.rb +34 -0
  317. data/lib/arel/visitors/informix.rb +62 -0
  318. data/lib/arel/visitors/mssql.rb +157 -0
  319. data/lib/arel/visitors/mysql.rb +83 -0
  320. data/lib/arel/visitors/oracle.rb +159 -0
  321. data/lib/arel/visitors/oracle12.rb +66 -0
  322. data/lib/arel/visitors/postgresql.rb +110 -0
  323. data/lib/arel/visitors/sqlite.rb +39 -0
  324. data/lib/arel/visitors/to_sql.rb +889 -0
  325. data/lib/arel/visitors/visitor.rb +46 -0
  326. data/lib/arel/visitors/where_sql.rb +23 -0
  327. data/lib/arel/visitors.rb +20 -0
  328. data/lib/arel/window_predications.rb +9 -0
  329. data/lib/arel.rb +51 -0
  330. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  331. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  332. data/lib/rails/generators/active_record/migration/migration_generator.rb +59 -9
  333. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  334. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +48 -0
  335. data/lib/rails/generators/active_record/migration.rb +41 -8
  336. data/lib/rails/generators/active_record/model/model_generator.rb +24 -22
  337. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  338. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +1 -1
  339. data/lib/rails/generators/active_record.rb +10 -16
  340. metadata +285 -149
  341. data/examples/associations.png +0 -0
  342. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
  343. data/lib/active_record/associations/join_helper.rb +0 -55
  344. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  345. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  346. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  347. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  348. data/lib/active_record/associations/preloader/has_many_through.rb +0 -15
  349. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  350. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  351. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  352. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  353. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -188
  354. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -426
  355. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -579
  356. data/lib/active_record/dynamic_finder_match.rb +0 -68
  357. data/lib/active_record/dynamic_scope_match.rb +0 -23
  358. data/lib/active_record/fixtures/file.rb +0 -65
  359. data/lib/active_record/identity_map.rb +0 -162
  360. data/lib/active_record/observer.rb +0 -121
  361. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  362. data/lib/active_record/serializers/xml_serializer.rb +0 -203
  363. data/lib/active_record/session_store.rb +0 -358
  364. data/lib/active_record/test_case.rb +0 -73
  365. data/lib/rails/generators/active_record/migration/templates/migration.rb +0 -34
  366. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
  367. data/lib/rails/generators/active_record/model/templates/model.rb +0 -12
  368. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  369. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  370. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  371. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,12 +1,12 @@
1
- require 'active_support/core_ext/array/wrap'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecord
4
4
  # = Active Record Autosave Association
5
5
  #
6
- # +AutosaveAssociation+ is a module that takes care of automatically saving
6
+ # AutosaveAssociation is a module that takes care of automatically saving
7
7
  # associated records when their parent is saved. In addition to saving, it
8
8
  # also destroys any associated records that were marked for destruction.
9
- # (See +mark_for_destruction+ and <tt>marked_for_destruction?</tt>).
9
+ # (See #mark_for_destruction and #marked_for_destruction?).
10
10
  #
11
11
  # Saving of the parent, its associations, and the destruction of marked
12
12
  # associations, all happen inside a transaction. This should never leave the
@@ -18,28 +18,27 @@ module ActiveRecord
18
18
  # Note that it also means that associations marked for destruction won't
19
19
  # be destroyed directly. They will however still be marked for destruction.
20
20
  #
21
- # Note that <tt>:autosave => false</tt> is not same as not declaring <tt>:autosave</tt>.
22
- # When the <tt>:autosave</tt> option is not present new associations are saved.
21
+ # Note that <tt>autosave: false</tt> is not same as not declaring <tt>:autosave</tt>.
22
+ # When the <tt>:autosave</tt> option is not present then new association records are
23
+ # saved but the updated association records are not saved.
23
24
  #
24
25
  # == Validation
25
26
  #
26
- # Children records are validated unless <tt>:validate</tt> is +false+.
27
+ # Child records are validated unless <tt>:validate</tt> is +false+.
27
28
  #
28
29
  # == Callbacks
29
30
  #
30
31
  # Association with autosave option defines several callbacks on your
31
32
  # model (before_save, after_create, after_update). Please note that
32
33
  # callbacks are executed in the order they were defined in
33
- # model. You should avoid modyfing the association content, before
34
+ # model. You should avoid modifying the association content, before
34
35
  # autosave callbacks are executed. Placing your callbacks after
35
36
  # associations is usually a good practice.
36
37
  #
37
- # == Examples
38
- #
39
38
  # === One-to-one Example
40
39
  #
41
- # class Post
42
- # has_one :author, :autosave => true
40
+ # class Post < ActiveRecord::Base
41
+ # has_one :author, autosave: true
43
42
  # end
44
43
  #
45
44
  # Saving changes to the parent and its associated model can now be performed
@@ -66,167 +65,173 @@ module ActiveRecord
66
65
  # Note that the model is _not_ yet removed from the database:
67
66
  #
68
67
  # id = post.author.id
69
- # Author.find_by_id(id).nil? # => false
68
+ # Author.find_by(id: id).nil? # => false
70
69
  #
71
70
  # post.save
72
71
  # post.reload.author # => nil
73
72
  #
74
73
  # Now it _is_ removed from the database:
75
74
  #
76
- # Author.find_by_id(id).nil? # => true
75
+ # Author.find_by(id: id).nil? # => true
77
76
  #
78
77
  # === One-to-many Example
79
78
  #
80
79
  # When <tt>:autosave</tt> is not declared new children are saved when their parent is saved:
81
80
  #
82
- # class Post
83
- # has_many :comments # :autosave option is no declared
81
+ # class Post < ActiveRecord::Base
82
+ # has_many :comments # :autosave option is not declared
84
83
  # end
85
84
  #
86
- # post = Post.new(:title => 'ruby rocks')
87
- # post.comments.build(:body => 'hello world')
85
+ # post = Post.new(title: 'ruby rocks')
86
+ # post.comments.build(body: 'hello world')
88
87
  # post.save # => saves both post and comment
89
88
  #
90
- # post = Post.create(:title => 'ruby rocks')
91
- # post.comments.build(:body => 'hello world')
89
+ # post = Post.create(title: 'ruby rocks')
90
+ # post.comments.build(body: 'hello world')
92
91
  # post.save # => saves both post and comment
93
92
  #
94
- # post = Post.create(:title => 'ruby rocks')
95
- # post.comments.create(:body => 'hello world')
93
+ # post = Post.create(title: 'ruby rocks')
94
+ # post.comments.create(body: 'hello world')
96
95
  # post.save # => saves both post and comment
97
96
  #
98
- # When <tt>:autosave</tt> is true all children is saved, no matter whether they are new records:
97
+ # When <tt>:autosave</tt> is true all children are saved, no matter whether they
98
+ # are new records or not:
99
99
  #
100
- # class Post
101
- # has_many :comments, :autosave => true
100
+ # class Post < ActiveRecord::Base
101
+ # has_many :comments, autosave: true
102
102
  # end
103
103
  #
104
- # post = Post.create(:title => 'ruby rocks')
105
- # post.comments.create(:body => 'hello world')
104
+ # post = Post.create(title: 'ruby rocks')
105
+ # post.comments.create(body: 'hello world')
106
106
  # post.comments[0].body = 'hi everyone'
107
- # post.save # => saves both post and comment, with 'hi everyone' as body
107
+ # post.comments.build(body: "good morning.")
108
+ # post.title += "!"
109
+ # post.save # => saves both post and comments.
108
110
  #
109
111
  # Destroying one of the associated models as part of the parent's save action
110
112
  # is as simple as marking it for destruction:
111
113
  #
112
- # post.comments.last.mark_for_destruction
113
- # post.comments.last.marked_for_destruction? # => true
114
+ # post.comments # => [#<Comment id: 1, ...>, #<Comment id: 2, ...]>
115
+ # post.comments[1].mark_for_destruction
116
+ # post.comments[1].marked_for_destruction? # => true
114
117
  # post.comments.length # => 2
115
118
  #
116
119
  # Note that the model is _not_ yet removed from the database:
117
120
  #
118
121
  # id = post.comments.last.id
119
- # Comment.find_by_id(id).nil? # => false
122
+ # Comment.find_by(id: id).nil? # => false
120
123
  #
121
124
  # post.save
122
125
  # post.reload.comments.length # => 1
123
126
  #
124
127
  # Now it _is_ removed from the database:
125
128
  #
126
- # Comment.find_by_id(id).nil? # => true
127
-
129
+ # Comment.find_by(id: id).nil? # => true
128
130
  module AutosaveAssociation
129
131
  extend ActiveSupport::Concern
130
132
 
131
- ASSOCIATION_TYPES = %w{ HasOne HasMany BelongsTo HasAndBelongsToMany }
132
-
133
133
  module AssociationBuilderExtension #:nodoc:
134
- def self.included(base)
135
- base.valid_options << :autosave
134
+ def self.build(model, reflection)
135
+ model.send(:add_autosave_association_callbacks, reflection)
136
136
  end
137
137
 
138
- def build
139
- reflection = super
140
- model.send(:add_autosave_association_callbacks, reflection)
141
- reflection
138
+ def self.valid_options
139
+ [ :autosave ]
142
140
  end
143
141
  end
144
142
 
145
143
  included do
146
- ASSOCIATION_TYPES.each do |type|
147
- Associations::Builder.const_get(type).send(:include, AssociationBuilderExtension)
148
- end
144
+ Associations::Builder::Association.extensions << AssociationBuilderExtension
145
+ mattr_accessor :index_nested_attribute_errors, instance_writer: false, default: false
149
146
  end
150
147
 
151
- module ClassMethods
148
+ module ClassMethods # :nodoc:
152
149
  private
153
150
 
154
- def define_non_cyclic_method(name, reflection, &block)
155
- define_method(name) do |*args|
156
- result = true; @_already_called ||= {}
157
- # Loop prevention for validation of associations
158
- unless @_already_called[[name, reflection.name]]
159
- begin
160
- @_already_called[[name, reflection.name]]=true
161
- result = instance_eval(&block)
162
- ensure
163
- @_already_called[[name, reflection.name]]=false
151
+ def define_non_cyclic_method(name, &block)
152
+ return if instance_methods(false).include?(name)
153
+ define_method(name) do |*args|
154
+ result = true; @_already_called ||= {}
155
+ # Loop prevention for validation of associations
156
+ unless @_already_called[name]
157
+ begin
158
+ @_already_called[name] = true
159
+ result = instance_eval(&block)
160
+ ensure
161
+ @_already_called[name] = false
162
+ end
164
163
  end
165
- end
166
164
 
167
- result
165
+ result
166
+ end
168
167
  end
169
- end
170
168
 
171
- # Adds validation and save callbacks for the association as specified by
172
- # the +reflection+.
173
- #
174
- # For performance reasons, we don't check whether to validate at runtime.
175
- # However the validation and callback methods are lazy and those methods
176
- # get created when they are invoked for the very first time. However,
177
- # this can change, for instance, when using nested attributes, which is
178
- # called _after_ the association has been defined. Since we don't want
179
- # the callbacks to get defined multiple times, there are guards that
180
- # check if the save or validation methods have already been defined
181
- # before actually defining them.
182
- def add_autosave_association_callbacks(reflection)
183
- save_method = :"autosave_associated_records_for_#{reflection.name}"
184
- validation_method = :"validate_associated_records_for_#{reflection.name}"
185
- collection = reflection.collection?
186
-
187
- unless method_defined?(save_method)
188
- if collection
169
+ # Adds validation and save callbacks for the association as specified by
170
+ # the +reflection+.
171
+ #
172
+ # For performance reasons, we don't check whether to validate at runtime.
173
+ # However the validation and callback methods are lazy and those methods
174
+ # get created when they are invoked for the very first time. However,
175
+ # this can change, for instance, when using nested attributes, which is
176
+ # called _after_ the association has been defined. Since we don't want
177
+ # the callbacks to get defined multiple times, there are guards that
178
+ # check if the save or validation methods have already been defined
179
+ # before actually defining them.
180
+ def add_autosave_association_callbacks(reflection)
181
+ save_method = :"autosave_associated_records_for_#{reflection.name}"
182
+
183
+ if reflection.collection?
189
184
  before_save :before_save_collection_association
185
+ after_save :after_save_collection_association
190
186
 
191
- define_non_cyclic_method(save_method, reflection) { save_collection_association(reflection) }
187
+ define_non_cyclic_method(save_method) { save_collection_association(reflection) }
192
188
  # Doesn't use after_save as that would save associations added in after_create/after_update twice
193
189
  after_create save_method
194
190
  after_update save_method
191
+ elsif reflection.has_one?
192
+ define_method(save_method) { save_has_one_association(reflection) } unless method_defined?(save_method)
193
+ # Configures two callbacks instead of a single after_save so that
194
+ # the model may rely on their execution order relative to its
195
+ # own callbacks.
196
+ #
197
+ # For example, given that after_creates run before after_saves, if
198
+ # we configured instead an after_save there would be no way to fire
199
+ # a custom after_create callback after the child association gets
200
+ # created.
201
+ after_create save_method
202
+ after_update save_method
195
203
  else
196
- if reflection.macro == :has_one
197
- define_method(save_method) { save_has_one_association(reflection) }
198
- # Configures two callbacks instead of a single after_save so that
199
- # the model may rely on their execution order relative to its
200
- # own callbacks.
201
- #
202
- # For example, given that after_creates run before after_saves, if
203
- # we configured instead an after_save there would be no way to fire
204
- # a custom after_create callback after the child association gets
205
- # created.
206
- after_create save_method
207
- after_update save_method
208
- else
209
- define_non_cyclic_method(save_method, reflection) { save_belongs_to_association(reflection) }
210
- before_save save_method
211
- end
204
+ define_non_cyclic_method(save_method) { throw(:abort) if save_belongs_to_association(reflection) == false }
205
+ before_save save_method
212
206
  end
207
+
208
+ define_autosave_validation_callbacks(reflection)
213
209
  end
214
210
 
215
- if reflection.validate? && !method_defined?(validation_method)
216
- method = (collection ? :validate_collection_association : :validate_single_association)
217
- define_non_cyclic_method(validation_method, reflection) { send(method, reflection) }
218
- validate validation_method
211
+ def define_autosave_validation_callbacks(reflection)
212
+ validation_method = :"validate_associated_records_for_#{reflection.name}"
213
+ if reflection.validate? && !method_defined?(validation_method)
214
+ if reflection.collection?
215
+ method = :validate_collection_association
216
+ else
217
+ method = :validate_single_association
218
+ end
219
+
220
+ define_non_cyclic_method(validation_method) { send(method, reflection) }
221
+ validate validation_method
222
+ after_validation :_ensure_no_duplicate_errors
223
+ end
219
224
  end
220
- end
221
225
  end
222
226
 
223
227
  # Reloads the attributes of the object as usual and clears <tt>marked_for_destruction</tt> flag.
224
228
  def reload(options = nil)
225
229
  @marked_for_destruction = false
230
+ @destroyed_by_association = nil
226
231
  super
227
232
  end
228
233
 
229
- # Marks this record to be destroyed as part of the parents save transaction.
234
+ # Marks this record to be destroyed as part of the parent's save transaction.
230
235
  # This does _not_ actually destroy the record instantly, rather child record will be destroyed
231
236
  # when <tt>parent.save</tt> is called.
232
237
  #
@@ -235,188 +240,269 @@ module ActiveRecord
235
240
  @marked_for_destruction = true
236
241
  end
237
242
 
238
- # Returns whether or not this record will be destroyed as part of the parents save transaction.
243
+ # Returns whether or not this record will be destroyed as part of the parent's save transaction.
239
244
  #
240
245
  # Only useful if the <tt>:autosave</tt> option on the parent is enabled for this associated model.
241
246
  def marked_for_destruction?
242
247
  @marked_for_destruction
243
248
  end
244
249
 
250
+ # Records the association that is being destroyed and destroying this
251
+ # record in the process.
252
+ def destroyed_by_association=(reflection)
253
+ @destroyed_by_association = reflection
254
+ end
255
+
256
+ # Returns the association for the parent being destroyed.
257
+ #
258
+ # Used to avoid updating the counter cache unnecessarily.
259
+ def destroyed_by_association
260
+ @destroyed_by_association
261
+ end
262
+
245
263
  # Returns whether or not this record has been changed in any way (including whether
246
264
  # any of its nested autosave associations are likewise changed)
247
265
  def changed_for_autosave?
248
- new_record? || changed? || marked_for_destruction? || nested_records_changed_for_autosave?
266
+ new_record? || has_changes_to_save? || marked_for_destruction? || nested_records_changed_for_autosave?
249
267
  end
250
268
 
251
269
  private
252
270
 
253
- # Returns the record for an association collection that should be validated
254
- # or saved. If +autosave+ is +false+ only new records will be returned,
255
- # unless the parent is/was a new record itself.
256
- def associated_records_to_validate_or_save(association, new_record, autosave)
257
- if new_record
258
- association && association.target
259
- elsif autosave
260
- association.target.find_all { |record| record.changed_for_autosave? }
261
- else
262
- association.target.find_all { |record| record.new_record? }
271
+ # Returns the record for an association collection that should be validated
272
+ # or saved. If +autosave+ is +false+ only new records will be returned,
273
+ # unless the parent is/was a new record itself.
274
+ def associated_records_to_validate_or_save(association, new_record, autosave)
275
+ if new_record
276
+ association && association.target
277
+ elsif autosave
278
+ association.target.find_all(&:changed_for_autosave?)
279
+ else
280
+ association.target.find_all(&:new_record?)
281
+ end
263
282
  end
264
- end
265
283
 
266
- # go through nested autosave associations that are loaded in memory (without loading
267
- # any new ones), and return true if is changed for autosave
268
- def nested_records_changed_for_autosave?
269
- self.class.reflect_on_all_autosave_associations.any? do |reflection|
270
- association = association_instance_get(reflection.name)
271
- association && Array.wrap(association.target).any? { |a| a.changed_for_autosave? }
284
+ # go through nested autosave associations that are loaded in memory (without loading
285
+ # any new ones), and return true if is changed for autosave
286
+ def nested_records_changed_for_autosave?
287
+ @_nested_records_changed_for_autosave_already_called ||= false
288
+ return false if @_nested_records_changed_for_autosave_already_called
289
+ begin
290
+ @_nested_records_changed_for_autosave_already_called = true
291
+ self.class._reflections.values.any? do |reflection|
292
+ if reflection.options[:autosave]
293
+ association = association_instance_get(reflection.name)
294
+ association && Array.wrap(association.target).any?(&:changed_for_autosave?)
295
+ end
296
+ end
297
+ ensure
298
+ @_nested_records_changed_for_autosave_already_called = false
299
+ end
272
300
  end
273
- end
274
301
 
275
- # Validate the association if <tt>:validate</tt> or <tt>:autosave</tt> is
276
- # turned on for the association.
277
- def validate_single_association(reflection)
278
- association = association_instance_get(reflection.name)
279
- record = association && association.reader
280
- association_valid?(reflection, record) if record
281
- end
302
+ # Validate the association if <tt>:validate</tt> or <tt>:autosave</tt> is
303
+ # turned on for the association.
304
+ def validate_single_association(reflection)
305
+ association = association_instance_get(reflection.name)
306
+ record = association && association.reader
307
+ association_valid?(reflection, record) if record && record.changed_for_autosave?
308
+ end
282
309
 
283
- # Validate the associated records if <tt>:validate</tt> or
284
- # <tt>:autosave</tt> is turned on for the association specified by
285
- # +reflection+.
286
- def validate_collection_association(reflection)
287
- if association = association_instance_get(reflection.name)
288
- if records = associated_records_to_validate_or_save(association, new_record?, reflection.options[:autosave])
289
- records.each { |record| association_valid?(reflection, record) }
310
+ # Validate the associated records if <tt>:validate</tt> or
311
+ # <tt>:autosave</tt> is turned on for the association specified by
312
+ # +reflection+.
313
+ def validate_collection_association(reflection)
314
+ if association = association_instance_get(reflection.name)
315
+ if records = associated_records_to_validate_or_save(association, new_record?, reflection.options[:autosave])
316
+ records.each_with_index { |record, index| association_valid?(reflection, record, index) }
317
+ end
290
318
  end
291
319
  end
292
- end
293
320
 
294
- # Returns whether or not the association is valid and applies any errors to
295
- # the parent, <tt>self</tt>, if it wasn't. Skips any <tt>:autosave</tt>
296
- # enabled records if they're marked_for_destruction? or destroyed.
297
- def association_valid?(reflection, record)
298
- return true if record.destroyed? || record.marked_for_destruction?
299
-
300
- unless valid = record.valid?
301
- if reflection.options[:autosave]
302
- record.errors.each do |attribute, message|
303
- attribute = "#{reflection.name}.#{attribute}"
304
- errors[attribute] << message
305
- errors[attribute].uniq!
321
+ # Returns whether or not the association is valid and applies any errors to
322
+ # the parent, <tt>self</tt>, if it wasn't. Skips any <tt>:autosave</tt>
323
+ # enabled records if they're marked_for_destruction? or destroyed.
324
+ def association_valid?(reflection, record, index = nil)
325
+ return true if record.destroyed? || (reflection.options[:autosave] && record.marked_for_destruction?)
326
+
327
+ context = validation_context unless [:create, :update].include?(validation_context)
328
+
329
+ unless valid = record.valid?(context)
330
+ if reflection.options[:autosave]
331
+ indexed_attribute = !index.nil? && (reflection.options[:index_errors] || ActiveRecord::Base.index_nested_attribute_errors)
332
+
333
+ record.errors.each do |attribute, message|
334
+ attribute = normalize_reflection_attribute(indexed_attribute, reflection, index, attribute)
335
+ errors[attribute] << message
336
+ errors[attribute].uniq!
337
+ end
338
+
339
+ record.errors.details.each_key do |attribute|
340
+ reflection_attribute =
341
+ normalize_reflection_attribute(indexed_attribute, reflection, index, attribute).to_sym
342
+
343
+ record.errors.details[attribute].each do |error|
344
+ errors.details[reflection_attribute] << error
345
+ errors.details[reflection_attribute].uniq!
346
+ end
347
+ end
348
+ else
349
+ errors.add(reflection.name)
306
350
  end
351
+ end
352
+ valid
353
+ end
354
+
355
+ def normalize_reflection_attribute(indexed_attribute, reflection, index, attribute)
356
+ if indexed_attribute
357
+ "#{reflection.name}[#{index}].#{attribute}"
307
358
  else
308
- errors.add(reflection.name)
359
+ "#{reflection.name}.#{attribute}"
309
360
  end
310
361
  end
311
- valid
312
- end
313
362
 
314
- # Is used as a before_save callback to check while saving a collection
315
- # association whether or not the parent was a new record before saving.
316
- def before_save_collection_association
317
- @new_record_before_save = new_record?
318
- true
319
- end
363
+ # Is used as a before_save callback to check while saving a collection
364
+ # association whether or not the parent was a new record before saving.
365
+ def before_save_collection_association
366
+ @new_record_before_save = new_record?
367
+ end
320
368
 
321
- # Saves any new associated records, or all loaded autosave associations if
322
- # <tt>:autosave</tt> is enabled on the association.
323
- #
324
- # In addition, it destroys all children that were marked for destruction
325
- # with mark_for_destruction.
326
- #
327
- # This all happens inside a transaction, _if_ the Transactions module is included into
328
- # ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
329
- def save_collection_association(reflection)
330
- if association = association_instance_get(reflection.name)
331
- autosave = reflection.options[:autosave]
332
-
333
- if records = associated_records_to_validate_or_save(association, @new_record_before_save, autosave)
334
- begin
335
- records.each do |record|
336
- next if record.destroyed?
337
-
338
- saved = true
339
-
340
- if autosave && record.marked_for_destruction?
341
- association.proxy.destroy(record)
342
- elsif autosave != false && (@new_record_before_save || record.new_record?)
343
- if autosave
344
- saved = association.insert_record(record, false)
345
- else
346
- association.insert_record(record) unless reflection.nested?
347
- end
348
- elsif autosave
349
- saved = record.save(:validate => false)
369
+ def after_save_collection_association
370
+ @new_record_before_save = false
371
+ end
372
+
373
+ # Saves any new associated records, or all loaded autosave associations if
374
+ # <tt>:autosave</tt> is enabled on the association.
375
+ #
376
+ # In addition, it destroys all children that were marked for destruction
377
+ # with #mark_for_destruction.
378
+ #
379
+ # This all happens inside a transaction, _if_ the Transactions module is included into
380
+ # ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
381
+ def save_collection_association(reflection)
382
+ if association = association_instance_get(reflection.name)
383
+ autosave = reflection.options[:autosave]
384
+
385
+ # By saving the instance variable in a local variable,
386
+ # we make the whole callback re-entrant.
387
+ new_record_before_save = @new_record_before_save
388
+
389
+ # reconstruct the scope now that we know the owner's id
390
+ association.reset_scope
391
+
392
+ if records = associated_records_to_validate_or_save(association, new_record_before_save, autosave)
393
+ if autosave
394
+ records_to_destroy = records.select(&:marked_for_destruction?)
395
+ records_to_destroy.each { |record| association.destroy(record) }
396
+ records -= records_to_destroy
350
397
  end
351
398
 
352
- raise ActiveRecord::Rollback unless saved
353
- end
354
- rescue
355
- records.each {|x| IdentityMap.remove(x) } if IdentityMap.enabled?
356
- raise
399
+ records.each do |record|
400
+ next if record.destroyed?
401
+
402
+ saved = true
403
+
404
+ if autosave != false && (new_record_before_save || record.new_record?)
405
+ if autosave
406
+ saved = association.insert_record(record, false)
407
+ elsif !reflection.nested?
408
+ association_saved = association.insert_record(record)
409
+
410
+ if reflection.validate?
411
+ errors.add(reflection.name) unless association_saved
412
+ saved = association_saved
413
+ end
414
+ end
415
+ elsif autosave
416
+ saved = record.save(validate: false)
417
+ end
418
+
419
+ raise(RecordInvalid.new(association.owner)) unless saved
420
+ end
357
421
  end
422
+ end
423
+ end
424
+
425
+ # Saves the associated record if it's new or <tt>:autosave</tt> is enabled
426
+ # on the association.
427
+ #
428
+ # In addition, it will destroy the association if it was marked for
429
+ # destruction with #mark_for_destruction.
430
+ #
431
+ # This all happens inside a transaction, _if_ the Transactions module is included into
432
+ # ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
433
+ def save_has_one_association(reflection)
434
+ association = association_instance_get(reflection.name)
435
+ record = association && association.load_target
436
+
437
+ if record && !record.destroyed?
438
+ autosave = reflection.options[:autosave]
439
+
440
+ if autosave && record.marked_for_destruction?
441
+ record.destroy
442
+ elsif autosave != false
443
+ key = reflection.options[:primary_key] ? send(reflection.options[:primary_key]) : id
444
+
445
+ if (autosave && record.changed_for_autosave?) || new_record? || record_changed?(reflection, record, key)
446
+ unless reflection.through_reflection
447
+ record[reflection.foreign_key] = key
448
+ if inverse_reflection = reflection.inverse_of
449
+ record.association(inverse_reflection.name).loaded!
450
+ end
451
+ end
358
452
 
453
+ saved = record.save(validate: !autosave)
454
+ raise ActiveRecord::Rollback if !saved && autosave
455
+ saved
456
+ end
457
+ end
359
458
  end
459
+ end
360
460
 
361
- # reconstruct the scope now that we know the owner's id
362
- association.send(:reset_scope) if association.respond_to?(:reset_scope)
461
+ # If the record is new or it has changed, returns true.
462
+ def record_changed?(reflection, record, key)
463
+ record.new_record? ||
464
+ association_foreign_key_changed?(reflection, record, key) ||
465
+ record.will_save_change_to_attribute?(reflection.foreign_key)
363
466
  end
364
- end
365
467
 
366
- # Saves the associated record if it's new or <tt>:autosave</tt> is enabled
367
- # on the association.
368
- #
369
- # In addition, it will destroy the association if it was marked for
370
- # destruction with mark_for_destruction.
371
- #
372
- # This all happens inside a transaction, _if_ the Transactions module is included into
373
- # ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
374
- def save_has_one_association(reflection)
375
- association = association_instance_get(reflection.name)
376
- record = association && association.load_target
377
- if record && !record.destroyed?
378
- autosave = reflection.options[:autosave]
379
-
380
- if autosave && record.marked_for_destruction?
381
- record.destroy
382
- else
383
- key = reflection.options[:primary_key] ? send(reflection.options[:primary_key]) : id
384
- if autosave != false && (new_record? || record.new_record? || record[reflection.foreign_key] != key || autosave)
385
- unless reflection.through_reflection
386
- record[reflection.foreign_key] = key
468
+ def association_foreign_key_changed?(reflection, record, key)
469
+ return false if reflection.through_reflection?
470
+
471
+ record.has_attribute?(reflection.foreign_key) && record[reflection.foreign_key] != key
472
+ end
473
+
474
+ # Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
475
+ #
476
+ # In addition, it will destroy the association if it was marked for destruction.
477
+ def save_belongs_to_association(reflection)
478
+ association = association_instance_get(reflection.name)
479
+ return unless association && association.loaded? && !association.stale_target?
480
+
481
+ record = association.load_target
482
+ if record && !record.destroyed?
483
+ autosave = reflection.options[:autosave]
484
+
485
+ if autosave && record.marked_for_destruction?
486
+ self[reflection.foreign_key] = nil
487
+ record.destroy
488
+ elsif autosave != false
489
+ saved = record.save(validate: !autosave) if record.new_record? || (autosave && record.changed_for_autosave?)
490
+
491
+ if association.updated?
492
+ association_id = record.send(reflection.options[:primary_key] || :id)
493
+ self[reflection.foreign_key] = association_id
494
+ association.loaded!
387
495
  end
388
496
 
389
- saved = record.save(:validate => !autosave)
390
- raise ActiveRecord::Rollback if !saved && autosave
391
- saved
497
+ saved if autosave
392
498
  end
393
499
  end
394
500
  end
395
- end
396
501
 
397
- # Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
398
- #
399
- # In addition, it will destroy the association if it was marked for destruction.
400
- def save_belongs_to_association(reflection)
401
- association = association_instance_get(reflection.name)
402
- record = association && association.load_target
403
- if record && !record.destroyed?
404
- autosave = reflection.options[:autosave]
405
-
406
- if autosave && record.marked_for_destruction?
407
- record.destroy
408
- elsif autosave != false
409
- saved = record.save(:validate => !autosave) if record.new_record? || (autosave && record.changed_for_autosave?)
410
-
411
- if association.updated?
412
- association_id = record.send(reflection.options[:primary_key] || :id)
413
- self[reflection.foreign_key] = association_id
414
- association.loaded!
415
- end
416
-
417
- saved if autosave
502
+ def _ensure_no_duplicate_errors
503
+ errors.messages.each_key do |attribute|
504
+ errors[attribute].uniq!
418
505
  end
419
506
  end
420
- end
421
507
  end
422
508
  end