activerecord 3.1.12 → 3.2.0.rc1
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.
- data/CHANGELOG.md +6263 -103
- data/README.rdoc +2 -2
- data/examples/performance.rb +55 -31
- data/lib/active_record.rb +28 -2
- data/lib/active_record/aggregations.rb +2 -2
- data/lib/active_record/associations.rb +82 -69
- data/lib/active_record/associations/association.rb +2 -37
- 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/attribute_assignment.rb +221 -0
- data/lib/active_record/attribute_methods.rb +212 -32
- 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 +69 -80
- data/lib/active_record/attribute_methods/serialization.rb +89 -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/autosave_association.rb +23 -8
- data/lib/active_record/base.rb +223 -1712
- 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 +7 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +36 -25
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +41 -13
- 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/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 +1 -1
- 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.rb +31 -76
- data/lib/active_record/fixtures/file.rb +65 -0
- data/lib/active_record/identity_map.rb +1 -7
- data/lib/active_record/inheritance.rb +167 -0
- data/lib/active_record/integration.rb +49 -0
- data/lib/active_record/locking/optimistic.rb +19 -11
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +3 -3
- data/lib/active_record/migration.rb +38 -29
- data/lib/active_record/migration/command_recorder.rb +7 -7
- data/lib/active_record/model_schema.rb +362 -0
- data/lib/active_record/nested_attributes.rb +3 -2
- data/lib/active_record/persistence.rb +51 -1
- 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 +133 -77
- data/lib/active_record/readonly_attributes.rb +26 -0
- data/lib/active_record/reflection.rb +7 -15
- data/lib/active_record/relation.rb +78 -35
- 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 +5 -4
- data/lib/active_record/relation/predicate_builder.rb +13 -16
- data/lib/active_record/relation/query_methods.rb +59 -4
- 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.rb +152 -0
- data/lib/active_record/scoping/default.rb +140 -0
- data/lib/active_record/scoping/named.rb +202 -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 +11 -11
- 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.rb +1 -1
- data/lib/active_record/validations/associated.rb +5 -4
- data/lib/active_record/validations/uniqueness.rb +4 -4
- data/lib/active_record/version.rb +3 -3
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +1 -5
- metadata +48 -38
- checksums.yaml +0 -7
- data/lib/active_record/named_scope.rb +0 -200
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
require 'active_support/core_ext/class/attribute'
|
3
|
+
|
4
|
+
module ActiveRecord
|
5
|
+
module ReadonlyAttributes
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
class_attribute :_attr_readonly, :instance_writer => false
|
10
|
+
self._attr_readonly = []
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
# Attributes listed as readonly will be used to create a new record but update operations will
|
15
|
+
# ignore these fields.
|
16
|
+
def attr_readonly(*attributes)
|
17
|
+
self._attr_readonly = Set.new(attributes.map { |a| a.to_s }) + (self._attr_readonly || [])
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns an array of all the attributes that have been specified as readonly.
|
21
|
+
def readonly_attributes
|
22
|
+
self._attr_readonly
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'active_support/core_ext/class/attribute'
|
2
|
-
require 'active_support/core_ext/module/deprecation'
|
3
2
|
require 'active_support/core_ext/object/inclusion'
|
4
3
|
|
5
4
|
module ActiveRecord
|
@@ -125,7 +124,7 @@ module ActiveRecord
|
|
125
124
|
# <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>'Money'</tt>
|
126
125
|
# <tt>has_many :clients</tt> returns <tt>'Client'</tt>
|
127
126
|
def class_name
|
128
|
-
@class_name ||= options[:class_name] || derive_class_name
|
127
|
+
@class_name ||= (options[:class_name] || derive_class_name).to_s
|
129
128
|
end
|
130
129
|
|
131
130
|
# Returns +true+ if +self+ and +other_aggregation+ have the same +name+ attribute, +active_record+ attribute,
|
@@ -178,15 +177,9 @@ module ActiveRecord
|
|
178
177
|
@collection = macro.in?([:has_many, :has_and_belongs_to_many])
|
179
178
|
end
|
180
179
|
|
181
|
-
# This is a hack so that we can tell if build_association was overridden, in order to
|
182
|
-
# provide an appropriate deprecation if the overridden method ignored the &block. Please
|
183
|
-
# see Association#build_record for details.
|
184
|
-
attr_accessor :original_build_association_called # :nodoc
|
185
|
-
|
186
180
|
# Returns a new, unsaved instance of the associated class. +options+ will
|
187
181
|
# be passed to the class's constructor.
|
188
182
|
def build_association(*options, &block)
|
189
|
-
@original_build_association_called = true
|
190
183
|
klass.new(*options, &block)
|
191
184
|
end
|
192
185
|
|
@@ -202,11 +195,6 @@ module ActiveRecord
|
|
202
195
|
@foreign_key ||= options[:foreign_key] || derive_foreign_key
|
203
196
|
end
|
204
197
|
|
205
|
-
def primary_key_name
|
206
|
-
foreign_key
|
207
|
-
end
|
208
|
-
deprecate :primary_key_name => :foreign_key
|
209
|
-
|
210
198
|
def foreign_type
|
211
199
|
@foreign_type ||= options[:foreign_type] || "#{name}_type"
|
212
200
|
end
|
@@ -274,6 +262,10 @@ module ActiveRecord
|
|
274
262
|
[self]
|
275
263
|
end
|
276
264
|
|
265
|
+
def nested?
|
266
|
+
false
|
267
|
+
end
|
268
|
+
|
277
269
|
# An array of arrays of conditions. Each item in the outside array corresponds to a reflection
|
278
270
|
# in the #chain. The inside arrays are simply conditions (and each condition may itself be
|
279
271
|
# a hash, array, arel predicate, etc...)
|
@@ -381,7 +373,7 @@ module ActiveRecord
|
|
381
373
|
delegate :foreign_key, :foreign_type, :association_foreign_key,
|
382
374
|
:active_record_primary_key, :type, :to => :source_reflection
|
383
375
|
|
384
|
-
# Gets the source of the through reflection.
|
376
|
+
# Gets the source of the through reflection. It checks both a singularized
|
385
377
|
# and pluralized form for <tt>:belongs_to</tt> or <tt>:has_many</tt>.
|
386
378
|
#
|
387
379
|
# class Post < ActiveRecord::Base
|
@@ -469,7 +461,7 @@ module ActiveRecord
|
|
469
461
|
source_reflection.source_macro
|
470
462
|
end
|
471
463
|
|
472
|
-
# A through association is nested
|
464
|
+
# A through association is nested if there would be more than one join table
|
473
465
|
def nested?
|
474
466
|
chain.length > 2 || through_reflection.macro == :has_and_belongs_to_many
|
475
467
|
end
|
@@ -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, :reorder, :reverse_order]
|
10
|
+
SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :from, :reorder, :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
|
@@ -403,7 +461,7 @@ module ActiveRecord
|
|
403
461
|
node.left.relation.name == table_name
|
404
462
|
}
|
405
463
|
|
406
|
-
Hash[equalities.map { |where| [where.left.name, where.right] }]
|
464
|
+
Hash[equalities.map { |where| [where.left.name, where.right] }]
|
407
465
|
end
|
408
466
|
|
409
467
|
def scope_for_create
|
@@ -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
|
@@ -66,11 +66,14 @@ module ActiveRecord
|
|
66
66
|
records = relation.where(table[primary_key].gteq(start)).all
|
67
67
|
|
68
68
|
while records.any?
|
69
|
+
records_size = records.size
|
70
|
+
primary_key_offset = records.last.id
|
71
|
+
|
69
72
|
yield records
|
70
73
|
|
71
|
-
break if
|
74
|
+
break if records_size < batch_size
|
72
75
|
|
73
|
-
if primary_key_offset
|
76
|
+
if primary_key_offset
|
74
77
|
records = relation.where(table[primary_key].gt(primary_key_offset)).to_a
|
75
78
|
else
|
76
79
|
raise "Primary key not included in the custom select clause"
|
@@ -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
|
+
scope = self.select(column_name)
|
181
|
+
self.connection.select_values(scope.to_sql).map! do |value|
|
182
|
+
type_cast_using_column(value, column_for(column_name))
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
165
186
|
private
|
166
187
|
|
167
188
|
def perform_calculation(operation, column_name, options = {})
|
@@ -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 Array.method_defined?(method)
|
36
|
+
::ActiveRecord::Delegation.delegate method, :to => :to_a
|
37
|
+
to_a.send(method, *args, &block)
|
38
|
+
elsif @klass.respond_to?(method)
|
39
|
+
::ActiveRecord::Delegation.delegate_to_scoped_klass(method)
|
40
|
+
scoping { @klass.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
|
#
|
@@ -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
|