activerecord 5.0.6 → 6.0.1

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

Potentially problematic release.


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

Files changed (358) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +638 -2023
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +8 -6
  5. data/examples/performance.rb +31 -29
  6. data/examples/simple.rb +5 -3
  7. data/lib/active_record/aggregations.rb +249 -246
  8. data/lib/active_record/association_relation.rb +24 -13
  9. data/lib/active_record/associations/alias_tracker.rb +24 -33
  10. data/lib/active_record/associations/association.rb +119 -56
  11. data/lib/active_record/associations/association_scope.rb +94 -94
  12. data/lib/active_record/associations/belongs_to_association.rb +58 -42
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
  14. data/lib/active_record/associations/builder/association.rb +18 -25
  15. data/lib/active_record/associations/builder/belongs_to.rb +43 -54
  16. data/lib/active_record/associations/builder/collection_association.rb +7 -18
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +42 -61
  18. data/lib/active_record/associations/builder/has_many.rb +4 -0
  19. data/lib/active_record/associations/builder/has_one.rb +37 -1
  20. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  21. data/lib/active_record/associations/collection_association.rb +80 -252
  22. data/lib/active_record/associations/collection_proxy.rb +158 -121
  23. data/lib/active_record/associations/foreign_association.rb +9 -0
  24. data/lib/active_record/associations/has_many_association.rb +23 -29
  25. data/lib/active_record/associations/has_many_through_association.rb +58 -44
  26. data/lib/active_record/associations/has_one_association.rb +59 -54
  27. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  28. data/lib/active_record/associations/join_dependency/join_association.rb +38 -90
  29. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
  31. data/lib/active_record/associations/join_dependency.rb +134 -176
  32. data/lib/active_record/associations/preloader/association.rb +84 -125
  33. data/lib/active_record/associations/preloader/through_association.rb +82 -75
  34. data/lib/active_record/associations/preloader.rb +90 -102
  35. data/lib/active_record/associations/singular_association.rb +12 -45
  36. data/lib/active_record/associations/through_association.rb +26 -14
  37. data/lib/active_record/associations.rb +1603 -1592
  38. data/lib/active_record/attribute_assignment.rb +54 -60
  39. data/lib/active_record/attribute_decorators.rb +38 -15
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +12 -7
  41. data/lib/active_record/attribute_methods/dirty.rb +179 -109
  42. data/lib/active_record/attribute_methods/primary_key.rb +86 -91
  43. data/lib/active_record/attribute_methods/query.rb +4 -3
  44. data/lib/active_record/attribute_methods/read.rb +21 -49
  45. data/lib/active_record/attribute_methods/serialization.rb +30 -7
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -64
  47. data/lib/active_record/attribute_methods/write.rb +35 -33
  48. data/lib/active_record/attribute_methods.rb +66 -106
  49. data/lib/active_record/attributes.rb +38 -24
  50. data/lib/active_record/autosave_association.rb +53 -32
  51. data/lib/active_record/base.rb +27 -24
  52. data/lib/active_record/callbacks.rb +63 -33
  53. data/lib/active_record/coders/json.rb +2 -0
  54. data/lib/active_record/coders/yaml_column.rb +11 -11
  55. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +553 -321
  56. data/lib/active_record/connection_adapters/abstract/database_limits.rb +23 -5
  57. data/lib/active_record/connection_adapters/abstract/database_statements.rb +213 -94
  58. data/lib/active_record/connection_adapters/abstract/query_cache.rb +59 -28
  59. data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -75
  60. data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
  61. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +33 -27
  62. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +207 -126
  63. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +68 -80
  64. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +369 -199
  65. data/lib/active_record/connection_adapters/abstract/transaction.rb +169 -78
  66. data/lib/active_record/connection_adapters/abstract_adapter.rb +363 -202
  67. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +405 -551
  68. data/lib/active_record/connection_adapters/column.rb +41 -13
  69. data/lib/active_record/connection_adapters/connection_specification.rb +172 -138
  70. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +11 -4
  71. data/lib/active_record/connection_adapters/mysql/column.rb +8 -31
  72. data/lib/active_record/connection_adapters/mysql/database_statements.rb +143 -49
  73. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +24 -22
  74. data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -20
  75. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +50 -45
  76. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +58 -56
  77. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +70 -36
  78. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
  79. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +12 -13
  80. data/lib/active_record/connection_adapters/mysql2_adapter.rb +49 -30
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +22 -7
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +60 -54
  83. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +5 -3
  84. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +22 -10
  85. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +6 -5
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
  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 +4 -2
  91. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -3
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +19 -17
  94. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  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 +7 -5
  98. data/lib/active_record/connection_adapters/postgresql/oid/{json.rb → oid.rb} +6 -1
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +31 -9
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +34 -30
  101. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
  102. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
  103. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +9 -4
  104. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid.rb +24 -21
  107. data/lib/active_record/connection_adapters/postgresql/quoting.rb +95 -35
  108. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
  109. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +147 -105
  111. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +35 -32
  112. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +380 -300
  113. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +26 -25
  114. data/lib/active_record/connection_adapters/postgresql/utils.rb +10 -6
  115. data/lib/active_record/connection_adapters/postgresql_adapter.rb +382 -275
  116. data/lib/active_record/connection_adapters/schema_cache.rb +46 -12
  117. data/lib/active_record/connection_adapters/sql_type_metadata.rb +13 -8
  118. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +3 -1
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +74 -19
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +3 -8
  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 +254 -262
  126. data/lib/active_record/connection_adapters/statement_pool.rb +9 -7
  127. data/lib/active_record/connection_handling.rb +159 -40
  128. data/lib/active_record/core.rb +202 -162
  129. data/lib/active_record/counter_cache.rb +57 -28
  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 +87 -86
  136. data/lib/active_record/enum.rb +60 -23
  137. data/lib/active_record/errors.rb +114 -18
  138. data/lib/active_record/explain.rb +4 -3
  139. data/lib/active_record/explain_registry.rb +3 -1
  140. data/lib/active_record/explain_subscriber.rb +9 -4
  141. data/lib/active_record/fixture_set/file.rb +13 -8
  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 +195 -502
  147. data/lib/active_record/gem_version.rb +4 -2
  148. data/lib/active_record/inheritance.rb +151 -97
  149. data/lib/active_record/insert_all.rb +179 -0
  150. data/lib/active_record/integration.rb +116 -25
  151. data/lib/active_record/internal_metadata.rb +15 -18
  152. data/lib/active_record/legacy_yaml_adapter.rb +4 -2
  153. data/lib/active_record/locking/optimistic.rb +78 -87
  154. data/lib/active_record/locking/pessimistic.rb +18 -6
  155. data/lib/active_record/log_subscriber.rb +48 -29
  156. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  157. data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
  158. data/lib/active_record/middleware/database_selector.rb +75 -0
  159. data/lib/active_record/migration/command_recorder.rb +143 -97
  160. data/lib/active_record/migration/compatibility.rb +174 -56
  161. data/lib/active_record/migration/join_table.rb +8 -6
  162. data/lib/active_record/migration.rb +367 -300
  163. data/lib/active_record/model_schema.rb +145 -139
  164. data/lib/active_record/nested_attributes.rb +214 -201
  165. data/lib/active_record/no_touching.rb +10 -1
  166. data/lib/active_record/null_relation.rb +13 -34
  167. data/lib/active_record/persistence.rb +442 -72
  168. data/lib/active_record/query_cache.rb +15 -14
  169. data/lib/active_record/querying.rb +36 -23
  170. data/lib/active_record/railtie.rb +128 -36
  171. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  172. data/lib/active_record/railties/console_sandbox.rb +2 -0
  173. data/lib/active_record/railties/controller_runtime.rb +34 -33
  174. data/lib/active_record/railties/databases.rake +309 -177
  175. data/lib/active_record/readonly_attributes.rb +5 -4
  176. data/lib/active_record/reflection.rb +211 -249
  177. data/lib/active_record/relation/batches/batch_enumerator.rb +3 -1
  178. data/lib/active_record/relation/batches.rb +99 -52
  179. data/lib/active_record/relation/calculations.rb +211 -172
  180. data/lib/active_record/relation/delegation.rb +67 -65
  181. data/lib/active_record/relation/finder_methods.rb +208 -247
  182. data/lib/active_record/relation/from_clause.rb +2 -8
  183. data/lib/active_record/relation/merger.rb +78 -61
  184. data/lib/active_record/relation/predicate_builder/array_handler.rb +20 -14
  185. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  186. data/lib/active_record/relation/predicate_builder/base_handler.rb +4 -3
  187. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +6 -4
  188. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  189. data/lib/active_record/relation/predicate_builder/range_handler.rb +7 -18
  190. data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
  191. data/lib/active_record/relation/predicate_builder.rb +86 -104
  192. data/lib/active_record/relation/query_attribute.rb +33 -2
  193. data/lib/active_record/relation/query_methods.rb +458 -329
  194. data/lib/active_record/relation/record_fetch_warning.rb +5 -3
  195. data/lib/active_record/relation/spawn_methods.rb +8 -7
  196. data/lib/active_record/relation/where_clause.rb +111 -95
  197. data/lib/active_record/relation/where_clause_factory.rb +6 -11
  198. data/lib/active_record/relation.rb +429 -318
  199. data/lib/active_record/result.rb +69 -39
  200. data/lib/active_record/runtime_registry.rb +5 -3
  201. data/lib/active_record/sanitization.rb +83 -99
  202. data/lib/active_record/schema.rb +7 -14
  203. data/lib/active_record/schema_dumper.rb +71 -69
  204. data/lib/active_record/schema_migration.rb +15 -5
  205. data/lib/active_record/scoping/default.rb +93 -95
  206. data/lib/active_record/scoping/named.rb +45 -25
  207. data/lib/active_record/scoping.rb +20 -19
  208. data/lib/active_record/secure_token.rb +4 -2
  209. data/lib/active_record/serialization.rb +2 -0
  210. data/lib/active_record/statement_cache.rb +63 -28
  211. data/lib/active_record/store.rb +121 -41
  212. data/lib/active_record/suppressor.rb +4 -1
  213. data/lib/active_record/table_metadata.rb +26 -20
  214. data/lib/active_record/tasks/database_tasks.rb +276 -85
  215. data/lib/active_record/tasks/mysql_database_tasks.rb +54 -90
  216. data/lib/active_record/tasks/postgresql_database_tasks.rb +78 -47
  217. data/lib/active_record/tasks/sqlite_database_tasks.rb +34 -16
  218. data/lib/active_record/test_databases.rb +23 -0
  219. data/lib/active_record/test_fixtures.rb +224 -0
  220. data/lib/active_record/timestamp.rb +70 -35
  221. data/lib/active_record/touch_later.rb +7 -4
  222. data/lib/active_record/transactions.rb +133 -149
  223. data/lib/active_record/translation.rb +3 -1
  224. data/lib/active_record/type/adapter_specific_registry.rb +44 -45
  225. data/lib/active_record/type/date.rb +2 -0
  226. data/lib/active_record/type/date_time.rb +2 -0
  227. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  228. data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
  229. data/lib/active_record/type/internal/timezone.rb +2 -0
  230. data/lib/active_record/type/json.rb +30 -0
  231. data/lib/active_record/type/serialized.rb +16 -8
  232. data/lib/active_record/type/text.rb +11 -0
  233. data/lib/active_record/type/time.rb +2 -1
  234. data/lib/active_record/type/type_map.rb +13 -15
  235. data/lib/active_record/type/unsigned_integer.rb +17 -0
  236. data/lib/active_record/type.rb +23 -17
  237. data/lib/active_record/type_caster/connection.rb +17 -12
  238. data/lib/active_record/type_caster/map.rb +5 -4
  239. data/lib/active_record/type_caster.rb +4 -2
  240. data/lib/active_record/validations/absence.rb +2 -0
  241. data/lib/active_record/validations/associated.rb +3 -1
  242. data/lib/active_record/validations/length.rb +2 -0
  243. data/lib/active_record/validations/presence.rb +4 -2
  244. data/lib/active_record/validations/uniqueness.rb +29 -42
  245. data/lib/active_record/validations.rb +7 -4
  246. data/lib/active_record/version.rb +3 -1
  247. data/lib/active_record.rb +36 -22
  248. data/lib/arel/alias_predication.rb +9 -0
  249. data/lib/arel/attributes/attribute.rb +37 -0
  250. data/lib/arel/attributes.rb +22 -0
  251. data/lib/arel/collectors/bind.rb +24 -0
  252. data/lib/arel/collectors/composite.rb +31 -0
  253. data/lib/arel/collectors/plain_string.rb +20 -0
  254. data/lib/arel/collectors/sql_string.rb +20 -0
  255. data/lib/arel/collectors/substitute_binds.rb +28 -0
  256. data/lib/arel/crud.rb +42 -0
  257. data/lib/arel/delete_manager.rb +18 -0
  258. data/lib/arel/errors.rb +9 -0
  259. data/lib/arel/expressions.rb +29 -0
  260. data/lib/arel/factory_methods.rb +49 -0
  261. data/lib/arel/insert_manager.rb +49 -0
  262. data/lib/arel/math.rb +45 -0
  263. data/lib/arel/nodes/and.rb +32 -0
  264. data/lib/arel/nodes/ascending.rb +23 -0
  265. data/lib/arel/nodes/binary.rb +52 -0
  266. data/lib/arel/nodes/bind_param.rb +36 -0
  267. data/lib/arel/nodes/case.rb +55 -0
  268. data/lib/arel/nodes/casted.rb +50 -0
  269. data/lib/arel/nodes/comment.rb +29 -0
  270. data/lib/arel/nodes/count.rb +12 -0
  271. data/lib/arel/nodes/delete_statement.rb +45 -0
  272. data/lib/arel/nodes/descending.rb +23 -0
  273. data/lib/arel/nodes/equality.rb +18 -0
  274. data/lib/arel/nodes/extract.rb +24 -0
  275. data/lib/arel/nodes/false.rb +16 -0
  276. data/lib/arel/nodes/full_outer_join.rb +8 -0
  277. data/lib/arel/nodes/function.rb +44 -0
  278. data/lib/arel/nodes/grouping.rb +8 -0
  279. data/lib/arel/nodes/in.rb +8 -0
  280. data/lib/arel/nodes/infix_operation.rb +80 -0
  281. data/lib/arel/nodes/inner_join.rb +8 -0
  282. data/lib/arel/nodes/insert_statement.rb +37 -0
  283. data/lib/arel/nodes/join_source.rb +20 -0
  284. data/lib/arel/nodes/matches.rb +18 -0
  285. data/lib/arel/nodes/named_function.rb +23 -0
  286. data/lib/arel/nodes/node.rb +50 -0
  287. data/lib/arel/nodes/node_expression.rb +13 -0
  288. data/lib/arel/nodes/outer_join.rb +8 -0
  289. data/lib/arel/nodes/over.rb +15 -0
  290. data/lib/arel/nodes/regexp.rb +16 -0
  291. data/lib/arel/nodes/right_outer_join.rb +8 -0
  292. data/lib/arel/nodes/select_core.rb +67 -0
  293. data/lib/arel/nodes/select_statement.rb +41 -0
  294. data/lib/arel/nodes/sql_literal.rb +16 -0
  295. data/lib/arel/nodes/string_join.rb +11 -0
  296. data/lib/arel/nodes/table_alias.rb +27 -0
  297. data/lib/arel/nodes/terminal.rb +16 -0
  298. data/lib/arel/nodes/true.rb +16 -0
  299. data/lib/arel/nodes/unary.rb +45 -0
  300. data/lib/arel/nodes/unary_operation.rb +20 -0
  301. data/lib/arel/nodes/unqualified_column.rb +22 -0
  302. data/lib/arel/nodes/update_statement.rb +41 -0
  303. data/lib/arel/nodes/values_list.rb +9 -0
  304. data/lib/arel/nodes/window.rb +126 -0
  305. data/lib/arel/nodes/with.rb +11 -0
  306. data/lib/arel/nodes.rb +68 -0
  307. data/lib/arel/order_predications.rb +13 -0
  308. data/lib/arel/predications.rb +257 -0
  309. data/lib/arel/select_manager.rb +271 -0
  310. data/lib/arel/table.rb +110 -0
  311. data/lib/arel/tree_manager.rb +72 -0
  312. data/lib/arel/update_manager.rb +34 -0
  313. data/lib/arel/visitors/depth_first.rb +204 -0
  314. data/lib/arel/visitors/dot.rb +297 -0
  315. data/lib/arel/visitors/ibm_db.rb +34 -0
  316. data/lib/arel/visitors/informix.rb +62 -0
  317. data/lib/arel/visitors/mssql.rb +157 -0
  318. data/lib/arel/visitors/mysql.rb +83 -0
  319. data/lib/arel/visitors/oracle.rb +159 -0
  320. data/lib/arel/visitors/oracle12.rb +66 -0
  321. data/lib/arel/visitors/postgresql.rb +110 -0
  322. data/lib/arel/visitors/sqlite.rb +39 -0
  323. data/lib/arel/visitors/to_sql.rb +889 -0
  324. data/lib/arel/visitors/visitor.rb +46 -0
  325. data/lib/arel/visitors/where_sql.rb +23 -0
  326. data/lib/arel/visitors.rb +20 -0
  327. data/lib/arel/window_predications.rb +9 -0
  328. data/lib/arel.rb +58 -0
  329. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  330. data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
  331. data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -35
  332. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +1 -1
  333. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +4 -2
  334. data/lib/rails/generators/active_record/migration.rb +17 -2
  335. data/lib/rails/generators/active_record/model/model_generator.rb +9 -29
  336. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +10 -1
  337. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  338. data/lib/rails/generators/active_record.rb +7 -5
  339. metadata +133 -50
  340. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  341. data/lib/active_record/associations/preloader/collection_association.rb +0 -17
  342. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  343. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  344. data/lib/active_record/associations/preloader/has_one.rb +0 -15
  345. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  346. data/lib/active_record/associations/preloader/singular_association.rb +0 -20
  347. data/lib/active_record/attribute/user_provided_default.rb +0 -28
  348. data/lib/active_record/attribute.rb +0 -213
  349. data/lib/active_record/attribute_mutation_tracker.rb +0 -70
  350. data/lib/active_record/attribute_set/builder.rb +0 -130
  351. data/lib/active_record/attribute_set.rb +0 -110
  352. data/lib/active_record/collection_cache_key.rb +0 -50
  353. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +0 -50
  354. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  355. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
  356. data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
  357. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -57
  358. data/lib/active_record/type/internal/abstract_json.rb +0 -33
@@ -1,25 +1,28 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
- # = Active Record Belongs To Association
3
4
  module Associations
5
+ # = Active Record Belongs To Association
4
6
  class BelongsToAssociation < SingularAssociation #:nodoc:
5
-
6
7
  def handle_dependency
7
- target.send(options[:dependent]) if load_target
8
- end
8
+ return unless load_target
9
9
 
10
- def replace(record)
11
- if record
12
- raise_on_type_mismatch!(record)
13
- update_counters_on_replace(record)
14
- replace_keys(record)
15
- set_inverse_instance(record)
16
- @updated = true
10
+ case options[:dependent]
11
+ when :destroy
12
+ target.destroy
13
+ raise ActiveRecord::Rollback unless target.destroyed?
17
14
  else
18
- decrement_counters
19
- remove_keys
15
+ target.send(options[:dependent])
20
16
  end
17
+ end
18
+
19
+ def inversed_from(record)
20
+ replace_keys(record)
21
+ super
22
+ end
21
23
 
22
- self.target = record
24
+ def default(&block)
25
+ writer(owner.instance_exec(&block)) if reader.nil?
23
26
  end
24
27
 
25
28
  def reset
@@ -31,26 +34,60 @@ module ActiveRecord
31
34
  @updated
32
35
  end
33
36
 
34
- def decrement_counters # :nodoc:
37
+ def decrement_counters
35
38
  update_counters(-1)
36
39
  end
37
40
 
38
- def increment_counters # :nodoc:
41
+ def increment_counters
39
42
  update_counters(1)
40
43
  end
41
44
 
45
+ def decrement_counters_before_last_save
46
+ if reflection.polymorphic?
47
+ model_was = owner.attribute_before_last_save(reflection.foreign_type).try(:constantize)
48
+ else
49
+ model_was = klass
50
+ end
51
+
52
+ foreign_key_was = owner.attribute_before_last_save(reflection.foreign_key)
53
+
54
+ if foreign_key_was && model_was < ActiveRecord::Base
55
+ update_counters_via_scope(model_was, foreign_key_was, -1)
56
+ end
57
+ end
58
+
59
+ def target_changed?
60
+ owner.saved_change_to_attribute?(reflection.foreign_key)
61
+ end
62
+
42
63
  private
64
+ def replace(record)
65
+ if record
66
+ raise_on_type_mismatch!(record)
67
+ set_inverse_instance(record)
68
+ @updated = true
69
+ end
70
+
71
+ replace_keys(record)
72
+
73
+ self.target = record
74
+ end
43
75
 
44
76
  def update_counters(by)
45
77
  if require_counter_update? && foreign_key_present?
46
78
  if target && !stale_target?
47
- target.increment!(reflection.counter_cache_column, by)
79
+ target.increment!(reflection.counter_cache_column, by, touch: reflection.options[:touch])
48
80
  else
49
- klass.update_counters(target_id, reflection.counter_cache_column => by)
81
+ update_counters_via_scope(klass, owner._read_attribute(reflection.foreign_key), by)
50
82
  end
51
83
  end
52
84
  end
53
85
 
86
+ def update_counters_via_scope(klass, foreign_key, by)
87
+ scope = klass.unscoped.where!(primary_key(klass) => foreign_key)
88
+ scope.update_counters(reflection.counter_cache_column => by, touch: reflection.options[:touch])
89
+ end
90
+
54
91
  def find_target?
55
92
  !loaded? && foreign_key_present? && klass
56
93
  end
@@ -59,25 +96,12 @@ module ActiveRecord
59
96
  reflection.counter_cache_column && owner.persisted?
60
97
  end
61
98
 
62
- def update_counters_on_replace(record)
63
- if require_counter_update? && different_target?(record)
64
- owner.instance_variable_set :@_after_replace_counter_called, true
65
- record.increment!(reflection.counter_cache_column)
66
- decrement_counters
67
- end
68
- end
69
-
70
- # Checks whether record is different to the current target, without loading it
71
- def different_target?(record)
72
- record.id != owner._read_attribute(reflection.foreign_key)
73
- end
74
-
75
99
  def replace_keys(record)
76
- owner[reflection.foreign_key] = record._read_attribute(reflection.association_primary_key(record.class))
100
+ owner[reflection.foreign_key] = record ? record._read_attribute(primary_key(record.class)) : nil
77
101
  end
78
102
 
79
- def remove_keys
80
- owner[reflection.foreign_key] = nil
103
+ def primary_key(klass)
104
+ reflection.association_primary_key(klass)
81
105
  end
82
106
 
83
107
  def foreign_key_present?
@@ -91,14 +115,6 @@ module ActiveRecord
91
115
  inverse && inverse.has_one?
92
116
  end
93
117
 
94
- def target_id
95
- if options[:primary_key]
96
- owner.send(reflection.name).try(:id)
97
- else
98
- owner._read_attribute(reflection.foreign_key)
99
- end
100
- end
101
-
102
118
  def stale_state
103
119
  result = owner._read_attribute(reflection.foreign_key) { |n| owner.send(:missing_attribute, n, caller) }
104
120
  result && result.to_s
@@ -1,26 +1,22 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
- # = Active Record Belongs To Polymorphic Association
3
4
  module Associations
5
+ # = Active Record Belongs To Polymorphic Association
4
6
  class BelongsToPolymorphicAssociation < BelongsToAssociation #:nodoc:
5
7
  def klass
6
8
  type = owner[reflection.foreign_type]
7
9
  type.presence && type.constantize
8
10
  end
9
11
 
10
- private
12
+ def target_changed?
13
+ super || owner.saved_change_to_attribute?(reflection.foreign_type)
14
+ end
11
15
 
16
+ private
12
17
  def replace_keys(record)
13
18
  super
14
- owner[reflection.foreign_type] = record.class.base_class.name
15
- end
16
-
17
- def remove_keys
18
- super
19
- owner[reflection.foreign_type] = nil
20
- end
21
-
22
- def different_target?(record)
23
- super || record.class != klass
19
+ owner[reflection.foreign_type] = record ? record.class.polymorphic_name : nil
24
20
  end
25
21
 
26
22
  def inverse_reflection_for(record)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This is the parent Association class which defines the variables
2
4
  # used by all associations.
3
5
  #
@@ -25,45 +27,32 @@ module ActiveRecord::Associations::Builder # :nodoc:
25
27
  "Please choose a different association name."
26
28
  end
27
29
 
28
- extension = define_extensions model, name, &block
29
- reflection = create_reflection model, name, scope, options, extension
30
+ reflection = create_reflection(model, name, scope, options, &block)
30
31
  define_accessors model, reflection
31
32
  define_callbacks model, reflection
32
33
  define_validations model, reflection
33
34
  reflection
34
35
  end
35
36
 
36
- def self.create_reflection(model, name, scope, options, extension = nil)
37
+ def self.create_reflection(model, name, scope, options, &block)
37
38
  raise ArgumentError, "association names must be a Symbol" unless name.kind_of?(Symbol)
38
39
 
39
- if scope.is_a?(Hash)
40
- options = scope
41
- scope = nil
42
- end
43
-
44
40
  validate_options(options)
45
41
 
46
- scope = build_scope(scope, extension)
42
+ extension = define_extensions(model, name, &block)
43
+ options[:extend] = [*options[:extend], extension] if extension
44
+
45
+ scope = build_scope(scope)
47
46
 
48
47
  ActiveRecord::Reflection.create(macro, name, scope, options, model)
49
48
  end
50
49
 
51
- def self.build_scope(scope, extension)
52
- new_scope = scope
53
-
50
+ def self.build_scope(scope)
54
51
  if scope && scope.arity == 0
55
- new_scope = proc { instance_exec(&scope) }
52
+ proc { instance_exec(&scope) }
53
+ else
54
+ scope
56
55
  end
57
-
58
- if extension
59
- new_scope = wrap_scope new_scope, extension
60
- end
61
-
62
- new_scope
63
- end
64
-
65
- def self.wrap_scope(scope, extension)
66
- scope
67
56
  end
68
57
 
69
58
  def self.macro
@@ -107,8 +96,8 @@ module ActiveRecord::Associations::Builder # :nodoc:
107
96
 
108
97
  def self.define_readers(mixin, name)
109
98
  mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
110
- def #{name}(*args)
111
- association(:#{name}).reader(*args)
99
+ def #{name}
100
+ association(:#{name}).reader
112
101
  end
113
102
  CODE
114
103
  end
@@ -139,5 +128,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
139
128
  name = reflection.name
140
129
  model.before_destroy lambda { |o| o.association(name).handle_dependency }
141
130
  end
131
+
132
+ private_class_method :build_scope, :macro, :valid_options, :validate_options, :define_extensions,
133
+ :define_callbacks, :define_accessors, :define_readers, :define_writers, :define_validations,
134
+ :valid_dependent_options, :check_dependent_options, :add_destroy_callbacks
142
135
  end
143
136
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord::Associations::Builder # :nodoc:
2
4
  class BelongsTo < SingularAssociation #:nodoc:
3
5
  def self.macro
@@ -5,7 +7,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
5
7
  end
6
8
 
7
9
  def self.valid_options(options)
8
- super + [:polymorphic, :touch, :counter_cache, :optional]
10
+ super + [:polymorphic, :touch, :counter_cache, :optional, :default]
9
11
  end
10
12
 
11
13
  def self.valid_dependent_options
@@ -16,72 +18,40 @@ module ActiveRecord::Associations::Builder # :nodoc:
16
18
  super
17
19
  add_counter_cache_callbacks(model, reflection) if reflection.options[:counter_cache]
18
20
  add_touch_callbacks(model, reflection) if reflection.options[:touch]
19
- end
20
-
21
- def self.define_accessors(mixin, reflection)
22
- super
23
- add_counter_cache_methods mixin
24
- end
25
-
26
- def self.add_counter_cache_methods(mixin)
27
- return if mixin.method_defined? :belongs_to_counter_cache_after_update
28
-
29
- mixin.class_eval do
30
- def belongs_to_counter_cache_after_update(reflection)
31
- foreign_key = reflection.foreign_key
32
- cache_column = reflection.counter_cache_column
33
-
34
- if (@_after_create_counter_called ||= false)
35
- @_after_create_counter_called = false
36
- elsif (@_after_replace_counter_called ||= false)
37
- @_after_replace_counter_called = false
38
- elsif attribute_changed?(foreign_key) && !new_record?
39
- if reflection.polymorphic?
40
- model = attribute(reflection.foreign_type).try(:constantize)
41
- model_was = attribute_was(reflection.foreign_type).try(:constantize)
42
- else
43
- model = reflection.klass
44
- model_was = reflection.klass
45
- end
46
-
47
- foreign_key_was = attribute_was foreign_key
48
- foreign_key = attribute foreign_key
49
-
50
- if foreign_key && model.respond_to?(:increment_counter)
51
- model.increment_counter(cache_column, foreign_key)
52
- end
53
-
54
- if foreign_key_was && model_was.respond_to?(:decrement_counter)
55
- model_was.decrement_counter(cache_column, foreign_key_was)
56
- end
57
- end
58
- end
59
- end
21
+ add_default_callbacks(model, reflection) if reflection.options[:default]
60
22
  end
61
23
 
62
24
  def self.add_counter_cache_callbacks(model, reflection)
63
25
  cache_column = reflection.counter_cache_column
64
26
 
65
27
  model.after_update lambda { |record|
66
- record.belongs_to_counter_cache_after_update(reflection)
28
+ association = association(reflection.name)
29
+
30
+ if association.target_changed?
31
+ association.increment_counters
32
+ association.decrement_counters_before_last_save
33
+ end
67
34
  }
68
35
 
69
36
  klass = reflection.class_name.safe_constantize
70
37
  klass.attr_readonly cache_column if klass && klass.respond_to?(:attr_readonly)
71
38
  end
72
39
 
73
- def self.touch_record(o, foreign_key, name, touch, touch_method) # :nodoc:
74
- old_foreign_id = o.changed_attributes[foreign_key]
40
+ def self.touch_record(o, changes, foreign_key, name, touch, touch_method) # :nodoc:
41
+ old_foreign_id = changes[foreign_key] && changes[foreign_key].first
75
42
 
76
43
  if old_foreign_id
77
44
  association = o.association(name)
78
45
  reflection = association.reflection
79
46
  if reflection.polymorphic?
80
- klass = o.public_send("#{reflection.foreign_type}_was").constantize
47
+ foreign_type = reflection.foreign_type
48
+ klass = changes[foreign_type] && changes[foreign_type].first || o.public_send(foreign_type)
49
+ klass = klass.constantize
81
50
  else
82
51
  klass = association.klass
83
52
  end
84
- old_record = klass.find_by(klass.primary_key => old_foreign_id)
53
+ primary_key = reflection.association_primary_key(klass)
54
+ old_record = klass.find_by(primary_key => old_foreign_id)
85
55
 
86
56
  if old_record
87
57
  if touch != true
@@ -104,16 +74,32 @@ module ActiveRecord::Associations::Builder # :nodoc:
104
74
 
105
75
  def self.add_touch_callbacks(model, reflection)
106
76
  foreign_key = reflection.foreign_key
107
- n = reflection.name
77
+ name = reflection.name
108
78
  touch = reflection.options[:touch]
109
79
 
110
- callback = lambda { |record|
111
- BelongsTo.touch_record(record, foreign_key, n, touch, belongs_to_touch_method)
112
- }
80
+ callback = lambda { |changes_method| lambda { |record|
81
+ BelongsTo.touch_record(record, record.send(changes_method), foreign_key, name, touch, belongs_to_touch_method)
82
+ }}
83
+
84
+ if reflection.counter_cache_column
85
+ touch_callback = callback.(:saved_changes)
86
+ update_callback = lambda { |record|
87
+ instance_exec(record, &touch_callback) unless association(reflection.name).target_changed?
88
+ }
89
+ model.after_update update_callback, if: :saved_changes?
90
+ else
91
+ model.after_create callback.(:saved_changes), if: :saved_changes?
92
+ model.after_update callback.(:saved_changes), if: :saved_changes?
93
+ model.after_destroy callback.(:changes_to_save)
94
+ end
113
95
 
114
- model.after_save callback, if: :changed?
115
- model.after_touch callback
116
- model.after_destroy callback
96
+ model.after_touch callback.(:changes_to_save)
97
+ end
98
+
99
+ def self.add_default_callbacks(model, reflection)
100
+ model.before_validation lambda { |o|
101
+ o.association(reflection.name).default(&reflection.options[:default])
102
+ }
117
103
  end
118
104
 
119
105
  def self.add_destroy_callbacks(model, reflection)
@@ -137,5 +123,8 @@ module ActiveRecord::Associations::Builder # :nodoc:
137
123
  model.validates_presence_of reflection.name, message: :required
138
124
  end
139
125
  end
126
+
127
+ private_class_method :macro, :valid_options, :valid_dependent_options, :define_callbacks, :define_validations,
128
+ :add_counter_cache_callbacks, :add_touch_callbacks, :add_default_callbacks, :add_destroy_callbacks
140
129
  end
141
130
  end
@@ -1,10 +1,9 @@
1
- # This class is inherited by the has_many and has_many_and_belongs_to_many association classes
1
+ # frozen_string_literal: true
2
2
 
3
- require 'active_record/associations'
3
+ require "active_record/associations"
4
4
 
5
5
  module ActiveRecord::Associations::Builder # :nodoc:
6
6
  class CollectionAssociation < Association #:nodoc:
7
-
8
7
  CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove]
9
8
 
10
9
  def self.valid_options(options)
@@ -21,11 +20,11 @@ module ActiveRecord::Associations::Builder # :nodoc:
21
20
  }
22
21
  end
23
22
 
24
- def self.define_extensions(model, name)
23
+ def self.define_extensions(model, name, &block)
25
24
  if block_given?
26
- extension_module_name = "#{model.name.demodulize}#{name.to_s.camelize}AssociationExtension"
27
- extension = Module.new(&Proc.new)
28
- model.parent.const_set(extension_module_name, extension)
25
+ extension_module_name = "#{name.to_s.camelize}AssociationExtension"
26
+ extension = Module.new(&block)
27
+ model.const_set(extension_module_name, extension)
29
28
  end
30
29
  end
31
30
 
@@ -68,16 +67,6 @@ module ActiveRecord::Associations::Builder # :nodoc:
68
67
  CODE
69
68
  end
70
69
 
71
- def self.wrap_scope(scope, mod)
72
- if scope
73
- if scope.arity > 0
74
- proc { |owner| instance_exec(owner, &scope).extending(mod) }
75
- else
76
- proc { instance_exec(&scope).extending(mod) }
77
- end
78
- else
79
- proc { extending(mod) }
80
- end
81
- end
70
+ private_class_method :valid_options, :define_callback, :define_extensions, :define_readers, :define_writers
82
71
  end
83
72
  end
@@ -1,38 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord::Associations::Builder # :nodoc:
2
4
  class HasAndBelongsToMany # :nodoc:
3
- class JoinTableResolver # :nodoc:
4
- KnownTable = Struct.new :join_table
5
-
6
- class KnownClass # :nodoc:
7
- def initialize(lhs_class, rhs_class_name)
8
- @lhs_class = lhs_class
9
- @rhs_class_name = rhs_class_name
10
- @join_table = nil
11
- end
12
-
13
- def join_table
14
- @join_table ||= [@lhs_class.table_name, klass.table_name].sort.join("\0").gsub(/^(.*[._])(.+)\0\1(.+)/, '\1\2_\3').tr("\0", "_")
15
- end
16
-
17
- private
18
-
19
- def klass
20
- @lhs_class.send(:compute_type, @rhs_class_name)
21
- end
22
- end
23
-
24
- def self.build(lhs_class, name, options)
25
- if options[:join_table]
26
- KnownTable.new options[:join_table].to_s
27
- else
28
- class_name = options.fetch(:class_name) {
29
- name.to_s.camelize.singularize
30
- }
31
- KnownClass.new lhs_class, class_name
32
- end
33
- end
34
- end
35
-
36
5
  attr_reader :lhs_model, :association_name, :options
37
6
 
38
7
  def initialize(association_name, lhs_model, options)
@@ -42,10 +11,8 @@ module ActiveRecord::Associations::Builder # :nodoc:
42
11
  end
43
12
 
44
13
  def through_model
45
- habtm = JoinTableResolver.build lhs_model, association_name, options
46
-
47
14
  join_model = Class.new(ActiveRecord::Base) {
48
- class << self;
15
+ class << self
49
16
  attr_accessor :left_model
50
17
  attr_accessor :name
51
18
  attr_accessor :table_name_resolver
@@ -54,7 +21,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
54
21
  end
55
22
 
56
23
  def self.table_name
57
- table_name_resolver.join_table
24
+ # Table name needs to be resolved lazily
25
+ # because RHS class might not have been loaded
26
+ @table_name ||= table_name_resolver.call
58
27
  end
59
28
 
60
29
  def self.compute_type(class_name)
@@ -78,13 +47,13 @@ module ActiveRecord::Associations::Builder # :nodoc:
78
47
 
79
48
  private
80
49
 
81
- def self.suppress_composite_primary_key(pk)
82
- pk unless pk.is_a?(Array)
83
- end
50
+ def self.suppress_composite_primary_key(pk)
51
+ pk unless pk.is_a?(Array)
52
+ end
84
53
  }
85
54
 
86
55
  join_model.name = "HABTM_#{association_name.to_s.camelize}"
87
- join_model.table_name_resolver = habtm
56
+ join_model.table_name_resolver = -> { table_name }
88
57
  join_model.left_model = lhs_model
89
58
 
90
59
  join_model.add_left_association :left_side, anonymous_class: lhs_model
@@ -94,7 +63,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
94
63
 
95
64
  def middle_reflection(join_model)
96
65
  middle_name = [lhs_model.name.downcase.pluralize,
97
- association_name].join('_'.freeze).gsub('::'.freeze, '_'.freeze).to_sym
66
+ association_name.to_s].sort.join("_").gsub("::", "_").to_sym
98
67
  middle_options = middle_options join_model
99
68
 
100
69
  HasMany.create_reflection(lhs_model,
@@ -105,29 +74,41 @@ module ActiveRecord::Associations::Builder # :nodoc:
105
74
 
106
75
  private
107
76
 
108
- def middle_options(join_model)
109
- middle_options = {}
110
- middle_options[:class_name] = "#{lhs_model.name}::#{join_model.name}"
111
- middle_options[:source] = join_model.left_reflection.name
112
- if options.key? :foreign_key
113
- middle_options[:foreign_key] = options[:foreign_key]
77
+ def middle_options(join_model)
78
+ middle_options = {}
79
+ middle_options[:class_name] = "#{lhs_model.name}::#{join_model.name}"
80
+ middle_options[:source] = join_model.left_reflection.name
81
+ if options.key? :foreign_key
82
+ middle_options[:foreign_key] = options[:foreign_key]
83
+ end
84
+ middle_options
114
85
  end
115
- middle_options
116
- end
117
-
118
- def belongs_to_options(options)
119
- rhs_options = {}
120
86
 
121
- if options.key? :class_name
122
- rhs_options[:foreign_key] = options[:class_name].to_s.foreign_key
123
- rhs_options[:class_name] = options[:class_name]
87
+ def table_name
88
+ if options[:join_table]
89
+ options[:join_table].to_s
90
+ else
91
+ class_name = options.fetch(:class_name) {
92
+ association_name.to_s.camelize.singularize
93
+ }
94
+ klass = lhs_model.send(:compute_type, class_name.to_s)
95
+ [lhs_model.table_name, klass.table_name].sort.join("\0").gsub(/^(.*[._])(.+)\0\1(.+)/, '\1\2_\3').tr("\0", "_")
96
+ end
124
97
  end
125
98
 
126
- if options.key? :association_foreign_key
127
- rhs_options[:foreign_key] = options[:association_foreign_key]
128
- end
99
+ def belongs_to_options(options)
100
+ rhs_options = {}
129
101
 
130
- rhs_options
131
- end
102
+ if options.key? :class_name
103
+ rhs_options[:foreign_key] = options[:class_name].to_s.foreign_key
104
+ rhs_options[:class_name] = options[:class_name]
105
+ end
106
+
107
+ if options.key? :association_foreign_key
108
+ rhs_options[:foreign_key] = options[:association_foreign_key]
109
+ end
110
+
111
+ rhs_options
112
+ end
132
113
  end
133
114
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord::Associations::Builder # :nodoc:
2
4
  class HasMany < CollectionAssociation #:nodoc:
3
5
  def self.macro
@@ -11,5 +13,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
11
13
  def self.valid_dependent_options
12
14
  [:destroy, :delete_all, :nullify, :restrict_with_error, :restrict_with_exception]
13
15
  end
16
+
17
+ private_class_method :macro, :valid_options, :valid_dependent_options
14
18
  end
15
19
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord::Associations::Builder # :nodoc:
2
4
  class HasOne < SingularAssociation #:nodoc:
3
5
  def self.macro
@@ -5,7 +7,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
5
7
  end
6
8
 
7
9
  def self.valid_options(options)
8
- valid = super + [:as]
10
+ valid = super + [:as, :touch]
9
11
  valid += [:through, :source, :source_type] if options[:through]
10
12
  valid
11
13
  end
@@ -14,6 +16,11 @@ module ActiveRecord::Associations::Builder # :nodoc:
14
16
  [:destroy, :delete, :nullify, :restrict_with_error, :restrict_with_exception]
15
17
  end
16
18
 
19
+ def self.define_callbacks(model, reflection)
20
+ super
21
+ add_touch_callbacks(model, reflection) if reflection.options[:touch]
22
+ end
23
+
17
24
  def self.add_destroy_callbacks(model, reflection)
18
25
  super unless reflection.options[:through]
19
26
  end
@@ -24,5 +31,34 @@ module ActiveRecord::Associations::Builder # :nodoc:
24
31
  model.validates_presence_of reflection.name, message: :required
25
32
  end
26
33
  end
34
+
35
+ def self.touch_record(o, name, touch)
36
+ record = o.send name
37
+
38
+ return unless record && record.persisted?
39
+
40
+ if touch != true
41
+ record.touch(touch)
42
+ else
43
+ record.touch
44
+ end
45
+ end
46
+
47
+ def self.add_touch_callbacks(model, reflection)
48
+ name = reflection.name
49
+ touch = reflection.options[:touch]
50
+
51
+ callback = lambda { |record|
52
+ HasOne.touch_record(record, name, touch)
53
+ }
54
+
55
+ model.after_create callback, if: :saved_changes?
56
+ model.after_update callback, if: :saved_changes?
57
+ model.after_destroy callback
58
+ model.after_touch callback
59
+ end
60
+
61
+ private_class_method :macro, :valid_options, :valid_dependent_options, :add_destroy_callbacks,
62
+ :define_callbacks, :define_validations, :add_touch_callbacks
27
63
  end
28
64
  end