activerecord 3.1.9 → 3.2.12
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.
- checksums.yaml +6 -6
- data/CHANGELOG.md +317 -336
- data/README.rdoc +3 -3
- data/examples/performance.rb +20 -1
- data/lib/active_record/aggregations.rb +1 -1
- data/lib/active_record/associations/alias_tracker.rb +3 -6
- data/lib/active_record/associations/association.rb +3 -42
- data/lib/active_record/associations/association_scope.rb +3 -15
- 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 +64 -31
- data/lib/active_record/associations/collection_proxy.rb +2 -37
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +1 -0
- data/lib/active_record/associations/has_many_association.rb +5 -1
- data/lib/active_record/associations/has_many_through_association.rb +28 -9
- data/lib/active_record/associations/has_one_association.rb +15 -13
- data/lib/active_record/associations/join_dependency.rb +2 -2
- data/lib/active_record/associations/preloader.rb +14 -10
- data/lib/active_record/associations/through_association.rb +7 -3
- data/lib/active_record/associations.rb +92 -76
- 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 +21 -11
- data/lib/active_record/attribute_methods/primary_key.rb +62 -25
- data/lib/active_record/attribute_methods/read.rb +73 -83
- data/lib/active_record/attribute_methods/serialization.rb +102 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +23 -17
- data/lib/active_record/attribute_methods/write.rb +31 -6
- data/lib/active_record/attribute_methods.rb +231 -30
- data/lib/active_record/autosave_association.rb +43 -22
- data/lib/active_record/base.rb +227 -1708
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +150 -148
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +85 -29
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +6 -33
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +10 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -6
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +37 -26
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +48 -19
- data/lib/active_record/connection_adapters/abstract_adapter.rb +77 -42
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +674 -0
- data/lib/active_record/connection_adapters/column.rb +37 -11
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +129 -581
- data/lib/active_record/connection_adapters/mysql_adapter.rb +137 -696
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +184 -86
- 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 +55 -32
- data/lib/active_record/counter_cache.rb +9 -4
- data/lib/active_record/dynamic_finder_match.rb +12 -0
- data/lib/active_record/dynamic_matchers.rb +84 -0
- data/lib/active_record/errors.rb +11 -1
- data/lib/active_record/explain.rb +85 -0
- data/lib/active_record/explain_subscriber.rb +25 -0
- data/lib/active_record/fixtures/file.rb +65 -0
- data/lib/active_record/fixtures.rb +56 -85
- data/lib/active_record/identity_map.rb +3 -4
- data/lib/active_record/inheritance.rb +174 -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 +68 -35
- data/lib/active_record/model_schema.rb +366 -0
- data/lib/active_record/nested_attributes.rb +3 -2
- data/lib/active_record/persistence.rb +57 -11
- data/lib/active_record/querying.rb +58 -0
- data/lib/active_record/railtie.rb +31 -29
- data/lib/active_record/railties/controller_runtime.rb +3 -1
- data/lib/active_record/railties/databases.rake +191 -110
- 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 +47 -15
- data/lib/active_record/relation/delegation.rb +49 -0
- data/lib/active_record/relation/finder_methods.rb +9 -7
- data/lib/active_record/relation/predicate_builder.rb +18 -7
- data/lib/active_record/relation/query_methods.rb +75 -9
- data/lib/active_record/relation/spawn_methods.rb +11 -2
- data/lib/active_record/relation.rb +78 -32
- data/lib/active_record/result.rb +1 -1
- data/lib/active_record/sanitization.rb +194 -0
- data/lib/active_record/schema_dumper.rb +12 -5
- 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 +4 -45
- data/lib/active_record/session_store.rb +17 -15
- data/lib/active_record/store.rb +52 -0
- data/lib/active_record/test_case.rb +11 -7
- data/lib/active_record/timestamp.rb +17 -3
- data/lib/active_record/transactions.rb +27 -6
- data/lib/active_record/translation.rb +22 -0
- data/lib/active_record/validations/associated.rb +5 -4
- data/lib/active_record/validations/uniqueness.rb +7 -7
- data/lib/active_record/validations.rb +1 -1
- data/lib/active_record/version.rb +2 -2
- data/lib/active_record.rb +38 -3
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb +12 -3
- data/lib/rails/generators/active_record/model/model_generator.rb +9 -1
- data/lib/rails/generators/active_record/model/templates/migration.rb +3 -5
- data/lib/rails/generators/active_record/model/templates/model.rb +5 -0
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +1 -5
- metadata +30 -10
- data/lib/active_record/named_scope.rb +0 -200
|
@@ -66,7 +66,7 @@ module ActiveRecord
|
|
|
66
66
|
calculate(:average, column_name, options)
|
|
67
67
|
end
|
|
68
68
|
|
|
69
|
-
# Calculates the minimum value on a given column.
|
|
69
|
+
# Calculates the minimum value on a given column. The value is returned
|
|
70
70
|
# with the same data type of the column, or +nil+ if there's no row. See
|
|
71
71
|
# +calculate+ for examples with options.
|
|
72
72
|
#
|
|
@@ -89,11 +89,15 @@ module ActiveRecord
|
|
|
89
89
|
# +calculate+ for examples with options.
|
|
90
90
|
#
|
|
91
91
|
# Person.sum('age') # => 4562
|
|
92
|
-
def sum(
|
|
93
|
-
|
|
92
|
+
def sum(*args)
|
|
93
|
+
if block_given?
|
|
94
|
+
self.to_a.sum(*args) {|*block_args| yield(*block_args)}
|
|
95
|
+
else
|
|
96
|
+
calculate(:sum, *args)
|
|
97
|
+
end
|
|
94
98
|
end
|
|
95
99
|
|
|
96
|
-
# This calculates aggregate values in the given column.
|
|
100
|
+
# This calculates aggregate values in the given column. Methods for count, sum, average,
|
|
97
101
|
# minimum, and maximum have been added as shortcuts. Options such as <tt>:conditions</tt>,
|
|
98
102
|
# <tt>:order</tt>, <tt>:group</tt>, <tt>:having</tt>, and <tt>:joins</tt> can be passed to customize the query.
|
|
99
103
|
#
|
|
@@ -101,7 +105,7 @@ module ActiveRecord
|
|
|
101
105
|
# * Single aggregate value: The single value is type cast to Fixnum for COUNT, Float
|
|
102
106
|
# for AVG, and the given column's type for everything else.
|
|
103
107
|
# * Grouped values: This returns an ordered hash of the values and groups them by the
|
|
104
|
-
# <tt>:group</tt> option.
|
|
108
|
+
# <tt>:group</tt> option. It takes either a column name, or the name of a belongs_to association.
|
|
105
109
|
#
|
|
106
110
|
# values = Person.maximum(:age, :group => 'last_name')
|
|
107
111
|
# puts values["Drake"]
|
|
@@ -119,7 +123,7 @@ module ActiveRecord
|
|
|
119
123
|
# Options:
|
|
120
124
|
# * <tt>:conditions</tt> - An SQL fragment like "administrator = 1" or [ "user_name = ?", username ].
|
|
121
125
|
# See conditions in the intro to ActiveRecord::Base.
|
|
122
|
-
# * <tt>:include</tt>: Eager loading, see Associations for details.
|
|
126
|
+
# * <tt>:include</tt>: Eager loading, see Associations for details. Since calculations don't load anything,
|
|
123
127
|
# the purpose of this is to access fields on joined tables in your conditions, order, or group clauses.
|
|
124
128
|
# * <tt>:joins</tt> - An SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id".
|
|
125
129
|
# (Rarely needed).
|
|
@@ -162,6 +166,23 @@ module ActiveRecord
|
|
|
162
166
|
0
|
|
163
167
|
end
|
|
164
168
|
|
|
169
|
+
# This method is designed to perform select by a single column as direct SQL query
|
|
170
|
+
# Returns <tt>Array</tt> with values of the specified column name
|
|
171
|
+
# The values has same data type as column.
|
|
172
|
+
#
|
|
173
|
+
# Examples:
|
|
174
|
+
#
|
|
175
|
+
# Person.pluck(:id) # SELECT people.id FROM people
|
|
176
|
+
# Person.uniq.pluck(:role) # SELECT DISTINCT role FROM people
|
|
177
|
+
# Person.where(:confirmed => true).limit(5).pluck(:id)
|
|
178
|
+
#
|
|
179
|
+
def pluck(column_name)
|
|
180
|
+
column_name = column_name.to_s
|
|
181
|
+
klass.connection.select_all(select(column_name).arel).map! do |attributes|
|
|
182
|
+
klass.type_cast_attribute(attributes.keys.first, klass.initialize_attributes(attributes))
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
165
186
|
private
|
|
166
187
|
|
|
167
188
|
def perform_calculation(operation, column_name, options = {})
|
|
@@ -223,10 +244,16 @@ module ActiveRecord
|
|
|
223
244
|
end
|
|
224
245
|
|
|
225
246
|
def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
247
|
+
group_attrs = @group_values
|
|
248
|
+
|
|
249
|
+
if group_attrs.first.respond_to?(:to_sym)
|
|
250
|
+
association = @klass.reflect_on_association(group_attrs.first.to_sym)
|
|
251
|
+
associated = group_attrs.size == 1 && association && association.macro == :belongs_to # only count belongs_to associations
|
|
252
|
+
group_fields = Array(associated ? association.foreign_key : group_attrs)
|
|
253
|
+
else
|
|
254
|
+
group_fields = group_attrs
|
|
255
|
+
end
|
|
256
|
+
|
|
230
257
|
group_aliases = group_fields.map { |field| column_alias_for(field) }
|
|
231
258
|
group_columns = group_aliases.zip(group_fields).map { |aliaz,field|
|
|
232
259
|
[aliaz, column_for(field)]
|
|
@@ -249,10 +276,14 @@ module ActiveRecord
|
|
|
249
276
|
select_values += @select_values unless @having_values.empty?
|
|
250
277
|
|
|
251
278
|
select_values.concat group_fields.zip(group_aliases).map { |field,aliaz|
|
|
252
|
-
|
|
279
|
+
if field.respond_to?(:as)
|
|
280
|
+
field.as(aliaz)
|
|
281
|
+
else
|
|
282
|
+
"#{field} AS #{aliaz}"
|
|
283
|
+
end
|
|
253
284
|
}
|
|
254
285
|
|
|
255
|
-
relation = except(:group).group(group
|
|
286
|
+
relation = except(:group).group(group)
|
|
256
287
|
relation.select_values = select_values
|
|
257
288
|
|
|
258
289
|
calculated_data = @klass.connection.select_all(relation)
|
|
@@ -264,10 +295,10 @@ module ActiveRecord
|
|
|
264
295
|
end
|
|
265
296
|
|
|
266
297
|
ActiveSupport::OrderedHash[calculated_data.map do |row|
|
|
267
|
-
key
|
|
298
|
+
key = group_columns.map { |aliaz, column|
|
|
268
299
|
type_cast_calculated_value(row[aliaz], column)
|
|
269
300
|
}
|
|
270
|
-
key
|
|
301
|
+
key = key.first if key.size == 1
|
|
271
302
|
key = key_records[key] if associated
|
|
272
303
|
[key, type_cast_calculated_value(row[aggregate_alias], column_for(column_name), operation)]
|
|
273
304
|
end]
|
|
@@ -282,6 +313,7 @@ module ActiveRecord
|
|
|
282
313
|
# column_alias_for("count(*)") # => "count_all"
|
|
283
314
|
# column_alias_for("count", "id") # => "count_id"
|
|
284
315
|
def column_alias_for(*keys)
|
|
316
|
+
keys.map! {|k| k.respond_to?(:to_sql) ? k.to_sql : k}
|
|
285
317
|
table_name = keys.join(' ')
|
|
286
318
|
table_name.downcase!
|
|
287
319
|
table_name.gsub!(/\*/, 'all')
|
|
@@ -293,7 +325,7 @@ module ActiveRecord
|
|
|
293
325
|
end
|
|
294
326
|
|
|
295
327
|
def column_for(field)
|
|
296
|
-
field_name = field.to_s.split('.').last
|
|
328
|
+
field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split('.').last
|
|
297
329
|
@klass.columns.detect { |c| c.name.to_s == field_name }
|
|
298
330
|
end
|
|
299
331
|
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require 'active_support/core_ext/module/delegation'
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module Delegation
|
|
5
|
+
# Set up common delegations for performance (avoids method_missing)
|
|
6
|
+
delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :to => :to_a
|
|
7
|
+
delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
|
|
8
|
+
:connection, :columns_hash, :auto_explain_threshold_in_seconds, :to => :klass
|
|
9
|
+
|
|
10
|
+
def self.delegate_to_scoped_klass(method)
|
|
11
|
+
if method.to_s =~ /\A[a-zA-Z_]\w*[!?]?\z/
|
|
12
|
+
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
13
|
+
def #{method}(*args, &block)
|
|
14
|
+
scoping { @klass.#{method}(*args, &block) }
|
|
15
|
+
end
|
|
16
|
+
RUBY
|
|
17
|
+
else
|
|
18
|
+
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
19
|
+
def #{method}(*args, &block)
|
|
20
|
+
scoping { @klass.send(#{method.inspect}, *args, &block) }
|
|
21
|
+
end
|
|
22
|
+
RUBY
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def respond_to?(method, include_private = false)
|
|
27
|
+
super || Array.method_defined?(method) ||
|
|
28
|
+
@klass.respond_to?(method, include_private) ||
|
|
29
|
+
arel.respond_to?(method, include_private)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
protected
|
|
33
|
+
|
|
34
|
+
def method_missing(method, *args, &block)
|
|
35
|
+
if @klass.respond_to?(method)
|
|
36
|
+
::ActiveRecord::Delegation.delegate_to_scoped_klass(method)
|
|
37
|
+
scoping { @klass.send(method, *args, &block) }
|
|
38
|
+
elsif Array.method_defined?(method)
|
|
39
|
+
::ActiveRecord::Delegation.delegate method, :to => :to_a
|
|
40
|
+
to_a.send(method, *args, &block)
|
|
41
|
+
elsif arel.respond_to?(method)
|
|
42
|
+
::ActiveRecord::Delegation.delegate method, :to => :arel
|
|
43
|
+
arel.send(method, *args, &block)
|
|
44
|
+
else
|
|
45
|
+
super
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -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)
|
|
@@ -185,13 +185,12 @@ module ActiveRecord
|
|
|
185
185
|
# Person.exists?(['name LIKE ?', "%#{query}%"])
|
|
186
186
|
# Person.exists?
|
|
187
187
|
def exists?(id = false)
|
|
188
|
-
return false if id.nil?
|
|
189
|
-
|
|
190
188
|
id = id.id if ActiveRecord::Base === id
|
|
189
|
+
return false if id.nil?
|
|
191
190
|
|
|
192
191
|
join_dependency = construct_join_dependency_for_association_find
|
|
193
192
|
relation = construct_relation_for_association_find(join_dependency)
|
|
194
|
-
relation = relation.except(:select).select("1").limit(1)
|
|
193
|
+
relation = relation.except(:select, :order).select("1 AS one").limit(1)
|
|
195
194
|
|
|
196
195
|
case id
|
|
197
196
|
when Array, Hash
|
|
@@ -200,7 +199,9 @@ module ActiveRecord
|
|
|
200
199
|
relation = relation.where(table[primary_key].eq(id)) if id
|
|
201
200
|
end
|
|
202
201
|
|
|
203
|
-
connection.select_value(relation) ? true : false
|
|
202
|
+
connection.select_value(relation, "#{name} Exists") ? true : false
|
|
203
|
+
rescue ThrowResult
|
|
204
|
+
false
|
|
204
205
|
end
|
|
205
206
|
|
|
206
207
|
protected
|
|
@@ -265,6 +266,7 @@ module ActiveRecord
|
|
|
265
266
|
if match.bang? && result.blank?
|
|
266
267
|
raise RecordNotFound, "Couldn't find #{@klass.name} with #{conditions.to_a.collect {|p| p.join(' = ')}.join(', ')}"
|
|
267
268
|
else
|
|
269
|
+
yield(result) if block_given?
|
|
268
270
|
result
|
|
269
271
|
end
|
|
270
272
|
end
|
|
@@ -289,7 +291,7 @@ module ActiveRecord
|
|
|
289
291
|
r.assign_attributes(unprotected_attributes_for_create, :without_protection => true)
|
|
290
292
|
end
|
|
291
293
|
yield(record) if block_given?
|
|
292
|
-
record.
|
|
294
|
+
record.send(match.save_method) if match.save_record?
|
|
293
295
|
end
|
|
294
296
|
|
|
295
297
|
record
|
|
@@ -6,7 +6,12 @@ module ActiveRecord
|
|
|
6
6
|
|
|
7
7
|
if allow_table_name && value.is_a?(Hash)
|
|
8
8
|
table = Arel::Table.new(column, engine)
|
|
9
|
-
|
|
9
|
+
|
|
10
|
+
if value.empty?
|
|
11
|
+
'1 = 2'
|
|
12
|
+
else
|
|
13
|
+
build_from_hash(engine, value, table, false)
|
|
14
|
+
end
|
|
10
15
|
else
|
|
11
16
|
column = column.to_s
|
|
12
17
|
|
|
@@ -22,21 +27,23 @@ module ActiveRecord
|
|
|
22
27
|
value = value.select(value.klass.arel_table[value.klass.primary_key]) if value.select_values.empty?
|
|
23
28
|
attribute.in(value.arel.ast)
|
|
24
29
|
when Array, ActiveRecord::Associations::CollectionProxy
|
|
25
|
-
values = value.to_a.map {
|
|
26
|
-
|
|
27
|
-
|
|
30
|
+
values = value.to_a.map {|x| x.is_a?(ActiveRecord::Base) ? x.id : x}
|
|
31
|
+
ranges, values = values.partition {|v| v.is_a?(Range) || v.is_a?(Arel::Relation)}
|
|
32
|
+
|
|
33
|
+
array_predicates = ranges.map {|range| attribute.in(range)}
|
|
28
34
|
|
|
29
35
|
if values.include?(nil)
|
|
30
36
|
values = values.compact
|
|
31
37
|
if values.empty?
|
|
32
|
-
attribute.eq
|
|
38
|
+
array_predicates << attribute.eq(nil)
|
|
33
39
|
else
|
|
34
|
-
attribute.in(values.compact).or
|
|
40
|
+
array_predicates << attribute.in(values.compact).or(attribute.eq(nil))
|
|
35
41
|
end
|
|
36
42
|
else
|
|
37
|
-
attribute.in(values)
|
|
43
|
+
array_predicates << attribute.in(values)
|
|
38
44
|
end
|
|
39
45
|
|
|
46
|
+
array_predicates.inject {|composite, predicate| composite.or(predicate)}
|
|
40
47
|
when Range, Arel::Relation
|
|
41
48
|
attribute.in(value)
|
|
42
49
|
when ActiveRecord::Base
|
|
@@ -44,6 +51,10 @@ module ActiveRecord
|
|
|
44
51
|
when Class
|
|
45
52
|
# FIXME: I think we need to deprecate this behavior
|
|
46
53
|
attribute.eq(value.name)
|
|
54
|
+
when Integer, ActiveSupport::Duration
|
|
55
|
+
# Arel treats integers as literals, but they should be quoted when compared with strings
|
|
56
|
+
column = engine.connection.schema_cache.columns_hash[table.name][attribute.name.to_s]
|
|
57
|
+
attribute.eq(Arel::Nodes::SqlLiteral.new(engine.connection.quote(value, column)))
|
|
47
58
|
else
|
|
48
59
|
attribute.eq(value)
|
|
49
60
|
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
|
|
|
@@ -210,12 +274,13 @@ module ActiveRecord
|
|
|
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
|
|
@@ -95,14 +91,77 @@ module ActiveRecord
|
|
|
95
91
|
scoping { @klass.create!(*args, &block) }
|
|
96
92
|
end
|
|
97
93
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
94
|
+
# Tries to load the first record; if it fails, then <tt>create</tt> is called with the same arguments as this method.
|
|
95
|
+
#
|
|
96
|
+
# Expects arguments in the same format as <tt>Base.create</tt>.
|
|
97
|
+
#
|
|
98
|
+
# ==== Examples
|
|
99
|
+
# # Find the first user named Penélope or create a new one.
|
|
100
|
+
# User.where(:first_name => 'Penélope').first_or_create
|
|
101
|
+
# # => <User id: 1, first_name: 'Penélope', last_name: nil>
|
|
102
|
+
#
|
|
103
|
+
# # Find the first user named Penélope or create a new one.
|
|
104
|
+
# # We already have one so the existing record will be returned.
|
|
105
|
+
# User.where(:first_name => 'Penélope').first_or_create
|
|
106
|
+
# # => <User id: 1, first_name: 'Penélope', last_name: nil>
|
|
107
|
+
#
|
|
108
|
+
# # Find the first user named Scarlett or create a new one with a particular last name.
|
|
109
|
+
# User.where(:first_name => 'Scarlett').first_or_create(:last_name => 'Johansson')
|
|
110
|
+
# # => <User id: 2, first_name: 'Scarlett', last_name: 'Johansson'>
|
|
111
|
+
#
|
|
112
|
+
# # Find the first user named Scarlett or create a new one with a different last name.
|
|
113
|
+
# # We already have one so the existing record will be returned.
|
|
114
|
+
# User.where(:first_name => 'Scarlett').first_or_create do |user|
|
|
115
|
+
# user.last_name = "O'Hara"
|
|
116
|
+
# end
|
|
117
|
+
# # => <User id: 2, first_name: 'Scarlett', last_name: 'Johansson'>
|
|
118
|
+
def first_or_create(attributes = nil, options = {}, &block)
|
|
119
|
+
first || create(attributes, options, &block)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Like <tt>first_or_create</tt> but calls <tt>create!</tt> so an exception is raised if the created record is invalid.
|
|
123
|
+
#
|
|
124
|
+
# Expects arguments in the same format as <tt>Base.create!</tt>.
|
|
125
|
+
def first_or_create!(attributes = nil, options = {}, &block)
|
|
126
|
+
first || create!(attributes, options, &block)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Like <tt>first_or_create</tt> but calls <tt>new</tt> instead of <tt>create</tt>.
|
|
130
|
+
#
|
|
131
|
+
# Expects arguments in the same format as <tt>Base.new</tt>.
|
|
132
|
+
def first_or_initialize(attributes = nil, options = {}, &block)
|
|
133
|
+
first || new(attributes, options, &block)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Runs EXPLAIN on the query or queries triggered by this relation and
|
|
137
|
+
# returns the result as a string. The string is formatted imitating the
|
|
138
|
+
# ones printed by the database shell.
|
|
139
|
+
#
|
|
140
|
+
# Note that this method actually runs the queries, since the results of some
|
|
141
|
+
# are needed by the next ones when eager loading is going on.
|
|
142
|
+
#
|
|
143
|
+
# Please see further details in the
|
|
144
|
+
# {Active Record Query Interface guide}[http://edgeguides.rubyonrails.org/active_record_querying.html#running-explain].
|
|
145
|
+
def explain
|
|
146
|
+
_, queries = collecting_queries_for_explain { exec_queries }
|
|
147
|
+
exec_explain(queries)
|
|
103
148
|
end
|
|
104
149
|
|
|
105
150
|
def to_a
|
|
151
|
+
# We monitor here the entire execution rather than individual SELECTs
|
|
152
|
+
# because from the point of view of the user fetching the records of a
|
|
153
|
+
# relation is a single unit of work. You want to know if this call takes
|
|
154
|
+
# too long, not if the individual queries take too long.
|
|
155
|
+
#
|
|
156
|
+
# It could be the case that none of the queries involved surpass the
|
|
157
|
+
# threshold, and at the same time the sum of them all does. The user
|
|
158
|
+
# should get a query plan logged in that case.
|
|
159
|
+
logging_query_plan do
|
|
160
|
+
exec_queries
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def exec_queries
|
|
106
165
|
return @records if loaded?
|
|
107
166
|
|
|
108
167
|
default_scoped = with_default_scope
|
|
@@ -133,6 +192,7 @@ module ActiveRecord
|
|
|
133
192
|
@loaded = true
|
|
134
193
|
@records
|
|
135
194
|
end
|
|
195
|
+
private :exec_queries
|
|
136
196
|
|
|
137
197
|
def as_json(options = nil) #:nodoc:
|
|
138
198
|
to_a.as_json(options)
|
|
@@ -178,7 +238,7 @@ module ActiveRecord
|
|
|
178
238
|
# Please check unscoped if you want to remove all previous scopes (including
|
|
179
239
|
# the default_scope) during the execution of a block.
|
|
180
240
|
def scoping
|
|
181
|
-
@klass.
|
|
241
|
+
@klass.with_scope(self, :overwrite) { yield }
|
|
182
242
|
end
|
|
183
243
|
|
|
184
244
|
# Updates all records with details given if they match a set of conditions supplied, limits and order can
|
|
@@ -253,8 +313,7 @@ module ActiveRecord
|
|
|
253
313
|
# Person.update(people.keys, people.values)
|
|
254
314
|
def update(id, attributes)
|
|
255
315
|
if id.is_a?(Array)
|
|
256
|
-
idx
|
|
257
|
-
id.collect { |one_id| idx += 1; update(one_id, attributes[idx]) }
|
|
316
|
+
id.each.with_index.map {|one_id, idx| update(one_id, attributes[idx])}
|
|
258
317
|
else
|
|
259
318
|
object = find(id)
|
|
260
319
|
object.update_attributes(attributes)
|
|
@@ -298,7 +357,7 @@ module ActiveRecord
|
|
|
298
357
|
end
|
|
299
358
|
|
|
300
359
|
# 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.
|
|
360
|
+
# therefore all callbacks and filters are fired off before the object is deleted. This method is
|
|
302
361
|
# less efficient than ActiveRecord#delete but allows cleanup methods and other actions to be run.
|
|
303
362
|
#
|
|
304
363
|
# This essentially finds the object (or multiple objects) with the given id, creates a new object
|
|
@@ -327,7 +386,7 @@ module ActiveRecord
|
|
|
327
386
|
# Deletes the records matching +conditions+ without instantiating the records first, and hence not
|
|
328
387
|
# calling the +destroy+ method nor invoking callbacks. This is a single SQL DELETE statement that
|
|
329
388
|
# 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.
|
|
389
|
+
# though, in particular <tt>:dependent</tt> rules defined on associations are not honored. Returns
|
|
331
390
|
# the number of rows affected.
|
|
332
391
|
#
|
|
333
392
|
# ==== Parameters
|
|
@@ -344,6 +403,8 @@ module ActiveRecord
|
|
|
344
403
|
# If you need to destroy dependent associations or call your <tt>before_*</tt> or
|
|
345
404
|
# +after_destroy+ callbacks, use the +destroy_all+ method instead.
|
|
346
405
|
def delete_all(conditions = nil)
|
|
406
|
+
raise ActiveRecordError.new("delete_all doesn't support limit scope") if self.limit_value
|
|
407
|
+
|
|
347
408
|
IdentityMap.repository[symbolized_base_class] = {} if IdentityMap.enabled?
|
|
348
409
|
if conditions
|
|
349
410
|
where(conditions).delete_all
|
|
@@ -447,20 +508,6 @@ module ActiveRecord
|
|
|
447
508
|
end
|
|
448
509
|
end
|
|
449
510
|
|
|
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
511
|
private
|
|
465
512
|
|
|
466
513
|
def references_eager_loaded_tables?
|
|
@@ -486,6 +533,5 @@ module ActiveRecord
|
|
|
486
533
|
# ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
|
|
487
534
|
string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map{ |s| s.downcase }.uniq - ['raw_sql_']
|
|
488
535
|
end
|
|
489
|
-
|
|
490
536
|
end
|
|
491
537
|
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>
|