activerecord 6.0.6.1 → 6.1.7.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (243) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1152 -779
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -2
  5. data/lib/active_record/aggregations.rb +5 -5
  6. data/lib/active_record/association_relation.rb +30 -12
  7. data/lib/active_record/associations/alias_tracker.rb +19 -15
  8. data/lib/active_record/associations/association.rb +49 -26
  9. data/lib/active_record/associations/association_scope.rb +18 -20
  10. data/lib/active_record/associations/belongs_to_association.rb +23 -10
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -3
  12. data/lib/active_record/associations/builder/association.rb +32 -5
  13. data/lib/active_record/associations/builder/belongs_to.rb +10 -7
  14. data/lib/active_record/associations/builder/collection_association.rb +5 -4
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -1
  16. data/lib/active_record/associations/builder/has_many.rb +6 -2
  17. data/lib/active_record/associations/builder/has_one.rb +11 -14
  18. data/lib/active_record/associations/builder/singular_association.rb +1 -1
  19. data/lib/active_record/associations/collection_association.rb +32 -18
  20. data/lib/active_record/associations/collection_proxy.rb +12 -5
  21. data/lib/active_record/associations/foreign_association.rb +13 -0
  22. data/lib/active_record/associations/has_many_association.rb +24 -2
  23. data/lib/active_record/associations/has_many_through_association.rb +10 -4
  24. data/lib/active_record/associations/has_one_association.rb +15 -1
  25. data/lib/active_record/associations/join_dependency/join_association.rb +37 -21
  26. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  27. data/lib/active_record/associations/join_dependency.rb +63 -49
  28. data/lib/active_record/associations/preloader/association.rb +14 -8
  29. data/lib/active_record/associations/preloader/through_association.rb +1 -1
  30. data/lib/active_record/associations/preloader.rb +5 -3
  31. data/lib/active_record/associations/singular_association.rb +1 -1
  32. data/lib/active_record/associations.rb +118 -11
  33. data/lib/active_record/attribute_assignment.rb +10 -8
  34. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
  35. data/lib/active_record/attribute_methods/dirty.rb +1 -11
  36. data/lib/active_record/attribute_methods/primary_key.rb +6 -2
  37. data/lib/active_record/attribute_methods/query.rb +3 -6
  38. data/lib/active_record/attribute_methods/read.rb +8 -11
  39. data/lib/active_record/attribute_methods/serialization.rb +11 -5
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -13
  41. data/lib/active_record/attribute_methods/write.rb +12 -20
  42. data/lib/active_record/attribute_methods.rb +64 -54
  43. data/lib/active_record/attributes.rb +33 -8
  44. data/lib/active_record/autosave_association.rb +47 -30
  45. data/lib/active_record/base.rb +2 -14
  46. data/lib/active_record/callbacks.rb +152 -22
  47. data/lib/active_record/coders/yaml_column.rb +1 -1
  48. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +185 -134
  49. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  50. data/lib/active_record/connection_adapters/abstract/database_statements.rb +66 -23
  51. data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -8
  52. data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
  53. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  54. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
  55. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +114 -26
  56. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +228 -83
  58. data/lib/active_record/connection_adapters/abstract/transaction.rb +92 -33
  59. data/lib/active_record/connection_adapters/abstract_adapter.rb +52 -76
  60. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +123 -87
  61. data/lib/active_record/connection_adapters/column.rb +15 -1
  62. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  63. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +24 -24
  65. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
  66. data/lib/active_record/connection_adapters/mysql/quoting.rb +18 -3
  67. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -6
  68. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  69. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +5 -2
  70. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +7 -4
  71. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  72. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -12
  73. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  74. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  75. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  76. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +14 -53
  77. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  78. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  79. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
  80. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
  84. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  86. data/lib/active_record/connection_adapters/postgresql/quoting.rb +30 -4
  87. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  88. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
  89. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
  90. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  91. data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -64
  92. data/lib/active_record/connection_adapters/schema_cache.rb +130 -15
  93. data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
  94. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +32 -5
  95. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
  96. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  97. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +36 -3
  98. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +48 -50
  99. data/lib/active_record/connection_adapters.rb +52 -0
  100. data/lib/active_record/connection_handling.rb +218 -71
  101. data/lib/active_record/core.rb +264 -63
  102. data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -0
  103. data/lib/active_record/database_configurations/database_config.rb +52 -9
  104. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  105. data/lib/active_record/database_configurations/url_config.rb +15 -40
  106. data/lib/active_record/database_configurations.rb +125 -85
  107. data/lib/active_record/delegated_type.rb +209 -0
  108. data/lib/active_record/destroy_association_async_job.rb +36 -0
  109. data/lib/active_record/enum.rb +69 -34
  110. data/lib/active_record/errors.rb +47 -12
  111. data/lib/active_record/explain.rb +9 -4
  112. data/lib/active_record/explain_subscriber.rb +1 -1
  113. data/lib/active_record/fixture_set/file.rb +10 -17
  114. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  115. data/lib/active_record/fixture_set/render_context.rb +1 -1
  116. data/lib/active_record/fixture_set/table_row.rb +2 -2
  117. data/lib/active_record/fixtures.rb +58 -9
  118. data/lib/active_record/gem_version.rb +3 -3
  119. data/lib/active_record/inheritance.rb +40 -18
  120. data/lib/active_record/insert_all.rb +38 -5
  121. data/lib/active_record/integration.rb +3 -5
  122. data/lib/active_record/internal_metadata.rb +18 -7
  123. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  124. data/lib/active_record/locking/optimistic.rb +24 -17
  125. data/lib/active_record/locking/pessimistic.rb +6 -2
  126. data/lib/active_record/log_subscriber.rb +27 -8
  127. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  128. data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
  129. data/lib/active_record/middleware/database_selector.rb +4 -1
  130. data/lib/active_record/migration/command_recorder.rb +47 -27
  131. data/lib/active_record/migration/compatibility.rb +72 -18
  132. data/lib/active_record/migration.rb +114 -84
  133. data/lib/active_record/model_schema.rb +89 -14
  134. data/lib/active_record/nested_attributes.rb +2 -3
  135. data/lib/active_record/no_touching.rb +1 -1
  136. data/lib/active_record/persistence.rb +50 -45
  137. data/lib/active_record/query_cache.rb +15 -5
  138. data/lib/active_record/querying.rb +11 -6
  139. data/lib/active_record/railtie.rb +64 -44
  140. data/lib/active_record/railties/console_sandbox.rb +2 -4
  141. data/lib/active_record/railties/databases.rake +279 -101
  142. data/lib/active_record/readonly_attributes.rb +4 -0
  143. data/lib/active_record/reflection.rb +60 -44
  144. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  145. data/lib/active_record/relation/batches.rb +38 -31
  146. data/lib/active_record/relation/calculations.rb +104 -43
  147. data/lib/active_record/relation/finder_methods.rb +44 -14
  148. data/lib/active_record/relation/from_clause.rb +1 -1
  149. data/lib/active_record/relation/merger.rb +20 -23
  150. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  151. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  152. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -6
  153. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  154. data/lib/active_record/relation/predicate_builder.rb +61 -38
  155. data/lib/active_record/relation/query_methods.rb +322 -196
  156. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  157. data/lib/active_record/relation/spawn_methods.rb +8 -7
  158. data/lib/active_record/relation/where_clause.rb +111 -61
  159. data/lib/active_record/relation.rb +100 -81
  160. data/lib/active_record/result.rb +41 -33
  161. data/lib/active_record/runtime_registry.rb +2 -2
  162. data/lib/active_record/sanitization.rb +6 -17
  163. data/lib/active_record/schema_dumper.rb +34 -4
  164. data/lib/active_record/schema_migration.rb +2 -8
  165. data/lib/active_record/scoping/default.rb +1 -3
  166. data/lib/active_record/scoping/named.rb +1 -17
  167. data/lib/active_record/secure_token.rb +16 -8
  168. data/lib/active_record/serialization.rb +5 -3
  169. data/lib/active_record/signed_id.rb +116 -0
  170. data/lib/active_record/statement_cache.rb +20 -4
  171. data/lib/active_record/store.rb +8 -3
  172. data/lib/active_record/suppressor.rb +2 -2
  173. data/lib/active_record/table_metadata.rb +42 -51
  174. data/lib/active_record/tasks/database_tasks.rb +140 -113
  175. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
  176. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
  177. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
  178. data/lib/active_record/test_databases.rb +5 -4
  179. data/lib/active_record/test_fixtures.rb +79 -31
  180. data/lib/active_record/timestamp.rb +4 -6
  181. data/lib/active_record/touch_later.rb +21 -21
  182. data/lib/active_record/transactions.rb +19 -66
  183. data/lib/active_record/type/serialized.rb +6 -2
  184. data/lib/active_record/type.rb +8 -1
  185. data/lib/active_record/type_caster/connection.rb +0 -1
  186. data/lib/active_record/type_caster/map.rb +8 -5
  187. data/lib/active_record/validations/associated.rb +1 -1
  188. data/lib/active_record/validations/numericality.rb +35 -0
  189. data/lib/active_record/validations/uniqueness.rb +24 -4
  190. data/lib/active_record/validations.rb +1 -0
  191. data/lib/active_record.rb +7 -14
  192. data/lib/arel/attributes/attribute.rb +4 -0
  193. data/lib/arel/collectors/bind.rb +5 -0
  194. data/lib/arel/collectors/composite.rb +8 -0
  195. data/lib/arel/collectors/sql_string.rb +7 -0
  196. data/lib/arel/collectors/substitute_binds.rb +7 -0
  197. data/lib/arel/nodes/binary.rb +82 -8
  198. data/lib/arel/nodes/bind_param.rb +8 -0
  199. data/lib/arel/nodes/casted.rb +21 -9
  200. data/lib/arel/nodes/equality.rb +6 -9
  201. data/lib/arel/nodes/grouping.rb +3 -0
  202. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  203. data/lib/arel/nodes/in.rb +8 -1
  204. data/lib/arel/nodes/infix_operation.rb +13 -1
  205. data/lib/arel/nodes/join_source.rb +1 -1
  206. data/lib/arel/nodes/node.rb +7 -6
  207. data/lib/arel/nodes/ordering.rb +27 -0
  208. data/lib/arel/nodes/sql_literal.rb +3 -0
  209. data/lib/arel/nodes/table_alias.rb +7 -3
  210. data/lib/arel/nodes/unary.rb +0 -1
  211. data/lib/arel/nodes.rb +3 -1
  212. data/lib/arel/predications.rb +12 -18
  213. data/lib/arel/select_manager.rb +1 -2
  214. data/lib/arel/table.rb +13 -5
  215. data/lib/arel/visitors/dot.rb +14 -2
  216. data/lib/arel/visitors/mysql.rb +11 -1
  217. data/lib/arel/visitors/postgresql.rb +15 -4
  218. data/lib/arel/visitors/to_sql.rb +89 -78
  219. data/lib/arel/visitors.rb +0 -7
  220. data/lib/arel.rb +5 -13
  221. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  222. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  223. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
  224. data/lib/rails/generators/active_record/migration.rb +6 -1
  225. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  226. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  227. metadata +25 -26
  228. data/lib/active_record/advisory_lock_base.rb +0 -18
  229. data/lib/active_record/attribute_decorators.rb +0 -88
  230. data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
  231. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  232. data/lib/active_record/define_callbacks.rb +0 -22
  233. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  234. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  235. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  236. data/lib/arel/attributes.rb +0 -22
  237. data/lib/arel/visitors/depth_first.rb +0 -203
  238. data/lib/arel/visitors/ibm_db.rb +0 -34
  239. data/lib/arel/visitors/informix.rb +0 -62
  240. data/lib/arel/visitors/mssql.rb +0 -156
  241. data/lib/arel/visitors/oracle.rb +0 -158
  242. data/lib/arel/visitors/oracle12.rb +0 -65
  243. data/lib/arel/visitors/where_sql.rb +0 -22
@@ -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|
@@ -75,6 +75,7 @@ module ActiveRecord
75
75
  def reset
76
76
  super
77
77
  @target = []
78
+ @replaced_or_added_targets = Set.new
78
79
  @association_ids = nil
79
80
  end
80
81
 
@@ -101,11 +102,11 @@ module ActiveRecord
101
102
  end
102
103
  end
103
104
 
104
- def build(attributes = {}, &block)
105
+ def build(attributes = nil, &block)
105
106
  if attributes.is_a?(Array)
106
107
  attributes.collect { |attr| build(attr, &block) }
107
108
  else
108
- add_to_target(build_record(attributes, &block))
109
+ add_to_target(build_record(attributes, &block), replace: true)
109
110
  end
110
111
  end
111
112
 
@@ -228,7 +229,7 @@ module ActiveRecord
228
229
  # If the collection has been loaded
229
230
  # it is equivalent to <tt>collection.size.zero?</tt>. If the
230
231
  # collection has not been loaded, it is equivalent to
231
- # <tt>collection.exists?</tt>. If the collection has not already been
232
+ # <tt>!collection.exists?</tt>. If the collection has not already been
232
233
  # loaded and you are going to fetch the records anyway it is better to
233
234
  # check <tt>collection.length.zero?</tt>.
234
235
  def empty?
@@ -278,11 +279,19 @@ module ActiveRecord
278
279
  target
279
280
  end
280
281
 
281
- def add_to_target(record, skip_callbacks = false, &block)
282
- if association_scope.distinct_value
283
- index = @target.index(record)
282
+ def add_to_target(record, skip_callbacks: false, replace: false, &block)
283
+ replace_on_target(record, skip_callbacks, replace: replace || association_scope.distinct_value, &block)
284
+ end
285
+
286
+ def target=(record)
287
+ return super unless ActiveRecord::Base.has_many_inversing
288
+
289
+ case record
290
+ when Array
291
+ super
292
+ else
293
+ replace_on_target(record, true, replace: true, inversing: true)
284
294
  end
285
- replace_on_target(record, index, skip_callbacks, &block)
286
295
  end
287
296
 
288
297
  def scope
@@ -297,6 +306,8 @@ module ActiveRecord
297
306
 
298
307
  def find_from_target?
299
308
  loaded? ||
309
+ owner.strict_loading? ||
310
+ reflection.strict_loading? ||
300
311
  owner.new_record? ||
301
312
  target.any? { |record| record.new_record? || record.changed? }
302
313
  end
@@ -329,14 +340,7 @@ module ActiveRecord
329
340
  end
330
341
  end
331
342
 
332
- persisted + memory
333
- end
334
-
335
- def build_record(attributes)
336
- previous = klass.current_scope(true) if block_given?
337
- super
338
- ensure
339
- klass.current_scope = previous if previous
343
+ persisted + memory.reject(&:persisted?)
340
344
  end
341
345
 
342
346
  def _create_record(attributes, raise = false, &block)
@@ -419,7 +423,7 @@ module ActiveRecord
419
423
  common_records = intersection(new_target, original_target)
420
424
  common_records.each do |record|
421
425
  skip_callbacks = true
422
- replace_on_target(record, @target.index(record), skip_callbacks)
426
+ replace_on_target(record, skip_callbacks, replace: true)
423
427
  end
424
428
  end
425
429
 
@@ -442,7 +446,11 @@ module ActiveRecord
442
446
  records
443
447
  end
444
448
 
445
- def replace_on_target(record, index, skip_callbacks)
449
+ def replace_on_target(record, skip_callbacks, replace:, inversing: false)
450
+ if replace && (!record.new_record? || @replaced_or_added_targets.include?(record))
451
+ index = @target.index(record)
452
+ end
453
+
446
454
  catch(:abort) do
447
455
  callback(:before_add, record)
448
456
  end || return unless skip_callbacks
@@ -453,6 +461,12 @@ module ActiveRecord
453
461
 
454
462
  yield(record) if block_given?
455
463
 
464
+ if !index && @replaced_or_added_targets.include?(record)
465
+ index = @target.index(record)
466
+ end
467
+
468
+ @replaced_or_added_targets << record if inversing || index || record.new_record?
469
+
456
470
  if index
457
471
  target[index] = record
458
472
  elsif @_was_loaded || !loaded?
@@ -101,7 +101,7 @@ module ActiveRecord
101
101
  # converting them into an array and iterating through them using
102
102
  # Array#select.
103
103
  #
104
- # person.pets.select { |pet| pet.name =~ /oo/ }
104
+ # person.pets.select { |pet| /oo/.match?(pet.name) }
105
105
  # # => [
106
106
  # # #<Pet id: 2, name: "Spook", person_id: 1>,
107
107
  # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
@@ -374,7 +374,7 @@ module ActiveRecord
374
374
  # person.pets
375
375
  # # => [#<Pet id: 1, name: "Gorby", group: "cats", person_id: 1>]
376
376
  #
377
- # other_pets = [Pet.new(name: 'Puff', group: 'celebrities']
377
+ # other_pets = [Pet.new(name: 'Puff', group: 'celebrities')]
378
378
  #
379
379
  # person.pets.replace(other_pets)
380
380
  #
@@ -921,7 +921,7 @@ module ActiveRecord
921
921
  !!@association.include?(record)
922
922
  end
923
923
 
924
- def proxy_association
924
+ def proxy_association # :nodoc:
925
925
  @association
926
926
  end
927
927
 
@@ -1087,17 +1087,24 @@ module ActiveRecord
1087
1087
  end
1088
1088
 
1089
1089
  def reset_scope # :nodoc:
1090
- @offsets = {}
1090
+ @offsets = @take = nil
1091
1091
  @scope = nil
1092
1092
  self
1093
1093
  end
1094
1094
 
1095
+ def inspect # :nodoc:
1096
+ load_target if find_from_target?
1097
+ super
1098
+ end
1099
+
1095
1100
  delegate_methods = [
1096
1101
  QueryMethods,
1097
1102
  SpawnMethods,
1098
1103
  ].flat_map { |klass|
1099
1104
  klass.public_instance_methods(false)
1100
- } - self.public_instance_methods(false) - [:select] + [:scoping, :values]
1105
+ } - self.public_instance_methods(false) - [:select] + [
1106
+ :scoping, :values, :insert, :insert_all, :insert!, :insert_all!, :upsert, :upsert_all
1107
+ ]
1101
1108
 
1102
1109
  delegate(*delegate_methods, to: :scope)
1103
1110
 
@@ -16,5 +16,18 @@ module ActiveRecord::Associations
16
16
  attrs[reflection.type] = nil if reflection.type.present?
17
17
  end
18
18
  end
19
+
20
+ private
21
+ # Sets the owner attributes on the given record
22
+ def set_owner_attributes(record)
23
+ return if options[:through]
24
+
25
+ key = owner._read_attribute(reflection.join_foreign_key)
26
+ record._write_attribute(reflection.join_primary_key, key)
27
+
28
+ if reflection.type
29
+ record._write_attribute(reflection.type, owner.class.polymorphic_name)
30
+ end
31
+ end
19
32
  end
20
33
  end
@@ -26,6 +26,28 @@ module ActiveRecord
26
26
  # No point in executing the counter update since we're going to destroy the parent anyway
27
27
  load_target.each { |t| t.destroyed_by_association = reflection }
28
28
  destroy_all
29
+ when :destroy_async
30
+ load_target.each do |t|
31
+ t.destroyed_by_association = reflection
32
+ end
33
+
34
+ unless target.empty?
35
+ association_class = target.first.class
36
+ primary_key_column = association_class.primary_key.to_sym
37
+
38
+ ids = target.collect do |assoc|
39
+ assoc.public_send(primary_key_column)
40
+ end
41
+
42
+ enqueue_destroy_association(
43
+ owner_model_name: owner.class.to_s,
44
+ owner_id: owner.id,
45
+ association_class: reflection.klass.to_s,
46
+ association_ids: ids,
47
+ association_primary_key_column: primary_key_column,
48
+ ensuring_owner_was_method: options.fetch(:ensuring_owner_was, nil)
49
+ )
50
+ end
29
51
  else
30
52
  delete_all
31
53
  end
@@ -52,7 +74,7 @@ module ActiveRecord
52
74
  # the loaded flag is set to true as well.
53
75
  def count_records
54
76
  count = if reflection.has_cached_counter?
55
- owner._read_attribute(reflection.counter_cache_column).to_i
77
+ owner.read_attribute(reflection.counter_cache_column).to_i
56
78
  else
57
79
  scope.count(:all)
58
80
  end
@@ -75,7 +97,7 @@ module ActiveRecord
75
97
  if reflection.counter_must_be_updated_by_has_many?
76
98
  counter = reflection.counter_cache_column
77
99
  owner.increment(counter, difference)
78
- owner.send(:clear_attribute_change, counter) # eww
100
+ owner.send(:"clear_#{counter}_change")
79
101
  end
80
102
  end
81
103
 
@@ -8,7 +8,7 @@ module ActiveRecord
8
8
 
9
9
  def initialize(owner, reflection)
10
10
  super
11
- @through_records = {}
11
+ @through_records = {}.compare_by_identity
12
12
  end
13
13
 
14
14
  def concat(*records)
@@ -54,7 +54,7 @@ module ActiveRecord
54
54
  # However, after insert_record has been called, the cache is cleared in
55
55
  # order to allow multiple instances of the same record in an association.
56
56
  def build_through_record(record)
57
- @through_records[record.object_id] ||= begin
57
+ @through_records[record] ||= begin
58
58
  ensure_mutable
59
59
 
60
60
  attributes = through_scope_attributes
@@ -65,7 +65,10 @@ module ActiveRecord
65
65
  end
66
66
  end
67
67
 
68
+ attr_reader :through_scope
69
+
68
70
  def through_scope_attributes
71
+ scope = through_scope || self.scope
69
72
  scope.where_values_hash(through_association.reflection.name.to_s).
70
73
  except!(through_association.reflection.foreign_key,
71
74
  through_association.reflection.klass.inheritance_column)
@@ -77,12 +80,13 @@ module ActiveRecord
77
80
  association.save!
78
81
  end
79
82
  ensure
80
- @through_records.delete(record.object_id)
83
+ @through_records.delete(record)
81
84
  end
82
85
 
83
86
  def build_record(attributes)
84
87
  ensure_not_nested
85
88
 
89
+ @through_scope = scope
86
90
  record = super
87
91
 
88
92
  inverse = source_reflection.inverse_of
@@ -95,6 +99,8 @@ module ActiveRecord
95
99
  end
96
100
 
97
101
  record
102
+ ensure
103
+ @through_scope = nil
98
104
  end
99
105
 
100
106
  def remove_records(existing_records, records, method)
@@ -202,7 +208,7 @@ module ActiveRecord
202
208
  end
203
209
  end
204
210
 
205
- @through_records.delete(record.object_id)
211
+ @through_records.delete(record)
206
212
  end
207
213
  end
208
214
 
@@ -32,6 +32,18 @@ module ActiveRecord
32
32
  target.destroyed_by_association = reflection
33
33
  target.destroy
34
34
  throw(:abort) unless target.destroyed?
35
+ when :destroy_async
36
+ primary_key_column = target.class.primary_key.to_sym
37
+ id = target.public_send(primary_key_column)
38
+
39
+ enqueue_destroy_association(
40
+ owner_model_name: owner.class.to_s,
41
+ owner_id: owner.id,
42
+ association_class: reflection.klass.to_s,
43
+ association_ids: [id],
44
+ association_primary_key_column: primary_key_column,
45
+ ensuring_owner_was_method: options.fetch(:ensuring_owner_was, nil)
46
+ )
35
47
  when :nullify
36
48
  target.update_columns(nullified_owner_attributes) if target.persisted?
37
49
  end
@@ -81,7 +93,9 @@ module ActiveRecord
81
93
  target.delete
82
94
  when :destroy
83
95
  target.destroyed_by_association = reflection
84
- target.destroy
96
+ if target.persisted?
97
+ target.destroy
98
+ end
85
99
  else
86
100
  nullify_owner_attributes(target)
87
101
  remove_inverse_instance(target)
@@ -14,7 +14,6 @@ module ActiveRecord
14
14
  super(reflection.klass, children)
15
15
 
16
16
  @reflection = reflection
17
- @tables = nil
18
17
  end
19
18
 
20
19
  def match?(other)
@@ -24,32 +23,47 @@ module ActiveRecord
24
23
 
25
24
  def join_constraints(foreign_table, foreign_klass, join_type, alias_tracker)
26
25
  joins = []
26
+ chain = []
27
+
28
+ reflection.chain.each do |reflection|
29
+ table, terminated = yield reflection
30
+ @table ||= table
31
+
32
+ if terminated
33
+ foreign_table, foreign_klass = table, reflection.klass
34
+ break
35
+ end
36
+
37
+ chain << [reflection, table]
38
+ end
27
39
 
28
40
  # The chain starts with the target table, but we want to end with it here (makes
29
41
  # more sense in this context), so we reverse
30
- reflection.chain.reverse_each.with_index(1) do |reflection, i|
31
- table = tables[-i]
42
+ chain.reverse_each do |reflection, table|
32
43
  klass = reflection.klass
33
44
 
34
- join_scope = reflection.join_scope(table, foreign_table, foreign_klass)
45
+ scope = reflection.join_scope(table, foreign_table, foreign_klass)
46
+
47
+ unless scope.references_values.empty?
48
+ associations = scope.eager_load_values | scope.includes_values
35
49
 
36
- unless join_scope.references_values.empty?
37
- join_dependency = join_scope.construct_join_dependency(
38
- join_scope.eager_load_values | join_scope.includes_values, Arel::Nodes::OuterJoin
39
- )
40
- join_scope.joins!(join_dependency)
50
+ unless associations.empty?
51
+ scope.joins! scope.construct_join_dependency(associations, Arel::Nodes::OuterJoin)
52
+ end
41
53
  end
42
54
 
43
- arel = join_scope.arel(alias_tracker.aliases)
55
+ arel = scope.arel(alias_tracker.aliases)
44
56
  nodes = arel.constraints.first
45
57
 
46
- others = nodes.children.extract! do |node|
47
- !Arel.fetch_attribute(node) { |attr| attr.relation.name == table.name }
58
+ if nodes.is_a?(Arel::Nodes::And)
59
+ others = nodes.children.extract! do |node|
60
+ !Arel.fetch_attribute(node) { |attr| attr.relation.name == table.name }
61
+ end
48
62
  end
49
63
 
50
- joins << table.create_join(table, table.create_on(nodes), join_type)
64
+ joins << join_type.new(table, Arel::Nodes::On.new(nodes))
51
65
 
52
- unless others.empty?
66
+ if others && !others.empty?
53
67
  joins.concat arel.join_sources
54
68
  append_constraints(joins.last, others)
55
69
  end
@@ -61,24 +75,26 @@ module ActiveRecord
61
75
  joins
62
76
  end
63
77
 
64
- def tables=(tables)
65
- @tables = tables
66
- @table = tables.first
67
- end
68
-
69
78
  def readonly?
70
79
  return @readonly if defined?(@readonly)
71
80
 
72
81
  @readonly = reflection.scope && reflection.scope_for(base_klass.unscoped).readonly_value
73
82
  end
74
83
 
84
+ def strict_loading?
85
+ return @strict_loading if defined?(@strict_loading)
86
+
87
+ @strict_loading = reflection.scope && reflection.scope_for(base_klass.unscoped).strict_loading_value
88
+ end
89
+
75
90
  private
76
91
  def append_constraints(join, constraints)
77
92
  if join.is_a?(Arel::Nodes::StringJoin)
78
- join_string = table.create_and(constraints.unshift(join.left))
93
+ join_string = Arel::Nodes::And.new(constraints.unshift join.left)
79
94
  join.left = Arel.sql(base_klass.connection.visitor.compile(join_string))
80
95
  else
81
- join.right.expr.children.concat(constraints)
96
+ right = join.right
97
+ right.expr = Arel::Nodes::And.new(constraints.unshift right.expr)
82
98
  end
83
99
  end
84
100
  end
@@ -17,7 +17,7 @@ module ActiveRecord
17
17
  # association.
18
18
  attr_reader :base_klass, :children
19
19
 
20
- delegate :table_name, :column_names, :primary_key, to: :base_klass
20
+ delegate :table_name, :column_names, :primary_key, :attribute_types, to: :base_klass
21
21
 
22
22
  def initialize(base_klass, children)
23
23
  @base_klass = base_klass