activerecord 4.2.11.3 → 5.0.0.beta1

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 (229) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1029 -1349
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -7
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record.rb +7 -3
  7. data/lib/active_record/aggregations.rb +35 -25
  8. data/lib/active_record/association_relation.rb +2 -2
  9. data/lib/active_record/associations.rb +305 -204
  10. data/lib/active_record/associations/alias_tracker.rb +19 -16
  11. data/lib/active_record/associations/association.rb +10 -8
  12. data/lib/active_record/associations/association_scope.rb +73 -102
  13. data/lib/active_record/associations/belongs_to_association.rb +20 -32
  14. data/lib/active_record/associations/builder/association.rb +28 -34
  15. data/lib/active_record/associations/builder/belongs_to.rb +41 -18
  16. data/lib/active_record/associations/builder/collection_association.rb +8 -24
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +11 -11
  18. data/lib/active_record/associations/builder/has_many.rb +4 -4
  19. data/lib/active_record/associations/builder/has_one.rb +10 -5
  20. data/lib/active_record/associations/builder/singular_association.rb +2 -9
  21. data/lib/active_record/associations/collection_association.rb +40 -43
  22. data/lib/active_record/associations/collection_proxy.rb +55 -29
  23. data/lib/active_record/associations/foreign_association.rb +1 -1
  24. data/lib/active_record/associations/has_many_association.rb +20 -71
  25. data/lib/active_record/associations/has_many_through_association.rb +8 -52
  26. data/lib/active_record/associations/has_one_association.rb +12 -5
  27. data/lib/active_record/associations/join_dependency.rb +28 -18
  28. data/lib/active_record/associations/join_dependency/join_association.rb +13 -12
  29. data/lib/active_record/associations/preloader.rb +13 -4
  30. data/lib/active_record/associations/preloader/association.rb +45 -51
  31. data/lib/active_record/associations/preloader/collection_association.rb +0 -6
  32. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  33. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  34. data/lib/active_record/associations/preloader/through_association.rb +5 -4
  35. data/lib/active_record/associations/singular_association.rb +6 -0
  36. data/lib/active_record/associations/through_association.rb +11 -3
  37. data/lib/active_record/attribute.rb +61 -17
  38. data/lib/active_record/attribute/user_provided_default.rb +23 -0
  39. data/lib/active_record/attribute_assignment.rb +27 -140
  40. data/lib/active_record/attribute_decorators.rb +6 -5
  41. data/lib/active_record/attribute_methods.rb +79 -26
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
  43. data/lib/active_record/attribute_methods/dirty.rb +46 -86
  44. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  45. data/lib/active_record/attribute_methods/query.rb +2 -2
  46. data/lib/active_record/attribute_methods/read.rb +26 -42
  47. data/lib/active_record/attribute_methods/serialization.rb +13 -16
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +42 -9
  49. data/lib/active_record/attribute_methods/write.rb +13 -24
  50. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  51. data/lib/active_record/attribute_set.rb +30 -3
  52. data/lib/active_record/attribute_set/builder.rb +6 -4
  53. data/lib/active_record/attributes.rb +194 -81
  54. data/lib/active_record/autosave_association.rb +33 -15
  55. data/lib/active_record/base.rb +30 -18
  56. data/lib/active_record/callbacks.rb +36 -40
  57. data/lib/active_record/coders/yaml_column.rb +20 -8
  58. data/lib/active_record/collection_cache_key.rb +31 -0
  59. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +431 -122
  60. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  61. data/lib/active_record/connection_adapters/abstract/database_statements.rb +40 -22
  62. data/lib/active_record/connection_adapters/abstract/quoting.rb +62 -8
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +46 -38
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +229 -185
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +52 -13
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +275 -115
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +32 -33
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +83 -32
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +384 -221
  70. data/lib/active_record/connection_adapters/column.rb +27 -41
  71. data/lib/active_record/connection_adapters/connection_specification.rb +2 -21
  72. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  73. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +57 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +69 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +59 -0
  76. data/lib/active_record/connection_adapters/mysql2_adapter.rb +22 -101
  77. data/lib/active_record/connection_adapters/postgresql/column.rb +6 -10
  78. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +3 -3
  79. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  80. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +23 -57
  81. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
  84. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
  85. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
  86. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  87. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  90. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +23 -16
  92. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
  93. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  96. data/lib/active_record/connection_adapters/postgresql/quoting.rb +18 -11
  97. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
  98. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
  99. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +54 -0
  100. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +174 -128
  101. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  102. data/lib/active_record/connection_adapters/postgresql_adapter.rb +184 -112
  103. data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
  104. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  105. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +15 -0
  106. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +134 -110
  107. data/lib/active_record/connection_adapters/statement_pool.rb +28 -11
  108. data/lib/active_record/connection_handling.rb +5 -5
  109. data/lib/active_record/core.rb +72 -104
  110. data/lib/active_record/counter_cache.rb +9 -20
  111. data/lib/active_record/dynamic_matchers.rb +1 -20
  112. data/lib/active_record/enum.rb +110 -76
  113. data/lib/active_record/errors.rb +72 -47
  114. data/lib/active_record/explain_registry.rb +1 -1
  115. data/lib/active_record/explain_subscriber.rb +1 -1
  116. data/lib/active_record/fixture_set/file.rb +19 -4
  117. data/lib/active_record/fixtures.rb +76 -40
  118. data/lib/active_record/gem_version.rb +4 -4
  119. data/lib/active_record/inheritance.rb +27 -40
  120. data/lib/active_record/integration.rb +4 -4
  121. data/lib/active_record/legacy_yaml_adapter.rb +18 -2
  122. data/lib/active_record/locale/en.yml +3 -2
  123. data/lib/active_record/locking/optimistic.rb +10 -14
  124. data/lib/active_record/locking/pessimistic.rb +1 -1
  125. data/lib/active_record/log_subscriber.rb +40 -22
  126. data/lib/active_record/migration.rb +304 -133
  127. data/lib/active_record/migration/command_recorder.rb +59 -18
  128. data/lib/active_record/migration/compatibility.rb +90 -0
  129. data/lib/active_record/model_schema.rb +92 -40
  130. data/lib/active_record/nested_attributes.rb +45 -34
  131. data/lib/active_record/null_relation.rb +15 -7
  132. data/lib/active_record/persistence.rb +112 -72
  133. data/lib/active_record/querying.rb +6 -5
  134. data/lib/active_record/railtie.rb +20 -13
  135. data/lib/active_record/railties/controller_runtime.rb +1 -1
  136. data/lib/active_record/railties/databases.rake +47 -38
  137. data/lib/active_record/readonly_attributes.rb +1 -1
  138. data/lib/active_record/reflection.rb +182 -57
  139. data/lib/active_record/relation.rb +152 -100
  140. data/lib/active_record/relation/batches.rb +133 -33
  141. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  142. data/lib/active_record/relation/calculations.rb +80 -101
  143. data/lib/active_record/relation/delegation.rb +6 -19
  144. data/lib/active_record/relation/finder_methods.rb +58 -46
  145. data/lib/active_record/relation/from_clause.rb +32 -0
  146. data/lib/active_record/relation/merger.rb +13 -42
  147. data/lib/active_record/relation/predicate_builder.rb +99 -105
  148. data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
  149. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +78 -0
  150. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  151. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  152. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  153. data/lib/active_record/relation/predicate_builder/range_handler.rb +17 -0
  154. data/lib/active_record/relation/query_attribute.rb +19 -0
  155. data/lib/active_record/relation/query_methods.rb +274 -238
  156. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  157. data/lib/active_record/relation/spawn_methods.rb +3 -6
  158. data/lib/active_record/relation/where_clause.rb +173 -0
  159. data/lib/active_record/relation/where_clause_factory.rb +37 -0
  160. data/lib/active_record/result.rb +4 -3
  161. data/lib/active_record/runtime_registry.rb +1 -1
  162. data/lib/active_record/sanitization.rb +94 -65
  163. data/lib/active_record/schema.rb +23 -22
  164. data/lib/active_record/schema_dumper.rb +33 -22
  165. data/lib/active_record/schema_migration.rb +10 -4
  166. data/lib/active_record/scoping.rb +17 -6
  167. data/lib/active_record/scoping/default.rb +19 -6
  168. data/lib/active_record/scoping/named.rb +39 -28
  169. data/lib/active_record/secure_token.rb +38 -0
  170. data/lib/active_record/serialization.rb +2 -4
  171. data/lib/active_record/statement_cache.rb +15 -13
  172. data/lib/active_record/store.rb +8 -3
  173. data/lib/active_record/suppressor.rb +54 -0
  174. data/lib/active_record/table_metadata.rb +64 -0
  175. data/lib/active_record/tasks/database_tasks.rb +30 -40
  176. data/lib/active_record/tasks/mysql_database_tasks.rb +7 -15
  177. data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -2
  178. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  179. data/lib/active_record/timestamp.rb +16 -9
  180. data/lib/active_record/touch_later.rb +58 -0
  181. data/lib/active_record/transactions.rb +138 -56
  182. data/lib/active_record/type.rb +66 -17
  183. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  184. data/lib/active_record/type/date.rb +2 -45
  185. data/lib/active_record/type/date_time.rb +2 -49
  186. data/lib/active_record/type/internal/abstract_json.rb +33 -0
  187. data/lib/active_record/type/internal/timezone.rb +15 -0
  188. data/lib/active_record/type/serialized.rb +9 -14
  189. data/lib/active_record/type/time.rb +3 -21
  190. data/lib/active_record/type/type_map.rb +4 -4
  191. data/lib/active_record/type_caster.rb +7 -0
  192. data/lib/active_record/type_caster/connection.rb +29 -0
  193. data/lib/active_record/type_caster/map.rb +19 -0
  194. data/lib/active_record/validations.rb +33 -32
  195. data/lib/active_record/validations/absence.rb +24 -0
  196. data/lib/active_record/validations/associated.rb +10 -3
  197. data/lib/active_record/validations/length.rb +36 -0
  198. data/lib/active_record/validations/presence.rb +12 -12
  199. data/lib/active_record/validations/uniqueness.rb +24 -21
  200. data/lib/rails/generators/active_record/migration.rb +7 -0
  201. data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
  202. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
  203. data/lib/rails/generators/active_record/migration/templates/migration.rb +4 -1
  204. data/lib/rails/generators/active_record/model/model_generator.rb +21 -15
  205. data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
  206. metadata +50 -35
  207. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  208. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  209. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
  210. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  211. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  212. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  213. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  214. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  215. data/lib/active_record/type/big_integer.rb +0 -13
  216. data/lib/active_record/type/binary.rb +0 -50
  217. data/lib/active_record/type/boolean.rb +0 -31
  218. data/lib/active_record/type/decimal.rb +0 -64
  219. data/lib/active_record/type/decimal_without_scale.rb +0 -11
  220. data/lib/active_record/type/decorator.rb +0 -14
  221. data/lib/active_record/type/float.rb +0 -19
  222. data/lib/active_record/type/integer.rb +0 -59
  223. data/lib/active_record/type/mutable.rb +0 -16
  224. data/lib/active_record/type/numeric.rb +0 -36
  225. data/lib/active_record/type/string.rb +0 -40
  226. data/lib/active_record/type/text.rb +0 -11
  227. data/lib/active_record/type/time_value.rb +0 -38
  228. data/lib/active_record/type/unsigned_integer.rb +0 -15
  229. data/lib/active_record/type/value.rb +0 -110
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord
2
- # = Active Record Belongs To Has One Association
2
+ # = Active Record Has One Association
3
3
  module Associations
4
4
  class HasOneAssociation < SingularAssociation #:nodoc:
5
5
  include ForeignAssociation
@@ -11,9 +11,16 @@ module ActiveRecord
11
11
 
12
12
  when :restrict_with_error
13
13
  if load_target
14
- record = klass.human_attribute_name(reflection.name).downcase
15
- owner.errors.add(:base, :"restrict_dependent_destroy.one", record: record)
16
- false
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)
23
+ throw(:abort)
17
24
  end
18
25
 
19
26
  else
@@ -58,7 +65,7 @@ module ActiveRecord
58
65
  when :destroy
59
66
  target.destroy
60
67
  when :nullify
61
- target.update_columns(reflection.foreign_key => nil)
68
+ target.update_columns(reflection.foreign_key => nil) if target.persisted?
62
69
  end
63
70
  end
64
71
  end
@@ -20,7 +20,7 @@ module ActiveRecord
20
20
  end
21
21
 
22
22
  def columns
23
- @tables.flat_map { |t| t.column_aliases }
23
+ @tables.flat_map(&:column_aliases)
24
24
  end
25
25
 
26
26
  # An array of [column_name, alias] pairs for the table
@@ -32,7 +32,7 @@ module ActiveRecord
32
32
  @alias_cache[node][column]
33
33
  end
34
34
 
35
- class Table < Struct.new(:node, :columns)
35
+ class Table < Struct.new(:node, :columns) # :nodoc:
36
36
  def table
37
37
  Arel::Nodes::TableAlias.new node.table, node.aliased_table_name
38
38
  end
@@ -93,8 +93,7 @@ module ActiveRecord
93
93
  # joins # => []
94
94
  #
95
95
  def initialize(base, associations, joins)
96
- @alias_tracker = AliasTracker.create(base.connection, joins)
97
- @alias_tracker.aliased_table_for(base.table_name, base.table_name) # Updates the count for base.table_name to 1
96
+ @alias_tracker = AliasTracker.create_with_joins(base.connection, base.table_name, joins, base.type_caster)
98
97
  tree = self.class.make_tree associations
99
98
  @join_root = JoinBase.new base, build(tree, base)
100
99
  @join_root.children.each { |child| construct_tables! @join_root, child }
@@ -104,9 +103,14 @@ module ActiveRecord
104
103
  join_root.drop(1).map!(&:reflection)
105
104
  end
106
105
 
107
- def join_constraints(outer_joins)
106
+ def join_constraints(outer_joins, join_type)
108
107
  joins = join_root.children.flat_map { |child|
109
- make_inner_joins join_root, child
108
+
109
+ if join_type == Arel::Nodes::OuterJoin
110
+ make_left_outer_joins join_root, child
111
+ else
112
+ make_inner_joins join_root, child
113
+ end
110
114
  }
111
115
 
112
116
  joins.concat outer_joins.flat_map { |oj|
@@ -132,9 +136,9 @@ module ActiveRecord
132
136
  def instantiate(result_set, aliases)
133
137
  primary_key = aliases.column_alias(join_root, join_root.primary_key)
134
138
 
135
- seen = Hash.new { |h,parent_klass|
136
- h[parent_klass] = Hash.new { |i,parent_id|
137
- i[parent_id] = Hash.new { |j,child_klass| j[child_klass] = {} }
139
+ seen = Hash.new { |i, object_id|
140
+ i[object_id] = Hash.new { |j, child_class|
141
+ j[child_class] = {}
138
142
  }
139
143
  }
140
144
 
@@ -177,6 +181,14 @@ module ActiveRecord
177
181
  [info] + child.children.flat_map { |c| make_outer_joins(child, c) }
178
182
  end
179
183
 
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
188
+
189
+ [info] + child.children.flat_map { |c| make_left_outer_joins(child, c) }
190
+ end
191
+
180
192
  def make_inner_joins(parent, child)
181
193
  tables = child.tables
182
194
  join_type = Arel::Nodes::InnerJoin
@@ -235,18 +247,15 @@ module ActiveRecord
235
247
 
236
248
  def construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
237
249
  return if ar_parent.nil?
238
- primary_id = ar_parent.id
239
250
 
240
251
  parent.children.each do |node|
241
252
  if node.reflection.collection?
242
253
  other = ar_parent.association(node.reflection.name)
243
254
  other.loaded!
244
- else
245
- if ar_parent.association_cache.key?(node.reflection.name)
246
- model = ar_parent.association(node.reflection.name).target
247
- construct(model, node, row, rs, seen, model_cache, aliases)
248
- next
249
- end
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
250
259
  end
251
260
 
252
261
  key = aliases.column_alias(node, node.primary_key)
@@ -257,13 +266,14 @@ module ActiveRecord
257
266
  next
258
267
  end
259
268
 
260
- model = seen[parent.base_klass][primary_id][node.base_klass][id]
269
+ model = seen[ar_parent.object_id][node.base_klass][id]
261
270
 
262
271
  if model
263
272
  construct(model, node, row, rs, seen, model_cache, aliases)
264
273
  else
265
274
  model = construct_model(ar_parent, node, row, model_cache, id, aliases)
266
- seen[parent.base_klass][primary_id][node.base_klass][id] = model
275
+ model.readonly!
276
+ seen[ar_parent.object_id][node.base_klass][id] = model
267
277
  construct(model, node, row, rs, seen, model_cache, aliases)
268
278
  end
269
279
  end
@@ -25,7 +25,7 @@ module ActiveRecord
25
25
 
26
26
  def join_constraints(foreign_table, foreign_klass, node, join_type, tables, scope_chain, chain)
27
27
  joins = []
28
- bind_values = []
28
+ binds = []
29
29
  tables = tables.reverse
30
30
 
31
31
  scope_chain_index = 0
@@ -43,29 +43,30 @@ module ActiveRecord
43
43
 
44
44
  constraint = build_constraint(klass, table, key, foreign_table, foreign_key)
45
45
 
46
+ predicate_builder = PredicateBuilder.new(TableMetadata.new(klass, table))
46
47
  scope_chain_items = scope_chain[scope_chain_index].map do |item|
47
48
  if item.is_a?(Relation)
48
49
  item
49
50
  else
50
- ActiveRecord::Relation.create(klass, table).instance_exec(node, &item)
51
+ ActiveRecord::Relation.create(klass, table, predicate_builder)
52
+ .instance_exec(node, &item)
51
53
  end
52
54
  end
53
55
  scope_chain_index += 1
54
56
 
55
- klass_scope =
56
- if klass.current_scope && klass.current_scope.values.blank?
57
- klass.unscoped
58
- else
59
- klass.send(:build_default_scope, ActiveRecord::Relation.create(klass, table))
60
- end
61
- scope_chain_items.concat [klass_scope].compact
57
+ relation = ActiveRecord::Relation.create(
58
+ klass,
59
+ table,
60
+ predicate_builder,
61
+ )
62
+ scope_chain_items.concat [klass.send(:build_default_scope, relation)].compact
62
63
 
63
64
  rel = scope_chain_items.inject(scope_chain_items.shift) do |left, right|
64
65
  left.merge right
65
66
  end
66
67
 
67
68
  if rel && !rel.arel.constraints.empty?
68
- bind_values.concat rel.bind_values
69
+ binds += rel.bound_attributes
69
70
  constraint = constraint.and rel.arel.constraints
70
71
  end
71
72
 
@@ -74,7 +75,7 @@ module ActiveRecord
74
75
  column = klass.columns_hash[reflection.type.to_s]
75
76
 
76
77
  substitute = klass.connection.substitute_at(column)
77
- bind_values.push [column, value]
78
+ binds << Relation::QueryAttribute.new(column.name, value, klass.type_for_attribute(column.name))
78
79
  constraint = constraint.and table[reflection.type].eq substitute
79
80
  end
80
81
 
@@ -84,7 +85,7 @@ module ActiveRecord
84
85
  foreign_table, foreign_klass = table, klass
85
86
  end
86
87
 
87
- JoinInformation.new joins, bind_values
88
+ JoinInformation.new joins, binds
88
89
  end
89
90
 
90
91
  # Builds equality condition.
@@ -54,6 +54,8 @@ module ActiveRecord
54
54
  autoload :BelongsTo, 'active_record/associations/preloader/belongs_to'
55
55
  end
56
56
 
57
+ NULL_RELATION = Struct.new(:values, :where_clause, :joins_values).new({}, Relation::WhereClause.empty, [])
58
+
57
59
  # Eager loads the named associations for the given Active Record record(s).
58
60
  #
59
61
  # In this description, 'association name' shall refer to the name passed
@@ -88,9 +90,6 @@ module ActiveRecord
88
90
  # [ :books, :author ]
89
91
  # { author: :avatar }
90
92
  # [ :books, { author: :avatar } ]
91
-
92
- NULL_RELATION = Struct.new(:values, :bind_values).new({}, [])
93
-
94
93
  def preload(records, associations, preload_scope = nil)
95
94
  records = Array.wrap(records).compact.uniq
96
95
  associations = Array.wrap(associations)
@@ -107,6 +106,7 @@ module ActiveRecord
107
106
 
108
107
  private
109
108
 
109
+ # Loads all the given data into +records+ for the +association+.
110
110
  def preloaders_on(association, records, scope)
111
111
  case association
112
112
  when Hash
@@ -116,7 +116,7 @@ module ActiveRecord
116
116
  when String
117
117
  preloaders_for_one(association.to_sym, records, scope)
118
118
  else
119
- raise ArgumentError, "#{association.inspect} was not recognised for preload"
119
+ raise ArgumentError, "#{association.inspect} was not recognized for preload"
120
120
  end
121
121
  end
122
122
 
@@ -132,6 +132,11 @@ module ActiveRecord
132
132
  }
133
133
  end
134
134
 
135
+ # Loads all the given data into +records+ for a singular +association+.
136
+ #
137
+ # Functions by instantiating a preloader class such as Preloader::HasManyThrough and
138
+ # call the +run+ method for each passed in class in the +records+ argument.
139
+ #
135
140
  # Not all records have the same class, so group then preload group on the reflection
136
141
  # itself so that if various subclass share the same association then we do not split
137
142
  # them unnecessarily
@@ -181,6 +186,10 @@ module ActiveRecord
181
186
  def self.preloaded_records; []; end
182
187
  end
183
188
 
189
+ # Returns a class containing the logic needed to load preload the data
190
+ # and attach it to a relation. For example +Preloader::Association+ or
191
+ # +Preloader::HasManyThrough+. The class returned implements a `run` method
192
+ # that accepts a preloader.
184
193
  def preloader_for(reflection, owners, rhs_klass)
185
194
  return NullPreloader unless rhs_klass
186
195
 
@@ -12,7 +12,6 @@ module ActiveRecord
12
12
  @preload_scope = preload_scope
13
13
  @model = owners.first && owners.first.class
14
14
  @scope = nil
15
- @owners_by_key = nil
16
15
  @preloaded_records = []
17
16
  end
18
17
 
@@ -33,7 +32,7 @@ module ActiveRecord
33
32
  end
34
33
 
35
34
  def query_scope(ids)
36
- scope.where(association_key.in(ids))
35
+ scope.where(association_key_name => ids)
37
36
  end
38
37
 
39
38
  def table
@@ -56,18 +55,6 @@ module ActiveRecord
56
55
  raise NotImplementedError
57
56
  end
58
57
 
59
- def owners_by_key
60
- @owners_by_key ||= if key_conversion_required?
61
- owners.group_by do |owner|
62
- owner[owner_key_name].to_s
63
- end
64
- else
65
- owners.group_by do |owner|
66
- owner[owner_key_name]
67
- end
68
- end
69
- end
70
-
71
58
  def options
72
59
  reflection.options
73
60
  end
@@ -75,32 +62,33 @@ module ActiveRecord
75
62
  private
76
63
 
77
64
  def associated_records_by_owner(preloader)
78
- owners_map = owners_by_key
79
- owner_keys = owners_map.keys.compact
80
-
81
- # Each record may have multiple owners, and vice-versa
82
- records_by_owner = owners.each_with_object({}) do |owner,h|
83
- h[owner] = []
65
+ records = load_records
66
+ owners.each_with_object({}) do |owner, result|
67
+ result[owner] = records[convert_key(owner[owner_key_name])] || []
84
68
  end
69
+ end
85
70
 
86
- if owner_keys.any?
87
- # Some databases impose a limit on the number of ids in a list (in Oracle it's 1000)
88
- # Make several smaller queries if necessary or make one query if the adapter supports it
89
- sliced = owner_keys.each_slice(klass.connection.in_clause_length || owner_keys.size)
90
-
91
- records = load_slices sliced
92
- records.each do |record, owner_key|
93
- owners_map[owner_key].each do |owner|
94
- records_by_owner[owner] << record
95
- end
71
+ def owner_keys
72
+ unless defined?(@owner_keys)
73
+ @owner_keys = owners.map do |owner|
74
+ owner[owner_key_name]
96
75
  end
76
+ @owner_keys.uniq!
77
+ @owner_keys.compact!
97
78
  end
98
-
99
- records_by_owner
79
+ @owner_keys
100
80
  end
101
81
 
102
82
  def key_conversion_required?
103
- association_key_type != owner_key_type
83
+ @key_conversion_required ||= association_key_type != owner_key_type
84
+ end
85
+
86
+ def convert_key(key)
87
+ if key_conversion_required?
88
+ key.to_s
89
+ else
90
+ key
91
+ end
104
92
  end
105
93
 
106
94
  def association_key_type
@@ -111,17 +99,17 @@ module ActiveRecord
111
99
  @model.type_for_attribute(owner_key_name.to_s).type
112
100
  end
113
101
 
114
- def load_slices(slices)
115
- @preloaded_records = slices.flat_map { |slice|
102
+ def load_records
103
+ return {} if owner_keys.empty?
104
+ # Some databases impose a limit on the number of ids in a list (in Oracle it's 1000)
105
+ # Make several smaller queries if necessary or make one query if the adapter supports it
106
+ slices = owner_keys.each_slice(klass.connection.in_clause_length || owner_keys.size)
107
+ @preloaded_records = slices.flat_map do |slice|
116
108
  records_for(slice)
117
- }
118
-
119
- @preloaded_records.map { |record|
120
- key = record[association_key_name]
121
- key = key.to_s if key_conversion_required?
122
-
123
- [record, key]
124
- }
109
+ end
110
+ @preloaded_records.group_by do |record|
111
+ convert_key(record[association_key_name])
112
+ end
125
113
  end
126
114
 
127
115
  def reflection_scope
@@ -131,19 +119,25 @@ module ActiveRecord
131
119
  def build_scope
132
120
  scope = klass.unscoped
133
121
 
134
- values = reflection_scope.values
135
- reflection_binds = reflection_scope.bind_values
122
+ values = reflection_scope.values
136
123
  preload_values = preload_scope.values
137
- preload_binds = preload_scope.bind_values
138
124
 
139
- scope.where_values = Array(values[:where]) + Array(preload_values[:where])
125
+ scope.where_clause = reflection_scope.where_clause + preload_scope.where_clause
140
126
  scope.references_values = Array(values[:references]) + Array(preload_values[:references])
141
- scope.bind_values = (reflection_binds + preload_binds)
142
127
 
143
- scope._select! preload_values[:select] || values[:select] || table[Arel.star]
128
+ if preload_values[:select] || values[:select]
129
+ scope._select!(preload_values[:select] || values[:select])
130
+ end
144
131
  scope.includes! preload_values[:includes] || values[:includes]
145
- scope.joins! preload_values[:joins] || values[:joins]
146
- scope.order! preload_values[:order] || values[:order]
132
+ if preload_scope.joins_values.any?
133
+ scope.joins!(preload_scope.joins_values)
134
+ else
135
+ scope.joins!(reflection_scope.joins_values)
136
+ end
137
+
138
+ if order_values = preload_values[:order] || values[:order]
139
+ scope.order!(order_values)
140
+ end
147
141
 
148
142
  if preload_values[:reordering] || values[:reordering]
149
143
  scope.reordering_value = true
@@ -2,13 +2,8 @@ module ActiveRecord
2
2
  module Associations
3
3
  class Preloader
4
4
  class CollectionAssociation < Association #:nodoc:
5
-
6
5
  private
7
6
 
8
- def build_scope
9
- super.order(preload_scope.values[:order] || reflection_scope.values[:order])
10
- end
11
-
12
7
  def preload(preloader)
13
8
  associated_records_by_owner(preloader).each do |owner, records|
14
9
  association = owner.association(reflection.name)
@@ -17,7 +12,6 @@ module ActiveRecord
17
12
  records.each { |record| association.set_inverse_instance(record) }
18
13
  end
19
14
  end
20
-
21
15
  end
22
16
  end
23
17
  end
@@ -8,7 +8,7 @@ module ActiveRecord
8
8
  records_by_owner = super
9
9
 
10
10
  if reflection_scope.distinct_value
11
- records_by_owner.each_value { |records| records.uniq! }
11
+ records_by_owner.each_value(&:uniq!)
12
12
  end
13
13
 
14
14
  records_by_owner
@@ -2,7 +2,6 @@ module ActiveRecord
2
2
  module Associations
3
3
  class Preloader
4
4
  class HasOne < SingularAssociation #:nodoc:
5
-
6
5
  def association_key_name
7
6
  reflection.foreign_key
8
7
  end
@@ -10,13 +9,6 @@ module ActiveRecord
10
9
  def owner_key_name
11
10
  reflection.active_record_primary_key
12
11
  end
13
-
14
- private
15
-
16
- def build_scope
17
- super.order(preload_scope.values[:order] || reflection_scope.values[:order])
18
- end
19
-
20
12
  end
21
13
  end
22
14
  end