activerecord 6.0.3.5 → 6.1.0.rc1

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 (245) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +774 -735
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -2
  5. data/lib/active_record.rb +7 -14
  6. data/lib/active_record/aggregations.rb +1 -1
  7. data/lib/active_record/association_relation.rb +22 -14
  8. data/lib/active_record/associations.rb +114 -11
  9. data/lib/active_record/associations/alias_tracker.rb +19 -15
  10. data/lib/active_record/associations/association.rb +40 -29
  11. data/lib/active_record/associations/association_scope.rb +17 -15
  12. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  14. data/lib/active_record/associations/builder/association.rb +9 -3
  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 -1
  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 +19 -6
  22. data/lib/active_record/associations/collection_proxy.rb +13 -5
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +24 -2
  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 +72 -50
  28. data/lib/active_record/associations/join_dependency/join_association.rb +36 -14
  29. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  30. data/lib/active_record/associations/preloader.rb +11 -5
  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 -8
  36. data/lib/active_record/attribute_methods.rb +52 -48
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
  38. data/lib/active_record/attribute_methods/dirty.rb +1 -11
  39. data/lib/active_record/attribute_methods/primary_key.rb +6 -2
  40. data/lib/active_record/attribute_methods/query.rb +3 -6
  41. data/lib/active_record/attribute_methods/read.rb +8 -11
  42. data/lib/active_record/attribute_methods/serialization.rb +4 -4
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -13
  44. data/lib/active_record/attribute_methods/write.rb +12 -20
  45. data/lib/active_record/attributes.rb +27 -7
  46. data/lib/active_record/autosave_association.rb +57 -40
  47. data/lib/active_record/base.rb +2 -14
  48. data/lib/active_record/callbacks.rb +32 -22
  49. data/lib/active_record/coders/yaml_column.rb +1 -1
  50. data/lib/active_record/connection_adapters.rb +50 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +186 -134
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -22
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -7
  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 +153 -116
  58. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +112 -27
  59. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  60. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +224 -85
  61. data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -24
  62. data/lib/active_record/connection_adapters/abstract_adapter.rb +36 -69
  63. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +129 -88
  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/database_statements.rb +23 -25
  68. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
  69. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  70. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +33 -6
  71. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  72. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  73. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +11 -7
  74. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  75. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -12
  76. data/lib/active_record/connection_adapters/pool_config.rb +63 -0
  77. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  78. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  79. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +13 -54
  80. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  82. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  83. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
  84. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -5
  90. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
  91. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  92. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  93. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
  94. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
  95. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  96. data/lib/active_record/connection_adapters/postgresql_adapter.rb +72 -55
  97. data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
  98. data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
  99. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +31 -6
  100. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
  101. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  102. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +37 -4
  103. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +49 -50
  104. data/lib/active_record/connection_handling.rb +210 -71
  105. data/lib/active_record/core.rb +220 -55
  106. data/lib/active_record/database_configurations.rb +124 -85
  107. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  108. data/lib/active_record/database_configurations/database_config.rb +52 -9
  109. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  110. data/lib/active_record/database_configurations/url_config.rb +15 -40
  111. data/lib/active_record/delegated_type.rb +209 -0
  112. data/lib/active_record/destroy_association_async_job.rb +36 -0
  113. data/lib/active_record/enum.rb +27 -10
  114. data/lib/active_record/errors.rb +47 -12
  115. data/lib/active_record/explain.rb +9 -4
  116. data/lib/active_record/explain_subscriber.rb +1 -1
  117. data/lib/active_record/fixture_set/file.rb +10 -17
  118. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  119. data/lib/active_record/fixture_set/render_context.rb +1 -1
  120. data/lib/active_record/fixture_set/table_row.rb +2 -2
  121. data/lib/active_record/fixtures.rb +54 -8
  122. data/lib/active_record/gem_version.rb +3 -3
  123. data/lib/active_record/inheritance.rb +40 -18
  124. data/lib/active_record/insert_all.rb +33 -6
  125. data/lib/active_record/integration.rb +3 -5
  126. data/lib/active_record/internal_metadata.rb +15 -4
  127. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  128. data/lib/active_record/locking/optimistic.rb +22 -16
  129. data/lib/active_record/locking/pessimistic.rb +6 -2
  130. data/lib/active_record/log_subscriber.rb +26 -8
  131. data/lib/active_record/middleware/database_selector.rb +4 -1
  132. data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
  133. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  134. data/lib/active_record/migration.rb +113 -83
  135. data/lib/active_record/migration/command_recorder.rb +47 -27
  136. data/lib/active_record/migration/compatibility.rb +67 -17
  137. data/lib/active_record/model_schema.rb +88 -13
  138. data/lib/active_record/nested_attributes.rb +2 -3
  139. data/lib/active_record/no_touching.rb +1 -1
  140. data/lib/active_record/persistence.rb +50 -45
  141. data/lib/active_record/query_cache.rb +15 -5
  142. data/lib/active_record/querying.rb +11 -6
  143. data/lib/active_record/railtie.rb +64 -44
  144. data/lib/active_record/railties/databases.rake +253 -98
  145. data/lib/active_record/readonly_attributes.rb +4 -0
  146. data/lib/active_record/reflection.rb +70 -57
  147. data/lib/active_record/relation.rb +96 -67
  148. data/lib/active_record/relation/batches.rb +38 -31
  149. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  150. data/lib/active_record/relation/calculations.rb +101 -44
  151. data/lib/active_record/relation/delegation.rb +2 -1
  152. data/lib/active_record/relation/finder_methods.rb +45 -15
  153. data/lib/active_record/relation/from_clause.rb +1 -1
  154. data/lib/active_record/relation/merger.rb +27 -25
  155. data/lib/active_record/relation/predicate_builder.rb +57 -33
  156. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  157. data/lib/active_record/relation/predicate_builder/association_query_value.rb +2 -2
  158. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +3 -3
  159. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  160. data/lib/active_record/relation/query_methods.rb +330 -195
  161. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  162. data/lib/active_record/relation/spawn_methods.rb +6 -5
  163. data/lib/active_record/relation/where_clause.rb +104 -57
  164. data/lib/active_record/result.rb +41 -33
  165. data/lib/active_record/runtime_registry.rb +2 -2
  166. data/lib/active_record/sanitization.rb +6 -17
  167. data/lib/active_record/schema_dumper.rb +34 -4
  168. data/lib/active_record/schema_migration.rb +0 -4
  169. data/lib/active_record/scoping/named.rb +6 -17
  170. data/lib/active_record/secure_token.rb +16 -8
  171. data/lib/active_record/serialization.rb +5 -3
  172. data/lib/active_record/signed_id.rb +116 -0
  173. data/lib/active_record/statement_cache.rb +20 -4
  174. data/lib/active_record/store.rb +2 -2
  175. data/lib/active_record/suppressor.rb +2 -2
  176. data/lib/active_record/table_metadata.rb +36 -52
  177. data/lib/active_record/tasks/database_tasks.rb +139 -113
  178. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
  179. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
  180. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
  181. data/lib/active_record/test_databases.rb +5 -4
  182. data/lib/active_record/test_fixtures.rb +37 -16
  183. data/lib/active_record/timestamp.rb +4 -6
  184. data/lib/active_record/touch_later.rb +21 -21
  185. data/lib/active_record/transactions.rb +15 -64
  186. data/lib/active_record/type.rb +8 -1
  187. data/lib/active_record/type/serialized.rb +6 -2
  188. data/lib/active_record/type/time.rb +10 -0
  189. data/lib/active_record/type_caster/connection.rb +0 -1
  190. data/lib/active_record/type_caster/map.rb +8 -5
  191. data/lib/active_record/validations.rb +1 -0
  192. data/lib/active_record/validations/numericality.rb +35 -0
  193. data/lib/active_record/validations/uniqueness.rb +24 -4
  194. data/lib/arel.rb +5 -13
  195. data/lib/arel/attributes/attribute.rb +4 -0
  196. data/lib/arel/collectors/bind.rb +5 -0
  197. data/lib/arel/collectors/composite.rb +8 -0
  198. data/lib/arel/collectors/sql_string.rb +7 -0
  199. data/lib/arel/collectors/substitute_binds.rb +7 -0
  200. data/lib/arel/nodes.rb +3 -1
  201. data/lib/arel/nodes/binary.rb +82 -8
  202. data/lib/arel/nodes/bind_param.rb +8 -0
  203. data/lib/arel/nodes/casted.rb +21 -9
  204. data/lib/arel/nodes/equality.rb +6 -9
  205. data/lib/arel/nodes/grouping.rb +3 -0
  206. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  207. data/lib/arel/nodes/in.rb +8 -1
  208. data/lib/arel/nodes/infix_operation.rb +13 -1
  209. data/lib/arel/nodes/join_source.rb +1 -1
  210. data/lib/arel/nodes/node.rb +7 -6
  211. data/lib/arel/nodes/ordering.rb +27 -0
  212. data/lib/arel/nodes/sql_literal.rb +3 -0
  213. data/lib/arel/nodes/table_alias.rb +7 -3
  214. data/lib/arel/nodes/unary.rb +0 -1
  215. data/lib/arel/predications.rb +12 -18
  216. data/lib/arel/select_manager.rb +1 -2
  217. data/lib/arel/table.rb +13 -5
  218. data/lib/arel/visitors.rb +0 -7
  219. data/lib/arel/visitors/dot.rb +14 -2
  220. data/lib/arel/visitors/mysql.rb +11 -1
  221. data/lib/arel/visitors/postgresql.rb +15 -4
  222. data/lib/arel/visitors/to_sql.rb +89 -78
  223. data/lib/rails/generators/active_record/migration.rb +6 -1
  224. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  225. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  226. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
  227. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  228. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  229. metadata +27 -28
  230. data/lib/active_record/advisory_lock_base.rb +0 -18
  231. data/lib/active_record/attribute_decorators.rb +0 -88
  232. data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
  233. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  234. data/lib/active_record/define_callbacks.rb +0 -22
  235. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  236. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  237. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  238. data/lib/arel/attributes.rb +0 -22
  239. data/lib/arel/visitors/depth_first.rb +0 -203
  240. data/lib/arel/visitors/ibm_db.rb +0 -34
  241. data/lib/arel/visitors/informix.rb +0 -62
  242. data/lib/arel/visitors/mssql.rb +0 -156
  243. data/lib/arel/visitors/oracle.rb +0 -158
  244. data/lib/arel/visitors/oracle12.rb +0 -65
  245. data/lib/arel/visitors/where_sql.rb +0 -22
@@ -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)
@@ -129,8 +131,12 @@ module ActiveRecord
129
131
  end
130
132
 
131
133
  def inversed_from(record)
132
- self.target = record
133
- @inversed = !!record
134
+ if inversable?(record)
135
+ self.target = record
136
+ @inversed = true
137
+ else
138
+ @inversed = false
139
+ end
134
140
  end
135
141
  alias :inversed_from_queries :inversed_from
136
142
 
@@ -191,27 +197,34 @@ module ActiveRecord
191
197
  set_inverse_instance(record)
192
198
  end
193
199
 
194
- def create(attributes = {}, &block)
200
+ def create(attributes = nil, &block)
195
201
  _create_record(attributes, &block)
196
202
  end
197
203
 
198
- def create!(attributes = {}, &block)
204
+ def create!(attributes = nil, &block)
199
205
  _create_record(attributes, true, &block)
200
206
  end
201
207
 
202
208
  private
203
209
  def find_target
210
+ if owner.strict_loading?
211
+ Base.strict_loading_violation!(owner: owner.class, association: klass)
212
+ end
213
+
214
+ if reflection.strict_loading?
215
+ Base.strict_loading_violation!(owner: owner.class, association: reflection.name)
216
+ end
217
+
204
218
  scope = self.scope
205
219
  return scope.to_a if skip_statement_cache?(scope)
206
220
 
207
- conn = klass.connection
208
- sc = reflection.association_scope_cache(conn, owner) do |params|
221
+ sc = reflection.association_scope_cache(klass, owner) do |params|
209
222
  as = AssociationScope.create { params.bind }
210
223
  target_scope.merge!(as.scope(self))
211
224
  end
212
225
 
213
226
  binds = AssociationScope.get_bind_values(owner, reflection.chain)
214
- sc.execute(binds, conn) { |record| set_inverse_instance(record) } || []
227
+ sc.execute(binds, klass.connection) { |record| set_inverse_instance(record) }
215
228
  end
216
229
 
217
230
  # The scope for this association.
@@ -240,25 +253,6 @@ module ActiveRecord
240
253
  !loaded? && (!owner.new_record? || foreign_key_present?) && klass
241
254
  end
242
255
 
243
- def creation_attributes
244
- attributes = {}
245
-
246
- if (reflection.has_one? || reflection.collection?) && !options[:through]
247
- attributes[reflection.foreign_key] = owner[reflection.active_record_primary_key]
248
-
249
- if reflection.type
250
- attributes[reflection.type] = owner.class.polymorphic_name
251
- end
252
- end
253
-
254
- attributes
255
- end
256
-
257
- # Sets the owner attributes on the given record
258
- def set_owner_attributes(record)
259
- creation_attributes.each { |key, value| record[key] = value }
260
- end
261
-
262
256
  # Returns true if there is a foreign key present on the owner which
263
257
  # references the target. This is used to determine whether we can load
264
258
  # the target if the owner is currently a new record (and therefore
@@ -306,7 +300,7 @@ module ActiveRecord
306
300
 
307
301
  # Returns true if record contains the foreign_key
308
302
  def foreign_key_for?(record)
309
- record.has_attribute?(reflection.foreign_key)
303
+ record._has_attribute?(reflection.foreign_key)
310
304
  end
311
305
 
312
306
  # This should be implemented to return the values of the relevant key(s) on the owner,
@@ -331,6 +325,23 @@ module ActiveRecord
331
325
  klass.scope_attributes? ||
332
326
  reflection.source_reflection.active_record.default_scopes.any?
333
327
  end
328
+
329
+ def enqueue_destroy_association(options)
330
+ owner.class.destroy_association_async_job&.perform_later(**options)
331
+ end
332
+
333
+ def inversable?(record)
334
+ record &&
335
+ ((!record.persisted? || !owner.persisted?) || matches_foreign_key?(record))
336
+ end
337
+
338
+ def matches_foreign_key?(record)
339
+ if foreign_key_for?(record)
340
+ record.read_attribute(reflection.foreign_key) == owner.id
341
+ else
342
+ owner.read_attribute(reflection.foreign_key) == record.id
343
+ end
344
+ end
334
345
  end
335
346
  end
336
347
  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,16 @@ 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
+ join_dependency = item.construct_join_dependency(
135
+ item.eager_load_values | item.includes_values, Arel::Nodes::OuterJoin
136
+ )
137
+ scope.joins!(*item.joins_values, join_dependency)
138
+ scope.left_outer_joins!(*item.left_outer_joins_values)
137
139
  end
138
140
 
139
141
  reflection.all_includes do
140
- scope.includes! item.includes_values
142
+ scope.includes_values |= item.includes_values
141
143
  end
142
144
 
143
145
  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
@@ -108,11 +120,9 @@ module ActiveRecord
108
120
  owner._read_attribute(reflection.foreign_key)
109
121
  end
110
122
 
111
- # NOTE - for now, we're only supporting inverse setting from belongs_to back onto
112
- # has_one associations.
113
123
  def invertible_for?(record)
114
124
  inverse = inverse_reflection_for(record)
115
- inverse && inverse.has_one?
125
+ inverse && (inverse.has_one? || ActiveRecord::Base.has_many_inversing)
116
126
  end
117
127
 
118
128
  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?
@@ -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,7 +74,7 @@ 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)
77
79
  end
78
80
 
@@ -118,7 +120,11 @@ module ActiveRecord::Associations::Builder # :nodoc:
118
120
  raise NotImplementedError
119
121
  end
120
122
 
121
- def self.check_dependent_options(dependent)
123
+ def self.check_dependent_options(dependent, model)
124
+ if dependent == :destroy_async && !model.destroy_association_async_job
125
+ err_message = "ActiveJob is required to use destroy_async on associations"
126
+ raise ActiveRecord::ActiveJobRequiredError, err_message
127
+ end
122
128
  unless valid_dependent_options.include? dependent
123
129
  raise ArgumentError, "The :dependent option must be one of #{valid_dependent_options}, but is :#{dependent}"
124
130
  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 + [:counter_cache, :optional, :default]
11
+ valid += [:polymorphic, :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)
@@ -55,19 +58,19 @@ module ActiveRecord::Associations::Builder # :nodoc:
55
58
 
56
59
  if old_record
57
60
  if touch != true
58
- old_record.send(touch_method, touch)
61
+ old_record.public_send(touch_method, touch)
59
62
  else
60
- old_record.send(touch_method)
63
+ old_record.public_send(touch_method)
61
64
  end
62
65
  end
63
66
  end
64
67
 
65
- record = o.send name
68
+ record = o.public_send name
66
69
  if record && record.persisted?
67
70
  if touch != true
68
- record.send(touch_method, touch)
71
+ record.public_send(touch_method, touch)
69
72
  else
70
- record.send(touch_method)
73
+ record.public_send(touch_method)
71
74
  end
72
75
  end
73
76
  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)
@@ -31,8 +30,10 @@ module ActiveRecord::Associations::Builder # :nodoc:
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
@@ -75,7 +75,6 @@ module ActiveRecord::Associations::Builder # :nodoc:
75
75
  def middle_options(join_model)
76
76
  middle_options = {}
77
77
  middle_options[:class_name] = "#{lhs_model.name}::#{join_model.name}"
78
- middle_options[:source] = join_model.left_reflection.name
79
78
  if options.key? :foreign_key
80
79
  middle_options[:foreign_key] = options[:foreign_key]
81
80
  end
@@ -7,11 +7,15 @@ module ActiveRecord::Associations::Builder # :nodoc:
7
7
  end
8
8
 
9
9
  def self.valid_options(options)
10
- super + [:primary_key, :dependent, :as, :through, :source, :source_type, :inverse_of, :counter_cache, :join_table, :foreign_type, :index_errors]
10
+ valid = super + [:counter_cache, :join_table, :index_errors, :ensuring_owner_was]
11
+ valid += [:as, :foreign_type] if options[:as]
12
+ valid += [:through, :source, :source_type] if options[:through]
13
+ valid += [:ensuring_owner_was] if options[:dependent] == :destroy_async
14
+ valid
11
15
  end
12
16
 
13
17
  def self.valid_dependent_options
14
- [:destroy, :delete_all, :nullify, :restrict_with_error, :restrict_with_exception]
18
+ [:destroy, :delete_all, :nullify, :restrict_with_error, :restrict_with_exception, :destroy_async]
15
19
  end
16
20
 
17
21
  private_class_method :macro, :valid_options, :valid_dependent_options
@@ -7,13 +7,15 @@ module ActiveRecord::Associations::Builder # :nodoc:
7
7
  end
8
8
 
9
9
  def self.valid_options(options)
10
- valid = super + [:as, :touch]
10
+ valid = super
11
+ valid += [:as, :foreign_type] if options[:as]
12
+ valid += [:ensuring_owner_was] if options[:dependent] == :destroy_async
11
13
  valid += [:through, :source, :source_type] if options[:through]
12
14
  valid
13
15
  end
14
16
 
15
17
  def self.valid_dependent_options
16
- [:destroy, :delete, :nullify, :restrict_with_error, :restrict_with_exception]
18
+ [:destroy, :destroy_async, :delete, :nullify, :restrict_with_error, :restrict_with_exception]
17
19
  end
18
20
 
19
21
  def self.define_callbacks(model, reflection)
@@ -32,15 +34,12 @@ module ActiveRecord::Associations::Builder # :nodoc:
32
34
  end
33
35
  end
34
36
 
35
- def self.touch_record(o, name, touch)
36
- record = o.send name
37
+ def self.touch_record(record, name, touch)
38
+ instance = record.send(name)
37
39
 
38
- return unless record && record.persisted?
39
-
40
- if touch != true
41
- record.touch(touch)
42
- else
43
- record.touch
40
+ if instance&.persisted?
41
+ touch != true ?
42
+ instance.touch(touch) : instance.touch
44
43
  end
45
44
  end
46
45
 
@@ -48,11 +47,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
48
47
  name = reflection.name
49
48
  touch = reflection.options[:touch]
50
49
 
51
- callback = lambda { |record|
52
- HasOne.touch_record(record, name, touch)
53
- }
54
-
50
+ callback = -> (record) { HasOne.touch_record(record, name, touch) }
55
51
  model.after_create callback, if: :saved_changes?
52
+ model.after_create_commit { association(name).reset_negative_cache }
56
53
  model.after_update callback, if: :saved_changes?
57
54
  model.after_destroy callback
58
55
  model.after_touch callback
@@ -5,7 +5,7 @@
5
5
  module ActiveRecord::Associations::Builder # :nodoc:
6
6
  class SingularAssociation < Association #:nodoc:
7
7
  def self.valid_options(options)
8
- super + [:foreign_type, :dependent, :primary_key, :inverse_of, :required]
8
+ super + [:required, :touch]
9
9
  end
10
10
 
11
11
  def self.define_accessors(model, reflection)
@@ -56,7 +56,7 @@ module ActiveRecord
56
56
  def ids_writer(ids)
57
57
  primary_key = reflection.association_primary_key
58
58
  pk_type = klass.type_for_attribute(primary_key)
59
- ids = Array(ids).reject(&:blank?)
59
+ ids = Array(ids).compact_blank
60
60
  ids.map! { |i| pk_type.cast(i) }
61
61
 
62
62
  records = klass.where(primary_key => ids).index_by do |r|
@@ -101,11 +101,11 @@ module ActiveRecord
101
101
  end
102
102
  end
103
103
 
104
- def build(attributes = {}, &block)
104
+ def build(attributes = nil, &block)
105
105
  if attributes.is_a?(Array)
106
106
  attributes.collect { |attr| build(attr, &block) }
107
107
  else
108
- add_to_target(build_record(attributes, &block))
108
+ add_to_target(build_record(attributes, &block), replace: true)
109
109
  end
110
110
  end
111
111
 
@@ -228,7 +228,7 @@ module ActiveRecord
228
228
  # If the collection has been loaded
229
229
  # it is equivalent to <tt>collection.size.zero?</tt>. If the
230
230
  # collection has not been loaded, it is equivalent to
231
- # <tt>collection.exists?</tt>. If the collection has not already been
231
+ # <tt>!collection.exists?</tt>. If the collection has not already been
232
232
  # loaded and you are going to fetch the records anyway it is better to
233
233
  # check <tt>collection.length.zero?</tt>.
234
234
  def empty?
@@ -278,13 +278,24 @@ module ActiveRecord
278
278
  target
279
279
  end
280
280
 
281
- def add_to_target(record, skip_callbacks = false, &block)
282
- if association_scope.distinct_value
281
+ def add_to_target(record, skip_callbacks: false, replace: false, &block)
282
+ if replace || association_scope.distinct_value
283
283
  index = @target.index(record)
284
284
  end
285
285
  replace_on_target(record, index, skip_callbacks, &block)
286
286
  end
287
287
 
288
+ def target=(record)
289
+ return super unless ActiveRecord::Base.has_many_inversing
290
+
291
+ case record
292
+ when Array
293
+ super
294
+ else
295
+ add_to_target(record, skip_callbacks: true, replace: true)
296
+ end
297
+ end
298
+
288
299
  def scope
289
300
  scope = super
290
301
  scope.none! if null_scope?
@@ -297,6 +308,8 @@ module ActiveRecord
297
308
 
298
309
  def find_from_target?
299
310
  loaded? ||
311
+ owner.strict_loading? ||
312
+ reflection.strict_loading? ||
300
313
  owner.new_record? ||
301
314
  target.any? { |record| record.new_record? || record.changed? }
302
315
  end