activerecord 3.1.11 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.md +6294 -97
- data/README.rdoc +2 -2
- data/examples/performance.rb +55 -31
- data/lib/active_record/aggregations.rb +2 -2
- data/lib/active_record/associations/association.rb +2 -42
- data/lib/active_record/associations/association_scope.rb +3 -30
- data/lib/active_record/associations/builder/association.rb +6 -4
- data/lib/active_record/associations/builder/belongs_to.rb +3 -3
- data/lib/active_record/associations/builder/collection_association.rb +2 -2
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +5 -6
- data/lib/active_record/associations/builder/singular_association.rb +3 -16
- data/lib/active_record/associations/collection_association.rb +55 -28
- data/lib/active_record/associations/collection_proxy.rb +1 -35
- data/lib/active_record/associations/has_many_association.rb +5 -1
- data/lib/active_record/associations/has_many_through_association.rb +11 -8
- data/lib/active_record/associations/join_dependency.rb +1 -1
- data/lib/active_record/associations/preloader/association.rb +3 -1
- data/lib/active_record/associations.rb +82 -69
- data/lib/active_record/attribute_assignment.rb +221 -0
- data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +32 -0
- data/lib/active_record/attribute_methods/dirty.rb +3 -3
- data/lib/active_record/attribute_methods/primary_key.rb +62 -25
- data/lib/active_record/attribute_methods/read.rb +72 -83
- data/lib/active_record/attribute_methods/serialization.rb +93 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +9 -14
- data/lib/active_record/attribute_methods/write.rb +27 -5
- data/lib/active_record/attribute_methods.rb +209 -30
- data/lib/active_record/autosave_association.rb +23 -8
- data/lib/active_record/base.rb +217 -1709
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +98 -132
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +82 -29
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +13 -42
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +9 -12
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +36 -25
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +43 -22
- data/lib/active_record/connection_adapters/abstract_adapter.rb +78 -43
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +653 -0
- data/lib/active_record/connection_adapters/column.rb +2 -2
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +138 -578
- data/lib/active_record/connection_adapters/mysql_adapter.rb +86 -658
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +144 -94
- data/lib/active_record/connection_adapters/schema_cache.rb +50 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -6
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +43 -22
- data/lib/active_record/counter_cache.rb +4 -3
- data/lib/active_record/dynamic_matchers.rb +79 -0
- data/lib/active_record/errors.rb +11 -1
- data/lib/active_record/explain.rb +83 -0
- data/lib/active_record/explain_subscriber.rb +21 -0
- data/lib/active_record/fixtures/file.rb +65 -0
- data/lib/active_record/fixtures.rb +31 -76
- data/lib/active_record/identity_map.rb +4 -11
- data/lib/active_record/inheritance.rb +167 -0
- data/lib/active_record/integration.rb +49 -0
- data/lib/active_record/locking/optimistic.rb +30 -25
- data/lib/active_record/locking/pessimistic.rb +23 -1
- data/lib/active_record/log_subscriber.rb +3 -3
- data/lib/active_record/migration/command_recorder.rb +8 -8
- data/lib/active_record/migration.rb +47 -30
- data/lib/active_record/model_schema.rb +366 -0
- data/lib/active_record/nested_attributes.rb +3 -2
- data/lib/active_record/persistence.rb +51 -9
- data/lib/active_record/querying.rb +58 -0
- data/lib/active_record/railtie.rb +24 -28
- data/lib/active_record/railties/controller_runtime.rb +3 -1
- data/lib/active_record/railties/databases.rake +134 -77
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +26 -0
- data/lib/active_record/reflection.rb +7 -15
- data/lib/active_record/relation/batches.rb +5 -2
- data/lib/active_record/relation/calculations.rb +27 -6
- data/lib/active_record/relation/delegation.rb +49 -0
- data/lib/active_record/relation/finder_methods.rb +6 -5
- data/lib/active_record/relation/predicate_builder.rb +12 -19
- data/lib/active_record/relation/query_methods.rb +76 -10
- data/lib/active_record/relation/spawn_methods.rb +11 -2
- data/lib/active_record/relation.rb +77 -34
- data/lib/active_record/result.rb +1 -1
- data/lib/active_record/sanitization.rb +194 -0
- data/lib/active_record/schema_dumper.rb +5 -2
- data/lib/active_record/scoping/default.rb +142 -0
- data/lib/active_record/scoping/named.rb +202 -0
- data/lib/active_record/scoping.rb +152 -0
- data/lib/active_record/serialization.rb +1 -43
- data/lib/active_record/serializers/xml_serializer.rb +2 -44
- data/lib/active_record/session_store.rb +15 -15
- data/lib/active_record/store.rb +50 -0
- data/lib/active_record/test_case.rb +11 -7
- data/lib/active_record/timestamp.rb +16 -3
- data/lib/active_record/transactions.rb +5 -5
- data/lib/active_record/translation.rb +22 -0
- data/lib/active_record/validations/associated.rb +5 -4
- data/lib/active_record/validations/uniqueness.rb +4 -4
- data/lib/active_record/validations.rb +1 -1
- data/lib/active_record/version.rb +2 -2
- data/lib/active_record.rb +28 -2
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb +9 -3
- data/lib/rails/generators/active_record/model/model_generator.rb +5 -1
- data/lib/rails/generators/active_record/model/templates/migration.rb +3 -5
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +1 -5
- metadata +50 -40
- checksums.yaml +0 -7
- data/lib/active_record/named_scope.rb +0 -200
|
@@ -83,7 +83,7 @@ module ActiveRecord
|
|
|
83
83
|
#
|
|
84
84
|
# Example for find with a lock: Imagine two concurrent transactions:
|
|
85
85
|
# each will read <tt>person.visits == 2</tt>, add 1 to it, and save, resulting
|
|
86
|
-
# in two saves of <tt>person.visits = 3</tt>.
|
|
86
|
+
# in two saves of <tt>person.visits = 3</tt>. By locking the row, the second
|
|
87
87
|
# transaction has to wait until the first is finished; we get the
|
|
88
88
|
# expected <tt>person.visits == 4</tt>.
|
|
89
89
|
#
|
|
@@ -134,7 +134,7 @@ module ActiveRecord
|
|
|
134
134
|
def last(*args)
|
|
135
135
|
if args.any?
|
|
136
136
|
if args.first.kind_of?(Integer) || (loaded? && !args.first.kind_of?(Hash))
|
|
137
|
-
if order_values.empty?
|
|
137
|
+
if order_values.empty?
|
|
138
138
|
order("#{primary_key} DESC").limit(*args).reverse
|
|
139
139
|
else
|
|
140
140
|
to_a.last(*args)
|
|
@@ -191,7 +191,7 @@ module ActiveRecord
|
|
|
191
191
|
|
|
192
192
|
join_dependency = construct_join_dependency_for_association_find
|
|
193
193
|
relation = construct_relation_for_association_find(join_dependency)
|
|
194
|
-
relation = relation.except(:select).select("1").limit(1)
|
|
194
|
+
relation = relation.except(:select, :order).select("1").limit(1)
|
|
195
195
|
|
|
196
196
|
case id
|
|
197
197
|
when Array, Hash
|
|
@@ -200,7 +200,7 @@ module ActiveRecord
|
|
|
200
200
|
relation = relation.where(table[primary_key].eq(id)) if id
|
|
201
201
|
end
|
|
202
202
|
|
|
203
|
-
connection.select_value(relation) ? true : false
|
|
203
|
+
connection.select_value(relation, "#{name} Exists") ? true : false
|
|
204
204
|
end
|
|
205
205
|
|
|
206
206
|
protected
|
|
@@ -208,7 +208,7 @@ module ActiveRecord
|
|
|
208
208
|
def find_with_associations
|
|
209
209
|
join_dependency = construct_join_dependency_for_association_find
|
|
210
210
|
relation = construct_relation_for_association_find(join_dependency)
|
|
211
|
-
rows = connection.select_all(relation, 'SQL', relation.bind_values
|
|
211
|
+
rows = connection.select_all(relation, 'SQL', relation.bind_values)
|
|
212
212
|
join_dependency.instantiate(rows)
|
|
213
213
|
rescue ThrowResult
|
|
214
214
|
[]
|
|
@@ -265,6 +265,7 @@ module ActiveRecord
|
|
|
265
265
|
if match.bang? && result.blank?
|
|
266
266
|
raise RecordNotFound, "Couldn't find #{@klass.name} with #{conditions.to_a.collect {|p| p.join(' = ')}.join(', ')}"
|
|
267
267
|
else
|
|
268
|
+
yield(result) if block_given?
|
|
268
269
|
result
|
|
269
270
|
end
|
|
270
271
|
end
|
|
@@ -1,21 +1,16 @@
|
|
|
1
1
|
module ActiveRecord
|
|
2
2
|
class PredicateBuilder # :nodoc:
|
|
3
|
-
def self.build_from_hash(engine, attributes, default_table
|
|
3
|
+
def self.build_from_hash(engine, attributes, default_table)
|
|
4
4
|
predicates = attributes.map do |column, value|
|
|
5
5
|
table = default_table
|
|
6
6
|
|
|
7
|
-
if
|
|
7
|
+
if value.is_a?(Hash)
|
|
8
8
|
table = Arel::Table.new(column, engine)
|
|
9
|
-
|
|
10
|
-
if value.empty?
|
|
11
|
-
'1 = 2'
|
|
12
|
-
else
|
|
13
|
-
build_from_hash(engine, value, table, false)
|
|
14
|
-
end
|
|
9
|
+
build_from_hash(engine, value, table)
|
|
15
10
|
else
|
|
16
11
|
column = column.to_s
|
|
17
12
|
|
|
18
|
-
if
|
|
13
|
+
if column.include?('.')
|
|
19
14
|
table_name, column = column.split('.', 2)
|
|
20
15
|
table = Arel::Table.new(table_name, engine)
|
|
21
16
|
end
|
|
@@ -27,21 +22,23 @@ module ActiveRecord
|
|
|
27
22
|
value = value.select(value.klass.arel_table[value.klass.primary_key]) if value.select_values.empty?
|
|
28
23
|
attribute.in(value.arel.ast)
|
|
29
24
|
when Array, ActiveRecord::Associations::CollectionProxy
|
|
30
|
-
values = value.to_a.map {
|
|
31
|
-
|
|
32
|
-
|
|
25
|
+
values = value.to_a.map {|x| x.is_a?(ActiveRecord::Base) ? x.id : x}
|
|
26
|
+
ranges, values = values.partition {|v| v.is_a?(Range) || v.is_a?(Arel::Relation)}
|
|
27
|
+
|
|
28
|
+
array_predicates = ranges.map {|range| attribute.in(range)}
|
|
33
29
|
|
|
34
30
|
if values.include?(nil)
|
|
35
31
|
values = values.compact
|
|
36
32
|
if values.empty?
|
|
37
|
-
attribute.eq
|
|
33
|
+
array_predicates << attribute.eq(nil)
|
|
38
34
|
else
|
|
39
|
-
attribute.in(values.compact).or
|
|
35
|
+
array_predicates << attribute.in(values.compact).or(attribute.eq(nil))
|
|
40
36
|
end
|
|
41
37
|
else
|
|
42
|
-
attribute.in(values)
|
|
38
|
+
array_predicates << attribute.in(values)
|
|
43
39
|
end
|
|
44
40
|
|
|
41
|
+
array_predicates.inject {|composite, predicate| composite.or(predicate)}
|
|
45
42
|
when Range, Arel::Relation
|
|
46
43
|
attribute.in(value)
|
|
47
44
|
when ActiveRecord::Base
|
|
@@ -49,10 +46,6 @@ module ActiveRecord
|
|
|
49
46
|
when Class
|
|
50
47
|
# FIXME: I think we need to deprecate this behavior
|
|
51
48
|
attribute.eq(value.name)
|
|
52
|
-
when Integer, ActiveSupport::Duration
|
|
53
|
-
# Arel treats integers as literals, but they should be quoted when compared with strings
|
|
54
|
-
column = engine.connection_pool.columns_hash[table.name][attribute.name.to_s]
|
|
55
|
-
attribute.eq(Arel::Nodes::SqlLiteral.new(engine.connection.quote(value, column)))
|
|
56
49
|
else
|
|
57
50
|
attribute.eq(value)
|
|
58
51
|
end
|
|
@@ -9,7 +9,8 @@ module ActiveRecord
|
|
|
9
9
|
:select_values, :group_values, :order_values, :joins_values,
|
|
10
10
|
:where_values, :having_values, :bind_values,
|
|
11
11
|
:limit_value, :offset_value, :lock_value, :readonly_value, :create_with_value,
|
|
12
|
-
:from_value, :
|
|
12
|
+
:from_value, :reordering_value, :reverse_order_value,
|
|
13
|
+
:uniq_value
|
|
13
14
|
|
|
14
15
|
def includes(*args)
|
|
15
16
|
args.reject! {|a| a.blank? }
|
|
@@ -38,7 +39,7 @@ module ActiveRecord
|
|
|
38
39
|
end
|
|
39
40
|
|
|
40
41
|
# Works in two unique ways.
|
|
41
|
-
#
|
|
42
|
+
#
|
|
42
43
|
# First: takes a block so it can be used just like Array#select.
|
|
43
44
|
#
|
|
44
45
|
# Model.scoped.select { |m| m.field == value }
|
|
@@ -56,16 +57,16 @@ module ActiveRecord
|
|
|
56
57
|
# array, it actually returns a relation object and can have other query
|
|
57
58
|
# methods appended to it, such as the other methods in ActiveRecord::QueryMethods.
|
|
58
59
|
#
|
|
59
|
-
#
|
|
60
|
+
# The argument to the method can also be an array of fields.
|
|
60
61
|
#
|
|
61
|
-
# >> Model.select(:field, :other_field, :and_one_more)
|
|
62
|
+
# >> Model.select([:field, :other_field, :and_one_more])
|
|
62
63
|
# => [#<Model field: "value", other_field: "value", and_one_more: "value">]
|
|
63
64
|
#
|
|
64
65
|
# Any attributes that do not have fields retrieved by a select
|
|
65
|
-
# will
|
|
66
|
+
# will raise a ActiveModel::MissingAttributeError when the getter method for that attribute is used:
|
|
66
67
|
#
|
|
67
68
|
# >> Model.select(:field).first.other_field
|
|
68
|
-
# =>
|
|
69
|
+
# => ActiveModel::MissingAttributeError: missing attribute: other_field
|
|
69
70
|
def select(value = Proc.new)
|
|
70
71
|
if block_given?
|
|
71
72
|
to_a.select {|*block_args| value.call(*block_args) }
|
|
@@ -92,11 +93,22 @@ module ActiveRecord
|
|
|
92
93
|
relation
|
|
93
94
|
end
|
|
94
95
|
|
|
96
|
+
# Replaces any existing order defined on the relation with the specified order.
|
|
97
|
+
#
|
|
98
|
+
# User.order('email DESC').reorder('id ASC') # generated SQL has 'ORDER BY id ASC'
|
|
99
|
+
#
|
|
100
|
+
# Subsequent calls to order on the same relation will be appended. For example:
|
|
101
|
+
#
|
|
102
|
+
# User.order('email DESC').reorder('id ASC').order('name ASC')
|
|
103
|
+
#
|
|
104
|
+
# generates a query with 'ORDER BY id ASC, name ASC'.
|
|
105
|
+
#
|
|
95
106
|
def reorder(*args)
|
|
96
107
|
return self if args.blank?
|
|
97
108
|
|
|
98
109
|
relation = clone
|
|
99
|
-
relation.
|
|
110
|
+
relation.reordering_value = true
|
|
111
|
+
relation.order_values = args.flatten
|
|
100
112
|
relation
|
|
101
113
|
end
|
|
102
114
|
|
|
@@ -176,6 +188,58 @@ module ActiveRecord
|
|
|
176
188
|
relation
|
|
177
189
|
end
|
|
178
190
|
|
|
191
|
+
# Specifies whether the records should be unique or not. For example:
|
|
192
|
+
#
|
|
193
|
+
# User.select(:name)
|
|
194
|
+
# # => Might return two records with the same name
|
|
195
|
+
#
|
|
196
|
+
# User.select(:name).uniq
|
|
197
|
+
# # => Returns 1 record per unique name
|
|
198
|
+
#
|
|
199
|
+
# User.select(:name).uniq.uniq(false)
|
|
200
|
+
# # => You can also remove the uniqueness
|
|
201
|
+
def uniq(value = true)
|
|
202
|
+
relation = clone
|
|
203
|
+
relation.uniq_value = value
|
|
204
|
+
relation
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Used to extend a scope with additional methods, either through
|
|
208
|
+
# a module or through a block provided.
|
|
209
|
+
#
|
|
210
|
+
# The object returned is a relation, which can be further extended.
|
|
211
|
+
#
|
|
212
|
+
# === Using a module
|
|
213
|
+
#
|
|
214
|
+
# module Pagination
|
|
215
|
+
# def page(number)
|
|
216
|
+
# # pagination code goes here
|
|
217
|
+
# end
|
|
218
|
+
# end
|
|
219
|
+
#
|
|
220
|
+
# scope = Model.scoped.extending(Pagination)
|
|
221
|
+
# scope.page(params[:page])
|
|
222
|
+
#
|
|
223
|
+
# You can also pass a list of modules:
|
|
224
|
+
#
|
|
225
|
+
# scope = Model.scoped.extending(Pagination, SomethingElse)
|
|
226
|
+
#
|
|
227
|
+
# === Using a block
|
|
228
|
+
#
|
|
229
|
+
# scope = Model.scoped.extending do
|
|
230
|
+
# def page(number)
|
|
231
|
+
# # pagination code goes here
|
|
232
|
+
# end
|
|
233
|
+
# end
|
|
234
|
+
# scope.page(params[:page])
|
|
235
|
+
#
|
|
236
|
+
# You can also use a block and a module list:
|
|
237
|
+
#
|
|
238
|
+
# scope = Model.scoped.extending(Pagination) do
|
|
239
|
+
# def per_page(number)
|
|
240
|
+
# # pagination code goes here
|
|
241
|
+
# end
|
|
242
|
+
# end
|
|
179
243
|
def extending(*modules)
|
|
180
244
|
modules << Module.new(&Proc.new) if block_given?
|
|
181
245
|
|
|
@@ -206,16 +270,17 @@ module ActiveRecord
|
|
|
206
270
|
arel.having(*@having_values.uniq.reject{|h| h.blank?}) unless @having_values.empty?
|
|
207
271
|
|
|
208
272
|
arel.take(connection.sanitize_limit(@limit_value)) if @limit_value
|
|
209
|
-
arel.skip(@offset_value
|
|
273
|
+
arel.skip(@offset_value) if @offset_value
|
|
210
274
|
|
|
211
275
|
arel.group(*@group_values.uniq.reject{|g| g.blank?}) unless @group_values.empty?
|
|
212
276
|
|
|
213
|
-
order = @
|
|
277
|
+
order = @order_values
|
|
214
278
|
order = reverse_sql_order(order) if @reverse_order_value
|
|
215
279
|
arel.order(*order.uniq.reject{|o| o.blank?}) unless order.empty?
|
|
216
280
|
|
|
217
281
|
build_select(arel, @select_values.uniq)
|
|
218
282
|
|
|
283
|
+
arel.distinct(@uniq_value)
|
|
219
284
|
arel.from(@from_value) if @from_value
|
|
220
285
|
arel.lock(@lock_value) if @lock_value
|
|
221
286
|
|
|
@@ -331,10 +396,11 @@ module ActiveRecord
|
|
|
331
396
|
|
|
332
397
|
order_query.map do |o|
|
|
333
398
|
case o
|
|
334
|
-
when Arel::Nodes::
|
|
399
|
+
when Arel::Nodes::Ordering
|
|
335
400
|
o.reverse
|
|
336
401
|
when String, Symbol
|
|
337
402
|
o.to_s.split(',').collect do |s|
|
|
403
|
+
s.strip!
|
|
338
404
|
s.gsub!(/\sasc\Z/i, ' DESC') || s.gsub!(/\sdesc\Z/i, ' ASC') || s.concat(' DESC')
|
|
339
405
|
end
|
|
340
406
|
else
|
|
@@ -22,7 +22,7 @@ module ActiveRecord
|
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
(Relation::MULTI_VALUE_METHODS - [:joins, :where]).each do |method|
|
|
25
|
+
(Relation::MULTI_VALUE_METHODS - [:joins, :where, :order]).each do |method|
|
|
26
26
|
value = r.send(:"#{method}_values")
|
|
27
27
|
merged_relation.send(:"#{method}_values=", merged_relation.send(:"#{method}_values") + value) if value.present?
|
|
28
28
|
end
|
|
@@ -48,7 +48,7 @@ module ActiveRecord
|
|
|
48
48
|
|
|
49
49
|
merged_relation.where_values = merged_wheres
|
|
50
50
|
|
|
51
|
-
(Relation::SINGLE_VALUE_METHODS - [:lock, :create_with]).each do |method|
|
|
51
|
+
(Relation::SINGLE_VALUE_METHODS - [:lock, :create_with, :reordering]).each do |method|
|
|
52
52
|
value = r.send(:"#{method}_value")
|
|
53
53
|
merged_relation.send(:"#{method}_value=", value) unless value.nil?
|
|
54
54
|
end
|
|
@@ -57,6 +57,15 @@ module ActiveRecord
|
|
|
57
57
|
|
|
58
58
|
merged_relation = merged_relation.create_with(r.create_with_value) unless r.create_with_value.empty?
|
|
59
59
|
|
|
60
|
+
if (r.reordering_value)
|
|
61
|
+
# override any order specified in the original relation
|
|
62
|
+
merged_relation.reordering_value = true
|
|
63
|
+
merged_relation.order_values = r.order_values
|
|
64
|
+
else
|
|
65
|
+
# merge in order_values from r
|
|
66
|
+
merged_relation.order_values += r.order_values
|
|
67
|
+
end
|
|
68
|
+
|
|
60
69
|
# Apply scope extension modules
|
|
61
70
|
merged_relation.send :apply_modules, r.extensions
|
|
62
71
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
1
2
|
require 'active_support/core_ext/object/blank'
|
|
2
|
-
require 'active_support/core_ext/module/delegation'
|
|
3
3
|
|
|
4
4
|
module ActiveRecord
|
|
5
5
|
# = Active Record Relation
|
|
@@ -7,13 +7,9 @@ module ActiveRecord
|
|
|
7
7
|
JoinOperation = Struct.new(:relation, :join_class, :on)
|
|
8
8
|
ASSOCIATION_METHODS = [:includes, :eager_load, :preload]
|
|
9
9
|
MULTI_VALUE_METHODS = [:select, :group, :order, :joins, :where, :having, :bind]
|
|
10
|
-
SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :from, :
|
|
10
|
+
SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :from, :reordering, :reverse_order, :uniq]
|
|
11
11
|
|
|
12
|
-
include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches
|
|
13
|
-
|
|
14
|
-
# These are explicitly delegated to improve performance (avoids method_missing)
|
|
15
|
-
delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to => :to_a
|
|
16
|
-
delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key, :to => :klass
|
|
12
|
+
include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation
|
|
17
13
|
|
|
18
14
|
attr_reader :table, :klass, :loaded
|
|
19
15
|
attr_accessor :extensions, :default_scoped
|
|
@@ -81,7 +77,6 @@ module ActiveRecord
|
|
|
81
77
|
end
|
|
82
78
|
|
|
83
79
|
def initialize_copy(other)
|
|
84
|
-
@bind_values = @bind_values.dup
|
|
85
80
|
reset
|
|
86
81
|
end
|
|
87
82
|
|
|
@@ -95,14 +90,77 @@ module ActiveRecord
|
|
|
95
90
|
scoping { @klass.create!(*args, &block) }
|
|
96
91
|
end
|
|
97
92
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
93
|
+
# Tries to load the first record; if it fails, then <tt>create</tt> is called with the same arguments as this method.
|
|
94
|
+
#
|
|
95
|
+
# Expects arguments in the same format as <tt>Base.create</tt>.
|
|
96
|
+
#
|
|
97
|
+
# ==== Examples
|
|
98
|
+
# # Find the first user named Penélope or create a new one.
|
|
99
|
+
# User.where(:first_name => 'Penélope').first_or_create
|
|
100
|
+
# # => <User id: 1, first_name: 'Penélope', last_name: nil>
|
|
101
|
+
#
|
|
102
|
+
# # Find the first user named Penélope or create a new one.
|
|
103
|
+
# # We already have one so the existing record will be returned.
|
|
104
|
+
# User.where(:first_name => 'Penélope').first_or_create
|
|
105
|
+
# # => <User id: 1, first_name: 'Penélope', last_name: nil>
|
|
106
|
+
#
|
|
107
|
+
# # Find the first user named Scarlett or create a new one with a particular last name.
|
|
108
|
+
# User.where(:first_name => 'Scarlett').first_or_create(:last_name => 'Johansson')
|
|
109
|
+
# # => <User id: 2, first_name: 'Scarlett', last_name: 'Johansson'>
|
|
110
|
+
#
|
|
111
|
+
# # Find the first user named Scarlett or create a new one with a different last name.
|
|
112
|
+
# # We already have one so the existing record will be returned.
|
|
113
|
+
# User.where(:first_name => 'Scarlett').first_or_create do |user|
|
|
114
|
+
# user.last_name = "O'Hara"
|
|
115
|
+
# end
|
|
116
|
+
# # => <User id: 2, first_name: 'Scarlett', last_name: 'Johansson'>
|
|
117
|
+
def first_or_create(attributes = nil, options = {}, &block)
|
|
118
|
+
first || create(attributes, options, &block)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Like <tt>first_or_create</tt> but calls <tt>create!</tt> so an exception is raised if the created record is invalid.
|
|
122
|
+
#
|
|
123
|
+
# Expects arguments in the same format as <tt>Base.create!</tt>.
|
|
124
|
+
def first_or_create!(attributes = nil, options = {}, &block)
|
|
125
|
+
first || create!(attributes, options, &block)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Like <tt>first_or_create</tt> but calls <tt>new</tt> instead of <tt>create</tt>.
|
|
129
|
+
#
|
|
130
|
+
# Expects arguments in the same format as <tt>Base.new</tt>.
|
|
131
|
+
def first_or_initialize(attributes = nil, options = {}, &block)
|
|
132
|
+
first || new(attributes, options, &block)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Runs EXPLAIN on the query or queries triggered by this relation and
|
|
136
|
+
# returns the result as a string. The string is formatted imitating the
|
|
137
|
+
# ones printed by the database shell.
|
|
138
|
+
#
|
|
139
|
+
# Note that this method actually runs the queries, since the results of some
|
|
140
|
+
# are needed by the next ones when eager loading is going on.
|
|
141
|
+
#
|
|
142
|
+
# Please see further details in the
|
|
143
|
+
# {Active Record Query Interface guide}[http://edgeguides.rubyonrails.org/active_record_querying.html#running-explain].
|
|
144
|
+
def explain
|
|
145
|
+
_, queries = collecting_queries_for_explain { exec_queries }
|
|
146
|
+
exec_explain(queries)
|
|
103
147
|
end
|
|
104
148
|
|
|
105
149
|
def to_a
|
|
150
|
+
# We monitor here the entire execution rather than individual SELECTs
|
|
151
|
+
# because from the point of view of the user fetching the records of a
|
|
152
|
+
# relation is a single unit of work. You want to know if this call takes
|
|
153
|
+
# too long, not if the individual queries take too long.
|
|
154
|
+
#
|
|
155
|
+
# It could be the case that none of the queries involved surpass the
|
|
156
|
+
# threshold, and at the same time the sum of them all does. The user
|
|
157
|
+
# should get a query plan logged in that case.
|
|
158
|
+
logging_query_plan do
|
|
159
|
+
exec_queries
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def exec_queries
|
|
106
164
|
return @records if loaded?
|
|
107
165
|
|
|
108
166
|
default_scoped = with_default_scope
|
|
@@ -133,6 +191,7 @@ module ActiveRecord
|
|
|
133
191
|
@loaded = true
|
|
134
192
|
@records
|
|
135
193
|
end
|
|
194
|
+
private :exec_queries
|
|
136
195
|
|
|
137
196
|
def as_json(options = nil) #:nodoc:
|
|
138
197
|
to_a.as_json(options)
|
|
@@ -178,7 +237,7 @@ module ActiveRecord
|
|
|
178
237
|
# Please check unscoped if you want to remove all previous scopes (including
|
|
179
238
|
# the default_scope) during the execution of a block.
|
|
180
239
|
def scoping
|
|
181
|
-
@klass.
|
|
240
|
+
@klass.with_scope(self, :overwrite) { yield }
|
|
182
241
|
end
|
|
183
242
|
|
|
184
243
|
# Updates all records with details given if they match a set of conditions supplied, limits and order can
|
|
@@ -253,8 +312,7 @@ module ActiveRecord
|
|
|
253
312
|
# Person.update(people.keys, people.values)
|
|
254
313
|
def update(id, attributes)
|
|
255
314
|
if id.is_a?(Array)
|
|
256
|
-
idx
|
|
257
|
-
id.collect { |one_id| idx += 1; update(one_id, attributes[idx]) }
|
|
315
|
+
id.each.with_index.map {|one_id, idx| update(one_id, attributes[idx])}
|
|
258
316
|
else
|
|
259
317
|
object = find(id)
|
|
260
318
|
object.update_attributes(attributes)
|
|
@@ -298,7 +356,7 @@ module ActiveRecord
|
|
|
298
356
|
end
|
|
299
357
|
|
|
300
358
|
# Destroy an object (or multiple objects) that has the given id, the object is instantiated first,
|
|
301
|
-
# therefore all callbacks and filters are fired off before the object is deleted.
|
|
359
|
+
# therefore all callbacks and filters are fired off before the object is deleted. This method is
|
|
302
360
|
# less efficient than ActiveRecord#delete but allows cleanup methods and other actions to be run.
|
|
303
361
|
#
|
|
304
362
|
# This essentially finds the object (or multiple objects) with the given id, creates a new object
|
|
@@ -327,7 +385,7 @@ module ActiveRecord
|
|
|
327
385
|
# Deletes the records matching +conditions+ without instantiating the records first, and hence not
|
|
328
386
|
# calling the +destroy+ method nor invoking callbacks. This is a single SQL DELETE statement that
|
|
329
387
|
# goes straight to the database, much more efficient than +destroy_all+. Be careful with relations
|
|
330
|
-
# though, in particular <tt>:dependent</tt> rules defined on associations are not honored.
|
|
388
|
+
# though, in particular <tt>:dependent</tt> rules defined on associations are not honored. Returns
|
|
331
389
|
# the number of rows affected.
|
|
332
390
|
#
|
|
333
391
|
# ==== Parameters
|
|
@@ -395,7 +453,7 @@ module ActiveRecord
|
|
|
395
453
|
end
|
|
396
454
|
|
|
397
455
|
def to_sql
|
|
398
|
-
@to_sql ||= klass.connection.to_sql(arel
|
|
456
|
+
@to_sql ||= klass.connection.to_sql(arel)
|
|
399
457
|
end
|
|
400
458
|
|
|
401
459
|
def where_values_hash
|
|
@@ -447,20 +505,6 @@ module ActiveRecord
|
|
|
447
505
|
end
|
|
448
506
|
end
|
|
449
507
|
|
|
450
|
-
protected
|
|
451
|
-
|
|
452
|
-
def method_missing(method, *args, &block)
|
|
453
|
-
if Array.method_defined?(method)
|
|
454
|
-
to_a.send(method, *args, &block)
|
|
455
|
-
elsif @klass.respond_to?(method)
|
|
456
|
-
scoping { @klass.send(method, *args, &block) }
|
|
457
|
-
elsif arel.respond_to?(method)
|
|
458
|
-
arel.send(method, *args, &block)
|
|
459
|
-
else
|
|
460
|
-
super
|
|
461
|
-
end
|
|
462
|
-
end
|
|
463
|
-
|
|
464
508
|
private
|
|
465
509
|
|
|
466
510
|
def references_eager_loaded_tables?
|
|
@@ -486,6 +530,5 @@ module ActiveRecord
|
|
|
486
530
|
# ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
|
|
487
531
|
string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map{ |s| s.downcase }.uniq - ['raw_sql_']
|
|
488
532
|
end
|
|
489
|
-
|
|
490
533
|
end
|
|
491
534
|
end
|
data/lib/active_record/result.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module ActiveRecord
|
|
2
2
|
###
|
|
3
3
|
# This class encapsulates a Result returned from calling +exec_query+ on any
|
|
4
|
-
# database connection adapter.
|
|
4
|
+
# database connection adapter. For example:
|
|
5
5
|
#
|
|
6
6
|
# x = ActiveRecord::Base.connection.exec_query('SELECT * FROM foo')
|
|
7
7
|
# x # => #<ActiveRecord::Result:0xdeadbeef>
|