activerecord 5.0.7 → 5.1.7

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 (219) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +657 -2080
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/examples/performance.rb +28 -28
  6. data/examples/simple.rb +3 -3
  7. data/lib/active_record/aggregations.rb +244 -244
  8. data/lib/active_record/association_relation.rb +5 -5
  9. data/lib/active_record/associations/alias_tracker.rb +10 -11
  10. data/lib/active_record/associations/association.rb +23 -5
  11. data/lib/active_record/associations/association_scope.rb +95 -81
  12. data/lib/active_record/associations/belongs_to_association.rb +7 -4
  13. data/lib/active_record/associations/builder/belongs_to.rb +30 -16
  14. data/lib/active_record/associations/builder/collection_association.rb +1 -2
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +27 -27
  16. data/lib/active_record/associations/collection_association.rb +36 -205
  17. data/lib/active_record/associations/collection_proxy.rb +132 -63
  18. data/lib/active_record/associations/has_many_association.rb +10 -19
  19. data/lib/active_record/associations/has_many_through_association.rb +12 -4
  20. data/lib/active_record/associations/has_one_association.rb +24 -28
  21. data/lib/active_record/associations/has_one_through_association.rb +5 -1
  22. data/lib/active_record/associations/join_dependency/join_association.rb +4 -28
  23. data/lib/active_record/associations/join_dependency/join_base.rb +1 -1
  24. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  25. data/lib/active_record/associations/join_dependency.rb +121 -118
  26. data/lib/active_record/associations/preloader/association.rb +64 -64
  27. data/lib/active_record/associations/preloader/belongs_to.rb +0 -2
  28. data/lib/active_record/associations/preloader/collection_association.rb +6 -6
  29. data/lib/active_record/associations/preloader/has_many.rb +0 -2
  30. data/lib/active_record/associations/preloader/singular_association.rb +6 -8
  31. data/lib/active_record/associations/preloader/through_association.rb +41 -41
  32. data/lib/active_record/associations/preloader.rb +94 -94
  33. data/lib/active_record/associations/singular_association.rb +8 -25
  34. data/lib/active_record/associations/through_association.rb +2 -5
  35. data/lib/active_record/associations.rb +1591 -1562
  36. data/lib/active_record/attribute/user_provided_default.rb +4 -2
  37. data/lib/active_record/attribute.rb +98 -71
  38. data/lib/active_record/attribute_assignment.rb +61 -61
  39. data/lib/active_record/attribute_decorators.rb +35 -13
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -7
  41. data/lib/active_record/attribute_methods/dirty.rb +229 -46
  42. data/lib/active_record/attribute_methods/primary_key.rb +74 -73
  43. data/lib/active_record/attribute_methods/read.rb +39 -35
  44. data/lib/active_record/attribute_methods/serialization.rb +7 -7
  45. data/lib/active_record/attribute_methods/time_zone_conversion.rb +35 -58
  46. data/lib/active_record/attribute_methods/write.rb +30 -33
  47. data/lib/active_record/attribute_methods.rb +56 -65
  48. data/lib/active_record/attribute_mutation_tracker.rb +63 -11
  49. data/lib/active_record/attribute_set/builder.rb +27 -33
  50. data/lib/active_record/attribute_set/yaml_encoder.rb +41 -0
  51. data/lib/active_record/attribute_set.rb +9 -6
  52. data/lib/active_record/attributes.rb +22 -22
  53. data/lib/active_record/autosave_association.rb +18 -13
  54. data/lib/active_record/base.rb +24 -22
  55. data/lib/active_record/callbacks.rb +56 -14
  56. data/lib/active_record/coders/yaml_column.rb +9 -11
  57. data/lib/active_record/collection_cache_key.rb +3 -4
  58. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +330 -284
  59. data/lib/active_record/connection_adapters/abstract/database_limits.rb +1 -3
  60. data/lib/active_record/connection_adapters/abstract/database_statements.rb +39 -37
  61. data/lib/active_record/connection_adapters/abstract/query_cache.rb +32 -27
  62. data/lib/active_record/connection_adapters/abstract/quoting.rb +62 -51
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +10 -20
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +74 -79
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +53 -41
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +120 -100
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +49 -43
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +165 -135
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +404 -424
  70. data/lib/active_record/connection_adapters/column.rb +26 -4
  71. data/lib/active_record/connection_adapters/connection_specification.rb +128 -118
  72. data/lib/active_record/connection_adapters/mysql/column.rb +6 -31
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +36 -49
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +22 -22
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +6 -12
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +49 -45
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +16 -19
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -28
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +43 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +7 -6
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +23 -27
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +32 -53
  83. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +3 -3
  84. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +19 -9
  85. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +5 -3
  86. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +1 -1
  89. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -3
  90. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +16 -16
  91. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +0 -10
  92. data/lib/active_record/connection_adapters/postgresql/oid/{rails_5_1_point.rb → legacy_point.rb} +9 -16
  93. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +13 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +28 -8
  96. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +32 -30
  97. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -1
  98. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +51 -51
  99. data/lib/active_record/connection_adapters/postgresql/oid.rb +22 -21
  100. data/lib/active_record/connection_adapters/postgresql/quoting.rb +40 -35
  101. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +15 -0
  102. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +37 -24
  103. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +19 -23
  104. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +182 -222
  105. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +6 -4
  106. data/lib/active_record/connection_adapters/postgresql/utils.rb +7 -5
  107. data/lib/active_record/connection_adapters/postgresql_adapter.rb +198 -167
  108. data/lib/active_record/connection_adapters/schema_cache.rb +16 -7
  109. data/lib/active_record/connection_adapters/sql_type_metadata.rb +3 -3
  110. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +1 -1
  111. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +16 -19
  112. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +1 -8
  113. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +28 -0
  114. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +17 -0
  115. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +32 -0
  116. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +184 -167
  117. data/lib/active_record/connection_adapters/statement_pool.rb +7 -7
  118. data/lib/active_record/connection_handling.rb +14 -26
  119. data/lib/active_record/core.rb +109 -93
  120. data/lib/active_record/counter_cache.rb +60 -13
  121. data/lib/active_record/define_callbacks.rb +20 -0
  122. data/lib/active_record/dynamic_matchers.rb +80 -79
  123. data/lib/active_record/enum.rb +8 -6
  124. data/lib/active_record/errors.rb +64 -15
  125. data/lib/active_record/explain.rb +1 -2
  126. data/lib/active_record/explain_registry.rb +1 -1
  127. data/lib/active_record/explain_subscriber.rb +7 -4
  128. data/lib/active_record/fixture_set/file.rb +11 -8
  129. data/lib/active_record/fixtures.rb +66 -53
  130. data/lib/active_record/gem_version.rb +1 -1
  131. data/lib/active_record/inheritance.rb +93 -79
  132. data/lib/active_record/integration.rb +7 -7
  133. data/lib/active_record/internal_metadata.rb +3 -16
  134. data/lib/active_record/legacy_yaml_adapter.rb +1 -1
  135. data/lib/active_record/locking/optimistic.rb +69 -74
  136. data/lib/active_record/locking/pessimistic.rb +10 -1
  137. data/lib/active_record/log_subscriber.rb +23 -28
  138. data/lib/active_record/migration/command_recorder.rb +94 -94
  139. data/lib/active_record/migration/compatibility.rb +100 -47
  140. data/lib/active_record/migration/join_table.rb +6 -6
  141. data/lib/active_record/migration.rb +153 -155
  142. data/lib/active_record/model_schema.rb +94 -107
  143. data/lib/active_record/nested_attributes.rb +200 -199
  144. data/lib/active_record/null_relation.rb +11 -34
  145. data/lib/active_record/persistence.rb +65 -50
  146. data/lib/active_record/query_cache.rb +2 -6
  147. data/lib/active_record/querying.rb +3 -4
  148. data/lib/active_record/railtie.rb +16 -17
  149. data/lib/active_record/railties/controller_runtime.rb +6 -2
  150. data/lib/active_record/railties/databases.rake +105 -133
  151. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  152. data/lib/active_record/readonly_attributes.rb +2 -2
  153. data/lib/active_record/reflection.rb +154 -108
  154. data/lib/active_record/relation/batches/batch_enumerator.rb +1 -1
  155. data/lib/active_record/relation/batches.rb +80 -51
  156. data/lib/active_record/relation/calculations.rb +169 -162
  157. data/lib/active_record/relation/delegation.rb +32 -31
  158. data/lib/active_record/relation/finder_methods.rb +197 -231
  159. data/lib/active_record/relation/merger.rb +58 -62
  160. data/lib/active_record/relation/predicate_builder/array_handler.rb +7 -5
  161. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +23 -23
  162. data/lib/active_record/relation/predicate_builder/base_handler.rb +3 -1
  163. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +0 -8
  164. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +12 -10
  165. data/lib/active_record/relation/predicate_builder/range_handler.rb +0 -8
  166. data/lib/active_record/relation/predicate_builder.rb +92 -89
  167. data/lib/active_record/relation/query_attribute.rb +1 -1
  168. data/lib/active_record/relation/query_methods.rb +255 -293
  169. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  170. data/lib/active_record/relation/spawn_methods.rb +4 -5
  171. data/lib/active_record/relation/where_clause.rb +80 -65
  172. data/lib/active_record/relation/where_clause_factory.rb +47 -8
  173. data/lib/active_record/relation.rb +93 -119
  174. data/lib/active_record/result.rb +41 -32
  175. data/lib/active_record/runtime_registry.rb +3 -3
  176. data/lib/active_record/sanitization.rb +176 -192
  177. data/lib/active_record/schema.rb +3 -3
  178. data/lib/active_record/schema_dumper.rb +15 -38
  179. data/lib/active_record/schema_migration.rb +8 -4
  180. data/lib/active_record/scoping/default.rb +90 -90
  181. data/lib/active_record/scoping/named.rb +11 -11
  182. data/lib/active_record/scoping.rb +6 -6
  183. data/lib/active_record/secure_token.rb +2 -2
  184. data/lib/active_record/statement_cache.rb +13 -15
  185. data/lib/active_record/store.rb +31 -32
  186. data/lib/active_record/suppressor.rb +2 -1
  187. data/lib/active_record/table_metadata.rb +9 -5
  188. data/lib/active_record/tasks/database_tasks.rb +65 -55
  189. data/lib/active_record/tasks/mysql_database_tasks.rb +76 -73
  190. data/lib/active_record/tasks/postgresql_database_tasks.rb +72 -47
  191. data/lib/active_record/tasks/sqlite_database_tasks.rb +18 -16
  192. data/lib/active_record/timestamp.rb +46 -25
  193. data/lib/active_record/touch_later.rb +1 -2
  194. data/lib/active_record/transactions.rb +97 -109
  195. data/lib/active_record/type/adapter_specific_registry.rb +46 -42
  196. data/lib/active_record/type/decimal_without_scale.rb +13 -0
  197. data/lib/active_record/type/hash_lookup_type_map.rb +3 -3
  198. data/lib/active_record/type/internal/abstract_json.rb +4 -0
  199. data/lib/active_record/type/serialized.rb +14 -8
  200. data/lib/active_record/type/text.rb +9 -0
  201. data/lib/active_record/type/time.rb +0 -1
  202. data/lib/active_record/type/type_map.rb +11 -15
  203. data/lib/active_record/type/unsigned_integer.rb +15 -0
  204. data/lib/active_record/type.rb +17 -13
  205. data/lib/active_record/type_caster/connection.rb +8 -6
  206. data/lib/active_record/type_caster/map.rb +3 -1
  207. data/lib/active_record/type_caster.rb +2 -2
  208. data/lib/active_record/validations/associated.rb +1 -1
  209. data/lib/active_record/validations/presence.rb +2 -2
  210. data/lib/active_record/validations/uniqueness.rb +8 -39
  211. data/lib/active_record/validations.rb +4 -4
  212. data/lib/active_record/version.rb +1 -1
  213. data/lib/active_record.rb +20 -20
  214. data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -34
  215. data/lib/rails/generators/active_record/migration.rb +1 -1
  216. data/lib/rails/generators/active_record/model/model_generator.rb +9 -9
  217. data/lib/rails/generators/active_record.rb +4 -4
  218. metadata +24 -13
  219. data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
@@ -16,25 +16,16 @@ module ActiveRecord
16
16
  when :restrict_with_error
17
17
  unless empty?
18
18
  record = owner.class.human_attribute_name(reflection.name).downcase
19
- message = owner.errors.generate_message(:base, :'restrict_dependent_destroy.many', record: record, raise: true) rescue nil
20
- if message
21
- ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
22
- The error key `:'restrict_dependent_destroy.many'` has been deprecated and will be removed in Rails 5.1.
23
- Please use `:'restrict_dependent_destroy.has_many'` instead.
24
- MESSAGE
25
- end
26
- owner.errors.add(:base, message || :'restrict_dependent_destroy.has_many', record: record)
19
+ owner.errors.add(:base, :'restrict_dependent_destroy.has_many', record: record)
27
20
  throw(:abort)
28
21
  end
29
22
 
23
+ when :destroy
24
+ # No point in executing the counter update since we're going to destroy the parent anyway
25
+ load_target.each { |t| t.destroyed_by_association = reflection }
26
+ destroy_all
30
27
  else
31
- if options[:dependent] == :destroy
32
- # No point in executing the counter update since we're going to destroy the parent anyway
33
- load_target.each { |t| t.destroyed_by_association = reflection }
34
- destroy_all
35
- else
36
- delete_all
37
- end
28
+ delete_all
38
29
  end
39
30
  end
40
31
 
@@ -68,15 +59,15 @@ module ActiveRecord
68
59
  # the loaded flag is set to true as well.
69
60
  def count_records
70
61
  count = if reflection.has_cached_counter?
71
- owner._read_attribute reflection.counter_cache_column
62
+ owner._read_attribute(reflection.counter_cache_column).to_i
72
63
  else
73
- scope.count
64
+ scope.count(:all)
74
65
  end
75
66
 
76
67
  # If there's nothing in the database and @target has no new records
77
68
  # we are certain the current target is an empty array. This is a
78
69
  # documented side-effect of the method that may avoid an extra SELECT.
79
- @target ||= [] and loaded! if count == 0
70
+ (@target ||= []) && loaded! if count == 0
80
71
 
81
72
  [association_scope.limit_value, count].compact.min
82
73
  end
@@ -104,7 +95,7 @@ module ActiveRecord
104
95
  end
105
96
 
106
97
  def delete_or_nullify_all_records(method)
107
- count = delete_count(method, self.scope)
98
+ count = delete_count(method, scope)
108
99
  update_counter(-count)
109
100
  end
110
101
 
@@ -38,7 +38,7 @@ module ActiveRecord
38
38
  def insert_record(record, validate = true, raise = false)
39
39
  ensure_not_nested
40
40
 
41
- if record.new_record? || record.changed?
41
+ if record.new_record? || record.has_changes_to_save?
42
42
  return unless super
43
43
  end
44
44
 
@@ -84,7 +84,10 @@ module ActiveRecord
84
84
  end
85
85
 
86
86
  def save_through_record(record)
87
- build_through_record(record).save!
87
+ association = build_through_record(record)
88
+ if association.changed?
89
+ association.save!
90
+ end
88
91
  ensure
89
92
  @through_records.delete(record.object_id)
90
93
  end
@@ -106,6 +109,11 @@ module ActiveRecord
106
109
  record
107
110
  end
108
111
 
112
+ def remove_records(existing_records, records, method)
113
+ super
114
+ delete_through_records(records)
115
+ end
116
+
109
117
  def target_reflection_has_associated_record?
110
118
  !(through_reflection.belongs_to? && owner[through_reflection.foreign_key].blank?)
111
119
  end
@@ -144,7 +152,7 @@ module ActiveRecord
144
152
  stmt.from scope.klass.arel_table
145
153
  stmt.wheres = arel.constraints
146
154
 
147
- count = scope.klass.connection.delete(stmt, 'SQL', scope.bound_attributes)
155
+ count = scope.klass.connection.delete(stmt, "SQL", scope.bound_attributes)
148
156
  end
149
157
  when :nullify
150
158
  count = scope.update_all(source_reflection.foreign_key => nil)
@@ -194,7 +202,7 @@ module ActiveRecord
194
202
 
195
203
  def find_target
196
204
  return [] unless target_reflection_has_associated_record?
197
- get_records
205
+ super
198
206
  end
199
207
 
200
208
  # NOTE - not sure that we can actually cope with inverses here
@@ -12,14 +12,7 @@ module ActiveRecord
12
12
  when :restrict_with_error
13
13
  if load_target
14
14
  record = owner.class.human_attribute_name(reflection.name).downcase
15
- message = owner.errors.generate_message(:base, :'restrict_dependent_destroy.one', record: record, raise: true) rescue nil
16
- if message
17
- ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
18
- The error key `:'restrict_dependent_destroy.one'` has been deprecated and will be removed in Rails 5.1.
19
- Please use `:'restrict_dependent_destroy.has_one'` instead.
20
- MESSAGE
21
- end
22
- owner.errors.add(:base, message || :'restrict_dependent_destroy.has_one', record: record)
15
+ owner.errors.add(:base, :'restrict_dependent_destroy.has_one', record: record)
23
16
  throw(:abort)
24
17
  end
25
18
 
@@ -32,10 +25,10 @@ module ActiveRecord
32
25
  raise_on_type_mismatch!(record) if record
33
26
  load_target
34
27
 
35
- return self.target if !(target || record)
28
+ return target unless target || record
36
29
 
37
30
  assigning_another_record = target != record
38
- if assigning_another_record || record.changed?
31
+ if assigning_another_record || record.has_changes_to_save?
39
32
  save &&= owner.persisted?
40
33
 
41
34
  transaction_if(save) do
@@ -60,12 +53,13 @@ module ActiveRecord
60
53
  def delete(method = options[:dependent])
61
54
  if load_target
62
55
  case method
63
- when :delete
64
- target.delete
65
- when :destroy
66
- target.destroy
67
- when :nullify
68
- target.update_columns(reflection.foreign_key => nil) if target.persisted?
56
+ when :delete
57
+ target.delete
58
+ when :destroy
59
+ target.destroyed_by_association = reflection
60
+ target.destroy
61
+ when :nullify
62
+ target.update_columns(reflection.foreign_key => nil) if target.persisted?
69
63
  end
70
64
  end
71
65
  end
@@ -82,18 +76,20 @@ module ActiveRecord
82
76
 
83
77
  def remove_target!(method)
84
78
  case method
85
- when :delete
86
- target.delete
87
- when :destroy
88
- target.destroy
89
- else
90
- nullify_owner_attributes(target)
91
-
92
- if target.persisted? && owner.persisted? && !target.save
93
- set_owner_attributes(target)
94
- raise RecordNotSaved, "Failed to remove the existing associated #{reflection.name}. " +
95
- "The record failed to save after its foreign key was set to nil."
96
- end
79
+ when :delete
80
+ target.delete
81
+ when :destroy
82
+ target.destroyed_by_association = reflection
83
+ target.destroy
84
+ else
85
+ nullify_owner_attributes(target)
86
+ remove_inverse_instance(target)
87
+
88
+ if target.persisted? && owner.persisted? && !target.save
89
+ set_owner_attributes(target)
90
+ raise RecordNotSaved, "Failed to remove the existing associated #{reflection.name}. " \
91
+ "The record failed to save after its foreign key was set to nil."
92
+ end
97
93
  end
98
94
  end
99
95
 
@@ -15,13 +15,17 @@ module ActiveRecord
15
15
  ensure_not_nested
16
16
 
17
17
  through_proxy = owner.association(through_reflection.name)
18
- through_record = through_proxy.send(:load_target)
18
+ through_record = through_proxy.load_target
19
19
 
20
20
  if through_record && !record
21
21
  through_record.destroy
22
22
  elsif record
23
23
  attributes = construct_join_attributes(record)
24
24
 
25
+ if through_record && through_record.destroyed?
26
+ through_record = through_proxy.tap(&:reload).target
27
+ end
28
+
25
29
  if through_record
26
30
  through_record.update(attributes)
27
31
  elsif owner.new_record?
@@ -1,4 +1,4 @@
1
- require 'active_record/associations/join_dependency/join_part'
1
+ require "active_record/associations/join_dependency/join_part"
2
2
 
3
3
  module ActiveRecord
4
4
  module Associations
@@ -23,48 +23,24 @@ module ActiveRecord
23
23
 
24
24
  JoinInformation = Struct.new :joins, :binds
25
25
 
26
- def join_constraints(foreign_table, foreign_klass, node, join_type, tables, scope_chain, chain)
26
+ def join_constraints(foreign_table, foreign_klass, join_type, tables, chain)
27
27
  joins = []
28
28
  binds = []
29
29
  tables = tables.reverse
30
30
 
31
- scope_chain_index = 0
32
- scope_chain = scope_chain.reverse
33
-
34
31
  # The chain starts with the target table, but we want to end with it here (makes
35
32
  # more sense in this context), so we reverse
36
33
  chain.reverse_each do |reflection|
37
34
  table = tables.shift
38
35
  klass = reflection.klass
39
36
 
40
- join_keys = reflection.join_keys(klass)
37
+ join_keys = reflection.join_keys
41
38
  key = join_keys.key
42
39
  foreign_key = join_keys.foreign_key
43
40
 
44
41
  constraint = build_constraint(klass, table, key, foreign_table, foreign_key)
45
42
 
46
- predicate_builder = PredicateBuilder.new(TableMetadata.new(klass, table))
47
- scope_chain_items = scope_chain[scope_chain_index].map do |item|
48
- if item.is_a?(Relation)
49
- item
50
- else
51
- ActiveRecord::Relation.create(klass, table, predicate_builder)
52
- .instance_exec(node, &item)
53
- end
54
- end
55
- scope_chain_index += 1
56
-
57
- relation = ActiveRecord::Relation.create(klass, table, predicate_builder)
58
- current_scope = klass.current_scope
59
-
60
- klass_scope =
61
- if current_scope && current_scope.empty_scope?
62
- relation
63
- else
64
- klass.send(:build_default_scope, relation)
65
- end
66
-
67
- rel = scope_chain_items.inject(klass_scope || scope_chain_items.shift, &:merge!)
43
+ rel = reflection.join_scope(table)
68
44
 
69
45
  if rel && !rel.arel.constraints.empty?
70
46
  binds += rel.bound_attributes
@@ -1,4 +1,4 @@
1
- require 'active_record/associations/join_dependency/join_part'
1
+ require "active_record/associations/join_dependency/join_part"
2
2
 
3
3
  module ActiveRecord
4
4
  module Associations
@@ -15,7 +15,7 @@ module ActiveRecord
15
15
  # association.
16
16
  attr_reader :base_klass, :children
17
17
 
18
- delegate :table_name, :column_names, :primary_key, :to => :base_klass
18
+ delegate :table_name, :column_names, :primary_key, to: :base_klass
19
19
 
20
20
  def initialize(base_klass, children)
21
21
  @base_klass = base_klass
@@ -1,18 +1,18 @@
1
1
  module ActiveRecord
2
2
  module Associations
3
3
  class JoinDependency # :nodoc:
4
- autoload :JoinBase, 'active_record/associations/join_dependency/join_base'
5
- autoload :JoinAssociation, 'active_record/associations/join_dependency/join_association'
4
+ autoload :JoinBase, "active_record/associations/join_dependency/join_base"
5
+ autoload :JoinAssociation, "active_record/associations/join_dependency/join_association"
6
6
 
7
7
  class Aliases # :nodoc:
8
8
  def initialize(tables)
9
9
  @tables = tables
10
- @alias_cache = tables.each_with_object({}) { |table,h|
11
- h[table.node] = table.columns.each_with_object({}) { |column,i|
10
+ @alias_cache = tables.each_with_object({}) { |table, h|
11
+ h[table.node] = table.columns.each_with_object({}) { |column, i|
12
12
  i[column.name] = column.alias
13
13
  }
14
14
  }
15
- @name_and_alias_cache = tables.each_with_object({}) { |table,h|
15
+ @name_and_alias_cache = tables.each_with_object({}) { |table, h|
16
16
  h[table.node] = table.columns.map { |column|
17
17
  [column.name, column.alias]
18
18
  }
@@ -32,7 +32,7 @@ module ActiveRecord
32
32
  @alias_cache[node][column]
33
33
  end
34
34
 
35
- class Table < Struct.new(:node, :columns) # :nodoc:
35
+ Table = Struct.new(:node, :columns) do # :nodoc:
36
36
  def table
37
37
  Arel::Nodes::TableAlias.new node.table, node.aliased_table_name
38
38
  end
@@ -62,7 +62,7 @@ module ActiveRecord
62
62
  walk_tree assoc, hash
63
63
  end
64
64
  when Hash
65
- associations.each do |k,v|
65
+ associations.each do |k, v|
66
66
  cache = hash[k] ||= {}
67
67
  walk_tree v, cache
68
68
  end
@@ -92,8 +92,9 @@ module ActiveRecord
92
92
  # associations # => [:appointments]
93
93
  # joins # => []
94
94
  #
95
- def initialize(base, associations, joins)
96
- @alias_tracker = AliasTracker.create_with_joins(base.connection, base.table_name, joins, base.type_caster)
95
+ def initialize(base, associations, joins, eager_loading: true)
96
+ @alias_tracker = AliasTracker.create_with_joins(base.connection, base.table_name, joins)
97
+ @eager_loading = eager_loading
97
98
  tree = self.class.make_tree associations
98
99
  @join_root = JoinBase.new base, build(tree, base)
99
100
  @join_root.children.each { |child| construct_tables! @join_root, child }
@@ -125,15 +126,15 @@ module ActiveRecord
125
126
  end
126
127
 
127
128
  def aliases
128
- Aliases.new join_root.each_with_index.map { |join_part,i|
129
- columns = join_part.column_names.each_with_index.map { |column_name,j|
129
+ @aliases ||= Aliases.new join_root.each_with_index.map { |join_part, i|
130
+ columns = join_part.column_names.each_with_index.map { |column_name, j|
130
131
  Aliases::Column.new column_name, "t#{i}_r#{j}"
131
132
  }
132
133
  Aliases::Table.new(join_part, columns)
133
134
  }
134
135
  end
135
136
 
136
- def instantiate(result_set, aliases)
137
+ def instantiate(result_set, &block)
137
138
  primary_key = aliases.column_alias(join_root, join_root.primary_key)
138
139
 
139
140
  seen = Hash.new { |i, object_id|
@@ -142,7 +143,7 @@ module ActiveRecord
142
143
  }
143
144
  }
144
145
 
145
- model_cache = Hash.new { |h,klass| h[klass] = {} }
146
+ model_cache = Hash.new { |h, klass| h[klass] = {} }
146
147
  parents = model_cache[join_root]
147
148
  column_aliases = aliases.column_aliases join_root
148
149
 
@@ -153,10 +154,10 @@ module ActiveRecord
153
154
  class_name: join_root.base_klass.name
154
155
  }
155
156
 
156
- message_bus.instrument('instantiation.active_record', payload) do
157
+ message_bus.instrument("instantiation.active_record", payload) do
157
158
  result_set.each { |row_hash|
158
159
  parent_key = primary_key ? row_hash[primary_key] : row_hash
159
- parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases)
160
+ parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, &block)
160
161
  construct(parent, join_root, row_hash, result_set, seen, model_cache, aliases)
161
162
  }
162
163
  end
@@ -166,139 +167,141 @@ module ActiveRecord
166
167
 
167
168
  private
168
169
 
169
- def make_constraints(parent, child, tables, join_type)
170
- chain = child.reflection.chain
171
- foreign_table = parent.table
172
- foreign_klass = parent.base_klass
173
- child.join_constraints(foreign_table, foreign_klass, child, join_type, tables, child.reflection.scope_chain, chain)
174
- end
170
+ def make_constraints(parent, child, tables, join_type)
171
+ chain = child.reflection.chain
172
+ foreign_table = parent.table
173
+ foreign_klass = parent.base_klass
174
+ child.join_constraints(foreign_table, foreign_klass, join_type, tables, chain)
175
+ end
175
176
 
176
- def make_outer_joins(parent, child)
177
- tables = table_aliases_for(parent, child)
178
- join_type = Arel::Nodes::OuterJoin
179
- info = make_constraints parent, child, tables, join_type
177
+ def make_outer_joins(parent, child)
178
+ tables = table_aliases_for(parent, child)
179
+ join_type = Arel::Nodes::OuterJoin
180
+ info = make_constraints parent, child, tables, join_type
180
181
 
181
- [info] + child.children.flat_map { |c| make_outer_joins(child, c) }
182
- end
182
+ [info] + child.children.flat_map { |c| make_outer_joins(child, c) }
183
+ end
183
184
 
184
- def make_left_outer_joins(parent, child)
185
- tables = child.tables
186
- join_type = Arel::Nodes::OuterJoin
187
- info = make_constraints parent, child, tables, join_type
185
+ def make_left_outer_joins(parent, child)
186
+ tables = child.tables
187
+ join_type = Arel::Nodes::OuterJoin
188
+ info = make_constraints parent, child, tables, join_type
188
189
 
189
- [info] + child.children.flat_map { |c| make_left_outer_joins(child, c) }
190
- end
190
+ [info] + child.children.flat_map { |c| make_left_outer_joins(child, c) }
191
+ end
191
192
 
192
- def make_inner_joins(parent, child)
193
- tables = child.tables
194
- join_type = Arel::Nodes::InnerJoin
195
- info = make_constraints parent, child, tables, join_type
193
+ def make_inner_joins(parent, child)
194
+ tables = child.tables
195
+ join_type = Arel::Nodes::InnerJoin
196
+ info = make_constraints parent, child, tables, join_type
196
197
 
197
- [info] + child.children.flat_map { |c| make_inner_joins(child, c) }
198
- end
198
+ [info] + child.children.flat_map { |c| make_inner_joins(child, c) }
199
+ end
199
200
 
200
- def table_aliases_for(parent, node)
201
- node.reflection.chain.map { |reflection|
202
- alias_tracker.aliased_table_for(
203
- reflection.table_name,
204
- table_alias_for(reflection, parent, reflection != node.reflection)
205
- )
206
- }
207
- end
201
+ def table_aliases_for(parent, node)
202
+ node.reflection.chain.map { |reflection|
203
+ alias_tracker.aliased_table_for(
204
+ reflection.table_name,
205
+ table_alias_for(reflection, parent, reflection != node.reflection),
206
+ reflection.klass.type_caster
207
+ )
208
+ }
209
+ end
208
210
 
209
- def construct_tables!(parent, node)
210
- node.tables = table_aliases_for(parent, node)
211
- node.children.each { |child| construct_tables! node, child }
212
- end
211
+ def construct_tables!(parent, node)
212
+ node.tables = table_aliases_for(parent, node)
213
+ node.children.each { |child| construct_tables! node, child }
214
+ end
213
215
 
214
- def table_alias_for(reflection, parent, join)
215
- name = "#{reflection.plural_name}_#{parent.table_name}"
216
- name << "_join" if join
217
- name
218
- end
216
+ def table_alias_for(reflection, parent, join)
217
+ name = "#{reflection.plural_name}_#{parent.table_name}"
218
+ name << "_join" if join
219
+ name
220
+ end
219
221
 
220
- def walk(left, right)
221
- intersection, missing = right.children.map { |node1|
222
- [left.children.find { |node2| node1.match? node2 }, node1]
223
- }.partition(&:first)
222
+ def walk(left, right)
223
+ intersection, missing = right.children.map { |node1|
224
+ [left.children.find { |node2| node1.match? node2 }, node1]
225
+ }.partition(&:first)
224
226
 
225
- ojs = missing.flat_map { |_,n| make_outer_joins left, n }
226
- intersection.flat_map { |l,r| walk l, r }.concat ojs
227
- end
227
+ ojs = missing.flat_map { |_, n| make_outer_joins left, n }
228
+ intersection.flat_map { |l, r| walk l, r }.concat ojs
229
+ end
228
230
 
229
- def find_reflection(klass, name)
230
- klass._reflect_on_association(name) or
231
- raise ConfigurationError, "Can't join '#{ klass.name }' to association named '#{ name }'; perhaps you misspelled it?"
232
- end
231
+ def find_reflection(klass, name)
232
+ klass._reflect_on_association(name) ||
233
+ raise(ConfigurationError, "Can't join '#{klass.name}' to association named '#{name}'; perhaps you misspelled it?")
234
+ end
233
235
 
234
- def build(associations, base_klass)
235
- associations.map do |name, right|
236
- reflection = find_reflection base_klass, name
237
- reflection.check_validity!
238
- reflection.check_eager_loadable!
236
+ def build(associations, base_klass)
237
+ associations.map do |name, right|
238
+ reflection = find_reflection base_klass, name
239
+ reflection.check_validity!
240
+ reflection.check_eager_loadable!
239
241
 
240
- if reflection.polymorphic?
241
- raise EagerLoadPolymorphicError.new(reflection)
242
- end
242
+ if reflection.polymorphic?
243
+ next unless @eager_loading
244
+ raise EagerLoadPolymorphicError.new(reflection)
245
+ end
243
246
 
244
- JoinAssociation.new reflection, build(right, reflection.klass)
247
+ JoinAssociation.new reflection, build(right, reflection.klass)
248
+ end.compact
245
249
  end
246
- end
247
250
 
248
- def construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
249
- return if ar_parent.nil?
251
+ def construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
252
+ return if ar_parent.nil?
253
+
254
+ parent.children.each do |node|
255
+ if node.reflection.collection?
256
+ other = ar_parent.association(node.reflection.name)
257
+ other.loaded!
258
+ elsif ar_parent.association_cached?(node.reflection.name)
259
+ model = ar_parent.association(node.reflection.name).target
260
+ construct(model, node, row, rs, seen, model_cache, aliases)
261
+ next
262
+ end
250
263
 
251
- parent.children.each do |node|
252
- if node.reflection.collection?
253
- other = ar_parent.association(node.reflection.name)
254
- other.loaded!
255
- elsif ar_parent.association_cached?(node.reflection.name)
256
- model = ar_parent.association(node.reflection.name).target
257
- construct(model, node, row, rs, seen, model_cache, aliases)
258
- next
259
- end
264
+ key = aliases.column_alias(node, node.primary_key)
265
+ id = row[key]
266
+ if id.nil?
267
+ nil_association = ar_parent.association(node.reflection.name)
268
+ nil_association.loaded!
269
+ next
270
+ end
260
271
 
261
- key = aliases.column_alias(node, node.primary_key)
262
- id = row[key]
263
- if id.nil?
264
- nil_association = ar_parent.association(node.reflection.name)
265
- nil_association.loaded!
266
- next
267
- end
272
+ model = seen[ar_parent.object_id][node.base_klass][id]
268
273
 
269
- model = seen[ar_parent.object_id][node.base_klass][id]
274
+ if model
275
+ construct(model, node, row, rs, seen, model_cache, aliases)
276
+ else
277
+ model = construct_model(ar_parent, node, row, model_cache, id, aliases)
270
278
 
271
- if model
272
- construct(model, node, row, rs, seen, model_cache, aliases)
273
- else
274
- model = construct_model(ar_parent, node, row, model_cache, id, aliases)
279
+ if node.reflection.scope_for(node.base_klass).readonly_value
280
+ model.readonly!
281
+ end
275
282
 
276
- if node.reflection.scope_for(node.base_klass).readonly_value
277
- model.readonly!
283
+ seen[ar_parent.object_id][node.base_klass][id] = model
284
+ construct(model, node, row, rs, seen, model_cache, aliases)
278
285
  end
279
-
280
- seen[ar_parent.object_id][node.base_klass][id] = model
281
- construct(model, node, row, rs, seen, model_cache, aliases)
282
286
  end
283
287
  end
284
- end
285
288
 
286
- def construct_model(record, node, row, model_cache, id, aliases)
287
- other = record.association(node.reflection.name)
289
+ def construct_model(record, node, row, model_cache, id, aliases)
290
+ other = record.association(node.reflection.name)
288
291
 
289
- model = model_cache[node][id] ||=
290
- node.instantiate(row, aliases.column_aliases(node)) do |m|
291
- other.set_inverse_instance(m)
292
+ model = model_cache[node][id] ||=
293
+ node.instantiate(row, aliases.column_aliases(node)) do |m|
294
+ other.set_inverse_instance(m)
295
+ end
296
+
297
+ if node.reflection.collection?
298
+ other.target.push(model)
299
+ else
300
+ other.target = model
292
301
  end
293
302
 
294
- if node.reflection.collection?
295
- other.target.push(model)
296
- else
297
- other.target = model
303
+ model
298
304
  end
299
-
300
- model
301
- end
302
305
  end
303
306
  end
304
307
  end