activerecord 3.2.22.5 → 4.0.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 +4 -4
- data/CHANGELOG.md +1024 -543
- data/MIT-LICENSE +1 -1
- data/README.rdoc +20 -29
- data/examples/performance.rb +1 -1
- data/lib/active_record.rb +55 -44
- data/lib/active_record/aggregations.rb +40 -34
- data/lib/active_record/associations.rb +204 -276
- data/lib/active_record/associations/alias_tracker.rb +1 -1
- data/lib/active_record/associations/association.rb +30 -35
- data/lib/active_record/associations/association_scope.rb +40 -40
- data/lib/active_record/associations/belongs_to_association.rb +15 -2
- data/lib/active_record/associations/builder/association.rb +81 -28
- data/lib/active_record/associations/builder/belongs_to.rb +35 -57
- data/lib/active_record/associations/builder/collection_association.rb +54 -40
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +23 -41
- data/lib/active_record/associations/builder/has_many.rb +8 -64
- data/lib/active_record/associations/builder/has_one.rb +13 -50
- data/lib/active_record/associations/builder/singular_association.rb +13 -13
- data/lib/active_record/associations/collection_association.rb +92 -88
- data/lib/active_record/associations/collection_proxy.rb +913 -63
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +12 -10
- data/lib/active_record/associations/has_many_association.rb +35 -9
- data/lib/active_record/associations/has_many_through_association.rb +24 -14
- data/lib/active_record/associations/has_one_association.rb +33 -13
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +2 -2
- data/lib/active_record/associations/join_dependency/join_association.rb +17 -22
- data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
- data/lib/active_record/associations/join_helper.rb +1 -11
- data/lib/active_record/associations/preloader.rb +14 -17
- data/lib/active_record/associations/preloader/association.rb +29 -33
- data/lib/active_record/associations/preloader/collection_association.rb +1 -1
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +1 -1
- data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
- data/lib/active_record/associations/preloader/has_one.rb +1 -1
- data/lib/active_record/associations/preloader/through_association.rb +13 -17
- data/lib/active_record/associations/singular_association.rb +11 -11
- data/lib/active_record/associations/through_association.rb +2 -2
- data/lib/active_record/attribute_assignment.rb +133 -153
- data/lib/active_record/attribute_methods.rb +196 -93
- data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
- data/lib/active_record/attribute_methods/dirty.rb +31 -28
- data/lib/active_record/attribute_methods/primary_key.rb +38 -30
- data/lib/active_record/attribute_methods/query.rb +5 -4
- data/lib/active_record/attribute_methods/read.rb +62 -91
- data/lib/active_record/attribute_methods/serialization.rb +97 -66
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -45
- data/lib/active_record/attribute_methods/write.rb +32 -39
- data/lib/active_record/autosave_association.rb +56 -70
- data/lib/active_record/base.rb +53 -450
- data/lib/active_record/callbacks.rb +53 -18
- data/lib/active_record/coders/yaml_column.rb +11 -9
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +353 -197
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +130 -131
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -19
- data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -3
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +101 -91
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +59 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +225 -96
- data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +99 -46
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +114 -36
- data/lib/active_record/connection_adapters/column.rb +46 -24
- data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
- data/lib/active_record/connection_adapters/mysql_adapter.rb +181 -64
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/cast.rb +132 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +347 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +158 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +448 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +454 -885
- data/lib/active_record/connection_adapters/schema_cache.rb +48 -16
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +574 -13
- data/lib/active_record/connection_handling.rb +98 -0
- data/lib/active_record/core.rb +428 -0
- data/lib/active_record/counter_cache.rb +106 -108
- data/lib/active_record/dynamic_matchers.rb +110 -63
- data/lib/active_record/errors.rb +25 -8
- data/lib/active_record/explain.rb +8 -58
- data/lib/active_record/explain_subscriber.rb +6 -3
- data/lib/active_record/fixture_set/file.rb +56 -0
- data/lib/active_record/fixtures.rb +146 -148
- data/lib/active_record/inheritance.rb +77 -59
- data/lib/active_record/integration.rb +5 -5
- data/lib/active_record/locale/en.yml +8 -1
- data/lib/active_record/locking/optimistic.rb +38 -42
- data/lib/active_record/locking/pessimistic.rb +4 -4
- data/lib/active_record/log_subscriber.rb +19 -9
- data/lib/active_record/migration.rb +318 -153
- data/lib/active_record/migration/command_recorder.rb +90 -31
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/model_schema.rb +69 -92
- data/lib/active_record/nested_attributes.rb +113 -148
- data/lib/active_record/null_relation.rb +65 -0
- data/lib/active_record/persistence.rb +188 -97
- data/lib/active_record/query_cache.rb +18 -36
- data/lib/active_record/querying.rb +19 -15
- data/lib/active_record/railtie.rb +91 -36
- data/lib/active_record/railties/console_sandbox.rb +0 -2
- data/lib/active_record/railties/controller_runtime.rb +2 -2
- data/lib/active_record/railties/databases.rake +90 -309
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +7 -3
- data/lib/active_record/reflection.rb +72 -56
- data/lib/active_record/relation.rb +241 -157
- data/lib/active_record/relation/batches.rb +25 -22
- data/lib/active_record/relation/calculations.rb +143 -121
- data/lib/active_record/relation/delegation.rb +96 -18
- data/lib/active_record/relation/finder_methods.rb +117 -183
- data/lib/active_record/relation/merger.rb +133 -0
- data/lib/active_record/relation/predicate_builder.rb +90 -42
- data/lib/active_record/relation/query_methods.rb +666 -136
- data/lib/active_record/relation/spawn_methods.rb +43 -150
- data/lib/active_record/result.rb +33 -6
- data/lib/active_record/sanitization.rb +24 -50
- data/lib/active_record/schema.rb +19 -12
- data/lib/active_record/schema_dumper.rb +31 -39
- data/lib/active_record/schema_migration.rb +36 -0
- data/lib/active_record/scoping.rb +0 -124
- data/lib/active_record/scoping/default.rb +48 -45
- data/lib/active_record/scoping/named.rb +74 -103
- data/lib/active_record/serialization.rb +6 -2
- data/lib/active_record/serializers/xml_serializer.rb +9 -15
- data/lib/active_record/store.rb +119 -15
- data/lib/active_record/tasks/database_tasks.rb +158 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +138 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
- data/lib/active_record/test_case.rb +61 -38
- data/lib/active_record/timestamp.rb +8 -9
- data/lib/active_record/transactions.rb +65 -51
- data/lib/active_record/validations.rb +17 -15
- data/lib/active_record/validations/associated.rb +20 -14
- data/lib/active_record/validations/presence.rb +65 -0
- data/lib/active_record/validations/uniqueness.rb +93 -52
- data/lib/active_record/version.rb +4 -4
- data/lib/rails/generators/active_record.rb +3 -5
- data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -7
- data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
- data/lib/rails/generators/active_record/model/model_generator.rb +4 -3
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -6
- data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
- metadata +53 -46
- data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
- data/lib/active_record/dynamic_finder_match.rb +0 -68
- data/lib/active_record/dynamic_scope_match.rb +0 -23
- data/lib/active_record/fixtures/file.rb +0 -65
- data/lib/active_record/identity_map.rb +0 -162
- data/lib/active_record/observer.rb +0 -121
- data/lib/active_record/session_store.rb +0 -360
- data/lib/rails/generators/active_record/migration.rb +0 -15
- data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
- data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,25 +1,106 @@
|
|
1
|
-
require '
|
1
|
+
require 'thread'
|
2
|
+
require 'thread_safe'
|
2
3
|
|
3
4
|
module ActiveRecord
|
4
|
-
module Delegation
|
5
|
-
|
5
|
+
module Delegation # :nodoc:
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
# This module creates compiled delegation methods dynamically at runtime, which makes
|
9
|
+
# subsequent calls to that method faster by avoiding method_missing. The delegations
|
10
|
+
# may vary depending on the klass of a relation, so we create a subclass of Relation
|
11
|
+
# for each different klass, and the delegations are compiled into that subclass only.
|
12
|
+
|
6
13
|
delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :to => :to_a
|
7
14
|
delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
|
8
|
-
:connection, :columns_hash, :
|
15
|
+
:connection, :columns_hash, :to => :klass
|
16
|
+
|
17
|
+
module ClassSpecificRelation
|
18
|
+
extend ActiveSupport::Concern
|
19
|
+
|
20
|
+
included do
|
21
|
+
@delegation_mutex = Mutex.new
|
22
|
+
end
|
23
|
+
|
24
|
+
module ClassMethods
|
25
|
+
def name
|
26
|
+
superclass.name
|
27
|
+
end
|
9
28
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
29
|
+
def delegate_to_scoped_klass(method)
|
30
|
+
@delegation_mutex.synchronize do
|
31
|
+
return if method_defined?(method)
|
32
|
+
|
33
|
+
if method.to_s =~ /\A[a-zA-Z_]\w*[!?]?\z/
|
34
|
+
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
35
|
+
def #{method}(*args, &block)
|
36
|
+
scoping { @klass.#{method}(*args, &block) }
|
37
|
+
end
|
38
|
+
RUBY
|
39
|
+
else
|
40
|
+
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
41
|
+
def #{method}(*args, &block)
|
42
|
+
scoping { @klass.send(#{method.inspect}, *args, &block) }
|
43
|
+
end
|
44
|
+
RUBY
|
45
|
+
end
|
15
46
|
end
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
47
|
+
end
|
48
|
+
|
49
|
+
def delegate(method, opts = {})
|
50
|
+
@delegation_mutex.synchronize do
|
51
|
+
return if method_defined?(method)
|
52
|
+
super
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
protected
|
58
|
+
|
59
|
+
def method_missing(method, *args, &block)
|
60
|
+
if @klass.respond_to?(method)
|
61
|
+
self.class.delegate_to_scoped_klass(method)
|
62
|
+
scoping { @klass.send(method, *args, &block) }
|
63
|
+
elsif Array.method_defined?(method)
|
64
|
+
self.class.delegate method, :to => :to_a
|
65
|
+
to_a.send(method, *args, &block)
|
66
|
+
elsif arel.respond_to?(method)
|
67
|
+
self.class.delegate method, :to => :arel
|
68
|
+
arel.send(method, *args, &block)
|
69
|
+
else
|
70
|
+
super
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
module ClassMethods
|
76
|
+
@@subclasses = ThreadSafe::Cache.new(:initial_capacity => 2)
|
77
|
+
|
78
|
+
def new(klass, *args)
|
79
|
+
relation = relation_class_for(klass).allocate
|
80
|
+
relation.__send__(:initialize, klass, *args)
|
81
|
+
relation
|
82
|
+
end
|
83
|
+
|
84
|
+
# This doesn't have to be thread-safe. relation_class_for guarantees that this will only be
|
85
|
+
# called exactly once for a given const name.
|
86
|
+
def const_missing(name)
|
87
|
+
const_set(name, Class.new(self) { include ClassSpecificRelation })
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
# Cache the constants in @@subclasses because looking them up via const_get
|
92
|
+
# make instantiation significantly slower.
|
93
|
+
def relation_class_for(klass)
|
94
|
+
if klass && (klass_name = klass.name)
|
95
|
+
my_cache = @@subclasses.compute_if_absent(self) { ThreadSafe::Cache.new }
|
96
|
+
# This hash is keyed by klass.name to avoid memory leaks in development mode
|
97
|
+
my_cache.compute_if_absent(klass_name) do
|
98
|
+
# Cache#compute_if_absent guarantees that the block will only executed once for the given klass_name
|
99
|
+
const_get("#{name.gsub('::', '_')}_#{klass_name.gsub('::', '_')}", false)
|
21
100
|
end
|
22
|
-
|
101
|
+
else
|
102
|
+
ActiveRecord::Relation
|
103
|
+
end
|
23
104
|
end
|
24
105
|
end
|
25
106
|
|
@@ -33,13 +114,10 @@ module ActiveRecord
|
|
33
114
|
|
34
115
|
def method_missing(method, *args, &block)
|
35
116
|
if @klass.respond_to?(method)
|
36
|
-
::ActiveRecord::Delegation.delegate_to_scoped_klass(method)
|
37
117
|
scoping { @klass.send(method, *args, &block) }
|
38
118
|
elsif Array.method_defined?(method)
|
39
|
-
::ActiveRecord::Delegation.delegate method, :to => :to_a
|
40
119
|
to_a.send(method, *args, &block)
|
41
120
|
elsif arel.respond_to?(method)
|
42
|
-
::ActiveRecord::Delegation.delegate method, :to => :arel
|
43
121
|
arel.send(method, *args, &block)
|
44
122
|
else
|
45
123
|
super
|
@@ -1,85 +1,21 @@
|
|
1
|
-
require 'active_support/core_ext/object/blank'
|
2
|
-
require 'active_support/core_ext/hash/indifferent_access'
|
3
|
-
|
4
1
|
module ActiveRecord
|
5
2
|
module FinderMethods
|
6
|
-
# Find
|
7
|
-
#
|
8
|
-
#
|
9
|
-
# If no record can be found for all of the listed ids, then RecordNotFound will be raised.
|
10
|
-
# * Find first - This will return the first record matched by the options used. These options can either be specific
|
11
|
-
# conditions or merely an order. If no record can be matched, +nil+ is returned. Use
|
12
|
-
# <tt>Model.find(:first, *args)</tt> or its shortcut <tt>Model.first(*args)</tt>.
|
13
|
-
# * Find last - This will return the last record matched by the options used. These options can either be specific
|
14
|
-
# conditions or merely an order. If no record can be matched, +nil+ is returned. Use
|
15
|
-
# <tt>Model.find(:last, *args)</tt> or its shortcut <tt>Model.last(*args)</tt>.
|
16
|
-
# * Find all - This will return all the records matched by the options used.
|
17
|
-
# If no records are found, an empty array is returned. Use
|
18
|
-
# <tt>Model.find(:all, *args)</tt> or its shortcut <tt>Model.all(*args)</tt>.
|
19
|
-
#
|
20
|
-
# All approaches accept an options hash as their last parameter.
|
21
|
-
#
|
22
|
-
# ==== Options
|
23
|
-
#
|
24
|
-
# * <tt>:conditions</tt> - An SQL fragment like "administrator = 1", <tt>["user_name = ?", username]</tt>,
|
25
|
-
# or <tt>["user_name = :user_name", { :user_name => user_name }]</tt>. See conditions in the intro.
|
26
|
-
# * <tt>:order</tt> - An SQL fragment like "created_at DESC, name".
|
27
|
-
# * <tt>:group</tt> - An attribute name by which the result should be grouped. Uses the <tt>GROUP BY</tt> SQL-clause.
|
28
|
-
# * <tt>:having</tt> - Combined with +:group+ this can be used to filter the records that a
|
29
|
-
# <tt>GROUP BY</tt> returns. Uses the <tt>HAVING</tt> SQL-clause.
|
30
|
-
# * <tt>:limit</tt> - An integer determining the limit on the number of rows that should be returned.
|
31
|
-
# * <tt>:offset</tt> - An integer determining the offset from where the rows should be fetched. So at 5,
|
32
|
-
# it would skip rows 0 through 4.
|
33
|
-
# * <tt>:joins</tt> - Either an SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id" (rarely needed),
|
34
|
-
# named associations in the same form used for the <tt>:include</tt> option, which will perform an
|
35
|
-
# <tt>INNER JOIN</tt> on the associated table(s),
|
36
|
-
# or an array containing a mixture of both strings and named associations.
|
37
|
-
# If the value is a string, then the records will be returned read-only since they will
|
38
|
-
# have attributes that do not correspond to the table's columns.
|
39
|
-
# Pass <tt>:readonly => false</tt> to override.
|
40
|
-
# * <tt>:include</tt> - Names associations that should be loaded alongside. The symbols named refer
|
41
|
-
# to already defined associations. See eager loading under Associations.
|
42
|
-
# * <tt>:select</tt> - By default, this is "*" as in "SELECT * FROM", but can be changed if you,
|
43
|
-
# for example, want to do a join but not include the joined columns. Takes a string with the SELECT SQL fragment (e.g. "id, name").
|
44
|
-
# * <tt>:from</tt> - By default, this is the table name of the class, but can be changed
|
45
|
-
# to an alternate table name (or even the name of a database view).
|
46
|
-
# * <tt>:readonly</tt> - Mark the returned records read-only so they cannot be saved or updated.
|
47
|
-
# * <tt>:lock</tt> - An SQL fragment like "FOR UPDATE" or "LOCK IN SHARE MODE".
|
48
|
-
# <tt>:lock => true</tt> gives connection's default exclusive lock, usually "FOR UPDATE".
|
3
|
+
# 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]).
|
4
|
+
# If no record can be found for all of the listed ids, then RecordNotFound will be raised. If the primary key
|
5
|
+
# is an integer, find by id coerces its arguments using +to_i+.
|
49
6
|
#
|
50
|
-
# ==== Examples
|
51
|
-
#
|
52
|
-
# # find by id
|
53
7
|
# Person.find(1) # returns the object for ID = 1
|
8
|
+
# Person.find("1") # returns the object for ID = 1
|
54
9
|
# Person.find(1, 2, 6) # returns an array for objects with IDs in (1, 2, 6)
|
55
10
|
# Person.find([7, 17]) # returns an array for objects with IDs in (7, 17)
|
56
11
|
# Person.find([1]) # returns an array for the object with ID = 1
|
57
12
|
# Person.where("administrator = 1").order("created_on DESC").find(1)
|
58
13
|
#
|
59
14
|
# Note that returned records may not be in the same order as the ids you
|
60
|
-
# provide since database rows are unordered. Give an explicit <tt
|
15
|
+
# provide since database rows are unordered. Give an explicit <tt>order</tt>
|
61
16
|
# to ensure the results are sorted.
|
62
17
|
#
|
63
|
-
# ====
|
64
|
-
#
|
65
|
-
# # find first
|
66
|
-
# Person.first # returns the first object fetched by SELECT * FROM people
|
67
|
-
# Person.where(["user_name = ?", user_name]).first
|
68
|
-
# Person.where(["user_name = :u", { :u => user_name }]).first
|
69
|
-
# Person.order("created_on DESC").offset(5).first
|
70
|
-
#
|
71
|
-
# # find last
|
72
|
-
# Person.last # returns the last object fetched by SELECT * FROM people
|
73
|
-
# Person.where(["user_name = ?", user_name]).last
|
74
|
-
# Person.order("created_on DESC").offset(5).last
|
75
|
-
#
|
76
|
-
# # find all
|
77
|
-
# Person.all # returns an array of objects for all the rows fetched by SELECT * FROM people
|
78
|
-
# Person.where(["category IN (?)", categories]).limit(50).all
|
79
|
-
# Person.where({ :friends => ["Bob", "Steve", "Fred"] }).all
|
80
|
-
# Person.offset(10).limit(10).all
|
81
|
-
# Person.includes([:account, :friends]).all
|
82
|
-
# Person.group("category").all
|
18
|
+
# ==== Find with lock
|
83
19
|
#
|
84
20
|
# Example for find with a lock: Imagine two concurrent transactions:
|
85
21
|
# each will read <tt>person.visits == 2</tt>, add 1 to it, and save, resulting
|
@@ -93,30 +29,62 @@ module ActiveRecord
|
|
93
29
|
# person.save!
|
94
30
|
# end
|
95
31
|
def find(*args)
|
96
|
-
|
97
|
-
|
98
|
-
options = args.extract_options!
|
99
|
-
|
100
|
-
if options.present?
|
101
|
-
apply_finder_options(options).find(*args)
|
32
|
+
if block_given?
|
33
|
+
to_a.find { |*block_args| yield(*block_args) }
|
102
34
|
else
|
103
|
-
|
104
|
-
when :first, :last, :all
|
105
|
-
send(args.first)
|
106
|
-
else
|
107
|
-
find_with_ids(*args)
|
108
|
-
end
|
35
|
+
find_with_ids(*args)
|
109
36
|
end
|
110
37
|
end
|
111
38
|
|
112
|
-
#
|
113
|
-
#
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
39
|
+
# Finds the first record matching the specified conditions. There
|
40
|
+
# is no implied ording so if order matters, you should specify it
|
41
|
+
# yourself.
|
42
|
+
#
|
43
|
+
# If no record is found, returns <tt>nil</tt>.
|
44
|
+
#
|
45
|
+
# Post.find_by name: 'Spartacus', rating: 4
|
46
|
+
# Post.find_by "published_at < ?", 2.weeks.ago
|
47
|
+
def find_by(*args)
|
48
|
+
where(*args).take
|
49
|
+
end
|
50
|
+
|
51
|
+
# Like <tt>find_by</tt>, except that if no record is found, raises
|
52
|
+
# an <tt>ActiveRecord::RecordNotFound</tt> error.
|
53
|
+
def find_by!(*args)
|
54
|
+
where(*args).take!
|
55
|
+
end
|
56
|
+
|
57
|
+
# Gives a record (or N records if a parameter is supplied) without any implied
|
58
|
+
# order. The order will depend on the database implementation.
|
59
|
+
# If an order is supplied it will be respected.
|
60
|
+
#
|
61
|
+
# Person.take # returns an object fetched by SELECT * FROM people LIMIT 1
|
62
|
+
# Person.take(5) # returns 5 objects fetched by SELECT * FROM people LIMIT 5
|
63
|
+
# Person.where(["name LIKE '%?'", name]).take
|
64
|
+
def take(limit = nil)
|
65
|
+
limit ? limit(limit).to_a : find_take
|
66
|
+
end
|
67
|
+
|
68
|
+
# Same as +take+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
|
69
|
+
# is found. Note that <tt>take!</tt> accepts no arguments.
|
70
|
+
def take!
|
71
|
+
take or raise RecordNotFound
|
72
|
+
end
|
73
|
+
|
74
|
+
# Find the first record (or first N records if a parameter is supplied).
|
75
|
+
# If no order is defined it will order by primary key.
|
76
|
+
#
|
77
|
+
# Person.first # returns the first object fetched by SELECT * FROM people
|
78
|
+
# Person.where(["user_name = ?", user_name]).first
|
79
|
+
# Person.where(["user_name = :u", { u: user_name }]).first
|
80
|
+
# Person.order("created_on DESC").offset(5).first
|
81
|
+
# Person.first(3) # returns the first three objects fetched by SELECT * FROM people LIMIT 3
|
82
|
+
def first(limit = nil)
|
83
|
+
if limit
|
84
|
+
if order_values.empty? && primary_key
|
85
|
+
order(arel_table[primary_key].asc).limit(limit).to_a
|
118
86
|
else
|
119
|
-
|
87
|
+
limit(limit).to_a
|
120
88
|
end
|
121
89
|
else
|
122
90
|
find_first
|
@@ -129,18 +97,27 @@ module ActiveRecord
|
|
129
97
|
first or raise RecordNotFound
|
130
98
|
end
|
131
99
|
|
132
|
-
#
|
133
|
-
#
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
100
|
+
# Find the last record (or last N records if a parameter is supplied).
|
101
|
+
# If no order is defined it will order by primary key.
|
102
|
+
#
|
103
|
+
# Person.last # returns the last object fetched by SELECT * FROM people
|
104
|
+
# Person.where(["user_name = ?", user_name]).last
|
105
|
+
# Person.order("created_on DESC").offset(5).last
|
106
|
+
# Person.last(3) # returns the last three objects fetched by SELECT * FROM people.
|
107
|
+
#
|
108
|
+
# Take note that in that last case, the results are sorted in ascending order:
|
109
|
+
#
|
110
|
+
# [#<Person id:2>, #<Person id:3>, #<Person id:4>]
|
111
|
+
#
|
112
|
+
# and not:
|
113
|
+
#
|
114
|
+
# [#<Person id:4>, #<Person id:3>, #<Person id:2>]
|
115
|
+
def last(limit = nil)
|
116
|
+
if limit
|
117
|
+
if order_values.empty? && primary_key
|
118
|
+
order(arel_table[primary_key].desc).limit(limit).reverse
|
142
119
|
else
|
143
|
-
|
120
|
+
to_a.last(limit)
|
144
121
|
end
|
145
122
|
else
|
146
123
|
find_last
|
@@ -153,14 +130,8 @@ module ActiveRecord
|
|
153
130
|
last or raise RecordNotFound
|
154
131
|
end
|
155
132
|
|
156
|
-
#
|
157
|
-
#
|
158
|
-
def all(*args)
|
159
|
-
args.any? ? apply_finder_options(args.first).to_a : to_a
|
160
|
-
end
|
161
|
-
|
162
|
-
# Returns true if a record exists in the table that matches the +id+ or
|
163
|
-
# conditions given, or false otherwise. The argument can take five forms:
|
133
|
+
# Returns +true+ if a record exists in the table that matches the +id+ or
|
134
|
+
# conditions given, or +false+ otherwise. The argument can take six forms:
|
164
135
|
#
|
165
136
|
# * Integer - Finds the record with this primary key.
|
166
137
|
# * String - Finds the record with a primary key corresponding to this
|
@@ -168,8 +139,9 @@ module ActiveRecord
|
|
168
139
|
# * Array - Finds the record that matches these +find+-style conditions
|
169
140
|
# (such as <tt>['color = ?', 'red']</tt>).
|
170
141
|
# * Hash - Finds the record that matches these +find+-style conditions
|
171
|
-
# (such as <tt>{:
|
172
|
-
# *
|
142
|
+
# (such as <tt>{color: 'red'}</tt>).
|
143
|
+
# * +false+ - Returns always +false+.
|
144
|
+
# * No args - Returns +false+ if the table is empty, +true+ otherwise.
|
173
145
|
#
|
174
146
|
# For more information about specifying conditions as a Hash or Array,
|
175
147
|
# see the Conditions section in the introduction to ActiveRecord::Base.
|
@@ -178,28 +150,28 @@ module ActiveRecord
|
|
178
150
|
# 'Jamie'</tt>), since it would be sanitized and then queried against
|
179
151
|
# the primary key column, like <tt>id = 'name = \'Jamie\''</tt>.
|
180
152
|
#
|
181
|
-
# ==== Examples
|
182
153
|
# Person.exists?(5)
|
183
154
|
# Person.exists?('5')
|
184
|
-
# Person.exists?(:name => "David")
|
185
155
|
# Person.exists?(['name LIKE ?', "%#{query}%"])
|
156
|
+
# Person.exists?(name: 'David')
|
157
|
+
# Person.exists?(false)
|
186
158
|
# Person.exists?
|
187
|
-
def exists?(
|
188
|
-
|
189
|
-
return false if
|
159
|
+
def exists?(conditions = :none)
|
160
|
+
conditions = conditions.id if Base === conditions
|
161
|
+
return false if !conditions
|
190
162
|
|
191
163
|
join_dependency = construct_join_dependency_for_association_find
|
192
164
|
relation = construct_relation_for_association_find(join_dependency)
|
193
165
|
relation = relation.except(:select, :order).select("1 AS one").limit(1)
|
194
166
|
|
195
|
-
case
|
167
|
+
case conditions
|
196
168
|
when Array, Hash
|
197
|
-
relation = relation.where(
|
169
|
+
relation = relation.where(conditions)
|
198
170
|
else
|
199
|
-
relation = relation.where(table[primary_key].eq(
|
171
|
+
relation = relation.where(table[primary_key].eq(conditions)) if conditions != :none
|
200
172
|
end
|
201
173
|
|
202
|
-
connection.select_value(relation, "#{name} Exists")
|
174
|
+
connection.select_value(relation, "#{name} Exists", relation.bind_values)
|
203
175
|
rescue ThrowResult
|
204
176
|
false
|
205
177
|
end
|
@@ -216,12 +188,12 @@ module ActiveRecord
|
|
216
188
|
end
|
217
189
|
|
218
190
|
def construct_join_dependency_for_association_find
|
219
|
-
including = (
|
191
|
+
including = (eager_load_values + includes_values).uniq
|
220
192
|
ActiveRecord::Associations::JoinDependency.new(@klass, including, [])
|
221
193
|
end
|
222
194
|
|
223
195
|
def construct_relation_for_association_calculations
|
224
|
-
including = (
|
196
|
+
including = (eager_load_values + includes_values).uniq
|
225
197
|
join_dependency = ActiveRecord::Associations::JoinDependency.new(@klass, including, arel.froms.first)
|
226
198
|
relation = except(:includes, :eager_load, :preload)
|
227
199
|
apply_join_dependency(relation, join_dependency)
|
@@ -251,10 +223,9 @@ module ActiveRecord
|
|
251
223
|
|
252
224
|
def construct_limited_ids_condition(relation)
|
253
225
|
orders = relation.order_values.map { |val| val.presence }.compact
|
254
|
-
values = @klass.connection.distinct("#{
|
226
|
+
values = @klass.connection.distinct("#{quoted_table_name}.#{primary_key}", orders)
|
255
227
|
|
256
228
|
relation = relation.dup.select(values)
|
257
|
-
relation.uniq_value = nil
|
258
229
|
|
259
230
|
id_rows = @klass.connection.select_all(relation.arel, 'SQL', relation.bind_values)
|
260
231
|
ids_array = id_rows.map {|row| row[primary_key]}
|
@@ -262,47 +233,7 @@ module ActiveRecord
|
|
262
233
|
ids_array.empty? ? raise(ThrowResult) : table[primary_key].in(ids_array)
|
263
234
|
end
|
264
235
|
|
265
|
-
def find_by_attributes(match, attributes, *args)
|
266
|
-
conditions = Hash[attributes.map {|a| [a, args[attributes.index(a)]]}]
|
267
|
-
result = where(conditions).send(match.finder)
|
268
|
-
|
269
|
-
if match.bang? && result.nil?
|
270
|
-
raise RecordNotFound, "Couldn't find #{@klass.name} with #{conditions.to_a.collect {|p| p.join(' = ')}.join(', ')}"
|
271
|
-
else
|
272
|
-
yield(result) if block_given?
|
273
|
-
result
|
274
|
-
end
|
275
|
-
end
|
276
|
-
|
277
|
-
def find_or_instantiator_by_attributes(match, attributes, *args)
|
278
|
-
options = args.size > 1 && args.last(2).all?{ |a| a.is_a?(Hash) } ? args.extract_options! : {}
|
279
|
-
protected_attributes_for_create, unprotected_attributes_for_create = {}, {}
|
280
|
-
args.each_with_index do |arg, i|
|
281
|
-
if arg.is_a?(Hash)
|
282
|
-
protected_attributes_for_create = args[i].with_indifferent_access
|
283
|
-
else
|
284
|
-
unprotected_attributes_for_create[attributes[i]] = args[i]
|
285
|
-
end
|
286
|
-
end
|
287
|
-
|
288
|
-
conditions = (protected_attributes_for_create.merge(unprotected_attributes_for_create)).slice(*attributes).symbolize_keys
|
289
|
-
|
290
|
-
record = where(conditions).first
|
291
|
-
|
292
|
-
unless record
|
293
|
-
record = @klass.new(protected_attributes_for_create, options) do |r|
|
294
|
-
r.assign_attributes(unprotected_attributes_for_create, :without_protection => true)
|
295
|
-
end
|
296
|
-
yield(record) if block_given?
|
297
|
-
record.send(match.save_method) if match.save_record?
|
298
|
-
end
|
299
|
-
|
300
|
-
record
|
301
|
-
end
|
302
|
-
|
303
236
|
def find_with_ids(*ids)
|
304
|
-
return to_a.find { |*block_args| yield(*block_args) } if block_given?
|
305
|
-
|
306
237
|
expects_array = ids.first.kind_of?(Array)
|
307
238
|
return ids.first if expects_array && ids.first.empty?
|
308
239
|
|
@@ -322,21 +253,11 @@ module ActiveRecord
|
|
322
253
|
def find_one(id)
|
323
254
|
id = id.id if ActiveRecord::Base === id
|
324
255
|
|
325
|
-
if IdentityMap.enabled? && where_values.blank? &&
|
326
|
-
limit_value.blank? && order_values.blank? &&
|
327
|
-
includes_values.blank? && preload_values.blank? &&
|
328
|
-
readonly_value.nil? && joins_values.blank? &&
|
329
|
-
!@klass.locking_enabled? &&
|
330
|
-
record = IdentityMap.get(@klass, id)
|
331
|
-
return record
|
332
|
-
end
|
333
|
-
|
334
256
|
column = columns_hash[primary_key]
|
335
|
-
|
336
|
-
substitute = connection.substitute_at(column, @bind_values.length)
|
257
|
+
substitute = connection.substitute_at(column, bind_values.length)
|
337
258
|
relation = where(table[primary_key].eq(substitute))
|
338
|
-
relation.bind_values
|
339
|
-
record = relation.
|
259
|
+
relation.bind_values += [[column, id]]
|
260
|
+
record = relation.take
|
340
261
|
|
341
262
|
unless record
|
342
263
|
conditions = arel.where_sql
|
@@ -348,18 +269,18 @@ module ActiveRecord
|
|
348
269
|
end
|
349
270
|
|
350
271
|
def find_some(ids)
|
351
|
-
result = where(table[primary_key].in(ids)).
|
272
|
+
result = where(table[primary_key].in(ids)).to_a
|
352
273
|
|
353
274
|
expected_size =
|
354
|
-
if
|
355
|
-
|
275
|
+
if limit_value && ids.size > limit_value
|
276
|
+
limit_value
|
356
277
|
else
|
357
278
|
ids.size
|
358
279
|
end
|
359
280
|
|
360
281
|
# 11 ids with limit 3, offset 9 should give 2 results.
|
361
|
-
if
|
362
|
-
expected_size = ids.size -
|
282
|
+
if offset_value && (ids.size - offset_value < expected_size)
|
283
|
+
expected_size = ids.size - offset_value
|
363
284
|
end
|
364
285
|
|
365
286
|
if result.size == expected_size
|
@@ -374,11 +295,24 @@ module ActiveRecord
|
|
374
295
|
end
|
375
296
|
end
|
376
297
|
|
298
|
+
def find_take
|
299
|
+
if loaded?
|
300
|
+
@records.first
|
301
|
+
else
|
302
|
+
@take ||= limit(1).to_a.first
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
377
306
|
def find_first
|
378
307
|
if loaded?
|
379
308
|
@records.first
|
380
309
|
else
|
381
|
-
@first ||=
|
310
|
+
@first ||=
|
311
|
+
if with_default_scope.order_values.empty? && primary_key
|
312
|
+
order(arel_table[primary_key].asc).limit(1).to_a.first
|
313
|
+
else
|
314
|
+
limit(1).to_a.first
|
315
|
+
end
|
382
316
|
end
|
383
317
|
end
|
384
318
|
|
@@ -390,7 +324,7 @@ module ActiveRecord
|
|
390
324
|
if offset_value || limit_value
|
391
325
|
to_a.last
|
392
326
|
else
|
393
|
-
reverse_order.limit(1).to_a
|
327
|
+
reverse_order.limit(1).to_a.first
|
394
328
|
end
|
395
329
|
end
|
396
330
|
end
|