activerecord 5.0.7.2 → 5.1.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +389 -2252
- 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.rb +20 -20
- data/lib/active_record/aggregations.rb +244 -244
- data/lib/active_record/association_relation.rb +5 -5
- data/lib/active_record/associations.rb +1579 -1569
- data/lib/active_record/associations/alias_tracker.rb +1 -1
- data/lib/active_record/associations/association.rb +23 -15
- data/lib/active_record/associations/association_scope.rb +83 -81
- data/lib/active_record/associations/belongs_to_association.rb +0 -1
- data/lib/active_record/associations/builder/belongs_to.rb +16 -14
- 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 +74 -241
- data/lib/active_record/associations/collection_proxy.rb +144 -70
- data/lib/active_record/associations/has_many_association.rb +15 -19
- data/lib/active_record/associations/has_many_through_association.rb +12 -5
- data/lib/active_record/associations/has_one_association.rb +22 -28
- data/lib/active_record/associations/has_one_through_association.rb +5 -1
- data/lib/active_record/associations/join_dependency.rb +117 -115
- data/lib/active_record/associations/join_dependency/join_association.rb +16 -13
- 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/preloader.rb +94 -94
- data/lib/active_record/associations/preloader/association.rb +87 -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 +34 -41
- data/lib/active_record/associations/singular_association.rb +8 -25
- data/lib/active_record/associations/through_association.rb +3 -6
- data/lib/active_record/attribute.rb +98 -71
- data/lib/active_record/attribute/user_provided_default.rb +4 -2
- data/lib/active_record/attribute_assignment.rb +61 -61
- data/lib/active_record/attribute_decorators.rb +35 -13
- data/lib/active_record/attribute_methods.rb +56 -65
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -7
- data/lib/active_record/attribute_methods/dirty.rb +216 -34
- data/lib/active_record/attribute_methods/primary_key.rb +78 -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 +36 -30
- data/lib/active_record/attribute_mutation_tracker.rb +53 -10
- data/lib/active_record/attribute_set.rb +9 -6
- data/lib/active_record/attribute_set/builder.rb +41 -49
- data/lib/active_record/attribute_set/yaml_encoder.rb +41 -0
- data/lib/active_record/attributes.rb +21 -21
- data/lib/active_record/autosave_association.rb +13 -13
- data/lib/active_record/base.rb +24 -22
- data/lib/active_record/callbacks.rb +52 -14
- data/lib/active_record/coders/yaml_column.rb +9 -11
- data/lib/active_record/collection_cache_key.rb +6 -17
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +320 -278
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +1 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +22 -34
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +31 -27
- data/lib/active_record/connection_adapters/abstract/quoting.rb +44 -57
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +9 -19
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +78 -79
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +53 -41
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +99 -93
- data/lib/active_record/connection_adapters/abstract/transaction.rb +1 -5
- data/lib/active_record/connection_adapters/abstract_adapter.rb +156 -128
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +424 -382
- data/lib/active_record/connection_adapters/column.rb +27 -5
- 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 +45 -43
- 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 +49 -31
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +5 -6
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +24 -26
- data/lib/active_record/connection_adapters/postgresql/column.rb +1 -28
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +46 -35
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid.rb +22 -21
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +9 -9
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
- 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/enum.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +16 -16
- 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 +28 -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/quoting.rb +38 -36
- 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 +161 -170
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -7
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +179 -152
- 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 -20
- 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_adapter.rb +187 -130
- 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 +110 -93
- data/lib/active_record/counter_cache.rb +62 -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 +58 -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 +3 -3
- 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 +64 -56
- data/lib/active_record/locking/pessimistic.rb +10 -1
- data/lib/active_record/log_subscriber.rb +29 -29
- data/lib/active_record/migration.rb +155 -172
- data/lib/active_record/migration/command_recorder.rb +94 -94
- data/lib/active_record/migration/compatibility.rb +76 -37
- data/lib/active_record/migration/join_table.rb +6 -6
- data/lib/active_record/model_schema.rb +85 -119
- data/lib/active_record/nested_attributes.rb +200 -199
- data/lib/active_record/null_relation.rb +10 -33
- data/lib/active_record/persistence.rb +45 -38
- data/lib/active_record/query_cache.rb +4 -8
- data/lib/active_record/querying.rb +2 -3
- 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 +125 -140
- 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 +79 -96
- data/lib/active_record/relation.rb +72 -115
- data/lib/active_record/relation/batches.rb +87 -58
- data/lib/active_record/relation/batches/batch_enumerator.rb +1 -1
- data/lib/active_record/relation/calculations.rb +154 -160
- data/lib/active_record/relation/delegation.rb +30 -29
- data/lib/active_record/relation/finder_methods.rb +195 -226
- data/lib/active_record/relation/merger.rb +58 -62
- data/lib/active_record/relation/predicate_builder.rb +92 -89
- 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/query_attribute.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +247 -295
- 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 +79 -65
- data/lib/active_record/relation/where_clause_factory.rb +47 -8
- data/lib/active_record/result.rb +29 -31
- data/lib/active_record/runtime_registry.rb +3 -3
- data/lib/active_record/sanitization.rb +182 -197
- data/lib/active_record/schema.rb +3 -3
- data/lib/active_record/schema_dumper.rb +14 -37
- data/lib/active_record/schema_migration.rb +3 -3
- data/lib/active_record/scoping.rb +9 -10
- data/lib/active_record/scoping/default.rb +87 -91
- data/lib/active_record/scoping/named.rb +16 -28
- 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 +72 -65
- data/lib/active_record/tasks/mysql_database_tasks.rb +75 -72
- data/lib/active_record/tasks/postgresql_database_tasks.rb +53 -48
- data/lib/active_record/tasks/sqlite_database_tasks.rb +18 -16
- data/lib/active_record/timestamp.rb +39 -25
- data/lib/active_record/touch_later.rb +1 -2
- data/lib/active_record/transactions.rb +98 -110
- data/lib/active_record/type.rb +17 -13
- data/lib/active_record/type/adapter_specific_registry.rb +46 -42
- data/lib/active_record/type/decimal_without_scale.rb +9 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +3 -3
- data/lib/active_record/type/serialized.rb +8 -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_caster.rb +2 -2
- data/lib/active_record/type_caster/connection.rb +8 -6
- data/lib/active_record/type_caster/map.rb +3 -1
- data/lib/active_record/validations.rb +4 -4
- 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/version.rb +1 -1
- data/lib/rails/generators/active_record.rb +4 -4
- data/lib/rails/generators/active_record/migration.rb +2 -2
- data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -34
- data/lib/rails/generators/active_record/model/model_generator.rb +9 -9
- metadata +22 -13
- data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'active_support/concern'
|
2
|
-
|
3
1
|
module ActiveRecord
|
4
2
|
module Delegation # :nodoc:
|
5
3
|
module DelegateCache # :nodoc:
|
@@ -17,7 +15,10 @@ module ActiveRecord
|
|
17
15
|
delegate = Class.new(klass) {
|
18
16
|
include ClassSpecificRelation
|
19
17
|
}
|
20
|
-
|
18
|
+
mangled_name = klass.name.gsub("::".freeze, "_".freeze)
|
19
|
+
const_set mangled_name, delegate
|
20
|
+
private_constant mangled_name
|
21
|
+
|
21
22
|
cache[klass] = delegate
|
22
23
|
end
|
23
24
|
end
|
@@ -41,7 +42,7 @@ module ActiveRecord
|
|
41
42
|
:shuffle, :split, :index, to: :records
|
42
43
|
|
43
44
|
delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
|
44
|
-
:connection, :columns_hash, :
|
45
|
+
:connection, :columns_hash, to: :klass
|
45
46
|
|
46
47
|
module ClassSpecificRelation # :nodoc:
|
47
48
|
extend ActiveSupport::Concern
|
@@ -59,7 +60,7 @@ module ActiveRecord
|
|
59
60
|
@delegation_mutex.synchronize do
|
60
61
|
return if method_defined?(method)
|
61
62
|
|
62
|
-
if
|
63
|
+
if /\A[a-zA-Z_]\w*[!?]?\z/.match?(method)
|
63
64
|
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
64
65
|
def #{method}(*args, &block)
|
65
66
|
scoping { @klass.#{method}(*args, &block) }
|
@@ -81,19 +82,19 @@ module ActiveRecord
|
|
81
82
|
end
|
82
83
|
end
|
83
84
|
|
84
|
-
|
85
|
+
private
|
85
86
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
87
|
+
def method_missing(method, *args, &block)
|
88
|
+
if @klass.respond_to?(method)
|
89
|
+
self.class.delegate_to_scoped_klass(method)
|
90
|
+
scoping { @klass.public_send(method, *args, &block) }
|
91
|
+
elsif arel.respond_to?(method)
|
92
|
+
self.class.delegate method, to: :arel
|
93
|
+
arel.public_send(method, *args, &block)
|
94
|
+
else
|
95
|
+
super
|
96
|
+
end
|
95
97
|
end
|
96
|
-
end
|
97
98
|
end
|
98
99
|
|
99
100
|
module ClassMethods # :nodoc:
|
@@ -103,26 +104,26 @@ module ActiveRecord
|
|
103
104
|
|
104
105
|
private
|
105
106
|
|
106
|
-
|
107
|
-
|
108
|
-
|
107
|
+
def relation_class_for(klass)
|
108
|
+
klass.relation_delegate_class(self)
|
109
|
+
end
|
109
110
|
end
|
110
111
|
|
111
|
-
def
|
112
|
+
def respond_to_missing?(method, include_private = false)
|
112
113
|
super || @klass.respond_to?(method, include_private) ||
|
113
114
|
arel.respond_to?(method, include_private)
|
114
115
|
end
|
115
116
|
|
116
|
-
|
117
|
+
private
|
117
118
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
119
|
+
def method_missing(method, *args, &block)
|
120
|
+
if @klass.respond_to?(method)
|
121
|
+
scoping { @klass.public_send(method, *args, &block) }
|
122
|
+
elsif arel.respond_to?(method)
|
123
|
+
arel.public_send(method, *args, &block)
|
124
|
+
else
|
125
|
+
super
|
126
|
+
end
|
125
127
|
end
|
126
|
-
end
|
127
128
|
end
|
128
129
|
end
|
@@ -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).
|
@@ -152,20 +152,12 @@ module ActiveRecord
|
|
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,8 +301,7 @@ 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
|
@@ -318,10 +309,10 @@ module ActiveRecord
|
|
318
309
|
|
319
310
|
return false if !conditions
|
320
311
|
|
321
|
-
relation = apply_join_dependency(self, construct_join_dependency)
|
312
|
+
relation = apply_join_dependency(self, construct_join_dependency(eager_loading: false))
|
322
313
|
return false if ActiveRecord::NullRelation === relation
|
323
314
|
|
324
|
-
relation = relation.except(:select, :
|
315
|
+
relation = relation.except(:select, :distinct).select(ONE_AS_ONE).limit(1)
|
325
316
|
|
326
317
|
case conditions
|
327
318
|
when Array, Hash
|
@@ -333,6 +324,8 @@ module ActiveRecord
|
|
333
324
|
end
|
334
325
|
|
335
326
|
connection.select_value(relation, "#{name} Exists", relation.bound_attributes) ? true : false
|
327
|
+
rescue ::RangeError
|
328
|
+
false
|
336
329
|
end
|
337
330
|
|
338
331
|
# This method is called whenever no records are found with either a single
|
@@ -343,258 +336,234 @@ module ActiveRecord
|
|
343
336
|
# of results obtained should be provided in the +result_size+ argument and
|
344
337
|
# the expected number of results should be provided in the +expected_size+
|
345
338
|
# argument.
|
346
|
-
def raise_record_not_found_exception!(ids, result_size, expected_size, key = primary_key)
|
339
|
+
def raise_record_not_found_exception!(ids = nil, result_size = nil, expected_size = nil, key = primary_key) # :nodoc:
|
347
340
|
conditions = arel.where_sql(@klass.arel_engine)
|
348
341
|
conditions = " [#{conditions}]" if conditions
|
349
342
|
name = @klass.name
|
350
343
|
|
351
|
-
if
|
344
|
+
if ids.nil?
|
345
|
+
error = "Couldn't find #{name}"
|
346
|
+
error << " with#{conditions}" if conditions
|
347
|
+
raise RecordNotFound.new(error, name)
|
348
|
+
elsif Array(ids).size == 1
|
352
349
|
error = "Couldn't find #{name} with '#{key}'=#{ids}#{conditions}"
|
353
350
|
raise RecordNotFound.new(error, name, key, ids)
|
354
351
|
else
|
355
352
|
error = "Couldn't find all #{name.pluralize} with '#{key}': "
|
356
353
|
error << "(#{ids.join(", ")})#{conditions} (found #{result_size} results, but was looking for #{expected_size})"
|
357
354
|
|
358
|
-
raise RecordNotFound,
|
355
|
+
raise RecordNotFound.new(error, name, primary_key, ids)
|
359
356
|
end
|
360
357
|
end
|
361
358
|
|
362
359
|
private
|
363
360
|
|
364
|
-
|
365
|
-
|
366
|
-
|
361
|
+
def offset_index
|
362
|
+
offset_value || 0
|
363
|
+
end
|
367
364
|
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
else
|
387
|
-
if ActiveRecord::NullRelation === relation
|
388
|
-
[]
|
365
|
+
def find_with_associations
|
366
|
+
# NOTE: the JoinDependency constructed here needs to know about
|
367
|
+
# any joins already present in `self`, so pass them in
|
368
|
+
#
|
369
|
+
# failing to do so means that in cases like activerecord/test/cases/associations/inner_join_association_test.rb:136
|
370
|
+
# incorrect SQL is generated. In that case, the join dependency for
|
371
|
+
# SpecialCategorizations is constructed without knowledge of the
|
372
|
+
# preexisting join in joins_values to categorizations (by way of
|
373
|
+
# the `has_many :through` for categories).
|
374
|
+
#
|
375
|
+
join_dependency = construct_join_dependency(joins_values)
|
376
|
+
|
377
|
+
aliases = join_dependency.aliases
|
378
|
+
relation = select aliases.columns
|
379
|
+
relation = apply_join_dependency(relation, join_dependency)
|
380
|
+
|
381
|
+
if block_given?
|
382
|
+
yield relation
|
389
383
|
else
|
390
|
-
|
391
|
-
|
392
|
-
|
384
|
+
if ActiveRecord::NullRelation === relation
|
385
|
+
[]
|
386
|
+
else
|
387
|
+
arel = relation.arel
|
388
|
+
rows = connection.select_all(arel, "SQL", relation.bound_attributes)
|
389
|
+
join_dependency.instantiate(rows, aliases)
|
390
|
+
end
|
393
391
|
end
|
394
392
|
end
|
395
|
-
end
|
396
393
|
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
394
|
+
def construct_join_dependency(joins = [], eager_loading: true)
|
395
|
+
including = eager_load_values + includes_values
|
396
|
+
ActiveRecord::Associations::JoinDependency.new(@klass, including, joins, eager_loading: eager_loading)
|
397
|
+
end
|
401
398
|
|
402
|
-
|
403
|
-
from = arel.froms.first
|
404
|
-
if Arel::Table === from
|
399
|
+
def construct_relation_for_association_calculations
|
405
400
|
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
401
|
end
|
412
|
-
end
|
413
402
|
|
414
|
-
|
415
|
-
|
416
|
-
|
403
|
+
def apply_join_dependency(relation, join_dependency)
|
404
|
+
relation = relation.except(:includes, :eager_load, :preload)
|
405
|
+
relation = relation.joins join_dependency
|
417
406
|
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
407
|
+
if using_limitable_reflections?(join_dependency.reflections)
|
408
|
+
relation
|
409
|
+
else
|
410
|
+
if relation.limit_value
|
411
|
+
limited_ids = limited_ids_for(relation)
|
412
|
+
limited_ids.empty? ? relation.none! : relation.where!(primary_key => limited_ids)
|
413
|
+
end
|
414
|
+
relation.except(:limit, :offset)
|
424
415
|
end
|
425
|
-
relation.except(:limit, :offset)
|
426
416
|
end
|
427
|
-
end
|
428
417
|
|
429
|
-
|
430
|
-
|
431
|
-
|
418
|
+
def limited_ids_for(relation)
|
419
|
+
values = @klass.connection.columns_for_distinct(
|
420
|
+
"#{quoted_table_name}.#{quoted_primary_key}", relation.order_values)
|
432
421
|
|
433
|
-
|
434
|
-
|
422
|
+
relation = relation.except(:select).select(values).distinct!
|
423
|
+
arel = relation.arel
|
435
424
|
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
def using_limitable_reflections?(reflections)
|
441
|
-
reflections.none?(&:collection?)
|
442
|
-
end
|
425
|
+
id_rows = @klass.connection.select_all(arel, "SQL", relation.bound_attributes)
|
426
|
+
id_rows.map { |row| row[primary_key] }
|
427
|
+
end
|
443
428
|
|
444
|
-
|
429
|
+
def using_limitable_reflections?(reflections)
|
430
|
+
reflections.none?(&:collection?)
|
431
|
+
end
|
445
432
|
|
446
|
-
|
447
|
-
raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
|
433
|
+
private
|
448
434
|
|
449
|
-
|
450
|
-
|
435
|
+
def find_with_ids(*ids)
|
436
|
+
raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
|
451
437
|
|
452
|
-
|
438
|
+
expects_array = ids.first.kind_of?(Array)
|
439
|
+
return ids.first if expects_array && ids.first.empty?
|
453
440
|
|
454
|
-
|
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)
|
462
|
-
end
|
463
|
-
rescue RangeError
|
464
|
-
raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range ID"
|
465
|
-
end
|
441
|
+
ids = ids.flatten.compact.uniq
|
466
442
|
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
443
|
+
case ids.size
|
444
|
+
when 0
|
445
|
+
raise RecordNotFound, "Couldn't find #{@klass.name} without an ID"
|
446
|
+
when 1
|
447
|
+
result = find_one(ids.first)
|
448
|
+
expects_array ? [ result ] : result
|
449
|
+
else
|
450
|
+
find_some(ids)
|
451
|
+
end
|
452
|
+
rescue ::RangeError
|
453
|
+
raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range ID"
|
454
|
+
end
|
475
455
|
|
476
|
-
|
477
|
-
|
456
|
+
def find_one(id)
|
457
|
+
if ActiveRecord::Base === id
|
458
|
+
raise ArgumentError, <<-MSG.squish
|
459
|
+
You are passing an instance of ActiveRecord::Base to `find`.
|
460
|
+
Please pass the id of the object by calling `.id`.
|
461
|
+
MSG
|
462
|
+
end
|
478
463
|
|
479
|
-
|
464
|
+
relation = where(primary_key => id)
|
465
|
+
record = relation.take
|
480
466
|
|
481
|
-
|
482
|
-
end
|
467
|
+
raise_record_not_found_exception!(id, 0, 1) unless record
|
483
468
|
|
484
|
-
|
485
|
-
|
469
|
+
record
|
470
|
+
end
|
486
471
|
|
487
|
-
|
472
|
+
def find_some(ids)
|
473
|
+
return find_some_ordered(ids) unless order_values.present?
|
488
474
|
|
489
|
-
|
490
|
-
if limit_value && ids.size > limit_value
|
491
|
-
limit_value
|
492
|
-
else
|
493
|
-
ids.size
|
494
|
-
end
|
475
|
+
result = where(primary_key => ids).to_a
|
495
476
|
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
477
|
+
expected_size =
|
478
|
+
if limit_value && ids.size > limit_value
|
479
|
+
limit_value
|
480
|
+
else
|
481
|
+
ids.size
|
482
|
+
end
|
500
483
|
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
end
|
506
|
-
end
|
484
|
+
# 11 ids with limit 3, offset 9 should give 2 results.
|
485
|
+
if offset_value && (ids.size - offset_value < expected_size)
|
486
|
+
expected_size = ids.size - offset_value
|
487
|
+
end
|
507
488
|
|
508
|
-
|
509
|
-
|
489
|
+
if result.size == expected_size
|
490
|
+
result
|
491
|
+
else
|
492
|
+
raise_record_not_found_exception!(ids, result.size, expected_size)
|
493
|
+
end
|
494
|
+
end
|
510
495
|
|
511
|
-
|
496
|
+
def find_some_ordered(ids)
|
497
|
+
ids = ids.slice(offset_value || 0, limit_value || ids.size) || []
|
512
498
|
|
513
|
-
|
514
|
-
pk_type = @klass.type_for_attribute(primary_key)
|
499
|
+
result = except(:limit, :offset).where(primary_key => ids).records
|
515
500
|
|
516
|
-
|
517
|
-
|
518
|
-
else
|
519
|
-
raise_record_not_found_exception!(ids, result.size, ids.size)
|
520
|
-
end
|
521
|
-
end
|
501
|
+
if result.size == ids.size
|
502
|
+
pk_type = @klass.type_for_attribute(primary_key)
|
522
503
|
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
end
|
504
|
+
records_by_id = result.index_by(&:id)
|
505
|
+
ids.map { |id| records_by_id.fetch(pk_type.cast(id)) }
|
506
|
+
else
|
507
|
+
raise_record_not_found_exception!(ids, result.size, ids.size)
|
508
|
+
end
|
509
|
+
end
|
530
510
|
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
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
|
546
|
-
end
|
547
|
-
end
|
511
|
+
def find_take
|
512
|
+
if loaded?
|
513
|
+
records.first
|
514
|
+
else
|
515
|
+
@take ||= limit(1).records.first
|
516
|
+
end
|
517
|
+
end
|
548
518
|
|
549
|
-
|
550
|
-
|
551
|
-
|
519
|
+
def find_take_with_limit(limit)
|
520
|
+
if loaded?
|
521
|
+
records.take(limit)
|
522
|
+
else
|
523
|
+
limit(limit).to_a
|
524
|
+
end
|
525
|
+
end
|
552
526
|
|
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
|
527
|
+
def find_nth(index)
|
528
|
+
@offsets[offset_index + index] ||= find_nth_with_limit(index, 1).first
|
529
|
+
end
|
565
530
|
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
# or by using a combination of reverse_order, limit, and offset,
|
581
|
-
# e.g., reverse_order.offset(index-1).first
|
582
|
-
end
|
583
|
-
end
|
584
|
-
|
585
|
-
private
|
531
|
+
def find_nth_with_limit(index, limit)
|
532
|
+
if loaded?
|
533
|
+
records[index, limit] || []
|
534
|
+
else
|
535
|
+
relation = if order_values.empty? && primary_key
|
536
|
+
order(arel_attribute(primary_key).asc)
|
537
|
+
else
|
538
|
+
self
|
539
|
+
end
|
540
|
+
|
541
|
+
relation = relation.offset(offset_index + index) unless index.zero?
|
542
|
+
relation.limit(limit).to_a
|
543
|
+
end
|
544
|
+
end
|
586
545
|
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
546
|
+
def find_nth_from_last(index)
|
547
|
+
if loaded?
|
548
|
+
records[-index]
|
549
|
+
else
|
550
|
+
relation = if order_values.empty? && primary_key
|
551
|
+
order(arel_attribute(primary_key).asc)
|
552
|
+
else
|
553
|
+
self
|
554
|
+
end
|
555
|
+
|
556
|
+
relation.to_a[-index]
|
557
|
+
# TODO: can be made more performant on large result sets by
|
558
|
+
# for instance, last(index)[-index] (which would require
|
559
|
+
# refactoring the last(n) finder method to make test suite pass),
|
560
|
+
# or by using a combination of reverse_order, limit, and offset,
|
561
|
+
# e.g., reverse_order.offset(index-1).first
|
562
|
+
end
|
563
|
+
end
|
595
564
|
|
596
|
-
|
597
|
-
|
598
|
-
|
565
|
+
def find_last(limit)
|
566
|
+
limit ? records.last(limit) : records.last
|
567
|
+
end
|
599
568
|
end
|
600
569
|
end
|