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
@@ -1,8 +1,8 @@
1
- require 'active_support/core_ext/string/filters'
1
+ require "active_support/core_ext/string/filters"
2
2
 
3
3
  module ActiveRecord
4
4
  module FinderMethods
5
- ONE_AS_ONE = '1 AS one'
5
+ ONE_AS_ONE = "1 AS one"
6
6
 
7
7
  # Find by id - This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]).
8
8
  # If one or more records can not be found for the requested ids, then RecordNotFound will be raised. If the primary key
@@ -76,7 +76,7 @@ module ActiveRecord
76
76
  # Post.find_by "published_at < ?", 2.weeks.ago
77
77
  def find_by(arg, *args)
78
78
  where(arg, *args).take
79
- rescue RangeError
79
+ rescue ::RangeError
80
80
  nil
81
81
  end
82
82
 
@@ -84,7 +84,7 @@ module ActiveRecord
84
84
  # an ActiveRecord::RecordNotFound error.
85
85
  def find_by!(arg, *args)
86
86
  where(arg, *args).take!
87
- rescue RangeError
87
+ rescue ::RangeError
88
88
  raise RecordNotFound.new("Couldn't find #{@klass.name} with an out of range value",
89
89
  @klass.name)
90
90
  end
@@ -97,13 +97,13 @@ module ActiveRecord
97
97
  # Person.take(5) # returns 5 objects fetched by SELECT * FROM people LIMIT 5
98
98
  # Person.where(["name LIKE '%?'", name]).take
99
99
  def take(limit = nil)
100
- limit ? limit(limit).to_a : find_take
100
+ limit ? find_take_with_limit(limit) : find_take
101
101
  end
102
102
 
103
103
  # Same as #take but raises ActiveRecord::RecordNotFound if no record
104
104
  # is found. Note that #take! accepts no arguments.
105
105
  def take!
106
- take or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]")
106
+ take || raise_record_not_found_exception!
107
107
  end
108
108
 
109
109
  # Find the first record (or first N records if a parameter is supplied).
@@ -117,7 +117,7 @@ module ActiveRecord
117
117
  #
118
118
  def first(limit = nil)
119
119
  if limit
120
- find_nth_with_limit_and_offset(0, limit, offset: offset_index)
120
+ find_nth_with_limit(0, limit)
121
121
  else
122
122
  find_nth 0
123
123
  end
@@ -126,7 +126,7 @@ module ActiveRecord
126
126
  # Same as #first but raises ActiveRecord::RecordNotFound if no record
127
127
  # is found. Note that #first! accepts no arguments.
128
128
  def first!
129
- find_nth! 0
129
+ first || raise_record_not_found_exception!
130
130
  end
131
131
 
132
132
  # Find the last record (or last N records if a parameter is supplied).
@@ -147,25 +147,17 @@ module ActiveRecord
147
147
  def last(limit = nil)
148
148
  return find_last(limit) if loaded? || limit_value
149
149
 
150
- result = limit(limit || 1)
150
+ result = limit(limit)
151
151
  result.order!(arel_attribute(primary_key)) if order_values.empty? && primary_key
152
152
  result = result.reverse_order!
153
153
 
154
154
  limit ? result.reverse : result.first
155
- rescue ActiveRecord::IrreversibleOrderError
156
- ActiveSupport::Deprecation.warn(<<-WARNING.squish)
157
- Finding a last element by loading the relation when SQL ORDER
158
- can not be reversed is deprecated.
159
- Rails 5.1 will raise ActiveRecord::IrreversibleOrderError in this case.
160
- Please call `to_a.last` if you still want to load the relation.
161
- WARNING
162
- find_last(limit)
163
155
  end
164
156
 
165
157
  # Same as #last but raises ActiveRecord::RecordNotFound if no record
166
158
  # is found. Note that #last! accepts no arguments.
167
159
  def last!
168
- last or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]")
160
+ last || raise_record_not_found_exception!
169
161
  end
170
162
 
171
163
  # Find the second record.
@@ -181,7 +173,7 @@ module ActiveRecord
181
173
  # Same as #second but raises ActiveRecord::RecordNotFound if no record
182
174
  # is found.
183
175
  def second!
184
- find_nth! 1
176
+ second || raise_record_not_found_exception!
185
177
  end
186
178
 
187
179
  # Find the third record.
@@ -197,7 +189,7 @@ module ActiveRecord
197
189
  # Same as #third but raises ActiveRecord::RecordNotFound if no record
198
190
  # is found.
199
191
  def third!
200
- find_nth! 2
192
+ third || raise_record_not_found_exception!
201
193
  end
202
194
 
203
195
  # Find the fourth record.
@@ -213,7 +205,7 @@ module ActiveRecord
213
205
  # Same as #fourth but raises ActiveRecord::RecordNotFound if no record
214
206
  # is found.
215
207
  def fourth!
216
- find_nth! 3
208
+ fourth || raise_record_not_found_exception!
217
209
  end
218
210
 
219
211
  # Find the fifth record.
@@ -229,7 +221,7 @@ module ActiveRecord
229
221
  # Same as #fifth but raises ActiveRecord::RecordNotFound if no record
230
222
  # is found.
231
223
  def fifth!
232
- find_nth! 4
224
+ fifth || raise_record_not_found_exception!
233
225
  end
234
226
 
235
227
  # Find the forty-second record. Also known as accessing "the reddit".
@@ -245,7 +237,7 @@ module ActiveRecord
245
237
  # Same as #forty_two but raises ActiveRecord::RecordNotFound if no record
246
238
  # is found.
247
239
  def forty_two!
248
- find_nth! 41
240
+ forty_two || raise_record_not_found_exception!
249
241
  end
250
242
 
251
243
  # Find the third-to-last record.
@@ -261,7 +253,7 @@ module ActiveRecord
261
253
  # Same as #third_to_last but raises ActiveRecord::RecordNotFound if no record
262
254
  # is found.
263
255
  def third_to_last!
264
- find_nth_from_last 3 or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]")
256
+ third_to_last || raise_record_not_found_exception!
265
257
  end
266
258
 
267
259
  # Find the second-to-last record.
@@ -277,7 +269,7 @@ module ActiveRecord
277
269
  # Same as #second_to_last but raises ActiveRecord::RecordNotFound if no record
278
270
  # is found.
279
271
  def second_to_last!
280
- find_nth_from_last 2 or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]")
272
+ second_to_last || raise_record_not_found_exception!
281
273
  end
282
274
 
283
275
  # Returns true if a record exists in the table that matches the +id+ or
@@ -309,30 +301,24 @@ module ActiveRecord
309
301
  # Person.exists?
310
302
  def exists?(conditions = :none)
311
303
  if Base === conditions
312
- conditions = conditions.id
313
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
304
+ raise ArgumentError, <<-MSG.squish
314
305
  You are passing an instance of ActiveRecord::Base to `exists?`.
315
306
  Please pass the id of the object by calling `.id`.
316
307
  MSG
317
308
  end
318
309
 
319
- return false if !conditions
310
+ return false if !conditions || limit_value == 0
320
311
 
321
- relation = apply_join_dependency(self, construct_join_dependency)
322
- return false if ActiveRecord::NullRelation === relation
312
+ relation = self unless eager_loading?
313
+ relation ||= apply_join_dependency(self, construct_join_dependency(eager_loading: false))
323
314
 
324
- relation = relation.except(:select, :order).select(ONE_AS_ONE).limit(1)
315
+ return false if ActiveRecord::NullRelation === relation
325
316
 
326
- case conditions
327
- when Array, Hash
328
- relation = relation.where(conditions)
329
- else
330
- unless conditions == :none
331
- relation = relation.where(primary_key => conditions)
332
- end
333
- end
317
+ relation = construct_relation_for_exists(relation, conditions)
334
318
 
335
319
  connection.select_value(relation, "#{name} Exists", relation.bound_attributes) ? true : false
320
+ rescue ::RangeError
321
+ false
336
322
  end
337
323
 
338
324
  # This method is called whenever no records are found with either a single
@@ -343,258 +329,238 @@ module ActiveRecord
343
329
  # of results obtained should be provided in the +result_size+ argument and
344
330
  # the expected number of results should be provided in the +expected_size+
345
331
  # argument.
346
- def raise_record_not_found_exception!(ids, result_size, expected_size, key = primary_key) #:nodoc:
332
+ def raise_record_not_found_exception!(ids = nil, result_size = nil, expected_size = nil, key = primary_key) # :nodoc:
347
333
  conditions = arel.where_sql(@klass.arel_engine)
348
334
  conditions = " [#{conditions}]" if conditions
349
335
  name = @klass.name
350
336
 
351
- if Array(ids).size == 1
337
+ if ids.nil?
338
+ error = "Couldn't find #{name}"
339
+ error << " with#{conditions}" if conditions
340
+ raise RecordNotFound.new(error, name)
341
+ elsif Array(ids).size == 1
352
342
  error = "Couldn't find #{name} with '#{key}'=#{ids}#{conditions}"
353
343
  raise RecordNotFound.new(error, name, key, ids)
354
344
  else
355
345
  error = "Couldn't find all #{name.pluralize} with '#{key}': "
356
346
  error << "(#{ids.join(", ")})#{conditions} (found #{result_size} results, but was looking for #{expected_size})"
357
347
 
358
- raise RecordNotFound, error
348
+ raise RecordNotFound.new(error, name, primary_key, ids)
359
349
  end
360
350
  end
361
351
 
362
352
  private
363
353
 
364
- def offset_index
365
- offset_value || 0
366
- end
354
+ def offset_index
355
+ offset_value || 0
356
+ end
367
357
 
368
- def find_with_associations
369
- # NOTE: the JoinDependency constructed here needs to know about
370
- # any joins already present in `self`, so pass them in
371
- #
372
- # failing to do so means that in cases like activerecord/test/cases/associations/inner_join_association_test.rb:136
373
- # incorrect SQL is generated. In that case, the join dependency for
374
- # SpecialCategorizations is constructed without knowledge of the
375
- # preexisting join in joins_values to categorizations (by way of
376
- # the `has_many :through` for categories).
377
- #
378
- join_dependency = construct_join_dependency(joins_values)
379
-
380
- aliases = join_dependency.aliases
381
- relation = select aliases.columns
382
- relation = apply_join_dependency(relation, join_dependency)
383
-
384
- if block_given?
385
- yield relation
386
- else
387
- if ActiveRecord::NullRelation === relation
388
- []
358
+ def find_with_associations
359
+ # NOTE: the JoinDependency constructed here needs to know about
360
+ # any joins already present in `self`, so pass them in
361
+ #
362
+ # failing to do so means that in cases like activerecord/test/cases/associations/inner_join_association_test.rb:136
363
+ # incorrect SQL is generated. In that case, the join dependency for
364
+ # SpecialCategorizations is constructed without knowledge of the
365
+ # preexisting join in joins_values to categorizations (by way of
366
+ # the `has_many :through` for categories).
367
+ #
368
+ join_dependency = construct_join_dependency(joins_values)
369
+
370
+ aliases = join_dependency.aliases
371
+ relation = select aliases.columns
372
+ relation = apply_join_dependency(relation, join_dependency)
373
+
374
+ yield relation, join_dependency
375
+ end
376
+
377
+ def construct_relation_for_exists(relation, conditions)
378
+ relation = relation.except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1)
379
+
380
+ case conditions
381
+ when Array, Hash
382
+ relation.where!(conditions) unless conditions.empty?
389
383
  else
390
- arel = relation.arel
391
- rows = connection.select_all(arel, 'SQL', relation.bound_attributes)
392
- join_dependency.instantiate(rows, aliases)
384
+ relation.where!(primary_key => conditions) unless conditions == :none
393
385
  end
386
+
387
+ relation
394
388
  end
395
- end
396
389
 
397
- def construct_join_dependency(joins = [])
398
- including = eager_load_values + includes_values
399
- ActiveRecord::Associations::JoinDependency.new(@klass, including, joins)
400
- end
390
+ def construct_join_dependency(joins = [], eager_loading: true)
391
+ including = eager_load_values + includes_values
392
+ ActiveRecord::Associations::JoinDependency.new(@klass, including, joins, eager_loading: eager_loading)
393
+ end
401
394
 
402
- def construct_relation_for_association_calculations
403
- from = arel.froms.first
404
- if Arel::Table === from
395
+ def construct_relation_for_association_calculations
405
396
  apply_join_dependency(self, construct_join_dependency(joins_values))
406
- else
407
- # FIXME: as far as I can tell, `from` will always be an Arel::Table.
408
- # There are no tests that test this branch, but presumably it's
409
- # possible for `from` to be a list?
410
- apply_join_dependency(self, construct_join_dependency(from))
411
397
  end
412
- end
413
398
 
414
- def apply_join_dependency(relation, join_dependency)
415
- relation = relation.except(:includes, :eager_load, :preload)
416
- relation = relation.joins join_dependency
399
+ def apply_join_dependency(relation, join_dependency)
400
+ relation = relation.except(:includes, :eager_load, :preload).joins!(join_dependency)
417
401
 
418
- if using_limitable_reflections?(join_dependency.reflections)
419
- relation
420
- else
421
- if relation.limit_value
422
- limited_ids = limited_ids_for(relation)
423
- limited_ids.empty? ? relation.none! : relation.where!(primary_key => limited_ids)
402
+ if using_limitable_reflections?(join_dependency.reflections)
403
+ relation
404
+ else
405
+ if relation.limit_value
406
+ limited_ids = limited_ids_for(relation)
407
+ limited_ids.empty? ? relation.none! : relation.where!(primary_key => limited_ids)
408
+ end
409
+ relation.except(:limit, :offset)
424
410
  end
425
- relation.except(:limit, :offset)
426
411
  end
427
- end
428
412
 
429
- def limited_ids_for(relation)
430
- values = @klass.connection.columns_for_distinct(
431
- "#{quoted_table_name}.#{quoted_primary_key}", relation.order_values)
413
+ def limited_ids_for(relation)
414
+ values = @klass.connection.columns_for_distinct(
415
+ "#{quoted_table_name}.#{quoted_primary_key}", relation.order_values)
432
416
 
433
- relation = relation.except(:select).select(values).distinct!
434
- arel = relation.arel
435
-
436
- id_rows = @klass.connection.select_all(arel, 'SQL', relation.bound_attributes)
437
- id_rows.map {|row| row[primary_key]}
438
- end
417
+ relation = relation.except(:select).select(values).distinct!
418
+ arel = relation.arel
439
419
 
440
- def using_limitable_reflections?(reflections)
441
- reflections.none?(&:collection?)
442
- end
420
+ id_rows = @klass.connection.select_all(arel, "SQL", relation.bound_attributes)
421
+ id_rows.map { |row| row[primary_key] }
422
+ end
443
423
 
444
- protected
424
+ def using_limitable_reflections?(reflections)
425
+ reflections.none?(&:collection?)
426
+ end
445
427
 
446
- def find_with_ids(*ids)
447
- raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
428
+ def find_with_ids(*ids)
429
+ raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
448
430
 
449
- expects_array = ids.first.kind_of?(Array)
450
- return ids.first if expects_array && ids.first.empty?
431
+ expects_array = ids.first.kind_of?(Array)
432
+ return [] if expects_array && ids.first.empty?
451
433
 
452
- ids = ids.flatten.compact.uniq
434
+ ids = ids.flatten.compact.uniq
453
435
 
454
- case ids.size
455
- when 0
456
- raise RecordNotFound, "Couldn't find #{@klass.name} without an ID"
457
- when 1
458
- result = find_one(ids.first)
459
- expects_array ? [ result ] : result
460
- else
461
- find_some(ids)
436
+ case ids.size
437
+ when 0
438
+ raise RecordNotFound, "Couldn't find #{@klass.name} without an ID"
439
+ when 1
440
+ result = find_one(ids.first)
441
+ expects_array ? [ result ] : result
442
+ else
443
+ find_some(ids)
444
+ end
445
+ rescue ::RangeError
446
+ raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range ID"
462
447
  end
463
- rescue RangeError
464
- raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range ID"
465
- end
466
448
 
467
- def find_one(id)
468
- if ActiveRecord::Base === id
469
- id = id.id
470
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
471
- You are passing an instance of ActiveRecord::Base to `find`.
472
- Please pass the id of the object by calling `.id`.
473
- MSG
474
- end
449
+ def find_one(id)
450
+ if ActiveRecord::Base === id
451
+ raise ArgumentError, <<-MSG.squish
452
+ You are passing an instance of ActiveRecord::Base to `find`.
453
+ Please pass the id of the object by calling `.id`.
454
+ MSG
455
+ end
475
456
 
476
- relation = where(primary_key => id)
477
- record = relation.take
457
+ relation = where(primary_key => id)
458
+ record = relation.take
478
459
 
479
- raise_record_not_found_exception!(id, 0, 1) unless record
460
+ raise_record_not_found_exception!(id, 0, 1) unless record
480
461
 
481
- record
482
- end
462
+ record
463
+ end
483
464
 
484
- def find_some(ids)
485
- return find_some_ordered(ids) unless order_values.present?
465
+ def find_some(ids)
466
+ return find_some_ordered(ids) unless order_values.present?
486
467
 
487
- result = where(primary_key => ids).to_a
468
+ result = where(primary_key => ids).to_a
488
469
 
489
- expected_size =
490
- if limit_value && ids.size > limit_value
491
- limit_value
492
- else
493
- ids.size
494
- end
470
+ expected_size =
471
+ if limit_value && ids.size > limit_value
472
+ limit_value
473
+ else
474
+ ids.size
475
+ end
495
476
 
496
- # 11 ids with limit 3, offset 9 should give 2 results.
497
- if offset_value && (ids.size - offset_value < expected_size)
498
- expected_size = ids.size - offset_value
499
- end
477
+ # 11 ids with limit 3, offset 9 should give 2 results.
478
+ if offset_value && (ids.size - offset_value < expected_size)
479
+ expected_size = ids.size - offset_value
480
+ end
500
481
 
501
- if result.size == expected_size
502
- result
503
- else
504
- raise_record_not_found_exception!(ids, result.size, expected_size)
482
+ if result.size == expected_size
483
+ result
484
+ else
485
+ raise_record_not_found_exception!(ids, result.size, expected_size)
486
+ end
505
487
  end
506
- end
507
488
 
508
- def find_some_ordered(ids)
509
- ids = ids.slice(offset_value || 0, limit_value || ids.size) || []
489
+ def find_some_ordered(ids)
490
+ ids = ids.slice(offset_value || 0, limit_value || ids.size) || []
510
491
 
511
- result = except(:limit, :offset).where(primary_key => ids).records
492
+ result = except(:limit, :offset).where(primary_key => ids).records
512
493
 
513
- if result.size == ids.size
514
- pk_type = @klass.type_for_attribute(primary_key)
494
+ if result.size == ids.size
495
+ pk_type = @klass.type_for_attribute(primary_key)
515
496
 
516
- records_by_id = result.index_by(&:id)
517
- ids.map { |id| records_by_id.fetch(pk_type.cast(id)) }
518
- else
519
- raise_record_not_found_exception!(ids, result.size, ids.size)
497
+ records_by_id = result.index_by(&:id)
498
+ ids.map { |id| records_by_id.fetch(pk_type.cast(id)) }
499
+ else
500
+ raise_record_not_found_exception!(ids, result.size, ids.size)
501
+ end
520
502
  end
521
- end
522
503
 
523
- def find_take
524
- if loaded?
525
- records.first
526
- else
527
- @take ||= limit(1).records.first
504
+ def find_take
505
+ if loaded?
506
+ records.first
507
+ else
508
+ @take ||= limit(1).records.first
509
+ end
528
510
  end
529
- end
530
511
 
531
- def find_nth(index, offset = nil)
532
- # TODO: once the offset argument is removed we rely on offset_index
533
- # within find_nth_with_limit, rather than pass it in via
534
- # find_nth_with_limit_and_offset
535
- if offset
536
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
537
- Passing an offset argument to find_nth is deprecated,
538
- please use Relation#offset instead.
539
- MSG
540
- end
541
- if loaded?
542
- records[index]
543
- else
544
- offset ||= offset_index
545
- @offsets[offset + index] ||= find_nth_with_limit_and_offset(index, 1, offset: offset).first
512
+ def find_take_with_limit(limit)
513
+ if loaded?
514
+ records.take(limit)
515
+ else
516
+ limit(limit).to_a
517
+ end
546
518
  end
547
- end
548
-
549
- def find_nth!(index)
550
- find_nth(index) or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]")
551
- end
552
519
 
553
- def find_nth_with_limit(index, limit)
554
- # TODO: once the offset argument is removed from find_nth,
555
- # find_nth_with_limit_and_offset can be merged into this method
556
- relation = if order_values.empty? && primary_key
557
- order(arel_attribute(primary_key).asc)
558
- else
559
- self
560
- end
561
-
562
- relation = relation.offset(index) unless index.zero?
563
- relation.limit(limit).to_a
564
- end
520
+ def find_nth(index)
521
+ @offsets[offset_index + index] ||= find_nth_with_limit(index, 1).first
522
+ end
565
523
 
566
- def find_nth_from_last(index)
567
- if loaded?
568
- records[-index]
569
- else
570
- relation = if order_values.empty? && primary_key
571
- order(arel_attribute(primary_key).asc)
572
- else
573
- self
574
- end
575
-
576
- relation.to_a[-index]
577
- # TODO: can be made more performant on large result sets by
578
- # for instance, last(index)[-index] (which would require
579
- # refactoring the last(n) finder method to make test suite pass),
580
- # or by using a combination of reverse_order, limit, and offset,
581
- # e.g., reverse_order.offset(index-1).first
524
+ def find_nth_with_limit(index, limit)
525
+ if loaded?
526
+ records[index, limit] || []
527
+ else
528
+ relation = if order_values.empty? && primary_key
529
+ order(arel_attribute(primary_key).asc)
530
+ else
531
+ self
532
+ end
533
+
534
+ if limit_value.nil? || index < limit_value
535
+ relation = relation.offset(offset_index + index) unless index.zero?
536
+ relation.limit(limit).to_a
537
+ else
538
+ []
539
+ end
540
+ end
582
541
  end
583
- end
584
-
585
- private
586
542
 
587
- def find_nth_with_limit_and_offset(index, limit, offset:) # :nodoc:
588
- if loaded?
589
- records[index, limit]
590
- else
591
- index += offset
592
- find_nth_with_limit(index, limit)
543
+ def find_nth_from_last(index)
544
+ if loaded?
545
+ records[-index]
546
+ else
547
+ relation = if order_values.empty? && primary_key
548
+ order(arel_attribute(primary_key).asc)
549
+ else
550
+ self
551
+ end
552
+
553
+ relation.to_a[-index]
554
+ # TODO: can be made more performant on large result sets by
555
+ # for instance, last(index)[-index] (which would require
556
+ # refactoring the last(n) finder method to make test suite pass),
557
+ # or by using a combination of reverse_order, limit, and offset,
558
+ # e.g., reverse_order.offset(index-1).first
559
+ end
593
560
  end
594
- end
595
561
 
596
- def find_last(limit)
597
- limit ? records.last(limit) : records.last
598
- end
562
+ def find_last(limit)
563
+ limit ? records.last(limit) : records.last
564
+ end
599
565
  end
600
566
  end