activerecord 4.2.11.3 → 5.0.7.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1638 -1132
- data/MIT-LICENSE +2 -2
- data/README.rdoc +7 -8
- data/examples/performance.rb +2 -3
- data/examples/simple.rb +0 -1
- data/lib/active_record.rb +7 -2
- data/lib/active_record/aggregations.rb +34 -21
- data/lib/active_record/association_relation.rb +7 -4
- data/lib/active_record/associations.rb +347 -218
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +22 -10
- data/lib/active_record/associations/association_scope.rb +75 -104
- data/lib/active_record/associations/belongs_to_association.rb +21 -32
- data/lib/active_record/associations/builder/association.rb +28 -34
- data/lib/active_record/associations/builder/belongs_to.rb +43 -18
- data/lib/active_record/associations/builder/collection_association.rb +7 -19
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +16 -11
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +11 -6
- data/lib/active_record/associations/builder/singular_association.rb +13 -11
- data/lib/active_record/associations/collection_association.rb +85 -69
- data/lib/active_record/associations/collection_proxy.rb +104 -46
- data/lib/active_record/associations/foreign_association.rb +1 -1
- data/lib/active_record/associations/has_many_association.rb +21 -78
- data/lib/active_record/associations/has_many_through_association.rb +6 -47
- data/lib/active_record/associations/has_one_association.rb +12 -5
- data/lib/active_record/associations/join_dependency.rb +38 -22
- data/lib/active_record/associations/join_dependency/join_association.rb +15 -14
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
- data/lib/active_record/associations/preloader.rb +14 -4
- data/lib/active_record/associations/preloader/association.rb +52 -71
- data/lib/active_record/associations/preloader/collection_association.rb +0 -7
- data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
- data/lib/active_record/associations/preloader/has_one.rb +0 -8
- data/lib/active_record/associations/preloader/singular_association.rb +0 -1
- data/lib/active_record/associations/preloader/through_association.rb +36 -17
- data/lib/active_record/associations/singular_association.rb +13 -1
- data/lib/active_record/associations/through_association.rb +12 -4
- data/lib/active_record/attribute.rb +69 -19
- data/lib/active_record/attribute/user_provided_default.rb +28 -0
- data/lib/active_record/attribute_assignment.rb +19 -140
- data/lib/active_record/attribute_decorators.rb +6 -5
- data/lib/active_record/attribute_methods.rb +69 -44
- data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
- data/lib/active_record/attribute_methods/dirty.rb +46 -86
- data/lib/active_record/attribute_methods/primary_key.rb +16 -3
- data/lib/active_record/attribute_methods/query.rb +2 -2
- data/lib/active_record/attribute_methods/read.rb +31 -59
- data/lib/active_record/attribute_methods/serialization.rb +13 -16
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -14
- data/lib/active_record/attribute_methods/write.rb +13 -37
- data/lib/active_record/attribute_mutation_tracker.rb +70 -0
- data/lib/active_record/attribute_set.rb +32 -3
- data/lib/active_record/attribute_set/builder.rb +42 -16
- data/lib/active_record/attributes.rb +199 -81
- data/lib/active_record/autosave_association.rb +54 -17
- data/lib/active_record/base.rb +32 -23
- data/lib/active_record/callbacks.rb +39 -43
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +20 -8
- data/lib/active_record/collection_cache_key.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +467 -189
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +66 -62
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +39 -4
- data/lib/active_record/connection_adapters/abstract/quoting.rb +86 -13
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -188
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +407 -156
- data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
- data/lib/active_record/connection_adapters/abstract_adapter.rb +177 -71
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +433 -399
- data/lib/active_record/connection_adapters/column.rb +28 -43
- data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +108 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +25 -166
- data/lib/active_record/connection_adapters/postgresql/column.rb +33 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +18 -72
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +37 -57
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +13 -3
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +56 -19
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +250 -154
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +264 -170
- data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +151 -194
- data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
- data/lib/active_record/connection_handling.rb +37 -14
- data/lib/active_record/core.rb +92 -108
- data/lib/active_record/counter_cache.rb +13 -24
- data/lib/active_record/dynamic_matchers.rb +1 -20
- data/lib/active_record/enum.rb +116 -76
- data/lib/active_record/errors.rb +87 -48
- data/lib/active_record/explain.rb +20 -9
- data/lib/active_record/explain_registry.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +26 -5
- data/lib/active_record/fixtures.rb +77 -41
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +32 -40
- data/lib/active_record/integration.rb +17 -14
- data/lib/active_record/internal_metadata.rb +56 -0
- data/lib/active_record/legacy_yaml_adapter.rb +18 -2
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +15 -15
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +48 -24
- data/lib/active_record/migration.rb +362 -111
- data/lib/active_record/migration/command_recorder.rb +59 -18
- data/lib/active_record/migration/compatibility.rb +126 -0
- data/lib/active_record/model_schema.rb +270 -73
- data/lib/active_record/nested_attributes.rb +58 -29
- data/lib/active_record/no_touching.rb +4 -0
- data/lib/active_record/null_relation.rb +16 -8
- data/lib/active_record/persistence.rb +152 -90
- data/lib/active_record/query_cache.rb +18 -23
- data/lib/active_record/querying.rb +12 -11
- data/lib/active_record/railtie.rb +23 -16
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +52 -41
- data/lib/active_record/readonly_attributes.rb +1 -1
- data/lib/active_record/reflection.rb +302 -115
- data/lib/active_record/relation.rb +187 -120
- data/lib/active_record/relation/batches.rb +141 -36
- data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
- data/lib/active_record/relation/calculations.rb +92 -117
- data/lib/active_record/relation/delegation.rb +8 -20
- data/lib/active_record/relation/finder_methods.rb +173 -89
- data/lib/active_record/relation/from_clause.rb +32 -0
- data/lib/active_record/relation/merger.rb +16 -42
- data/lib/active_record/relation/predicate_builder.rb +120 -107
- data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/query_attribute.rb +19 -0
- data/lib/active_record/relation/query_methods.rb +308 -244
- data/lib/active_record/relation/record_fetch_warning.rb +49 -0
- data/lib/active_record/relation/spawn_methods.rb +4 -7
- data/lib/active_record/relation/where_clause.rb +174 -0
- data/lib/active_record/relation/where_clause_factory.rb +38 -0
- data/lib/active_record/result.rb +11 -4
- data/lib/active_record/runtime_registry.rb +1 -1
- data/lib/active_record/sanitization.rb +105 -66
- data/lib/active_record/schema.rb +26 -22
- data/lib/active_record/schema_dumper.rb +54 -37
- data/lib/active_record/schema_migration.rb +11 -14
- data/lib/active_record/scoping.rb +34 -16
- data/lib/active_record/scoping/default.rb +28 -10
- data/lib/active_record/scoping/named.rb +59 -26
- data/lib/active_record/secure_token.rb +38 -0
- data/lib/active_record/serialization.rb +3 -5
- data/lib/active_record/statement_cache.rb +17 -15
- data/lib/active_record/store.rb +8 -3
- data/lib/active_record/suppressor.rb +58 -0
- data/lib/active_record/table_metadata.rb +69 -0
- data/lib/active_record/tasks/database_tasks.rb +66 -49
- data/lib/active_record/tasks/mysql_database_tasks.rb +6 -14
- data/lib/active_record/tasks/postgresql_database_tasks.rb +12 -3
- data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
- data/lib/active_record/timestamp.rb +20 -9
- data/lib/active_record/touch_later.rb +63 -0
- data/lib/active_record/transactions.rb +139 -57
- data/lib/active_record/type.rb +66 -17
- data/lib/active_record/type/adapter_specific_registry.rb +130 -0
- data/lib/active_record/type/date.rb +2 -45
- data/lib/active_record/type/date_time.rb +2 -49
- data/lib/active_record/type/internal/abstract_json.rb +33 -0
- data/lib/active_record/type/internal/timezone.rb +15 -0
- data/lib/active_record/type/serialized.rb +15 -14
- data/lib/active_record/type/time.rb +10 -16
- data/lib/active_record/type/type_map.rb +4 -4
- data/lib/active_record/type_caster.rb +7 -0
- data/lib/active_record/type_caster/connection.rb +29 -0
- data/lib/active_record/type_caster/map.rb +19 -0
- data/lib/active_record/validations.rb +33 -32
- data/lib/active_record/validations/absence.rb +23 -0
- data/lib/active_record/validations/associated.rb +10 -3
- data/lib/active_record/validations/length.rb +24 -0
- data/lib/active_record/validations/presence.rb +11 -12
- data/lib/active_record/validations/uniqueness.rb +33 -33
- data/lib/rails/generators/active_record/migration.rb +15 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
- data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +33 -16
- data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
- metadata +58 -34
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/serializers/xml_serializer.rb +0 -193
- data/lib/active_record/type/big_integer.rb +0 -13
- data/lib/active_record/type/binary.rb +0 -50
- data/lib/active_record/type/boolean.rb +0 -31
- data/lib/active_record/type/decimal.rb +0 -64
- data/lib/active_record/type/decimal_without_scale.rb +0 -11
- data/lib/active_record/type/decorator.rb +0 -14
- data/lib/active_record/type/float.rb +0 -19
- data/lib/active_record/type/integer.rb +0 -59
- data/lib/active_record/type/mutable.rb +0 -16
- data/lib/active_record/type/numeric.rb +0 -36
- data/lib/active_record/type/string.rb +0 -40
- data/lib/active_record/type/text.rb +0 -11
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/unsigned_integer.rb +0 -15
- data/lib/active_record/type/value.rb +0 -110
@@ -1,15 +1,13 @@
|
|
1
|
-
require 'set'
|
2
1
|
require 'active_support/concern'
|
3
|
-
require 'active_support/deprecation'
|
4
2
|
|
5
3
|
module ActiveRecord
|
6
4
|
module Delegation # :nodoc:
|
7
|
-
module DelegateCache
|
8
|
-
def relation_delegate_class(klass)
|
5
|
+
module DelegateCache # :nodoc:
|
6
|
+
def relation_delegate_class(klass)
|
9
7
|
@relation_delegate_cache[klass]
|
10
8
|
end
|
11
9
|
|
12
|
-
def initialize_relation_delegate_cache
|
10
|
+
def initialize_relation_delegate_cache
|
13
11
|
@relation_delegate_cache = cache = {}
|
14
12
|
[
|
15
13
|
ActiveRecord::Relation,
|
@@ -19,7 +17,7 @@ module ActiveRecord
|
|
19
17
|
delegate = Class.new(klass) {
|
20
18
|
include ClassSpecificRelation
|
21
19
|
}
|
22
|
-
const_set klass.name.gsub('::', '_'), delegate
|
20
|
+
const_set klass.name.gsub('::'.freeze, '_'.freeze), delegate
|
23
21
|
cache[klass] = delegate
|
24
22
|
end
|
25
23
|
end
|
@@ -37,13 +35,10 @@ module ActiveRecord
|
|
37
35
|
# may vary depending on the klass of a relation, so we create a subclass of Relation
|
38
36
|
# for each different klass, and the delegations are compiled into that subclass only.
|
39
37
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
].to_set # :nodoc:
|
45
|
-
|
46
|
-
delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :join, to: :to_a
|
38
|
+
delegate :to_xml, :encode_with, :length, :collect, :map, :each, :all?, :include?, :to_ary, :join,
|
39
|
+
:[], :&, :|, :+, :-, :sample, :reverse, :compact, :in_groups, :in_groups_of,
|
40
|
+
:to_sentence, :to_formatted_s,
|
41
|
+
:shuffle, :split, :index, to: :records
|
47
42
|
|
48
43
|
delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
|
49
44
|
:connection, :columns_hash, :to => :klass
|
@@ -115,21 +110,14 @@ module ActiveRecord
|
|
115
110
|
|
116
111
|
def respond_to?(method, include_private = false)
|
117
112
|
super || @klass.respond_to?(method, include_private) ||
|
118
|
-
array_delegable?(method) ||
|
119
113
|
arel.respond_to?(method, include_private)
|
120
114
|
end
|
121
115
|
|
122
116
|
protected
|
123
117
|
|
124
|
-
def array_delegable?(method)
|
125
|
-
Array.method_defined?(method) && BLACKLISTED_ARRAY_METHODS.exclude?(method)
|
126
|
-
end
|
127
|
-
|
128
118
|
def method_missing(method, *args, &block)
|
129
119
|
if @klass.respond_to?(method)
|
130
120
|
scoping { @klass.public_send(method, *args, &block) }
|
131
|
-
elsif array_delegable?(method)
|
132
|
-
to_a.public_send(method, *args, &block)
|
133
121
|
elsif arel.respond_to?(method)
|
134
122
|
arel.public_send(method, *args, &block)
|
135
123
|
else
|
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'active_support/deprecation'
|
2
1
|
require 'active_support/core_ext/string/filters'
|
3
2
|
|
4
3
|
module ActiveRecord
|
@@ -6,7 +5,7 @@ module ActiveRecord
|
|
6
5
|
ONE_AS_ONE = '1 AS one'
|
7
6
|
|
8
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]).
|
9
|
-
# If
|
8
|
+
# If one or more records can not be found for the requested ids, then RecordNotFound will be raised. If the primary key
|
10
9
|
# is an integer, find by id coerces its arguments using +to_i+.
|
11
10
|
#
|
12
11
|
# Person.find(1) # returns the object for ID = 1
|
@@ -17,11 +16,9 @@ module ActiveRecord
|
|
17
16
|
# Person.find([1]) # returns an array for the object with ID = 1
|
18
17
|
# Person.where("administrator = 1").order("created_on DESC").find(1)
|
19
18
|
#
|
20
|
-
# <tt>ActiveRecord::RecordNotFound</tt> will be raised if one or more ids are not found.
|
21
|
-
#
|
22
19
|
# NOTE: The returned records may not be in the same order as the ids you
|
23
|
-
# provide since database rows are unordered. You
|
24
|
-
# option if you want the results
|
20
|
+
# provide since database rows are unordered. You will need to provide an explicit QueryMethods#order
|
21
|
+
# option if you want the results to be sorted.
|
25
22
|
#
|
26
23
|
# ==== Find with lock
|
27
24
|
#
|
@@ -37,7 +34,7 @@ module ActiveRecord
|
|
37
34
|
# person.save!
|
38
35
|
# end
|
39
36
|
#
|
40
|
-
# ==== Variations of
|
37
|
+
# ==== Variations of #find
|
41
38
|
#
|
42
39
|
# Person.where(name: 'Spartacus', rating: 4)
|
43
40
|
# # returns a chainable list (which can be empty).
|
@@ -45,13 +42,13 @@ module ActiveRecord
|
|
45
42
|
# Person.find_by(name: 'Spartacus', rating: 4)
|
46
43
|
# # returns the first item or nil.
|
47
44
|
#
|
48
|
-
# Person.
|
45
|
+
# Person.find_or_initialize_by(name: 'Spartacus', rating: 4)
|
49
46
|
# # returns the first item or returns a new instance (requires you call .save to persist against the database).
|
50
47
|
#
|
51
|
-
# Person.
|
52
|
-
# # returns the first item or creates it and returns it
|
48
|
+
# Person.find_or_create_by(name: 'Spartacus', rating: 4)
|
49
|
+
# # returns the first item or creates it and returns it.
|
53
50
|
#
|
54
|
-
# ==== Alternatives for
|
51
|
+
# ==== Alternatives for #find
|
55
52
|
#
|
56
53
|
# Person.where(name: 'Spartacus', rating: 4).exists?(conditions = :none)
|
57
54
|
# # returns a boolean indicating if any record with the given conditions exist.
|
@@ -60,16 +57,13 @@ module ActiveRecord
|
|
60
57
|
# # returns a chainable list of instances with only the mentioned fields.
|
61
58
|
#
|
62
59
|
# Person.where(name: 'Spartacus', rating: 4).ids
|
63
|
-
# # returns an Array of ids
|
60
|
+
# # returns an Array of ids.
|
64
61
|
#
|
65
62
|
# Person.where(name: 'Spartacus', rating: 4).pluck(:field1, :field2)
|
66
|
-
# # returns an Array of the required fields
|
63
|
+
# # returns an Array of the required fields.
|
67
64
|
def find(*args)
|
68
|
-
if block_given?
|
69
|
-
|
70
|
-
else
|
71
|
-
find_with_ids(*args)
|
72
|
-
end
|
65
|
+
return super if block_given?
|
66
|
+
find_with_ids(*args)
|
73
67
|
end
|
74
68
|
|
75
69
|
# Finds the first record matching the specified conditions. There
|
@@ -80,18 +74,19 @@ module ActiveRecord
|
|
80
74
|
#
|
81
75
|
# Post.find_by name: 'Spartacus', rating: 4
|
82
76
|
# Post.find_by "published_at < ?", 2.weeks.ago
|
83
|
-
def find_by(*args)
|
84
|
-
where(*args).take
|
77
|
+
def find_by(arg, *args)
|
78
|
+
where(arg, *args).take
|
85
79
|
rescue RangeError
|
86
80
|
nil
|
87
81
|
end
|
88
82
|
|
89
|
-
# Like
|
90
|
-
# an
|
91
|
-
def find_by!(*args)
|
92
|
-
where(*args).take!
|
83
|
+
# Like #find_by, except that if no record is found, raises
|
84
|
+
# an ActiveRecord::RecordNotFound error.
|
85
|
+
def find_by!(arg, *args)
|
86
|
+
where(arg, *args).take!
|
93
87
|
rescue RangeError
|
94
|
-
raise RecordNotFound
|
88
|
+
raise RecordNotFound.new("Couldn't find #{@klass.name} with an out of range value",
|
89
|
+
@klass.name)
|
95
90
|
end
|
96
91
|
|
97
92
|
# Gives a record (or N records if a parameter is supplied) without any implied
|
@@ -105,10 +100,10 @@ module ActiveRecord
|
|
105
100
|
limit ? limit(limit).to_a : find_take
|
106
101
|
end
|
107
102
|
|
108
|
-
# Same as
|
109
|
-
# is found. Note that
|
103
|
+
# Same as #take but raises ActiveRecord::RecordNotFound if no record
|
104
|
+
# is found. Note that #take! accepts no arguments.
|
110
105
|
def take!
|
111
|
-
take or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
|
106
|
+
take or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]")
|
112
107
|
end
|
113
108
|
|
114
109
|
# Find the first record (or first N records if a parameter is supplied).
|
@@ -122,14 +117,14 @@ module ActiveRecord
|
|
122
117
|
#
|
123
118
|
def first(limit = nil)
|
124
119
|
if limit
|
125
|
-
|
120
|
+
find_nth_with_limit_and_offset(0, limit, offset: offset_index)
|
126
121
|
else
|
127
|
-
find_nth
|
122
|
+
find_nth 0
|
128
123
|
end
|
129
124
|
end
|
130
125
|
|
131
|
-
# Same as
|
132
|
-
# is found. Note that
|
126
|
+
# Same as #first but raises ActiveRecord::RecordNotFound if no record
|
127
|
+
# is found. Note that #first! accepts no arguments.
|
133
128
|
def first!
|
134
129
|
find_nth! 0
|
135
130
|
end
|
@@ -150,21 +145,27 @@ module ActiveRecord
|
|
150
145
|
#
|
151
146
|
# [#<Person id:4>, #<Person id:3>, #<Person id:2>]
|
152
147
|
def last(limit = nil)
|
153
|
-
if
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
148
|
+
return find_last(limit) if loaded? || limit_value
|
149
|
+
|
150
|
+
result = limit(limit || 1)
|
151
|
+
result.order!(arel_attribute(primary_key)) if order_values.empty? && primary_key
|
152
|
+
result = result.reverse_order!
|
153
|
+
|
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)
|
162
163
|
end
|
163
164
|
|
164
|
-
# Same as
|
165
|
-
# is found. Note that
|
165
|
+
# Same as #last but raises ActiveRecord::RecordNotFound if no record
|
166
|
+
# is found. Note that #last! accepts no arguments.
|
166
167
|
def last!
|
167
|
-
last or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
|
168
|
+
last or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]")
|
168
169
|
end
|
169
170
|
|
170
171
|
# Find the second record.
|
@@ -174,10 +175,10 @@ module ActiveRecord
|
|
174
175
|
# Person.offset(3).second # returns the second object from OFFSET 3 (which is OFFSET 4)
|
175
176
|
# Person.where(["user_name = :u", { u: user_name }]).second
|
176
177
|
def second
|
177
|
-
find_nth
|
178
|
+
find_nth 1
|
178
179
|
end
|
179
180
|
|
180
|
-
# Same as
|
181
|
+
# Same as #second but raises ActiveRecord::RecordNotFound if no record
|
181
182
|
# is found.
|
182
183
|
def second!
|
183
184
|
find_nth! 1
|
@@ -190,10 +191,10 @@ module ActiveRecord
|
|
190
191
|
# Person.offset(3).third # returns the third object from OFFSET 3 (which is OFFSET 5)
|
191
192
|
# Person.where(["user_name = :u", { u: user_name }]).third
|
192
193
|
def third
|
193
|
-
find_nth
|
194
|
+
find_nth 2
|
194
195
|
end
|
195
196
|
|
196
|
-
# Same as
|
197
|
+
# Same as #third but raises ActiveRecord::RecordNotFound if no record
|
197
198
|
# is found.
|
198
199
|
def third!
|
199
200
|
find_nth! 2
|
@@ -206,10 +207,10 @@ module ActiveRecord
|
|
206
207
|
# Person.offset(3).fourth # returns the fourth object from OFFSET 3 (which is OFFSET 6)
|
207
208
|
# Person.where(["user_name = :u", { u: user_name }]).fourth
|
208
209
|
def fourth
|
209
|
-
find_nth
|
210
|
+
find_nth 3
|
210
211
|
end
|
211
212
|
|
212
|
-
# Same as
|
213
|
+
# Same as #fourth but raises ActiveRecord::RecordNotFound if no record
|
213
214
|
# is found.
|
214
215
|
def fourth!
|
215
216
|
find_nth! 3
|
@@ -222,10 +223,10 @@ module ActiveRecord
|
|
222
223
|
# Person.offset(3).fifth # returns the fifth object from OFFSET 3 (which is OFFSET 7)
|
223
224
|
# Person.where(["user_name = :u", { u: user_name }]).fifth
|
224
225
|
def fifth
|
225
|
-
find_nth
|
226
|
+
find_nth 4
|
226
227
|
end
|
227
228
|
|
228
|
-
# Same as
|
229
|
+
# Same as #fifth but raises ActiveRecord::RecordNotFound if no record
|
229
230
|
# is found.
|
230
231
|
def fifth!
|
231
232
|
find_nth! 4
|
@@ -238,17 +239,49 @@ module ActiveRecord
|
|
238
239
|
# Person.offset(3).forty_two # returns the forty-second object from OFFSET 3 (which is OFFSET 44)
|
239
240
|
# Person.where(["user_name = :u", { u: user_name }]).forty_two
|
240
241
|
def forty_two
|
241
|
-
find_nth
|
242
|
+
find_nth 41
|
242
243
|
end
|
243
244
|
|
244
|
-
# Same as
|
245
|
+
# Same as #forty_two but raises ActiveRecord::RecordNotFound if no record
|
245
246
|
# is found.
|
246
247
|
def forty_two!
|
247
248
|
find_nth! 41
|
248
249
|
end
|
249
250
|
|
250
|
-
#
|
251
|
-
#
|
251
|
+
# Find the third-to-last record.
|
252
|
+
# If no order is defined it will order by primary key.
|
253
|
+
#
|
254
|
+
# Person.third_to_last # returns the third-to-last object fetched by SELECT * FROM people
|
255
|
+
# Person.offset(3).third_to_last # returns the third-to-last object from OFFSET 3
|
256
|
+
# Person.where(["user_name = :u", { u: user_name }]).third_to_last
|
257
|
+
def third_to_last
|
258
|
+
find_nth_from_last 3
|
259
|
+
end
|
260
|
+
|
261
|
+
# Same as #third_to_last but raises ActiveRecord::RecordNotFound if no record
|
262
|
+
# is found.
|
263
|
+
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)}]")
|
265
|
+
end
|
266
|
+
|
267
|
+
# Find the second-to-last record.
|
268
|
+
# If no order is defined it will order by primary key.
|
269
|
+
#
|
270
|
+
# Person.second_to_last # returns the second-to-last object fetched by SELECT * FROM people
|
271
|
+
# Person.offset(3).second_to_last # returns the second-to-last object from OFFSET 3
|
272
|
+
# Person.where(["user_name = :u", { u: user_name }]).second_to_last
|
273
|
+
def second_to_last
|
274
|
+
find_nth_from_last 2
|
275
|
+
end
|
276
|
+
|
277
|
+
# Same as #second_to_last but raises ActiveRecord::RecordNotFound if no record
|
278
|
+
# is found.
|
279
|
+
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)}]")
|
281
|
+
end
|
282
|
+
|
283
|
+
# Returns true if a record exists in the table that matches the +id+ or
|
284
|
+
# conditions given, or false otherwise. The argument can take six forms:
|
252
285
|
#
|
253
286
|
# * Integer - Finds the record with this primary key.
|
254
287
|
# * String - Finds the record with a primary key corresponding to this
|
@@ -261,7 +294,7 @@ module ActiveRecord
|
|
261
294
|
# * No args - Returns +false+ if the table is empty, +true+ otherwise.
|
262
295
|
#
|
263
296
|
# For more information about specifying conditions as a hash or array,
|
264
|
-
# see the Conditions section in the introduction to
|
297
|
+
# see the Conditions section in the introduction to ActiveRecord::Base.
|
265
298
|
#
|
266
299
|
# Note: You can't pass in a condition as a string (like <tt>name =
|
267
300
|
# 'Jamie'</tt>), since it would be sanitized and then queried against
|
@@ -279,7 +312,7 @@ module ActiveRecord
|
|
279
312
|
conditions = conditions.id
|
280
313
|
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
281
314
|
You are passing an instance of ActiveRecord::Base to `exists?`.
|
282
|
-
Please pass the id of the object by calling `.id
|
315
|
+
Please pass the id of the object by calling `.id`.
|
283
316
|
MSG
|
284
317
|
end
|
285
318
|
|
@@ -299,29 +332,31 @@ module ActiveRecord
|
|
299
332
|
end
|
300
333
|
end
|
301
334
|
|
302
|
-
connection.select_value(relation, "#{name} Exists", relation.
|
335
|
+
connection.select_value(relation, "#{name} Exists", relation.bound_attributes) ? true : false
|
303
336
|
end
|
304
337
|
|
305
338
|
# This method is called whenever no records are found with either a single
|
306
|
-
# id or multiple ids and raises
|
339
|
+
# id or multiple ids and raises an ActiveRecord::RecordNotFound exception.
|
307
340
|
#
|
308
341
|
# The error message is different depending on whether a single id or
|
309
342
|
# multiple ids are provided. If multiple ids are provided, then the number
|
310
343
|
# of results obtained should be provided in the +result_size+ argument and
|
311
344
|
# the expected number of results should be provided in the +expected_size+
|
312
345
|
# argument.
|
313
|
-
def raise_record_not_found_exception!(ids, result_size, expected_size) #:nodoc:
|
314
|
-
conditions = arel.where_sql
|
346
|
+
def raise_record_not_found_exception!(ids, result_size, expected_size, key = primary_key) #:nodoc:
|
347
|
+
conditions = arel.where_sql(@klass.arel_engine)
|
315
348
|
conditions = " [#{conditions}]" if conditions
|
349
|
+
name = @klass.name
|
316
350
|
|
317
351
|
if Array(ids).size == 1
|
318
|
-
error = "Couldn't find #{
|
352
|
+
error = "Couldn't find #{name} with '#{key}'=#{ids}#{conditions}"
|
353
|
+
raise RecordNotFound.new(error, name, key, ids)
|
319
354
|
else
|
320
|
-
error = "Couldn't find all #{
|
355
|
+
error = "Couldn't find all #{name.pluralize} with '#{key}': "
|
321
356
|
error << "(#{ids.join(", ")})#{conditions} (found #{result_size} results, but was looking for #{expected_size})"
|
322
|
-
end
|
323
357
|
|
324
|
-
|
358
|
+
raise RecordNotFound, error
|
359
|
+
end
|
325
360
|
end
|
326
361
|
|
327
362
|
private
|
@@ -353,7 +388,7 @@ module ActiveRecord
|
|
353
388
|
[]
|
354
389
|
else
|
355
390
|
arel = relation.arel
|
356
|
-
rows = connection.select_all(arel, 'SQL',
|
391
|
+
rows = connection.select_all(arel, 'SQL', relation.bound_attributes)
|
357
392
|
join_dependency.instantiate(rows, aliases)
|
358
393
|
end
|
359
394
|
end
|
@@ -385,7 +420,7 @@ module ActiveRecord
|
|
385
420
|
else
|
386
421
|
if relation.limit_value
|
387
422
|
limited_ids = limited_ids_for(relation)
|
388
|
-
limited_ids.empty? ? relation.none! : relation.where!(
|
423
|
+
limited_ids.empty? ? relation.none! : relation.where!(primary_key => limited_ids)
|
389
424
|
end
|
390
425
|
relation.except(:limit, :offset)
|
391
426
|
end
|
@@ -398,12 +433,12 @@ module ActiveRecord
|
|
398
433
|
relation = relation.except(:select).select(values).distinct!
|
399
434
|
arel = relation.arel
|
400
435
|
|
401
|
-
id_rows = @klass.connection.select_all(arel, 'SQL',
|
436
|
+
id_rows = @klass.connection.select_all(arel, 'SQL', relation.bound_attributes)
|
402
437
|
id_rows.map {|row| row[primary_key]}
|
403
438
|
end
|
404
439
|
|
405
440
|
def using_limitable_reflections?(reflections)
|
406
|
-
reflections.none?
|
441
|
+
reflections.none?(&:collection?)
|
407
442
|
end
|
408
443
|
|
409
444
|
protected
|
@@ -434,7 +469,7 @@ module ActiveRecord
|
|
434
469
|
id = id.id
|
435
470
|
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
436
471
|
You are passing an instance of ActiveRecord::Base to `find`.
|
437
|
-
Please pass the id of the object by calling `.id
|
472
|
+
Please pass the id of the object by calling `.id`.
|
438
473
|
MSG
|
439
474
|
end
|
440
475
|
|
@@ -447,6 +482,8 @@ module ActiveRecord
|
|
447
482
|
end
|
448
483
|
|
449
484
|
def find_some(ids)
|
485
|
+
return find_some_ordered(ids) unless order_values.present?
|
486
|
+
|
450
487
|
result = where(primary_key => ids).to_a
|
451
488
|
|
452
489
|
expected_size =
|
@@ -468,49 +505,96 @@ module ActiveRecord
|
|
468
505
|
end
|
469
506
|
end
|
470
507
|
|
508
|
+
def find_some_ordered(ids)
|
509
|
+
ids = ids.slice(offset_value || 0, limit_value || ids.size) || []
|
510
|
+
|
511
|
+
result = except(:limit, :offset).where(primary_key => ids).records
|
512
|
+
|
513
|
+
if result.size == ids.size
|
514
|
+
pk_type = @klass.type_for_attribute(primary_key)
|
515
|
+
|
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)
|
520
|
+
end
|
521
|
+
end
|
522
|
+
|
471
523
|
def find_take
|
472
524
|
if loaded?
|
473
|
-
|
525
|
+
records.first
|
474
526
|
else
|
475
|
-
@take ||= limit(1).
|
527
|
+
@take ||= limit(1).records.first
|
476
528
|
end
|
477
529
|
end
|
478
530
|
|
479
|
-
def find_nth(index, offset)
|
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
|
480
541
|
if loaded?
|
481
|
-
|
542
|
+
records[index]
|
482
543
|
else
|
483
|
-
offset
|
484
|
-
@offsets[offset] ||=
|
544
|
+
offset ||= offset_index
|
545
|
+
@offsets[offset + index] ||= find_nth_with_limit_and_offset(index, 1, offset: offset).first
|
485
546
|
end
|
486
547
|
end
|
487
548
|
|
488
549
|
def find_nth!(index)
|
489
|
-
find_nth(index
|
550
|
+
find_nth(index) or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]")
|
490
551
|
end
|
491
552
|
|
492
|
-
def find_nth_with_limit(
|
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
|
493
556
|
relation = if order_values.empty? && primary_key
|
494
|
-
order(
|
557
|
+
order(arel_attribute(primary_key).asc)
|
495
558
|
else
|
496
559
|
self
|
497
560
|
end
|
498
561
|
|
499
|
-
relation = relation.offset(
|
562
|
+
relation = relation.offset(index) unless index.zero?
|
500
563
|
relation.limit(limit).to_a
|
501
564
|
end
|
502
565
|
|
503
|
-
def
|
566
|
+
def find_nth_from_last(index)
|
504
567
|
if loaded?
|
505
|
-
|
568
|
+
records[-index]
|
506
569
|
else
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
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
|
513
582
|
end
|
514
583
|
end
|
584
|
+
|
585
|
+
private
|
586
|
+
|
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)
|
593
|
+
end
|
594
|
+
end
|
595
|
+
|
596
|
+
def find_last(limit)
|
597
|
+
limit ? records.last(limit) : records.last
|
598
|
+
end
|
515
599
|
end
|
516
600
|
end
|