activerecord 5.2.6 → 6.1.3.2

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 (316) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1038 -571
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +7 -5
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +13 -12
  7. data/lib/active_record/aggregations.rb +9 -8
  8. data/lib/active_record/association_relation.rb +30 -10
  9. data/lib/active_record/associations.rb +137 -25
  10. data/lib/active_record/associations/alias_tracker.rb +19 -16
  11. data/lib/active_record/associations/association.rb +95 -42
  12. data/lib/active_record/associations/association_scope.rb +23 -21
  13. data/lib/active_record/associations/belongs_to_association.rb +54 -46
  14. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -6
  15. data/lib/active_record/associations/builder/association.rb +45 -22
  16. data/lib/active_record/associations/builder/belongs_to.rb +29 -59
  17. data/lib/active_record/associations/builder/collection_association.rb +8 -17
  18. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -41
  19. data/lib/active_record/associations/builder/has_many.rb +8 -2
  20. data/lib/active_record/associations/builder/has_one.rb +33 -2
  21. data/lib/active_record/associations/builder/singular_association.rb +3 -1
  22. data/lib/active_record/associations/collection_association.rb +31 -29
  23. data/lib/active_record/associations/collection_proxy.rb +25 -21
  24. data/lib/active_record/associations/foreign_association.rb +20 -0
  25. data/lib/active_record/associations/has_many_association.rb +26 -13
  26. data/lib/active_record/associations/has_many_through_association.rb +24 -18
  27. data/lib/active_record/associations/has_one_association.rb +43 -31
  28. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  29. data/lib/active_record/associations/join_dependency.rb +91 -60
  30. data/lib/active_record/associations/join_dependency/join_association.rb +44 -22
  31. data/lib/active_record/associations/join_dependency/join_part.rb +5 -5
  32. data/lib/active_record/associations/preloader.rb +47 -34
  33. data/lib/active_record/associations/preloader/association.rb +71 -43
  34. data/lib/active_record/associations/preloader/through_association.rb +49 -40
  35. data/lib/active_record/associations/singular_association.rb +3 -17
  36. data/lib/active_record/associations/through_association.rb +1 -1
  37. data/lib/active_record/attribute_assignment.rb +17 -19
  38. data/lib/active_record/attribute_methods.rb +81 -143
  39. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -7
  40. data/lib/active_record/attribute_methods/dirty.rb +101 -40
  41. data/lib/active_record/attribute_methods/primary_key.rb +20 -25
  42. data/lib/active_record/attribute_methods/query.rb +4 -8
  43. data/lib/active_record/attribute_methods/read.rb +14 -56
  44. data/lib/active_record/attribute_methods/serialization.rb +12 -7
  45. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
  46. data/lib/active_record/attribute_methods/write.rb +18 -34
  47. data/lib/active_record/attributes.rb +46 -9
  48. data/lib/active_record/autosave_association.rb +57 -42
  49. data/lib/active_record/base.rb +4 -17
  50. data/lib/active_record/callbacks.rb +158 -43
  51. data/lib/active_record/coders/yaml_column.rb +1 -2
  52. data/lib/active_record/connection_adapters.rb +50 -0
  53. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +272 -130
  54. data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -36
  55. data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -146
  56. data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -14
  57. data/lib/active_record/connection_adapters/abstract/quoting.rb +98 -47
  58. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  59. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -110
  60. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +207 -90
  61. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -4
  62. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +385 -144
  63. data/lib/active_record/connection_adapters/abstract/transaction.rb +155 -68
  64. data/lib/active_record/connection_adapters/abstract_adapter.rb +228 -98
  65. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +243 -275
  66. data/lib/active_record/connection_adapters/column.rb +30 -12
  67. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  68. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  69. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  70. data/lib/active_record/connection_adapters/mysql/database_statements.rb +86 -32
  71. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  72. data/lib/active_record/connection_adapters/mysql/quoting.rb +59 -7
  73. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +34 -10
  74. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +48 -32
  75. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +18 -7
  76. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +139 -19
  77. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
  78. data/lib/active_record/connection_adapters/mysql2_adapter.rb +53 -18
  79. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  80. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +37 -28
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +38 -54
  83. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  86. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  87. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -2
  91. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +3 -4
  93. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +3 -4
  96. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
  97. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  98. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  99. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +15 -3
  100. data/lib/active_record/connection_adapters/postgresql/quoting.rb +47 -10
  101. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  102. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +19 -4
  103. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  104. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  105. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +120 -100
  106. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +31 -26
  107. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  108. data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -120
  109. data/lib/active_record/connection_adapters/schema_cache.rb +127 -21
  110. data/lib/active_record/connection_adapters/sql_type_metadata.rb +19 -6
  111. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
  112. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -7
  113. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  114. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +77 -13
  115. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +174 -186
  116. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  117. data/lib/active_record/connection_handling.rb +293 -33
  118. data/lib/active_record/core.rb +323 -97
  119. data/lib/active_record/counter_cache.rb +8 -30
  120. data/lib/active_record/database_configurations.rb +272 -0
  121. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  122. data/lib/active_record/database_configurations/database_config.rb +80 -0
  123. data/lib/active_record/database_configurations/hash_config.rb +96 -0
  124. data/lib/active_record/database_configurations/url_config.rb +53 -0
  125. data/lib/active_record/delegated_type.rb +209 -0
  126. data/lib/active_record/destroy_association_async_job.rb +36 -0
  127. data/lib/active_record/dynamic_matchers.rb +3 -4
  128. data/lib/active_record/enum.rb +111 -37
  129. data/lib/active_record/errors.rb +62 -19
  130. data/lib/active_record/explain.rb +10 -6
  131. data/lib/active_record/explain_subscriber.rb +1 -1
  132. data/lib/active_record/fixture_set/file.rb +10 -17
  133. data/lib/active_record/fixture_set/model_metadata.rb +32 -0
  134. data/lib/active_record/fixture_set/render_context.rb +17 -0
  135. data/lib/active_record/fixture_set/table_row.rb +152 -0
  136. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  137. data/lib/active_record/fixtures.rb +200 -481
  138. data/lib/active_record/gem_version.rb +4 -4
  139. data/lib/active_record/inheritance.rb +53 -24
  140. data/lib/active_record/insert_all.rb +208 -0
  141. data/lib/active_record/integration.rb +67 -17
  142. data/lib/active_record/internal_metadata.rb +26 -9
  143. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  144. data/lib/active_record/locking/optimistic.rb +37 -23
  145. data/lib/active_record/locking/pessimistic.rb +9 -5
  146. data/lib/active_record/log_subscriber.rb +35 -35
  147. data/lib/active_record/middleware/database_selector.rb +77 -0
  148. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  149. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  150. data/lib/active_record/migration.rb +206 -157
  151. data/lib/active_record/migration/command_recorder.rb +96 -44
  152. data/lib/active_record/migration/compatibility.rb +142 -64
  153. data/lib/active_record/migration/join_table.rb +0 -1
  154. data/lib/active_record/model_schema.rb +148 -22
  155. data/lib/active_record/nested_attributes.rb +4 -7
  156. data/lib/active_record/no_touching.rb +8 -1
  157. data/lib/active_record/null_relation.rb +0 -1
  158. data/lib/active_record/persistence.rb +267 -59
  159. data/lib/active_record/query_cache.rb +21 -4
  160. data/lib/active_record/querying.rb +40 -23
  161. data/lib/active_record/railtie.rb +115 -58
  162. data/lib/active_record/railties/console_sandbox.rb +2 -4
  163. data/lib/active_record/railties/controller_runtime.rb +30 -35
  164. data/lib/active_record/railties/databases.rake +408 -78
  165. data/lib/active_record/readonly_attributes.rb +4 -0
  166. data/lib/active_record/reflection.rb +109 -93
  167. data/lib/active_record/relation.rb +374 -104
  168. data/lib/active_record/relation/batches.rb +44 -35
  169. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  170. data/lib/active_record/relation/calculations.rb +153 -90
  171. data/lib/active_record/relation/delegation.rb +35 -50
  172. data/lib/active_record/relation/finder_methods.rb +64 -39
  173. data/lib/active_record/relation/from_clause.rb +5 -1
  174. data/lib/active_record/relation/merger.rb +32 -40
  175. data/lib/active_record/relation/predicate_builder.rb +62 -45
  176. data/lib/active_record/relation/predicate_builder/array_handler.rb +13 -13
  177. data/lib/active_record/relation/predicate_builder/association_query_value.rb +5 -9
  178. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  179. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +11 -10
  180. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  181. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  182. data/lib/active_record/relation/query_attribute.rb +13 -8
  183. data/lib/active_record/relation/query_methods.rb +475 -186
  184. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  185. data/lib/active_record/relation/spawn_methods.rb +9 -9
  186. data/lib/active_record/relation/where_clause.rb +111 -61
  187. data/lib/active_record/result.rb +64 -38
  188. data/lib/active_record/runtime_registry.rb +2 -2
  189. data/lib/active_record/sanitization.rb +22 -41
  190. data/lib/active_record/schema.rb +2 -11
  191. data/lib/active_record/schema_dumper.rb +54 -9
  192. data/lib/active_record/schema_migration.rb +7 -9
  193. data/lib/active_record/scoping.rb +8 -9
  194. data/lib/active_record/scoping/default.rb +4 -6
  195. data/lib/active_record/scoping/named.rb +17 -24
  196. data/lib/active_record/secure_token.rb +16 -8
  197. data/lib/active_record/serialization.rb +5 -3
  198. data/lib/active_record/signed_id.rb +116 -0
  199. data/lib/active_record/statement_cache.rb +49 -6
  200. data/lib/active_record/store.rb +88 -9
  201. data/lib/active_record/suppressor.rb +2 -2
  202. data/lib/active_record/table_metadata.rb +42 -43
  203. data/lib/active_record/tasks/database_tasks.rb +277 -81
  204. data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
  205. data/lib/active_record/tasks/postgresql_database_tasks.rb +27 -32
  206. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -17
  207. data/lib/active_record/test_databases.rb +24 -0
  208. data/lib/active_record/test_fixtures.rb +246 -0
  209. data/lib/active_record/timestamp.rb +43 -32
  210. data/lib/active_record/touch_later.rb +23 -22
  211. data/lib/active_record/transactions.rb +62 -118
  212. data/lib/active_record/translation.rb +1 -1
  213. data/lib/active_record/type.rb +10 -5
  214. data/lib/active_record/type/adapter_specific_registry.rb +3 -13
  215. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  216. data/lib/active_record/type/serialized.rb +6 -3
  217. data/lib/active_record/type/time.rb +10 -0
  218. data/lib/active_record/type/type_map.rb +0 -1
  219. data/lib/active_record/type/unsigned_integer.rb +0 -1
  220. data/lib/active_record/type_caster/connection.rb +15 -15
  221. data/lib/active_record/type_caster/map.rb +8 -8
  222. data/lib/active_record/validations.rb +4 -3
  223. data/lib/active_record/validations/associated.rb +1 -2
  224. data/lib/active_record/validations/numericality.rb +35 -0
  225. data/lib/active_record/validations/uniqueness.rb +38 -30
  226. data/lib/arel.rb +54 -0
  227. data/lib/arel/alias_predication.rb +9 -0
  228. data/lib/arel/attributes/attribute.rb +41 -0
  229. data/lib/arel/collectors/bind.rb +29 -0
  230. data/lib/arel/collectors/composite.rb +39 -0
  231. data/lib/arel/collectors/plain_string.rb +20 -0
  232. data/lib/arel/collectors/sql_string.rb +27 -0
  233. data/lib/arel/collectors/substitute_binds.rb +35 -0
  234. data/lib/arel/crud.rb +42 -0
  235. data/lib/arel/delete_manager.rb +18 -0
  236. data/lib/arel/errors.rb +9 -0
  237. data/lib/arel/expressions.rb +29 -0
  238. data/lib/arel/factory_methods.rb +49 -0
  239. data/lib/arel/insert_manager.rb +49 -0
  240. data/lib/arel/math.rb +45 -0
  241. data/lib/arel/nodes.rb +70 -0
  242. data/lib/arel/nodes/and.rb +32 -0
  243. data/lib/arel/nodes/ascending.rb +23 -0
  244. data/lib/arel/nodes/binary.rb +126 -0
  245. data/lib/arel/nodes/bind_param.rb +44 -0
  246. data/lib/arel/nodes/case.rb +55 -0
  247. data/lib/arel/nodes/casted.rb +62 -0
  248. data/lib/arel/nodes/comment.rb +29 -0
  249. data/lib/arel/nodes/count.rb +12 -0
  250. data/lib/arel/nodes/delete_statement.rb +45 -0
  251. data/lib/arel/nodes/descending.rb +23 -0
  252. data/lib/arel/nodes/equality.rb +15 -0
  253. data/lib/arel/nodes/extract.rb +24 -0
  254. data/lib/arel/nodes/false.rb +16 -0
  255. data/lib/arel/nodes/full_outer_join.rb +8 -0
  256. data/lib/arel/nodes/function.rb +44 -0
  257. data/lib/arel/nodes/grouping.rb +11 -0
  258. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  259. data/lib/arel/nodes/in.rb +15 -0
  260. data/lib/arel/nodes/infix_operation.rb +92 -0
  261. data/lib/arel/nodes/inner_join.rb +8 -0
  262. data/lib/arel/nodes/insert_statement.rb +37 -0
  263. data/lib/arel/nodes/join_source.rb +20 -0
  264. data/lib/arel/nodes/matches.rb +18 -0
  265. data/lib/arel/nodes/named_function.rb +23 -0
  266. data/lib/arel/nodes/node.rb +51 -0
  267. data/lib/arel/nodes/node_expression.rb +13 -0
  268. data/lib/arel/nodes/ordering.rb +27 -0
  269. data/lib/arel/nodes/outer_join.rb +8 -0
  270. data/lib/arel/nodes/over.rb +15 -0
  271. data/lib/arel/nodes/regexp.rb +16 -0
  272. data/lib/arel/nodes/right_outer_join.rb +8 -0
  273. data/lib/arel/nodes/select_core.rb +67 -0
  274. data/lib/arel/nodes/select_statement.rb +41 -0
  275. data/lib/arel/nodes/sql_literal.rb +19 -0
  276. data/lib/arel/nodes/string_join.rb +11 -0
  277. data/lib/arel/nodes/table_alias.rb +31 -0
  278. data/lib/arel/nodes/terminal.rb +16 -0
  279. data/lib/arel/nodes/true.rb +16 -0
  280. data/lib/arel/nodes/unary.rb +44 -0
  281. data/lib/arel/nodes/unary_operation.rb +20 -0
  282. data/lib/arel/nodes/unqualified_column.rb +22 -0
  283. data/lib/arel/nodes/update_statement.rb +41 -0
  284. data/lib/arel/nodes/values_list.rb +9 -0
  285. data/lib/arel/nodes/window.rb +126 -0
  286. data/lib/arel/nodes/with.rb +11 -0
  287. data/lib/arel/order_predications.rb +13 -0
  288. data/lib/arel/predications.rb +250 -0
  289. data/lib/arel/select_manager.rb +270 -0
  290. data/lib/arel/table.rb +118 -0
  291. data/lib/arel/tree_manager.rb +72 -0
  292. data/lib/arel/update_manager.rb +34 -0
  293. data/lib/arel/visitors.rb +13 -0
  294. data/lib/arel/visitors/dot.rb +308 -0
  295. data/lib/arel/visitors/mysql.rb +93 -0
  296. data/lib/arel/visitors/postgresql.rb +120 -0
  297. data/lib/arel/visitors/sqlite.rb +38 -0
  298. data/lib/arel/visitors/to_sql.rb +899 -0
  299. data/lib/arel/visitors/visitor.rb +45 -0
  300. data/lib/arel/window_predications.rb +9 -0
  301. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  302. data/lib/rails/generators/active_record/migration.rb +19 -2
  303. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -5
  304. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +3 -1
  305. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +7 -5
  306. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  307. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  308. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  309. metadata +119 -34
  310. data/lib/active_record/attribute_decorators.rb +0 -90
  311. data/lib/active_record/collection_cache_key.rb +0 -53
  312. data/lib/active_record/connection_adapters/connection_specification.rb +0 -287
  313. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -33
  314. data/lib/active_record/define_callbacks.rb +0 -22
  315. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -19
  316. data/lib/active_record/relation/where_clause_factory.rb +0 -34
@@ -6,7 +6,7 @@ module ActiveRecord
6
6
  class BelongsToPolymorphicAssociation < BelongsToAssociation #:nodoc:
7
7
  def klass
8
8
  type = owner[reflection.foreign_type]
9
- type.presence && type.constantize
9
+ type.presence && owner.class.polymorphic_class_for(type)
10
10
  end
11
11
 
12
12
  def target_changed?
@@ -14,13 +14,14 @@ module ActiveRecord
14
14
  end
15
15
 
16
16
  private
17
- def replace_keys(record)
17
+ def replace_keys(record, force: false)
18
18
  super
19
- owner[reflection.foreign_type] = record ? record.class.polymorphic_name : nil
20
- end
21
19
 
22
- def different_target?(record)
23
- super || record.class != klass
20
+ target_type = record ? record.class.polymorphic_name : nil
21
+
22
+ if force || owner[reflection.foreign_type] != target_type
23
+ owner[reflection.foreign_type] = target_type
24
+ end
24
25
  end
25
26
 
26
27
  def inverse_reflection_for(record)
@@ -18,7 +18,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
18
18
  end
19
19
  self.extensions = []
20
20
 
21
- VALID_OPTIONS = [:class_name, :anonymous_class, :foreign_key, :validate] # :nodoc:
21
+ VALID_OPTIONS = [
22
+ :class_name, :anonymous_class, :primary_key, :foreign_key, :dependent, :validate, :inverse_of, :strict_loading
23
+ ].freeze # :nodoc:
22
24
 
23
25
  def self.build(model, name, scope, options, &block)
24
26
  if model.dangerous_attribute_method?(name)
@@ -27,40 +29,32 @@ module ActiveRecord::Associations::Builder # :nodoc:
27
29
  "Please choose a different association name."
28
30
  end
29
31
 
30
- extension = define_extensions model, name, &block
31
- reflection = create_reflection model, name, scope, options, extension
32
+ reflection = create_reflection(model, name, scope, options, &block)
32
33
  define_accessors model, reflection
33
34
  define_callbacks model, reflection
34
35
  define_validations model, reflection
35
36
  reflection
36
37
  end
37
38
 
38
- def self.create_reflection(model, name, scope, options, extension = nil)
39
+ def self.create_reflection(model, name, scope, options, &block)
39
40
  raise ArgumentError, "association names must be a Symbol" unless name.kind_of?(Symbol)
40
41
 
41
42
  validate_options(options)
42
43
 
43
- scope = build_scope(scope, extension)
44
+ extension = define_extensions(model, name, &block)
45
+ options[:extend] = [*options[:extend], extension] if extension
46
+
47
+ scope = build_scope(scope)
44
48
 
45
49
  ActiveRecord::Reflection.create(macro, name, scope, options, model)
46
50
  end
47
51
 
48
- def self.build_scope(scope, extension)
49
- new_scope = scope
50
-
52
+ def self.build_scope(scope)
51
53
  if scope && scope.arity == 0
52
- new_scope = proc { instance_exec(&scope) }
53
- end
54
-
55
- if extension
56
- new_scope = wrap_scope new_scope, extension
54
+ proc { instance_exec(&scope) }
55
+ else
56
+ scope
57
57
  end
58
-
59
- new_scope
60
- end
61
-
62
- def self.wrap_scope(scope, extension)
63
- scope
64
58
  end
65
59
 
66
60
  def self.macro
@@ -80,8 +74,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
80
74
 
81
75
  def self.define_callbacks(model, reflection)
82
76
  if dependent = reflection.options[:dependent]
83
- check_dependent_options(dependent)
77
+ check_dependent_options(dependent, model)
84
78
  add_destroy_callbacks(model, reflection)
79
+ add_after_commit_jobs_callback(model, dependent)
85
80
  end
86
81
 
87
82
  Association.extensions.each do |extension|
@@ -126,7 +121,11 @@ module ActiveRecord::Associations::Builder # :nodoc:
126
121
  raise NotImplementedError
127
122
  end
128
123
 
129
- def self.check_dependent_options(dependent)
124
+ def self.check_dependent_options(dependent, model)
125
+ if dependent == :destroy_async && !model.destroy_association_async_job
126
+ err_message = "ActiveJob is required to use destroy_async on associations"
127
+ raise ActiveRecord::ActiveJobRequiredError, err_message
128
+ end
130
129
  unless valid_dependent_options.include? dependent
131
130
  raise ArgumentError, "The :dependent option must be one of #{valid_dependent_options}, but is :#{dependent}"
132
131
  end
@@ -134,7 +133,31 @@ module ActiveRecord::Associations::Builder # :nodoc:
134
133
 
135
134
  def self.add_destroy_callbacks(model, reflection)
136
135
  name = reflection.name
137
- model.before_destroy lambda { |o| o.association(name).handle_dependency }
136
+ model.before_destroy(->(o) { o.association(name).handle_dependency })
138
137
  end
138
+
139
+ def self.add_after_commit_jobs_callback(model, dependent)
140
+ if dependent == :destroy_async
141
+ mixin = model.generated_association_methods
142
+
143
+ unless mixin.method_defined?(:_after_commit_jobs)
144
+ model.after_commit(-> do
145
+ _after_commit_jobs.each do |job_class, job_arguments|
146
+ job_class.perform_later(**job_arguments)
147
+ end
148
+ end)
149
+
150
+ mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
151
+ def _after_commit_jobs
152
+ @_after_commit_jobs ||= []
153
+ end
154
+ CODE
155
+ end
156
+ end
157
+ end
158
+
159
+ private_class_method :build_scope, :macro, :valid_options, :validate_options, :define_extensions,
160
+ :define_callbacks, :define_accessors, :define_readers, :define_writers, :define_validations,
161
+ :valid_dependent_options, :check_dependent_options, :add_destroy_callbacks, :add_after_commit_jobs_callback
139
162
  end
140
163
  end
@@ -7,11 +7,14 @@ module ActiveRecord::Associations::Builder # :nodoc:
7
7
  end
8
8
 
9
9
  def self.valid_options(options)
10
- super + [:polymorphic, :touch, :counter_cache, :optional, :default]
10
+ valid = super + [:polymorphic, :counter_cache, :optional, :default]
11
+ valid += [:foreign_type] if options[:polymorphic]
12
+ valid += [:ensuring_owner_was] if options[:dependent] == :destroy_async
13
+ valid
11
14
  end
12
15
 
13
16
  def self.valid_dependent_options
14
- [:destroy, :delete]
17
+ [:destroy, :delete, :destroy_async]
15
18
  end
16
19
 
17
20
  def self.define_callbacks(model, reflection)
@@ -21,58 +24,16 @@ module ActiveRecord::Associations::Builder # :nodoc:
21
24
  add_default_callbacks(model, reflection) if reflection.options[:default]
22
25
  end
23
26
 
24
- def self.define_accessors(mixin, reflection)
25
- super
26
- add_counter_cache_methods mixin
27
- end
28
-
29
- def self.add_counter_cache_methods(mixin)
30
- return if mixin.method_defined? :belongs_to_counter_cache_after_update
31
-
32
- mixin.class_eval do
33
- def belongs_to_counter_cache_after_update(reflection)
34
- foreign_key = reflection.foreign_key
35
- cache_column = reflection.counter_cache_column
36
-
37
- if (@_after_replace_counter_called ||= false)
38
- @_after_replace_counter_called = false
39
- elsif association(reflection.name).target_changed?
40
- if reflection.polymorphic?
41
- model = attribute_in_database(reflection.foreign_type).try(:constantize)
42
- model_was = attribute_before_last_save(reflection.foreign_type).try(:constantize)
43
- else
44
- model = reflection.klass
45
- model_was = reflection.klass
46
- end
47
-
48
- foreign_key_was = attribute_before_last_save foreign_key
49
- foreign_key = attribute_in_database foreign_key
50
-
51
- if foreign_key && model.respond_to?(:increment_counter)
52
- foreign_key = counter_cache_target(reflection, model, foreign_key)
53
- model.increment_counter(cache_column, foreign_key)
54
- end
55
-
56
- if foreign_key_was && model_was.respond_to?(:decrement_counter)
57
- foreign_key_was = counter_cache_target(reflection, model_was, foreign_key_was)
58
- model_was.decrement_counter(cache_column, foreign_key_was)
59
- end
60
- end
61
- end
62
-
63
- private
64
- def counter_cache_target(reflection, model, foreign_key)
65
- primary_key = reflection.association_primary_key(model)
66
- model.unscoped.where!(primary_key => foreign_key)
67
- end
68
- end
69
- end
70
-
71
27
  def self.add_counter_cache_callbacks(model, reflection)
72
28
  cache_column = reflection.counter_cache_column
73
29
 
74
30
  model.after_update lambda { |record|
75
- record.belongs_to_counter_cache_after_update(reflection)
31
+ association = association(reflection.name)
32
+
33
+ if association.target_changed?
34
+ association.increment_counters
35
+ association.decrement_counters_before_last_save
36
+ end
76
37
  }
77
38
 
78
39
  klass = reflection.class_name.safe_constantize
@@ -97,38 +58,44 @@ module ActiveRecord::Associations::Builder # :nodoc:
97
58
 
98
59
  if old_record
99
60
  if touch != true
100
- old_record.send(touch_method, touch)
61
+ old_record.public_send(touch_method, touch)
101
62
  else
102
- old_record.send(touch_method)
63
+ old_record.public_send(touch_method)
103
64
  end
104
65
  end
105
66
  end
106
67
 
107
- record = o.send name
68
+ record = o.public_send name
108
69
  if record && record.persisted?
109
70
  if touch != true
110
- record.send(touch_method, touch)
71
+ record.public_send(touch_method, touch)
111
72
  else
112
- record.send(touch_method)
73
+ record.public_send(touch_method)
113
74
  end
114
75
  end
115
76
  end
116
77
 
117
78
  def self.add_touch_callbacks(model, reflection)
118
79
  foreign_key = reflection.foreign_key
119
- n = reflection.name
80
+ name = reflection.name
120
81
  touch = reflection.options[:touch]
121
82
 
122
83
  callback = lambda { |changes_method| lambda { |record|
123
- BelongsTo.touch_record(record, record.send(changes_method), foreign_key, n, touch, belongs_to_touch_method)
84
+ BelongsTo.touch_record(record, record.send(changes_method), foreign_key, name, touch, belongs_to_touch_method)
124
85
  }}
125
86
 
126
- unless reflection.counter_cache_column
87
+ if reflection.counter_cache_column
88
+ touch_callback = callback.(:saved_changes)
89
+ update_callback = lambda { |record|
90
+ instance_exec(record, &touch_callback) unless association(reflection.name).target_changed?
91
+ }
92
+ model.after_update update_callback, if: :saved_changes?
93
+ else
127
94
  model.after_create callback.(:saved_changes), if: :saved_changes?
95
+ model.after_update callback.(:saved_changes), if: :saved_changes?
128
96
  model.after_destroy callback.(:changes_to_save)
129
97
  end
130
98
 
131
- model.after_update callback.(:saved_changes), if: :saved_changes?
132
99
  model.after_touch callback.(:changes_to_save)
133
100
  end
134
101
 
@@ -159,5 +126,8 @@ module ActiveRecord::Associations::Builder # :nodoc:
159
126
  model.validates_presence_of reflection.name, message: :required
160
127
  end
161
128
  end
129
+
130
+ private_class_method :macro, :valid_options, :valid_dependent_options, :define_callbacks, :define_validations,
131
+ :add_counter_cache_callbacks, :add_touch_callbacks, :add_default_callbacks, :add_destroy_callbacks
162
132
  end
163
133
  end
@@ -7,8 +7,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
7
7
  CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove]
8
8
 
9
9
  def self.valid_options(options)
10
- super + [:table_name, :before_add,
11
- :after_add, :before_remove, :after_remove, :extend]
10
+ super + [:before_add, :after_add, :before_remove, :after_remove, :extend]
12
11
  end
13
12
 
14
13
  def self.define_callbacks(model, reflection)
@@ -22,17 +21,19 @@ module ActiveRecord::Associations::Builder # :nodoc:
22
21
 
23
22
  def self.define_extensions(model, name, &block)
24
23
  if block_given?
25
- extension_module_name = "#{model.name.demodulize}#{name.to_s.camelize}AssociationExtension"
24
+ extension_module_name = "#{name.to_s.camelize}AssociationExtension"
26
25
  extension = Module.new(&block)
27
- model.parent.const_set(extension_module_name, extension)
26
+ model.const_set(extension_module_name, extension)
28
27
  end
29
28
  end
30
29
 
31
30
  def self.define_callback(model, callback_name, name, options)
32
31
  full_callback_name = "#{callback_name}_for_#{name}"
33
32
 
34
- # TODO : why do i need method_defined? I think its because of the inheritance chain
35
- model.class_attribute full_callback_name unless model.method_defined?(full_callback_name)
33
+ unless model.method_defined?(full_callback_name)
34
+ model.class_attribute(full_callback_name, instance_accessor: false, instance_predicate: false)
35
+ end
36
+
36
37
  callbacks = Array(options[callback_name.to_sym]).map do |callback|
37
38
  case callback
38
39
  when Symbol
@@ -67,16 +68,6 @@ module ActiveRecord::Associations::Builder # :nodoc:
67
68
  CODE
68
69
  end
69
70
 
70
- def self.wrap_scope(scope, mod)
71
- if scope
72
- if scope.arity > 0
73
- proc { |owner| instance_exec(owner, &scope).extending(mod) }
74
- else
75
- proc { instance_exec(&scope).extending(mod) }
76
- end
77
- else
78
- proc { extending(mod) }
79
- end
80
- end
71
+ private_class_method :valid_options, :define_callback, :define_extensions, :define_readers, :define_writers
81
72
  end
82
73
  end
@@ -2,39 +2,6 @@
2
2
 
3
3
  module ActiveRecord::Associations::Builder # :nodoc:
4
4
  class HasAndBelongsToMany # :nodoc:
5
- class JoinTableResolver # :nodoc:
6
- KnownTable = Struct.new :join_table
7
-
8
- class KnownClass # :nodoc:
9
- def initialize(lhs_class, rhs_class_name)
10
- @lhs_class = lhs_class
11
- @rhs_class_name = rhs_class_name
12
- @join_table = nil
13
- end
14
-
15
- def join_table
16
- @join_table ||= [@lhs_class.table_name, klass.table_name].sort.join("\0").gsub(/^(.*[._])(.+)\0\1(.+)/, '\1\2_\3').tr("\0", "_")
17
- end
18
-
19
- private
20
-
21
- def klass
22
- @lhs_class.send(:compute_type, @rhs_class_name)
23
- end
24
- end
25
-
26
- def self.build(lhs_class, name, options)
27
- if options[:join_table]
28
- KnownTable.new options[:join_table].to_s
29
- else
30
- class_name = options.fetch(:class_name) {
31
- name.to_s.camelize.singularize
32
- }
33
- KnownClass.new lhs_class, class_name.to_s
34
- end
35
- end
36
- end
37
-
38
5
  attr_reader :lhs_model, :association_name, :options
39
6
 
40
7
  def initialize(association_name, lhs_model, options)
@@ -44,8 +11,6 @@ module ActiveRecord::Associations::Builder # :nodoc:
44
11
  end
45
12
 
46
13
  def through_model
47
- habtm = JoinTableResolver.build lhs_model, association_name, options
48
-
49
14
  join_model = Class.new(ActiveRecord::Base) {
50
15
  class << self
51
16
  attr_accessor :left_model
@@ -56,7 +21,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
56
21
  end
57
22
 
58
23
  def self.table_name
59
- 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
60
27
  end
61
28
 
62
29
  def self.compute_type(class_name)
@@ -79,14 +46,13 @@ module ActiveRecord::Associations::Builder # :nodoc:
79
46
  end
80
47
 
81
48
  private
82
-
83
49
  def self.suppress_composite_primary_key(pk)
84
50
  pk unless pk.is_a?(Array)
85
51
  end
86
52
  }
87
53
 
88
54
  join_model.name = "HABTM_#{association_name.to_s.camelize}"
89
- join_model.table_name_resolver = habtm
55
+ join_model.table_name_resolver = -> { table_name }
90
56
  join_model.left_model = lhs_model
91
57
 
92
58
  join_model.add_left_association :left_side, anonymous_class: lhs_model
@@ -96,7 +62,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
96
62
 
97
63
  def middle_reflection(join_model)
98
64
  middle_name = [lhs_model.name.downcase.pluralize,
99
- association_name].join("_".freeze).gsub("::".freeze, "_".freeze).to_sym
65
+ association_name.to_s].sort.join("_").gsub("::", "_").to_sym
100
66
  middle_options = middle_options join_model
101
67
 
102
68
  HasMany.create_reflection(lhs_model,
@@ -106,17 +72,27 @@ module ActiveRecord::Associations::Builder # :nodoc:
106
72
  end
107
73
 
108
74
  private
109
-
110
75
  def middle_options(join_model)
111
76
  middle_options = {}
112
77
  middle_options[:class_name] = "#{lhs_model.name}::#{join_model.name}"
113
- middle_options[:source] = join_model.left_reflection.name
114
78
  if options.key? :foreign_key
115
79
  middle_options[:foreign_key] = options[:foreign_key]
116
80
  end
117
81
  middle_options
118
82
  end
119
83
 
84
+ def table_name
85
+ if options[:join_table]
86
+ options[:join_table].to_s
87
+ else
88
+ class_name = options.fetch(:class_name) {
89
+ association_name.to_s.camelize.singularize
90
+ }
91
+ klass = lhs_model.send(:compute_type, class_name.to_s)
92
+ [lhs_model.table_name, klass.table_name].sort.join("\0").gsub(/^(.*[._])(.+)\0\1(.+)/, '\1\2_\3').tr("\0", "_")
93
+ end
94
+ end
95
+
120
96
  def belongs_to_options(options)
121
97
  rhs_options = {}
122
98