activerecord 4.2.11.3 → 5.0.7.2

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 (251) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1638 -1132
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +7 -8
  5. data/examples/performance.rb +2 -3
  6. data/examples/simple.rb +0 -1
  7. data/lib/active_record.rb +7 -2
  8. data/lib/active_record/aggregations.rb +34 -21
  9. data/lib/active_record/association_relation.rb +7 -4
  10. data/lib/active_record/associations.rb +347 -218
  11. data/lib/active_record/associations/alias_tracker.rb +19 -16
  12. data/lib/active_record/associations/association.rb +22 -10
  13. data/lib/active_record/associations/association_scope.rb +75 -104
  14. data/lib/active_record/associations/belongs_to_association.rb +21 -32
  15. data/lib/active_record/associations/builder/association.rb +28 -34
  16. data/lib/active_record/associations/builder/belongs_to.rb +43 -18
  17. data/lib/active_record/associations/builder/collection_association.rb +7 -19
  18. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +16 -11
  19. data/lib/active_record/associations/builder/has_many.rb +4 -4
  20. data/lib/active_record/associations/builder/has_one.rb +11 -6
  21. data/lib/active_record/associations/builder/singular_association.rb +13 -11
  22. data/lib/active_record/associations/collection_association.rb +85 -69
  23. data/lib/active_record/associations/collection_proxy.rb +104 -46
  24. data/lib/active_record/associations/foreign_association.rb +1 -1
  25. data/lib/active_record/associations/has_many_association.rb +21 -78
  26. data/lib/active_record/associations/has_many_through_association.rb +6 -47
  27. data/lib/active_record/associations/has_one_association.rb +12 -5
  28. data/lib/active_record/associations/join_dependency.rb +38 -22
  29. data/lib/active_record/associations/join_dependency/join_association.rb +15 -14
  30. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  31. data/lib/active_record/associations/preloader.rb +14 -4
  32. data/lib/active_record/associations/preloader/association.rb +52 -71
  33. data/lib/active_record/associations/preloader/collection_association.rb +0 -7
  34. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  35. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  36. data/lib/active_record/associations/preloader/singular_association.rb +0 -1
  37. data/lib/active_record/associations/preloader/through_association.rb +36 -17
  38. data/lib/active_record/associations/singular_association.rb +13 -1
  39. data/lib/active_record/associations/through_association.rb +12 -4
  40. data/lib/active_record/attribute.rb +69 -19
  41. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  42. data/lib/active_record/attribute_assignment.rb +19 -140
  43. data/lib/active_record/attribute_decorators.rb +6 -5
  44. data/lib/active_record/attribute_methods.rb +69 -44
  45. data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
  46. data/lib/active_record/attribute_methods/dirty.rb +46 -86
  47. data/lib/active_record/attribute_methods/primary_key.rb +16 -3
  48. data/lib/active_record/attribute_methods/query.rb +2 -2
  49. data/lib/active_record/attribute_methods/read.rb +31 -59
  50. data/lib/active_record/attribute_methods/serialization.rb +13 -16
  51. data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -14
  52. data/lib/active_record/attribute_methods/write.rb +13 -37
  53. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  54. data/lib/active_record/attribute_set.rb +32 -3
  55. data/lib/active_record/attribute_set/builder.rb +42 -16
  56. data/lib/active_record/attributes.rb +199 -81
  57. data/lib/active_record/autosave_association.rb +54 -17
  58. data/lib/active_record/base.rb +32 -23
  59. data/lib/active_record/callbacks.rb +39 -43
  60. data/lib/active_record/coders/json.rb +1 -1
  61. data/lib/active_record/coders/yaml_column.rb +20 -8
  62. data/lib/active_record/collection_cache_key.rb +50 -0
  63. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +467 -189
  64. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  65. data/lib/active_record/connection_adapters/abstract/database_statements.rb +66 -62
  66. data/lib/active_record/connection_adapters/abstract/query_cache.rb +39 -4
  67. data/lib/active_record/connection_adapters/abstract/quoting.rb +86 -13
  68. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  69. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
  70. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -188
  71. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
  72. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +407 -156
  73. data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
  74. data/lib/active_record/connection_adapters/abstract_adapter.rb +177 -71
  75. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +433 -399
  76. data/lib/active_record/connection_adapters/column.rb +28 -43
  77. data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
  78. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  79. data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
  80. data/lib/active_record/connection_adapters/mysql/database_statements.rb +108 -0
  81. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
  82. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  83. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
  84. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
  85. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
  86. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
  87. data/lib/active_record/connection_adapters/mysql2_adapter.rb +25 -166
  88. data/lib/active_record/connection_adapters/postgresql/column.rb +33 -11
  89. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +18 -72
  90. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  92. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +37 -57
  93. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +3 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -2
  95. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
  96. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
  97. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +13 -3
  98. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  99. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  100. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
  101. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  102. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
  104. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
  105. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  106. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +56 -19
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
  111. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +250 -154
  113. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  114. data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -2
  115. data/lib/active_record/connection_adapters/postgresql_adapter.rb +264 -170
  116. data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
  117. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  118. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  119. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  120. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  121. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +151 -194
  122. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  123. data/lib/active_record/connection_handling.rb +37 -14
  124. data/lib/active_record/core.rb +92 -108
  125. data/lib/active_record/counter_cache.rb +13 -24
  126. data/lib/active_record/dynamic_matchers.rb +1 -20
  127. data/lib/active_record/enum.rb +116 -76
  128. data/lib/active_record/errors.rb +87 -48
  129. data/lib/active_record/explain.rb +20 -9
  130. data/lib/active_record/explain_registry.rb +1 -1
  131. data/lib/active_record/explain_subscriber.rb +1 -1
  132. data/lib/active_record/fixture_set/file.rb +26 -5
  133. data/lib/active_record/fixtures.rb +77 -41
  134. data/lib/active_record/gem_version.rb +4 -4
  135. data/lib/active_record/inheritance.rb +32 -40
  136. data/lib/active_record/integration.rb +17 -14
  137. data/lib/active_record/internal_metadata.rb +56 -0
  138. data/lib/active_record/legacy_yaml_adapter.rb +18 -2
  139. data/lib/active_record/locale/en.yml +3 -2
  140. data/lib/active_record/locking/optimistic.rb +15 -15
  141. data/lib/active_record/locking/pessimistic.rb +1 -1
  142. data/lib/active_record/log_subscriber.rb +48 -24
  143. data/lib/active_record/migration.rb +362 -111
  144. data/lib/active_record/migration/command_recorder.rb +59 -18
  145. data/lib/active_record/migration/compatibility.rb +126 -0
  146. data/lib/active_record/model_schema.rb +270 -73
  147. data/lib/active_record/nested_attributes.rb +58 -29
  148. data/lib/active_record/no_touching.rb +4 -0
  149. data/lib/active_record/null_relation.rb +16 -8
  150. data/lib/active_record/persistence.rb +152 -90
  151. data/lib/active_record/query_cache.rb +18 -23
  152. data/lib/active_record/querying.rb +12 -11
  153. data/lib/active_record/railtie.rb +23 -16
  154. data/lib/active_record/railties/controller_runtime.rb +1 -1
  155. data/lib/active_record/railties/databases.rake +52 -41
  156. data/lib/active_record/readonly_attributes.rb +1 -1
  157. data/lib/active_record/reflection.rb +302 -115
  158. data/lib/active_record/relation.rb +187 -120
  159. data/lib/active_record/relation/batches.rb +141 -36
  160. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  161. data/lib/active_record/relation/calculations.rb +92 -117
  162. data/lib/active_record/relation/delegation.rb +8 -20
  163. data/lib/active_record/relation/finder_methods.rb +173 -89
  164. data/lib/active_record/relation/from_clause.rb +32 -0
  165. data/lib/active_record/relation/merger.rb +16 -42
  166. data/lib/active_record/relation/predicate_builder.rb +120 -107
  167. data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
  168. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  169. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  170. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  171. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  172. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  173. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  174. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  175. data/lib/active_record/relation/query_attribute.rb +19 -0
  176. data/lib/active_record/relation/query_methods.rb +308 -244
  177. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  178. data/lib/active_record/relation/spawn_methods.rb +4 -7
  179. data/lib/active_record/relation/where_clause.rb +174 -0
  180. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  181. data/lib/active_record/result.rb +11 -4
  182. data/lib/active_record/runtime_registry.rb +1 -1
  183. data/lib/active_record/sanitization.rb +105 -66
  184. data/lib/active_record/schema.rb +26 -22
  185. data/lib/active_record/schema_dumper.rb +54 -37
  186. data/lib/active_record/schema_migration.rb +11 -14
  187. data/lib/active_record/scoping.rb +34 -16
  188. data/lib/active_record/scoping/default.rb +28 -10
  189. data/lib/active_record/scoping/named.rb +59 -26
  190. data/lib/active_record/secure_token.rb +38 -0
  191. data/lib/active_record/serialization.rb +3 -5
  192. data/lib/active_record/statement_cache.rb +17 -15
  193. data/lib/active_record/store.rb +8 -3
  194. data/lib/active_record/suppressor.rb +58 -0
  195. data/lib/active_record/table_metadata.rb +69 -0
  196. data/lib/active_record/tasks/database_tasks.rb +66 -49
  197. data/lib/active_record/tasks/mysql_database_tasks.rb +6 -14
  198. data/lib/active_record/tasks/postgresql_database_tasks.rb +12 -3
  199. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  200. data/lib/active_record/timestamp.rb +20 -9
  201. data/lib/active_record/touch_later.rb +63 -0
  202. data/lib/active_record/transactions.rb +139 -57
  203. data/lib/active_record/type.rb +66 -17
  204. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  205. data/lib/active_record/type/date.rb +2 -45
  206. data/lib/active_record/type/date_time.rb +2 -49
  207. data/lib/active_record/type/internal/abstract_json.rb +33 -0
  208. data/lib/active_record/type/internal/timezone.rb +15 -0
  209. data/lib/active_record/type/serialized.rb +15 -14
  210. data/lib/active_record/type/time.rb +10 -16
  211. data/lib/active_record/type/type_map.rb +4 -4
  212. data/lib/active_record/type_caster.rb +7 -0
  213. data/lib/active_record/type_caster/connection.rb +29 -0
  214. data/lib/active_record/type_caster/map.rb +19 -0
  215. data/lib/active_record/validations.rb +33 -32
  216. data/lib/active_record/validations/absence.rb +23 -0
  217. data/lib/active_record/validations/associated.rb +10 -3
  218. data/lib/active_record/validations/length.rb +24 -0
  219. data/lib/active_record/validations/presence.rb +11 -12
  220. data/lib/active_record/validations/uniqueness.rb +33 -33
  221. data/lib/rails/generators/active_record/migration.rb +15 -0
  222. data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -5
  223. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
  224. data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -1
  225. data/lib/rails/generators/active_record/model/model_generator.rb +33 -16
  226. data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
  227. data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
  228. metadata +58 -34
  229. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  230. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  231. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
  232. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  233. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  234. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  235. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  236. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  237. data/lib/active_record/type/big_integer.rb +0 -13
  238. data/lib/active_record/type/binary.rb +0 -50
  239. data/lib/active_record/type/boolean.rb +0 -31
  240. data/lib/active_record/type/decimal.rb +0 -64
  241. data/lib/active_record/type/decimal_without_scale.rb +0 -11
  242. data/lib/active_record/type/decorator.rb +0 -14
  243. data/lib/active_record/type/float.rb +0 -19
  244. data/lib/active_record/type/integer.rb +0 -59
  245. data/lib/active_record/type/mutable.rb +0 -16
  246. data/lib/active_record/type/numeric.rb +0 -36
  247. data/lib/active_record/type/string.rb +0 -40
  248. data/lib/active_record/type/text.rb +0 -11
  249. data/lib/active_record/type/time_value.rb +0 -38
  250. data/lib/active_record/type/unsigned_integer.rb +0 -15
  251. data/lib/active_record/type/value.rb +0 -110
@@ -62,8 +62,8 @@ module ActiveRecord
62
62
  hash
63
63
  end
64
64
 
65
- def instantiate(row, aliases)
66
- base_klass.instantiate(extract_record(row, aliases))
65
+ def instantiate(row, aliases, &block)
66
+ base_klass.instantiate(extract_record(row, aliases), &block)
67
67
  end
68
68
  end
69
69
  end
@@ -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
@@ -179,8 +184,13 @@ module ActiveRecord
179
184
  def self.new(klass, owners, reflection, preload_scope); self; end
180
185
  def self.run(preloader); end
181
186
  def self.preloaded_records; []; end
187
+ def self.owners; []; end
182
188
  end
183
189
 
190
+ # Returns a class containing the logic needed to load preload the data
191
+ # and attach it to a relation. For example +Preloader::Association+ or
192
+ # +Preloader::HasManyThrough+. The class returned implements a `run` method
193
+ # that accepts a preloader.
184
194
  def preloader_for(reflection, owners, rhs_klass)
185
195
  return NullPreloader unless rhs_klass
186
196
 
@@ -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
@@ -48,7 +47,7 @@ module ActiveRecord
48
47
  # This is overridden by HABTM as the condition should be on the foreign_key column in
49
48
  # the join table
50
49
  def association_key
51
- table[association_key_name]
50
+ klass.arel_attribute(association_key_name, table)
52
51
  end
53
52
 
54
53
  # The name of the key on the model which declares the association
@@ -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,47 @@ 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 do |record|
66
+ owner = owners_by_key[convert_key(record[association_key_name])]
67
+ association = owner.association(reflection.name)
68
+ association.set_inverse_instance(record)
84
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)
71
+ owners.each_with_object({}) do |owner, result|
72
+ result[owner] = records[convert_key(owner[owner_key_name])] || []
73
+ end
74
+ end
90
75
 
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
76
+ def owner_keys
77
+ unless defined?(@owner_keys)
78
+ @owner_keys = owners.map do |owner|
79
+ owner[owner_key_name]
96
80
  end
81
+ @owner_keys.uniq!
82
+ @owner_keys.compact!
97
83
  end
84
+ @owner_keys
85
+ end
98
86
 
99
- records_by_owner
87
+ def owners_by_key
88
+ unless defined?(@owners_by_key)
89
+ @owners_by_key = owners.each_with_object({}) do |owner, h|
90
+ h[convert_key(owner[owner_key_name])] = owner
91
+ end
92
+ end
93
+ @owners_by_key
100
94
  end
101
95
 
102
96
  def key_conversion_required?
103
- association_key_type != owner_key_type
97
+ @key_conversion_required ||= association_key_type != owner_key_type
98
+ end
99
+
100
+ def convert_key(key)
101
+ if key_conversion_required?
102
+ key.to_s
103
+ else
104
+ key
105
+ end
104
106
  end
105
107
 
106
108
  def association_key_type
@@ -111,54 +113,33 @@ module ActiveRecord
111
113
  @model.type_for_attribute(owner_key_name.to_s).type
112
114
  end
113
115
 
114
- def load_slices(slices)
115
- @preloaded_records = slices.flat_map { |slice|
116
- 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
- }
116
+ def load_records(&block)
117
+ return {} if owner_keys.empty?
118
+ # Some databases impose a limit on the number of ids in a list (in Oracle it's 1000)
119
+ # Make several smaller queries if necessary or make one query if the adapter supports it
120
+ slices = owner_keys.each_slice(klass.connection.in_clause_length || owner_keys.size)
121
+ @preloaded_records = slices.flat_map do |slice|
122
+ records_for(slice).load(&block)
123
+ end
124
+ @preloaded_records.group_by do |record|
125
+ convert_key(record[association_key_name])
126
+ end
125
127
  end
126
128
 
127
129
  def reflection_scope
128
- @reflection_scope ||= reflection.scope ? klass.unscoped.instance_exec(nil, &reflection.scope) : klass.unscoped
130
+ @reflection_scope ||= reflection.scope_for(klass)
129
131
  end
130
132
 
131
133
  def build_scope
132
- scope = klass.unscoped
133
-
134
- values = reflection_scope.values
135
- reflection_binds = reflection_scope.bind_values
136
- preload_values = preload_scope.values
137
- preload_binds = preload_scope.bind_values
138
-
139
- scope.where_values = Array(values[:where]) + Array(preload_values[:where])
140
- scope.references_values = Array(values[:references]) + Array(preload_values[:references])
141
- scope.bind_values = (reflection_binds + preload_binds)
142
-
143
- scope._select! preload_values[:select] || values[:select] || table[Arel.star]
144
- scope.includes! preload_values[:includes] || values[:includes]
145
- scope.joins! preload_values[:joins] || values[:joins]
146
- scope.order! preload_values[:order] || values[:order]
147
-
148
- if preload_values[:reordering] || values[:reordering]
149
- scope.reordering_value = true
150
- end
151
-
152
- if preload_values[:readonly] || values[:readonly]
153
- scope.readonly!
154
- end
134
+ scope = klass.scope_for_association
155
135
 
156
- if options[:as]
157
- scope.where!(klass.table_name => { reflection.type => model.base_class.sti_name })
136
+ if reflection.type
137
+ scope.where!(reflection.type => model.base_class.sti_name)
158
138
  end
159
139
 
160
- scope.unscope_values = Array(values[:unscope]) + Array(preload_values[:unscope])
161
- klass.default_scoped.merge(scope)
140
+ scope.merge!(reflection_scope)
141
+ scope.merge!(preload_scope) if preload_scope != NULL_RELATION
142
+ scope
162
143
  end
163
144
  end
164
145
  end
@@ -2,22 +2,15 @@ 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)
15
10
  association.loaded!
16
11
  association.target.concat(records)
17
- records.each { |record| association.set_inverse_instance(record) }
18
12
  end
19
13
  end
20
-
21
14
  end
22
15
  end
23
16
  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
@@ -11,7 +11,6 @@ module ActiveRecord
11
11
 
12
12
  association = owner.association(reflection.name)
13
13
  association.target = record
14
- association.set_inverse_instance(record) if record
15
14
  end
16
15
  end
17
16
 
@@ -18,7 +18,8 @@ module ActiveRecord
18
18
  through_records = owners.map do |owner|
19
19
  association = owner.association through_reflection.name
20
20
 
21
- [owner, Array(association.reader)]
21
+ center = target_records_from_association(association)
22
+ [owner, Array(center)]
22
23
  end
23
24
 
24
25
  reset_association owners, through_reflection.name
@@ -37,28 +38,35 @@ module ActiveRecord
37
38
  }
38
39
  end
39
40
 
40
- record_offset = {}
41
- @preloaded_records.each_with_index do |record,i|
42
- record_offset[record] = i
43
- end
44
-
45
- through_records.each_with_object({}) { |(lhs,center),records_by_owner|
41
+ through_records.each_with_object({}) do |(lhs,center), records_by_owner|
46
42
  pl_to_middle = center.group_by { |record| middle_to_pl[record] }
47
43
 
48
44
  records_by_owner[lhs] = pl_to_middle.flat_map do |pl, middles|
49
45
  rhs_records = middles.flat_map { |r|
50
46
  association = r.association source_reflection.name
51
47
 
52
- association.reader
48
+ target_records_from_association(association)
53
49
  }.compact
54
50
 
55
- rhs_records.sort_by { |rhs| record_offset[rhs] }
51
+ # Respect the order on `reflection_scope` if it exists, else use the natural order.
52
+ if reflection_scope.values[:order].present?
53
+ @id_map ||= id_to_index_map @preloaded_records
54
+ rhs_records.sort_by { |rhs| @id_map[rhs] }
55
+ else
56
+ rhs_records
57
+ end
56
58
  end
57
- }
59
+ end
58
60
  end
59
61
 
60
62
  private
61
63
 
64
+ def id_to_index_map(ids)
65
+ id_map = {}
66
+ ids.each_with_index { |id, index| id_map[id] = index }
67
+ id_map
68
+ end
69
+
62
70
  def reset_association(owners, association_name)
63
71
  should_reset = (through_scope != through_reflection.klass.unscoped) ||
64
72
  (reflection.options[:source_type] && through_reflection.collection?)
@@ -71,25 +79,36 @@ module ActiveRecord
71
79
  end
72
80
  end
73
81
 
74
-
75
82
  def through_scope
76
83
  scope = through_reflection.klass.unscoped
84
+ values = reflection_scope.values
77
85
 
78
86
  if options[:source_type]
79
87
  scope.where! reflection.foreign_type => options[:source_type]
80
88
  else
81
- unless reflection_scope.where_values.empty?
82
- scope.includes_values = Array(reflection_scope.values[:includes] || options[:source])
83
- scope.where_values = reflection_scope.values[:where]
84
- scope.bind_values = reflection_scope.bind_values
89
+ unless reflection_scope.where_clause.empty?
90
+ scope.includes_values = Array(values[:includes] || options[:source])
91
+ scope.where_clause = reflection_scope.where_clause
92
+ if joins = values[:joins]
93
+ scope.joins!(source_reflection.name => joins)
94
+ end
95
+ if left_outer_joins = values[:left_outer_joins]
96
+ scope.left_outer_joins!(source_reflection.name => left_outer_joins)
97
+ end
85
98
  end
86
99
 
87
- scope.references! reflection_scope.values[:references]
88
- scope = scope.order reflection_scope.values[:order] if scope.eager_loading?
100
+ scope.references! values[:references]
101
+ if scope.eager_loading? && order_values = values[:order]
102
+ scope = scope.order(order_values)
103
+ end
89
104
  end
90
105
 
91
106
  scope
92
107
  end
108
+
109
+ def target_records_from_association(association)
110
+ association.loaded? ? association.target : association.reader
111
+ end
93
112
  end
94
113
  end
95
114
  end
@@ -4,6 +4,11 @@ module ActiveRecord
4
4
  # Implements the reader method, e.g. foo.bar for Foo.has_one :bar
5
5
  def reader(force_reload = false)
6
6
  if force_reload && klass
7
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
8
+ Passing an argument to force an association to reload is now
9
+ deprecated and will be removed in Rails 5.1. Please call `reload_#{reflection.name}` instead.
10
+ MSG
11
+
7
12
  klass.uncached { reload }
8
13
  elsif !loaded? || stale_target?
9
14
  reload
@@ -32,6 +37,13 @@ module ActiveRecord
32
37
  record
33
38
  end
34
39
 
40
+ # Implements the reload reader method, e.g. foo.reload_bar for
41
+ # Foo.has_one :bar
42
+ def force_reload_reader
43
+ klass.uncached { reload }
44
+ target
45
+ end
46
+
35
47
  private
36
48
 
37
49
  def create_scope
@@ -39,7 +51,7 @@ module ActiveRecord
39
51
  end
40
52
 
41
53
  def get_records
42
- return scope.limit(1).to_a if skip_statement_cache?
54
+ return scope.limit(1).records if skip_statement_cache?
43
55
 
44
56
  conn = klass.connection
45
57
  sc = reflection.association_scope_cache(conn, owner) do