activerecord 6.0.0 → 6.1.3

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 (270) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1045 -575
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  5. data/lib/active_record.rb +7 -13
  6. data/lib/active_record/aggregations.rb +5 -6
  7. data/lib/active_record/association_relation.rb +30 -10
  8. data/lib/active_record/associations.rb +120 -13
  9. data/lib/active_record/associations/alias_tracker.rb +19 -16
  10. data/lib/active_record/associations/association.rb +49 -29
  11. data/lib/active_record/associations/association_scope.rb +19 -15
  12. data/lib/active_record/associations/belongs_to_association.rb +22 -8
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -3
  14. data/lib/active_record/associations/builder/association.rb +32 -5
  15. data/lib/active_record/associations/builder/belongs_to.rb +10 -7
  16. data/lib/active_record/associations/builder/collection_association.rb +5 -4
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -3
  18. data/lib/active_record/associations/builder/has_many.rb +6 -2
  19. data/lib/active_record/associations/builder/has_one.rb +11 -14
  20. data/lib/active_record/associations/builder/singular_association.rb +1 -1
  21. data/lib/active_record/associations/collection_association.rb +25 -8
  22. data/lib/active_record/associations/collection_proxy.rb +14 -7
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +24 -3
  25. data/lib/active_record/associations/has_many_through_association.rb +10 -4
  26. data/lib/active_record/associations/has_one_association.rb +15 -1
  27. data/lib/active_record/associations/join_dependency.rb +77 -42
  28. data/lib/active_record/associations/join_dependency/join_association.rb +39 -16
  29. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  30. data/lib/active_record/associations/preloader.rb +13 -8
  31. data/lib/active_record/associations/preloader/association.rb +51 -25
  32. data/lib/active_record/associations/preloader/through_association.rb +2 -2
  33. data/lib/active_record/associations/singular_association.rb +1 -1
  34. data/lib/active_record/associations/through_association.rb +1 -1
  35. data/lib/active_record/attribute_assignment.rb +10 -9
  36. data/lib/active_record/attribute_methods.rb +64 -54
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -10
  38. data/lib/active_record/attribute_methods/dirty.rb +3 -13
  39. data/lib/active_record/attribute_methods/primary_key.rb +6 -4
  40. data/lib/active_record/attribute_methods/query.rb +3 -6
  41. data/lib/active_record/attribute_methods/read.rb +8 -12
  42. data/lib/active_record/attribute_methods/serialization.rb +11 -6
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
  44. data/lib/active_record/attribute_methods/write.rb +12 -21
  45. data/lib/active_record/attributes.rb +33 -9
  46. data/lib/active_record/autosave_association.rb +63 -44
  47. data/lib/active_record/base.rb +2 -14
  48. data/lib/active_record/callbacks.rb +153 -24
  49. data/lib/active_record/coders/yaml_column.rb +1 -2
  50. data/lib/active_record/connection_adapters.rb +50 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +202 -138
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +87 -38
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +5 -10
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
  56. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +152 -116
  58. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +141 -52
  59. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  60. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +267 -105
  61. data/lib/active_record/connection_adapters/abstract/transaction.rb +82 -35
  62. data/lib/active_record/connection_adapters/abstract_adapter.rb +74 -77
  63. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +149 -115
  64. data/lib/active_record/connection_adapters/column.rb +15 -1
  65. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  66. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  67. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  68. data/lib/active_record/connection_adapters/mysql/database_statements.rb +30 -36
  69. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  70. data/lib/active_record/connection_adapters/mysql/quoting.rb +18 -3
  71. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -7
  72. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  73. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +5 -2
  74. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +17 -13
  75. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  76. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -13
  77. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  78. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  79. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  80. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +21 -56
  81. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +0 -1
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  84. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +0 -1
  88. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
  90. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  92. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -6
  95. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  96. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -2
  97. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  98. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  99. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +7 -3
  100. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -1
  101. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  102. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +72 -54
  103. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  104. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  105. data/lib/active_record/connection_adapters/postgresql_adapter.rb +83 -65
  106. data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
  107. data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
  108. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +38 -12
  109. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -2
  110. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  111. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -5
  112. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -57
  113. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  114. data/lib/active_record/connection_handling.rb +219 -81
  115. data/lib/active_record/core.rb +253 -67
  116. data/lib/active_record/counter_cache.rb +4 -1
  117. data/lib/active_record/database_configurations.rb +124 -85
  118. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  119. data/lib/active_record/database_configurations/database_config.rb +52 -9
  120. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  121. data/lib/active_record/database_configurations/url_config.rb +15 -41
  122. data/lib/active_record/delegated_type.rb +209 -0
  123. data/lib/active_record/destroy_association_async_job.rb +36 -0
  124. data/lib/active_record/dynamic_matchers.rb +2 -3
  125. data/lib/active_record/enum.rb +82 -38
  126. data/lib/active_record/errors.rb +47 -12
  127. data/lib/active_record/explain.rb +9 -5
  128. data/lib/active_record/explain_subscriber.rb +1 -1
  129. data/lib/active_record/fixture_set/file.rb +10 -17
  130. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  131. data/lib/active_record/fixture_set/render_context.rb +1 -1
  132. data/lib/active_record/fixture_set/table_row.rb +2 -3
  133. data/lib/active_record/fixture_set/table_rows.rb +0 -1
  134. data/lib/active_record/fixtures.rb +58 -12
  135. data/lib/active_record/gem_version.rb +2 -2
  136. data/lib/active_record/inheritance.rb +40 -21
  137. data/lib/active_record/insert_all.rb +39 -10
  138. data/lib/active_record/integration.rb +3 -5
  139. data/lib/active_record/internal_metadata.rb +16 -7
  140. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  141. data/lib/active_record/locking/optimistic.rb +33 -18
  142. data/lib/active_record/locking/pessimistic.rb +6 -2
  143. data/lib/active_record/log_subscriber.rb +28 -9
  144. data/lib/active_record/middleware/database_selector.rb +4 -2
  145. data/lib/active_record/middleware/database_selector/resolver.rb +14 -14
  146. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  147. data/lib/active_record/migration.rb +115 -85
  148. data/lib/active_record/migration/command_recorder.rb +53 -45
  149. data/lib/active_record/migration/compatibility.rb +71 -20
  150. data/lib/active_record/migration/join_table.rb +0 -1
  151. data/lib/active_record/model_schema.rb +120 -15
  152. data/lib/active_record/nested_attributes.rb +2 -5
  153. data/lib/active_record/no_touching.rb +1 -1
  154. data/lib/active_record/null_relation.rb +0 -1
  155. data/lib/active_record/persistence.rb +50 -46
  156. data/lib/active_record/query_cache.rb +15 -5
  157. data/lib/active_record/querying.rb +12 -7
  158. data/lib/active_record/railtie.rb +65 -45
  159. data/lib/active_record/railties/console_sandbox.rb +2 -4
  160. data/lib/active_record/railties/databases.rake +277 -97
  161. data/lib/active_record/readonly_attributes.rb +4 -0
  162. data/lib/active_record/reflection.rb +77 -63
  163. data/lib/active_record/relation.rb +107 -67
  164. data/lib/active_record/relation/batches.rb +38 -32
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  166. data/lib/active_record/relation/calculations.rb +102 -45
  167. data/lib/active_record/relation/delegation.rb +9 -7
  168. data/lib/active_record/relation/finder_methods.rb +55 -17
  169. data/lib/active_record/relation/from_clause.rb +5 -1
  170. data/lib/active_record/relation/merger.rb +27 -26
  171. data/lib/active_record/relation/predicate_builder.rb +59 -40
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  173. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  174. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -6
  175. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  176. data/lib/active_record/relation/query_methods.rb +343 -180
  177. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  178. data/lib/active_record/relation/spawn_methods.rb +8 -8
  179. data/lib/active_record/relation/where_clause.rb +107 -61
  180. data/lib/active_record/result.rb +41 -34
  181. data/lib/active_record/runtime_registry.rb +2 -2
  182. data/lib/active_record/sanitization.rb +6 -17
  183. data/lib/active_record/schema_dumper.rb +34 -4
  184. data/lib/active_record/schema_migration.rb +2 -8
  185. data/lib/active_record/scoping.rb +0 -1
  186. data/lib/active_record/scoping/default.rb +0 -1
  187. data/lib/active_record/scoping/named.rb +7 -18
  188. data/lib/active_record/secure_token.rb +16 -8
  189. data/lib/active_record/serialization.rb +5 -3
  190. data/lib/active_record/signed_id.rb +116 -0
  191. data/lib/active_record/statement_cache.rb +20 -4
  192. data/lib/active_record/store.rb +3 -3
  193. data/lib/active_record/suppressor.rb +2 -2
  194. data/lib/active_record/table_metadata.rb +42 -36
  195. data/lib/active_record/tasks/database_tasks.rb +140 -113
  196. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -36
  197. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -27
  198. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -10
  199. data/lib/active_record/test_databases.rb +5 -4
  200. data/lib/active_record/test_fixtures.rb +38 -16
  201. data/lib/active_record/timestamp.rb +4 -7
  202. data/lib/active_record/touch_later.rb +20 -21
  203. data/lib/active_record/transactions.rb +26 -73
  204. data/lib/active_record/type.rb +8 -2
  205. data/lib/active_record/type/adapter_specific_registry.rb +2 -5
  206. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  207. data/lib/active_record/type/serialized.rb +6 -3
  208. data/lib/active_record/type/time.rb +10 -0
  209. data/lib/active_record/type/type_map.rb +0 -1
  210. data/lib/active_record/type/unsigned_integer.rb +0 -1
  211. data/lib/active_record/type_caster/connection.rb +0 -1
  212. data/lib/active_record/type_caster/map.rb +8 -5
  213. data/lib/active_record/validations.rb +3 -3
  214. data/lib/active_record/validations/associated.rb +1 -2
  215. data/lib/active_record/validations/numericality.rb +35 -0
  216. data/lib/active_record/validations/uniqueness.rb +24 -4
  217. data/lib/arel.rb +15 -12
  218. data/lib/arel/attributes/attribute.rb +4 -0
  219. data/lib/arel/collectors/bind.rb +5 -0
  220. data/lib/arel/collectors/composite.rb +8 -0
  221. data/lib/arel/collectors/sql_string.rb +7 -0
  222. data/lib/arel/collectors/substitute_binds.rb +7 -0
  223. data/lib/arel/nodes.rb +3 -1
  224. data/lib/arel/nodes/binary.rb +82 -8
  225. data/lib/arel/nodes/bind_param.rb +8 -0
  226. data/lib/arel/nodes/casted.rb +21 -9
  227. data/lib/arel/nodes/equality.rb +6 -9
  228. data/lib/arel/nodes/grouping.rb +3 -0
  229. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  230. data/lib/arel/nodes/in.rb +8 -1
  231. data/lib/arel/nodes/infix_operation.rb +13 -1
  232. data/lib/arel/nodes/join_source.rb +1 -1
  233. data/lib/arel/nodes/node.rb +7 -6
  234. data/lib/arel/nodes/ordering.rb +27 -0
  235. data/lib/arel/nodes/sql_literal.rb +3 -0
  236. data/lib/arel/nodes/table_alias.rb +7 -3
  237. data/lib/arel/nodes/unary.rb +0 -1
  238. data/lib/arel/predications.rb +17 -24
  239. data/lib/arel/select_manager.rb +1 -2
  240. data/lib/arel/table.rb +13 -5
  241. data/lib/arel/visitors.rb +0 -7
  242. data/lib/arel/visitors/dot.rb +14 -3
  243. data/lib/arel/visitors/mysql.rb +11 -1
  244. data/lib/arel/visitors/postgresql.rb +15 -5
  245. data/lib/arel/visitors/sqlite.rb +0 -1
  246. data/lib/arel/visitors/to_sql.rb +89 -79
  247. data/lib/arel/visitors/visitor.rb +0 -1
  248. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  249. data/lib/rails/generators/active_record/migration.rb +6 -2
  250. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  251. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  252. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
  253. data/lib/rails/generators/active_record/model/model_generator.rb +38 -2
  254. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  255. metadata +30 -27
  256. data/lib/active_record/attribute_decorators.rb +0 -90
  257. data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
  258. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  259. data/lib/active_record/define_callbacks.rb +0 -22
  260. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  261. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  262. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  263. data/lib/arel/attributes.rb +0 -22
  264. data/lib/arel/visitors/depth_first.rb +0 -204
  265. data/lib/arel/visitors/ibm_db.rb +0 -34
  266. data/lib/arel/visitors/informix.rb +0 -62
  267. data/lib/arel/visitors/mssql.rb +0 -157
  268. data/lib/arel/visitors/oracle.rb +0 -159
  269. data/lib/arel/visitors/oracle12.rb +0 -66
  270. data/lib/arel/visitors/where_sql.rb +0 -23
@@ -6,9 +6,14 @@ module ActiveRecord
6
6
  module Associations
7
7
  # Keeps track of table aliases for ActiveRecord::Associations::JoinDependency
8
8
  class AliasTracker # :nodoc:
9
- def self.create(connection, initial_table, joins)
9
+ def self.create(connection, initial_table, joins, aliases = nil)
10
10
  if joins.empty?
11
- aliases = Hash.new(0)
11
+ aliases ||= Hash.new(0)
12
+ elsif aliases
13
+ default_proc = aliases.default_proc || proc { 0 }
14
+ aliases.default_proc = proc { |h, k|
15
+ h[k] = initial_count_for(connection, k, joins) + default_proc.call(h, k)
16
+ }
12
17
  else
13
18
  aliases = Hash.new { |h, k|
14
19
  h[k] = initial_count_for(connection, k, joins)
@@ -32,8 +37,6 @@ module ActiveRecord
32
37
  ).size
33
38
  elsif join.is_a?(Arel::Nodes::Join)
34
39
  join.left.name == name ? 1 : 0
35
- elsif join.is_a?(Hash)
36
- join[name]
37
40
  else
38
41
  raise ArgumentError, "joins list should be initialized by list of Arel::Nodes::Join"
39
42
  end
@@ -48,31 +51,31 @@ module ActiveRecord
48
51
  @connection = connection
49
52
  end
50
53
 
51
- def aliased_table_for(table_name, aliased_name, type_caster)
52
- if aliases[table_name].zero?
54
+ def aliased_table_for(arel_table, table_name = nil)
55
+ table_name ||= arel_table.name
56
+
57
+ if aliases[table_name] == 0
53
58
  # If it's zero, we can have our table_name
54
59
  aliases[table_name] = 1
55
- Arel::Table.new(table_name, type_caster: type_caster)
60
+ arel_table = arel_table.alias(table_name) if arel_table.name != table_name
56
61
  else
57
62
  # Otherwise, we need to use an alias
58
- aliased_name = @connection.table_alias_for(aliased_name)
63
+ aliased_name = @connection.table_alias_for(yield)
59
64
 
60
65
  # Update the count
61
- aliases[aliased_name] += 1
66
+ count = aliases[aliased_name] += 1
62
67
 
63
- table_alias = if aliases[aliased_name] > 1
64
- "#{truncate(aliased_name)}_#{aliases[aliased_name]}"
65
- else
66
- aliased_name
67
- end
68
- Arel::Table.new(table_name, type_caster: type_caster).alias(table_alias)
68
+ aliased_name = "#{truncate(aliased_name)}_#{count}" if count > 1
69
+
70
+ arel_table = arel_table.alias(aliased_name)
69
71
  end
72
+
73
+ arel_table
70
74
  end
71
75
 
72
76
  attr_reader :aliases
73
77
 
74
78
  private
75
-
76
79
  def truncate(name)
77
80
  name.slice(0, @connection.table_alias_length - 2)
78
81
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/array/wrap"
4
-
5
3
  module ActiveRecord
6
4
  module Associations
7
5
  # = Active Record Associations
@@ -56,6 +54,10 @@ module ActiveRecord
56
54
  @inversed = false
57
55
  end
58
56
 
57
+ def reset_negative_cache # :nodoc:
58
+ reset if loaded? && target.nil?
59
+ end
60
+
59
61
  # Reloads the \target and returns +self+ on success.
60
62
  # The QueryCache is cleared if +force+ is true.
61
63
  def reload(force = false)
@@ -95,7 +97,11 @@ module ActiveRecord
95
97
  end
96
98
 
97
99
  def scope
98
- target_scope.merge!(association_scope)
100
+ if (scope = klass.current_scope) && scope.try(:proxy_association) == self
101
+ scope.spawn
102
+ else
103
+ target_scope.merge!(association_scope)
104
+ end
99
105
  end
100
106
 
101
107
  def reset_scope
@@ -128,7 +134,15 @@ module ActiveRecord
128
134
  self.target = record
129
135
  @inversed = !!record
130
136
  end
131
- alias :inversed_from_queries :inversed_from
137
+
138
+ def inversed_from_queries(record)
139
+ if inversable?(record)
140
+ self.target = record
141
+ @inversed = true
142
+ else
143
+ @inversed = false
144
+ end
145
+ end
132
146
 
133
147
  # Returns the class of the target. belongs_to polymorphic overrides this to look at the
134
148
  # polymorphic_type field on the owner.
@@ -187,27 +201,30 @@ module ActiveRecord
187
201
  set_inverse_instance(record)
188
202
  end
189
203
 
190
- def create(attributes = {}, &block)
204
+ def create(attributes = nil, &block)
191
205
  _create_record(attributes, &block)
192
206
  end
193
207
 
194
- def create!(attributes = {}, &block)
208
+ def create!(attributes = nil, &block)
195
209
  _create_record(attributes, true, &block)
196
210
  end
197
211
 
198
212
  private
199
213
  def find_target
214
+ if (owner.strict_loading? || reflection.strict_loading?) && owner.validation_context.nil?
215
+ Base.strict_loading_violation!(owner: owner.class, reflection: reflection)
216
+ end
217
+
200
218
  scope = self.scope
201
219
  return scope.to_a if skip_statement_cache?(scope)
202
220
 
203
- conn = klass.connection
204
- sc = reflection.association_scope_cache(conn, owner) do |params|
221
+ sc = reflection.association_scope_cache(klass, owner) do |params|
205
222
  as = AssociationScope.create { params.bind }
206
223
  target_scope.merge!(as.scope(self))
207
224
  end
208
225
 
209
226
  binds = AssociationScope.get_bind_values(owner, reflection.chain)
210
- sc.execute(binds, conn) { |record| set_inverse_instance(record) } || []
227
+ sc.execute(binds, klass.connection) { |record| set_inverse_instance(record) }
211
228
  end
212
229
 
213
230
  # The scope for this association.
@@ -236,25 +253,6 @@ module ActiveRecord
236
253
  !loaded? && (!owner.new_record? || foreign_key_present?) && klass
237
254
  end
238
255
 
239
- def creation_attributes
240
- attributes = {}
241
-
242
- if (reflection.has_one? || reflection.collection?) && !options[:through]
243
- attributes[reflection.foreign_key] = owner[reflection.active_record_primary_key]
244
-
245
- if reflection.type
246
- attributes[reflection.type] = owner.class.polymorphic_name
247
- end
248
- end
249
-
250
- attributes
251
- end
252
-
253
- # Sets the owner attributes on the given record
254
- def set_owner_attributes(record)
255
- creation_attributes.each { |key, value| record[key] = value }
256
- end
257
-
258
256
  # Returns true if there is a foreign key present on the owner which
259
257
  # references the target. This is used to determine whether we can load
260
258
  # the target if the owner is currently a new record (and therefore
@@ -302,7 +300,7 @@ module ActiveRecord
302
300
 
303
301
  # Returns true if record contains the foreign_key
304
302
  def foreign_key_for?(record)
305
- record.has_attribute?(reflection.foreign_key)
303
+ record._has_attribute?(reflection.foreign_key)
306
304
  end
307
305
 
308
306
  # This should be implemented to return the values of the relevant key(s) on the owner,
@@ -327,6 +325,28 @@ module ActiveRecord
327
325
  klass.scope_attributes? ||
328
326
  reflection.source_reflection.active_record.default_scopes.any?
329
327
  end
328
+
329
+ def enqueue_destroy_association(options)
330
+ job_class = owner.class.destroy_association_async_job
331
+
332
+ if job_class
333
+ owner._after_commit_jobs.push([job_class, options])
334
+ end
335
+ end
336
+
337
+ def inversable?(record)
338
+ record &&
339
+ ((!record.persisted? || !owner.persisted?) || matches_foreign_key?(record))
340
+ end
341
+
342
+ def matches_foreign_key?(record)
343
+ if foreign_key_for?(record)
344
+ record.read_attribute(reflection.foreign_key) == owner.id ||
345
+ (foreign_key_for?(owner) && owner.read_attribute(reflection.foreign_key) == record.id)
346
+ else
347
+ owner.read_attribute(reflection.foreign_key) == record.id
348
+ end
349
+ end
330
350
  end
331
351
  end
332
352
  end
@@ -52,17 +52,16 @@ module ActiveRecord
52
52
  attr_reader :value_transformation
53
53
 
54
54
  def join(table, constraint)
55
- table.create_join(table, table.create_on(constraint))
55
+ Arel::Nodes::LeadingJoin.new(table, Arel::Nodes::On.new(constraint))
56
56
  end
57
57
 
58
58
  def last_chain_scope(scope, reflection, owner)
59
- join_keys = reflection.join_keys
60
- key = join_keys.key
61
- foreign_key = join_keys.foreign_key
59
+ primary_key = reflection.join_primary_key
60
+ foreign_key = reflection.join_foreign_key
62
61
 
63
62
  table = reflection.aliased_table
64
63
  value = transform_value(owner[foreign_key])
65
- scope = apply_scope(scope, table, key, value)
64
+ scope = apply_scope(scope, table, primary_key, value)
66
65
 
67
66
  if reflection.type
68
67
  polymorphic_type = transform_value(owner.class.polymorphic_name)
@@ -77,13 +76,12 @@ module ActiveRecord
77
76
  end
78
77
 
79
78
  def next_chain_scope(scope, reflection, next_reflection)
80
- join_keys = reflection.join_keys
81
- key = join_keys.key
82
- foreign_key = join_keys.foreign_key
79
+ primary_key = reflection.join_primary_key
80
+ foreign_key = reflection.join_foreign_key
83
81
 
84
82
  table = reflection.aliased_table
85
83
  foreign_table = next_reflection.aliased_table
86
- constraint = table[key].eq(foreign_table[foreign_key])
84
+ constraint = table[primary_key].eq(foreign_table[foreign_key])
87
85
 
88
86
  if reflection.type
89
87
  value = transform_value(next_reflection.klass.polymorphic_name)
@@ -108,11 +106,9 @@ module ActiveRecord
108
106
  name = reflection.name
109
107
  chain = [Reflection::RuntimeReflection.new(reflection, association)]
110
108
  reflection.chain.drop(1).each do |refl|
111
- aliased_table = tracker.aliased_table_for(
112
- refl.table_name,
113
- refl.alias_candidate(name),
114
- refl.klass.type_caster
115
- )
109
+ aliased_table = tracker.aliased_table_for(refl.klass.arel_table) do
110
+ refl.alias_candidate(name)
111
+ end
116
112
  chain << ReflectionProxy.new(refl, aliased_table)
117
113
  end
118
114
  chain
@@ -134,10 +130,18 @@ module ActiveRecord
134
130
 
135
131
  if scope_chain_item == chain_head.scope
136
132
  scope.merge! item.except(:where, :includes, :unscope, :order)
133
+ elsif !item.references_values.empty?
134
+ scope.merge! item.only(:joins, :left_outer_joins)
135
+
136
+ associations = item.eager_load_values | item.includes_values
137
+
138
+ unless associations.empty?
139
+ scope.joins! item.construct_join_dependency(associations, Arel::Nodes::OuterJoin)
140
+ end
137
141
  end
138
142
 
139
143
  reflection.all_includes do
140
- scope.includes! item.includes_values
144
+ scope.includes_values |= item.includes_values
141
145
  end
142
146
 
143
147
  scope.unscope!(*item.unscope_values)
@@ -11,8 +11,20 @@ module ActiveRecord
11
11
  when :destroy
12
12
  target.destroy
13
13
  raise ActiveRecord::Rollback unless target.destroyed?
14
+ when :destroy_async
15
+ id = owner.public_send(reflection.foreign_key.to_sym)
16
+ primary_key_column = reflection.active_record_primary_key.to_sym
17
+
18
+ enqueue_destroy_association(
19
+ owner_model_name: owner.class.to_s,
20
+ owner_id: owner.id,
21
+ association_class: reflection.klass.to_s,
22
+ association_ids: [id],
23
+ association_primary_key_column: primary_key_column,
24
+ ensuring_owner_was_method: options.fetch(:ensuring_owner_was, nil)
25
+ )
14
26
  else
15
- target.send(options[:dependent])
27
+ target.public_send(options[:dependent])
16
28
  end
17
29
  end
18
30
 
@@ -44,7 +56,7 @@ module ActiveRecord
44
56
 
45
57
  def decrement_counters_before_last_save
46
58
  if reflection.polymorphic?
47
- model_was = owner.attribute_before_last_save(reflection.foreign_type).try(:constantize)
59
+ model_was = owner.attribute_before_last_save(reflection.foreign_type)&.constantize
48
60
  else
49
61
  model_was = klass
50
62
  end
@@ -68,7 +80,7 @@ module ActiveRecord
68
80
  @updated = true
69
81
  end
70
82
 
71
- replace_keys(record)
83
+ replace_keys(record, force: true)
72
84
 
73
85
  self.target = record
74
86
  end
@@ -96,8 +108,12 @@ module ActiveRecord
96
108
  reflection.counter_cache_column && owner.persisted?
97
109
  end
98
110
 
99
- def replace_keys(record)
100
- owner[reflection.foreign_key] = record ? record._read_attribute(primary_key(record.class)) : nil
111
+ def replace_keys(record, force: false)
112
+ target_key = record ? record._read_attribute(primary_key(record.class)) : nil
113
+
114
+ if force || owner[reflection.foreign_key] != target_key
115
+ owner[reflection.foreign_key] = target_key
116
+ end
101
117
  end
102
118
 
103
119
  def primary_key(klass)
@@ -108,11 +124,9 @@ module ActiveRecord
108
124
  owner._read_attribute(reflection.foreign_key)
109
125
  end
110
126
 
111
- # NOTE - for now, we're only supporting inverse setting from belongs_to back onto
112
- # has_one associations.
113
127
  def invertible_for?(record)
114
128
  inverse = inverse_reflection_for(record)
115
- inverse && inverse.has_one?
129
+ inverse && (inverse.has_one? || ActiveRecord::Base.has_many_inversing)
116
130
  end
117
131
 
118
132
  def stale_state
@@ -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,9 +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
19
+
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
20
25
  end
21
26
 
22
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)
@@ -72,8 +74,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
72
74
 
73
75
  def self.define_callbacks(model, reflection)
74
76
  if dependent = reflection.options[:dependent]
75
- check_dependent_options(dependent)
77
+ check_dependent_options(dependent, model)
76
78
  add_destroy_callbacks(model, reflection)
79
+ add_after_commit_jobs_callback(model, dependent)
77
80
  end
78
81
 
79
82
  Association.extensions.each do |extension|
@@ -118,7 +121,11 @@ module ActiveRecord::Associations::Builder # :nodoc:
118
121
  raise NotImplementedError
119
122
  end
120
123
 
121
- 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
122
129
  unless valid_dependent_options.include? dependent
123
130
  raise ArgumentError, "The :dependent option must be one of #{valid_dependent_options}, but is :#{dependent}"
124
131
  end
@@ -126,11 +133,31 @@ module ActiveRecord::Associations::Builder # :nodoc:
126
133
 
127
134
  def self.add_destroy_callbacks(model, reflection)
128
135
  name = reflection.name
129
- model.before_destroy lambda { |o| o.association(name).handle_dependency }
136
+ model.before_destroy(->(o) { o.association(name).handle_dependency })
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
130
157
  end
131
158
 
132
159
  private_class_method :build_scope, :macro, :valid_options, :validate_options, :define_extensions,
133
160
  :define_callbacks, :define_accessors, :define_readers, :define_writers, :define_validations,
134
- :valid_dependent_options, :check_dependent_options, :add_destroy_callbacks
161
+ :valid_dependent_options, :check_dependent_options, :add_destroy_callbacks, :add_after_commit_jobs_callback
135
162
  end
136
163
  end