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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +657 -2080
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/examples/performance.rb +28 -28
- data/examples/simple.rb +3 -3
- data/lib/active_record/aggregations.rb +244 -244
- data/lib/active_record/association_relation.rb +5 -5
- data/lib/active_record/associations/alias_tracker.rb +10 -11
- data/lib/active_record/associations/association.rb +23 -5
- data/lib/active_record/associations/association_scope.rb +95 -81
- data/lib/active_record/associations/belongs_to_association.rb +7 -4
- data/lib/active_record/associations/builder/belongs_to.rb +30 -16
- data/lib/active_record/associations/builder/collection_association.rb +1 -2
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +27 -27
- data/lib/active_record/associations/collection_association.rb +36 -205
- data/lib/active_record/associations/collection_proxy.rb +132 -63
- data/lib/active_record/associations/has_many_association.rb +10 -19
- data/lib/active_record/associations/has_many_through_association.rb +12 -4
- data/lib/active_record/associations/has_one_association.rb +24 -28
- data/lib/active_record/associations/has_one_through_association.rb +5 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +4 -28
- data/lib/active_record/associations/join_dependency/join_base.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +121 -118
- data/lib/active_record/associations/preloader/association.rb +64 -64
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -2
- data/lib/active_record/associations/preloader/collection_association.rb +6 -6
- data/lib/active_record/associations/preloader/has_many.rb +0 -2
- data/lib/active_record/associations/preloader/singular_association.rb +6 -8
- data/lib/active_record/associations/preloader/through_association.rb +41 -41
- data/lib/active_record/associations/preloader.rb +94 -94
- data/lib/active_record/associations/singular_association.rb +8 -25
- data/lib/active_record/associations/through_association.rb +2 -5
- data/lib/active_record/associations.rb +1591 -1562
- data/lib/active_record/attribute/user_provided_default.rb +4 -2
- data/lib/active_record/attribute.rb +98 -71
- data/lib/active_record/attribute_assignment.rb +61 -61
- data/lib/active_record/attribute_decorators.rb +35 -13
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -7
- data/lib/active_record/attribute_methods/dirty.rb +229 -46
- data/lib/active_record/attribute_methods/primary_key.rb +74 -73
- data/lib/active_record/attribute_methods/read.rb +39 -35
- data/lib/active_record/attribute_methods/serialization.rb +7 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +35 -58
- data/lib/active_record/attribute_methods/write.rb +30 -33
- data/lib/active_record/attribute_methods.rb +56 -65
- data/lib/active_record/attribute_mutation_tracker.rb +63 -11
- data/lib/active_record/attribute_set/builder.rb +27 -33
- data/lib/active_record/attribute_set/yaml_encoder.rb +41 -0
- data/lib/active_record/attribute_set.rb +9 -6
- data/lib/active_record/attributes.rb +22 -22
- data/lib/active_record/autosave_association.rb +18 -13
- data/lib/active_record/base.rb +24 -22
- data/lib/active_record/callbacks.rb +56 -14
- data/lib/active_record/coders/yaml_column.rb +9 -11
- data/lib/active_record/collection_cache_key.rb +3 -4
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +330 -284
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +1 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +39 -37
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +32 -27
- data/lib/active_record/connection_adapters/abstract/quoting.rb +62 -51
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +10 -20
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +74 -79
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +53 -41
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +120 -100
- data/lib/active_record/connection_adapters/abstract/transaction.rb +49 -43
- data/lib/active_record/connection_adapters/abstract_adapter.rb +165 -135
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +404 -424
- data/lib/active_record/connection_adapters/column.rb +26 -4
- data/lib/active_record/connection_adapters/connection_specification.rb +128 -118
- data/lib/active_record/connection_adapters/mysql/column.rb +6 -31
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +36 -49
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +22 -22
- data/lib/active_record/connection_adapters/mysql/quoting.rb +6 -12
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +49 -45
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +16 -19
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -28
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +43 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +7 -6
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +23 -27
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +32 -53
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +16 -16
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +0 -10
- data/lib/active_record/connection_adapters/postgresql/oid/{rails_5_1_point.rb → legacy_point.rb} +9 -16
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +28 -8
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +32 -30
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +51 -51
- data/lib/active_record/connection_adapters/postgresql/oid.rb +22 -21
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +40 -35
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +37 -24
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +19 -23
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +182 -222
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +6 -4
- data/lib/active_record/connection_adapters/postgresql/utils.rb +7 -5
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +198 -167
- data/lib/active_record/connection_adapters/schema_cache.rb +16 -7
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +3 -3
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +16 -19
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +1 -8
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +28 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +17 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +32 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +184 -167
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -7
- data/lib/active_record/connection_handling.rb +14 -26
- data/lib/active_record/core.rb +109 -93
- data/lib/active_record/counter_cache.rb +60 -13
- data/lib/active_record/define_callbacks.rb +20 -0
- data/lib/active_record/dynamic_matchers.rb +80 -79
- data/lib/active_record/enum.rb +8 -6
- data/lib/active_record/errors.rb +64 -15
- data/lib/active_record/explain.rb +1 -2
- data/lib/active_record/explain_registry.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +7 -4
- data/lib/active_record/fixture_set/file.rb +11 -8
- data/lib/active_record/fixtures.rb +66 -53
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/inheritance.rb +93 -79
- data/lib/active_record/integration.rb +7 -7
- data/lib/active_record/internal_metadata.rb +3 -16
- data/lib/active_record/legacy_yaml_adapter.rb +1 -1
- data/lib/active_record/locking/optimistic.rb +69 -74
- data/lib/active_record/locking/pessimistic.rb +10 -1
- data/lib/active_record/log_subscriber.rb +23 -28
- data/lib/active_record/migration/command_recorder.rb +94 -94
- data/lib/active_record/migration/compatibility.rb +100 -47
- data/lib/active_record/migration/join_table.rb +6 -6
- data/lib/active_record/migration.rb +153 -155
- data/lib/active_record/model_schema.rb +94 -107
- data/lib/active_record/nested_attributes.rb +200 -199
- data/lib/active_record/null_relation.rb +11 -34
- data/lib/active_record/persistence.rb +65 -50
- data/lib/active_record/query_cache.rb +2 -6
- data/lib/active_record/querying.rb +3 -4
- data/lib/active_record/railtie.rb +16 -17
- data/lib/active_record/railties/controller_runtime.rb +6 -2
- data/lib/active_record/railties/databases.rake +105 -133
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +2 -2
- data/lib/active_record/reflection.rb +154 -108
- data/lib/active_record/relation/batches/batch_enumerator.rb +1 -1
- data/lib/active_record/relation/batches.rb +80 -51
- data/lib/active_record/relation/calculations.rb +169 -162
- data/lib/active_record/relation/delegation.rb +32 -31
- data/lib/active_record/relation/finder_methods.rb +197 -231
- data/lib/active_record/relation/merger.rb +58 -62
- data/lib/active_record/relation/predicate_builder/array_handler.rb +7 -5
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +23 -23
- data/lib/active_record/relation/predicate_builder/base_handler.rb +3 -1
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +0 -8
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +12 -10
- data/lib/active_record/relation/predicate_builder/range_handler.rb +0 -8
- data/lib/active_record/relation/predicate_builder.rb +92 -89
- data/lib/active_record/relation/query_attribute.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +255 -293
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +4 -5
- data/lib/active_record/relation/where_clause.rb +80 -65
- data/lib/active_record/relation/where_clause_factory.rb +47 -8
- data/lib/active_record/relation.rb +93 -119
- data/lib/active_record/result.rb +41 -32
- data/lib/active_record/runtime_registry.rb +3 -3
- data/lib/active_record/sanitization.rb +176 -192
- data/lib/active_record/schema.rb +3 -3
- data/lib/active_record/schema_dumper.rb +15 -38
- data/lib/active_record/schema_migration.rb +8 -4
- data/lib/active_record/scoping/default.rb +90 -90
- data/lib/active_record/scoping/named.rb +11 -11
- data/lib/active_record/scoping.rb +6 -6
- data/lib/active_record/secure_token.rb +2 -2
- data/lib/active_record/statement_cache.rb +13 -15
- data/lib/active_record/store.rb +31 -32
- data/lib/active_record/suppressor.rb +2 -1
- data/lib/active_record/table_metadata.rb +9 -5
- data/lib/active_record/tasks/database_tasks.rb +65 -55
- data/lib/active_record/tasks/mysql_database_tasks.rb +76 -73
- data/lib/active_record/tasks/postgresql_database_tasks.rb +72 -47
- data/lib/active_record/tasks/sqlite_database_tasks.rb +18 -16
- data/lib/active_record/timestamp.rb +46 -25
- data/lib/active_record/touch_later.rb +1 -2
- data/lib/active_record/transactions.rb +97 -109
- data/lib/active_record/type/adapter_specific_registry.rb +46 -42
- data/lib/active_record/type/decimal_without_scale.rb +13 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +3 -3
- data/lib/active_record/type/internal/abstract_json.rb +4 -0
- data/lib/active_record/type/serialized.rb +14 -8
- data/lib/active_record/type/text.rb +9 -0
- data/lib/active_record/type/time.rb +0 -1
- data/lib/active_record/type/type_map.rb +11 -15
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type.rb +17 -13
- data/lib/active_record/type_caster/connection.rb +8 -6
- data/lib/active_record/type_caster/map.rb +3 -1
- data/lib/active_record/type_caster.rb +2 -2
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record/validations/presence.rb +2 -2
- data/lib/active_record/validations/uniqueness.rb +8 -39
- data/lib/active_record/validations.rb +4 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +20 -20
- data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -34
- data/lib/rails/generators/active_record/migration.rb +1 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +9 -9
- data/lib/rails/generators/active_record.rb +4 -4
- metadata +24 -13
- data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
@@ -1,8 +1,8 @@
|
|
1
|
-
require
|
1
|
+
require "active_support/core_ext/string/filters"
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module FinderMethods
|
5
|
-
ONE_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 ?
|
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
|
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
|
-
|
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
|
-
|
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
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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 =
|
322
|
-
|
312
|
+
relation = self unless eager_loading?
|
313
|
+
relation ||= apply_join_dependency(self, construct_join_dependency(eager_loading: false))
|
323
314
|
|
324
|
-
|
315
|
+
return false if ActiveRecord::NullRelation === relation
|
325
316
|
|
326
|
-
|
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)
|
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
|
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,
|
348
|
+
raise RecordNotFound.new(error, name, primary_key, ids)
|
359
349
|
end
|
360
350
|
end
|
361
351
|
|
362
352
|
private
|
363
353
|
|
364
|
-
|
365
|
-
|
366
|
-
|
354
|
+
def offset_index
|
355
|
+
offset_value || 0
|
356
|
+
end
|
367
357
|
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
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
|
-
|
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
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
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
|
-
|
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
|
-
|
415
|
-
|
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
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
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
|
-
|
430
|
-
|
431
|
-
|
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
|
-
|
434
|
-
|
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
|
-
|
441
|
-
|
442
|
-
|
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
|
-
|
424
|
+
def using_limitable_reflections?(reflections)
|
425
|
+
reflections.none?(&:collection?)
|
426
|
+
end
|
445
427
|
|
446
|
-
|
447
|
-
|
428
|
+
def find_with_ids(*ids)
|
429
|
+
raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
|
448
430
|
|
449
|
-
|
450
|
-
|
431
|
+
expects_array = ids.first.kind_of?(Array)
|
432
|
+
return [] if expects_array && ids.first.empty?
|
451
433
|
|
452
|
-
|
434
|
+
ids = ids.flatten.compact.uniq
|
453
435
|
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
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
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
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
|
-
|
477
|
-
|
457
|
+
relation = where(primary_key => id)
|
458
|
+
record = relation.take
|
478
459
|
|
479
|
-
|
460
|
+
raise_record_not_found_exception!(id, 0, 1) unless record
|
480
461
|
|
481
|
-
|
482
|
-
|
462
|
+
record
|
463
|
+
end
|
483
464
|
|
484
|
-
|
485
|
-
|
465
|
+
def find_some(ids)
|
466
|
+
return find_some_ordered(ids) unless order_values.present?
|
486
467
|
|
487
|
-
|
468
|
+
result = where(primary_key => ids).to_a
|
488
469
|
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
470
|
+
expected_size =
|
471
|
+
if limit_value && ids.size > limit_value
|
472
|
+
limit_value
|
473
|
+
else
|
474
|
+
ids.size
|
475
|
+
end
|
495
476
|
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
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
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
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
|
-
|
509
|
-
|
489
|
+
def find_some_ordered(ids)
|
490
|
+
ids = ids.slice(offset_value || 0, limit_value || ids.size) || []
|
510
491
|
|
511
|
-
|
492
|
+
result = except(:limit, :offset).where(primary_key => ids).records
|
512
493
|
|
513
|
-
|
514
|
-
|
494
|
+
if result.size == ids.size
|
495
|
+
pk_type = @klass.type_for_attribute(primary_key)
|
515
496
|
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
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
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
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
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
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
|
-
|
554
|
-
|
555
|
-
|
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
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
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
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
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
|
-
|
597
|
-
|
598
|
-
|
562
|
+
def find_last(limit)
|
563
|
+
limit ? records.last(limit) : records.last
|
564
|
+
end
|
599
565
|
end
|
600
566
|
end
|