activerecord 3.0.0.rc → 3.0.0.rc2
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 +6 -1
- data/README.rdoc +9 -9
- data/lib/active_record/aggregations.rb +64 -51
- data/lib/active_record/association_preload.rb +11 -9
- data/lib/active_record/associations.rb +300 -204
- data/lib/active_record/associations/association_collection.rb +7 -2
- data/lib/active_record/associations/belongs_to_association.rb +9 -5
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +7 -6
- data/lib/active_record/associations/has_many_association.rb +6 -6
- data/lib/active_record/associations/has_many_through_association.rb +4 -3
- data/lib/active_record/associations/has_one_association.rb +7 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +2 -1
- data/lib/active_record/attribute_methods/write.rb +2 -2
- data/lib/active_record/autosave_association.rb +54 -72
- data/lib/active_record/base.rb +167 -108
- data/lib/active_record/callbacks.rb +43 -35
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +8 -11
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +0 -8
- data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +8 -6
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +5 -3
- data/lib/active_record/connection_adapters/mysql_adapter.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +9 -5
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +5 -5
- data/lib/active_record/dynamic_finder_match.rb +3 -3
- data/lib/active_record/dynamic_scope_match.rb +1 -1
- data/lib/active_record/errors.rb +9 -5
- data/lib/active_record/fixtures.rb +36 -22
- data/lib/active_record/locale/en.yml +2 -2
- data/lib/active_record/migration.rb +36 -36
- data/lib/active_record/named_scope.rb +23 -11
- data/lib/active_record/nested_attributes.rb +3 -3
- data/lib/active_record/observer.rb +3 -3
- data/lib/active_record/persistence.rb +44 -29
- data/lib/active_record/railtie.rb +5 -8
- data/lib/active_record/railties/databases.rake +1 -1
- data/lib/active_record/reflection.rb +52 -52
- data/lib/active_record/relation.rb +26 -19
- data/lib/active_record/relation/batches.rb +4 -4
- data/lib/active_record/relation/calculations.rb +58 -34
- data/lib/active_record/relation/finder_methods.rb +21 -12
- data/lib/active_record/relation/query_methods.rb +26 -31
- data/lib/active_record/relation/spawn_methods.rb +17 -5
- data/lib/active_record/schema.rb +1 -1
- data/lib/active_record/schema_dumper.rb +12 -12
- data/lib/active_record/serialization.rb +1 -1
- data/lib/active_record/serializers/xml_serializer.rb +1 -1
- data/lib/active_record/session_store.rb +9 -9
- data/lib/active_record/test_case.rb +2 -2
- data/lib/active_record/timestamp.rb +31 -32
- data/lib/active_record/validations/associated.rb +4 -3
- data/lib/active_record/validations/uniqueness.rb +15 -11
- data/lib/active_record/version.rb +1 -1
- metadata +17 -16
@@ -16,7 +16,11 @@ module ActiveRecord
|
|
16
16
|
config.generators.orm :active_record, :migration => true,
|
17
17
|
:timestamps => true
|
18
18
|
|
19
|
-
config.app_middleware.insert_after "::ActionDispatch::Callbacks",
|
19
|
+
config.app_middleware.insert_after "::ActionDispatch::Callbacks",
|
20
|
+
"ActiveRecord::QueryCache"
|
21
|
+
|
22
|
+
config.app_middleware.insert_after "::ActionDispatch::Callbacks",
|
23
|
+
"ActiveRecord::ConnectionAdapters::ConnectionManagement"
|
20
24
|
|
21
25
|
rake_tasks do
|
22
26
|
load "active_record/railties/databases.rake"
|
@@ -74,13 +78,6 @@ module ActiveRecord
|
|
74
78
|
end
|
75
79
|
end
|
76
80
|
|
77
|
-
initializer "active_record.add_concurrency_middleware" do |app|
|
78
|
-
if app.config.allow_concurrency
|
79
|
-
app.config.middleware.insert_after "::ActionDispatch::Callbacks",
|
80
|
-
"ActiveRecord::ConnectionAdapters::ConnectionManagement"
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
81
|
config.after_initialize do
|
85
82
|
ActiveSupport.on_load(:active_record) do
|
86
83
|
instantiate_observers
|
@@ -339,7 +339,7 @@ namespace :db do
|
|
339
339
|
end
|
340
340
|
|
341
341
|
namespace :structure do
|
342
|
-
desc "Dump the database structure to
|
342
|
+
desc "Dump the database structure to an SQL file"
|
343
343
|
task :dump => :environment do
|
344
344
|
abcs = ActiveRecord::Base.configurations
|
345
345
|
case abcs[Rails.env]["adapter"]
|
@@ -3,14 +3,14 @@ module ActiveRecord
|
|
3
3
|
module Reflection # :nodoc:
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
|
6
|
-
# Reflection
|
7
|
-
# about their associations and aggregations. This information can,
|
8
|
-
# for example, be used in a form builder that
|
9
|
-
# and
|
10
|
-
# and
|
6
|
+
# Reflection enables to interrogate Active Record classes and objects
|
7
|
+
# about their associations and aggregations. This information can,
|
8
|
+
# for example, be used in a form builder that takes an Active Record object
|
9
|
+
# and creates input fields for all of the attributes depending on their type
|
10
|
+
# and displays the associations to other objects.
|
11
11
|
#
|
12
|
-
#
|
13
|
-
# classes
|
12
|
+
# MacroReflection class has info for AggregateReflection and AssociationReflection
|
13
|
+
# classes.
|
14
14
|
module ClassMethods
|
15
15
|
def create_reflection(macro, name, options, active_record)
|
16
16
|
case macro
|
@@ -24,7 +24,7 @@ module ActiveRecord
|
|
24
24
|
reflection
|
25
25
|
end
|
26
26
|
|
27
|
-
# Returns a hash containing all AssociationReflection objects for the current class
|
27
|
+
# Returns a hash containing all AssociationReflection objects for the current class.
|
28
28
|
# Example:
|
29
29
|
#
|
30
30
|
# Invoice.reflections
|
@@ -39,17 +39,17 @@ module ActiveRecord
|
|
39
39
|
reflections.values.select { |reflection| reflection.is_a?(AggregateReflection) }
|
40
40
|
end
|
41
41
|
|
42
|
-
# Returns the AggregateReflection object for the named +aggregation+ (use the symbol).
|
42
|
+
# Returns the AggregateReflection object for the named +aggregation+ (use the symbol).
|
43
43
|
#
|
44
|
-
# Account.reflect_on_aggregation(:balance) #
|
44
|
+
# Account.reflect_on_aggregation(:balance) # => the balance AggregateReflection
|
45
45
|
#
|
46
46
|
def reflect_on_aggregation(aggregation)
|
47
47
|
reflections[aggregation].is_a?(AggregateReflection) ? reflections[aggregation] : nil
|
48
48
|
end
|
49
49
|
|
50
|
-
# Returns an array of AssociationReflection objects for all the
|
51
|
-
# associations in the class. If you only want to reflect on a certain
|
52
|
-
# association type, pass in the symbol (<tt>:has_many</tt>, <tt>:has_one</tt>,
|
50
|
+
# Returns an array of AssociationReflection objects for all the
|
51
|
+
# associations in the class. If you only want to reflect on a certain
|
52
|
+
# association type, pass in the symbol (<tt>:has_many</tt>, <tt>:has_one</tt>,
|
53
53
|
# <tt>:belongs_to</tt>) as the first parameter.
|
54
54
|
#
|
55
55
|
# Example:
|
@@ -78,8 +78,7 @@ module ActiveRecord
|
|
78
78
|
end
|
79
79
|
|
80
80
|
|
81
|
-
# Abstract base class for AggregateReflection and AssociationReflection
|
82
|
-
# describes the interface available for both of those classes. Objects of
|
81
|
+
# Abstract base class for AggregateReflection and AssociationReflection. Objects of
|
83
82
|
# AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
|
84
83
|
class MacroReflection
|
85
84
|
attr_reader :active_record
|
@@ -89,36 +88,35 @@ module ActiveRecord
|
|
89
88
|
end
|
90
89
|
|
91
90
|
# Returns the name of the macro.
|
92
|
-
#
|
93
|
-
# <tt>
|
94
|
-
|
95
|
-
|
96
|
-
end
|
91
|
+
#
|
92
|
+
# <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>:balance</tt>
|
93
|
+
# <tt>has_many :clients</tt> returns <tt>:clients</tt>
|
94
|
+
attr_reader :name
|
97
95
|
|
98
|
-
# Returns the macro type.
|
99
|
-
#
|
100
|
-
# <tt>
|
101
|
-
|
102
|
-
|
103
|
-
end
|
96
|
+
# Returns the macro type.
|
97
|
+
#
|
98
|
+
# <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>:composed_of</tt>
|
99
|
+
# <tt>has_many :clients</tt> returns <tt>:has_many</tt>
|
100
|
+
attr_reader :macro
|
104
101
|
|
105
|
-
# Returns the hash of options used for the macro.
|
106
|
-
#
|
107
|
-
# <tt>
|
108
|
-
|
109
|
-
|
110
|
-
end
|
102
|
+
# Returns the hash of options used for the macro.
|
103
|
+
#
|
104
|
+
# <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>{ :class_name => "Money" }</tt>
|
105
|
+
# <tt>has_many :clients</tt> returns +{}+
|
106
|
+
attr_reader :options
|
111
107
|
|
112
|
-
# Returns the class for the macro.
|
113
|
-
#
|
114
|
-
# <tt>
|
108
|
+
# Returns the class for the macro.
|
109
|
+
#
|
110
|
+
# <tt>composed_of :balance, :class_name => 'Money'</tt> returns the Money class
|
111
|
+
# <tt>has_many :clients</tt> returns the Client class
|
115
112
|
def klass
|
116
113
|
@klass ||= class_name.constantize
|
117
114
|
end
|
118
115
|
|
119
|
-
# Returns the class name for the macro.
|
120
|
-
#
|
121
|
-
# <tt>
|
116
|
+
# Returns the class name for the macro.
|
117
|
+
#
|
118
|
+
# <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>'Money'</tt>
|
119
|
+
# <tt>has_many :clients</tt> returns <tt>'Client'</tt>
|
122
120
|
def class_name
|
123
121
|
@class_name ||= options[:class_name] || derive_class_name
|
124
122
|
end
|
@@ -133,11 +131,6 @@ module ActiveRecord
|
|
133
131
|
@sanitized_conditions ||= klass.send(:sanitize_sql, options[:conditions]) if options[:conditions]
|
134
132
|
end
|
135
133
|
|
136
|
-
# Returns +true+ if +self+ is a +belongs_to+ reflection.
|
137
|
-
def belongs_to?
|
138
|
-
macro == :belongs_to
|
139
|
-
end
|
140
|
-
|
141
134
|
private
|
142
135
|
def derive_class_name
|
143
136
|
name.to_s.camelize
|
@@ -145,15 +138,15 @@ module ActiveRecord
|
|
145
138
|
end
|
146
139
|
|
147
140
|
|
148
|
-
# Holds all the meta-data about an aggregation as it was specified in the
|
141
|
+
# Holds all the meta-data about an aggregation as it was specified in the
|
149
142
|
# Active Record class.
|
150
143
|
class AggregateReflection < MacroReflection #:nodoc:
|
151
144
|
end
|
152
145
|
|
153
|
-
# Holds all the meta-data about an association as it was specified in the
|
146
|
+
# Holds all the meta-data about an association as it was specified in the
|
154
147
|
# Active Record class.
|
155
148
|
class AssociationReflection < MacroReflection #:nodoc:
|
156
|
-
# Returns the target association's class
|
149
|
+
# Returns the target association's class.
|
157
150
|
#
|
158
151
|
# class Author < ActiveRecord::Base
|
159
152
|
# has_many :books
|
@@ -162,7 +155,7 @@ module ActiveRecord
|
|
162
155
|
# Author.reflect_on_association(:books).klass
|
163
156
|
# # => Book
|
164
157
|
#
|
165
|
-
# <b>Note:</b>
|
158
|
+
# <b>Note:</b> Do not call +klass.new+ or +klass.create+ to instantiate
|
166
159
|
# a new association object. Use +build_association+ or +create_association+
|
167
160
|
# instead. This allows plugins to hook into association object creation.
|
168
161
|
def klass
|
@@ -209,6 +202,10 @@ module ActiveRecord
|
|
209
202
|
@primary_key_name ||= options[:foreign_key] || derive_primary_key_name
|
210
203
|
end
|
211
204
|
|
205
|
+
def primary_key_column
|
206
|
+
@primary_key_column ||= klass.columns.find { |c| c.name == klass.primary_key }
|
207
|
+
end
|
208
|
+
|
212
209
|
def association_foreign_key
|
213
210
|
@association_foreign_key ||= @options[:association_foreign_key] || class_name.foreign_key
|
214
211
|
end
|
@@ -273,7 +270,7 @@ module ActiveRecord
|
|
273
270
|
end
|
274
271
|
|
275
272
|
# Returns whether or not this association reflection is for a collection
|
276
|
-
# association. Returns +true+ if the +macro+ is
|
273
|
+
# association. Returns +true+ if the +macro+ is either +has_many+ or
|
277
274
|
# +has_and_belongs_to_many+, +false+ otherwise.
|
278
275
|
def collection?
|
279
276
|
@collection
|
@@ -283,7 +280,7 @@ module ActiveRecord
|
|
283
280
|
# the parent's validation.
|
284
281
|
#
|
285
282
|
# Unless you explicitly disable validation with
|
286
|
-
# <tt>:validate => false</tt>,
|
283
|
+
# <tt>:validate => false</tt>, validation will take place when:
|
287
284
|
#
|
288
285
|
# * you explicitly enable validation; <tt>:validate => true</tt>
|
289
286
|
# * you use autosave; <tt>:autosave => true</tt>
|
@@ -303,6 +300,11 @@ module ActiveRecord
|
|
303
300
|
dependent_conditions
|
304
301
|
end
|
305
302
|
|
303
|
+
# Returns +true+ if +self+ is a +belongs_to+ reflection.
|
304
|
+
def belongs_to?
|
305
|
+
macro == :belongs_to
|
306
|
+
end
|
307
|
+
|
306
308
|
private
|
307
309
|
def derive_class_name
|
308
310
|
class_name = name.to_s.camelize
|
@@ -327,8 +329,6 @@ module ActiveRecord
|
|
327
329
|
# Gets the source of the through reflection. It checks both a singularized
|
328
330
|
# and pluralized form for <tt>:belongs_to</tt> or <tt>:has_many</tt>.
|
329
331
|
#
|
330
|
-
# (The <tt>:tags</tt> association on Tagging below.)
|
331
|
-
#
|
332
332
|
# class Post < ActiveRecord::Base
|
333
333
|
# has_many :taggings
|
334
334
|
# has_many :tags, :through => :taggings
|
@@ -339,7 +339,7 @@ module ActiveRecord
|
|
339
339
|
end
|
340
340
|
|
341
341
|
# Returns the AssociationReflection object specified in the <tt>:through</tt> option
|
342
|
-
# of a HasManyThrough or HasOneThrough association.
|
342
|
+
# of a HasManyThrough or HasOneThrough association.
|
343
343
|
#
|
344
344
|
# class Post < ActiveRecord::Base
|
345
345
|
# has_many :taggings
|
@@ -67,7 +67,8 @@ module ActiveRecord
|
|
67
67
|
preload += @includes_values unless eager_loading?
|
68
68
|
preload.each {|associations| @klass.send(:preload_associations, @records, associations) }
|
69
69
|
|
70
|
-
# @readonly_value is true only if set explicitly. @implicit_readonly is true if there
|
70
|
+
# @readonly_value is true only if set explicitly. @implicit_readonly is true if there
|
71
|
+
# are JOINS and no explicit SELECT.
|
71
72
|
readonly = @readonly_value.nil? ? @implicit_readonly : @readonly_value
|
72
73
|
@records.each { |record| record.readonly! } if readonly
|
73
74
|
|
@@ -99,7 +100,7 @@ module ActiveRecord
|
|
99
100
|
if block_given?
|
100
101
|
to_a.many? { |*block_args| yield(*block_args) }
|
101
102
|
else
|
102
|
-
@limit_value
|
103
|
+
@limit_value ? to_a.many? : size > 1
|
103
104
|
end
|
104
105
|
end
|
105
106
|
|
@@ -108,7 +109,7 @@ module ActiveRecord
|
|
108
109
|
# ==== Example
|
109
110
|
#
|
110
111
|
# Comment.where(:post_id => 1).scoping do
|
111
|
-
# Comment.first
|
112
|
+
# Comment.first # SELECT * FROM comments WHERE post_id = 1
|
112
113
|
# end
|
113
114
|
#
|
114
115
|
# Please check unscoped if you want to remove all previous scopes (including
|
@@ -130,7 +131,8 @@ module ActiveRecord
|
|
130
131
|
# ==== Parameters
|
131
132
|
#
|
132
133
|
# * +updates+ - A string, array, or hash representing the SET part of an SQL statement.
|
133
|
-
# * +conditions+ - A string, array, or hash representing the WHERE part of an SQL statement.
|
134
|
+
# * +conditions+ - A string, array, or hash representing the WHERE part of an SQL statement.
|
135
|
+
# See conditions in the intro.
|
134
136
|
# * +options+ - Additional options are <tt>:limit</tt> and <tt>:order</tt>, see the examples for usage.
|
135
137
|
#
|
136
138
|
# ==== Examples
|
@@ -144,7 +146,7 @@ module ActiveRecord
|
|
144
146
|
# # Update all avatars migrated more than a week ago
|
145
147
|
# Avatar.update_all ['migrated_at = ?', Time.now.utc], ['migrated_at > ?', 1.week.ago]
|
146
148
|
#
|
147
|
-
# # Update all books that match
|
149
|
+
# # Update all books that match conditions, but limit it to 5 ordered by date
|
148
150
|
# Book.update_all "author = 'David'", "title LIKE '%Rails%'", :order => 'created_at', :limit => 5
|
149
151
|
def update_all(updates, conditions = nil, options = {})
|
150
152
|
if conditions || options.present?
|
@@ -152,7 +154,7 @@ module ActiveRecord
|
|
152
154
|
else
|
153
155
|
# Apply limit and order only if they're both present
|
154
156
|
if @limit_value.present? == @order_values.present?
|
155
|
-
arel.update(@klass.send(:sanitize_sql_for_assignment, updates))
|
157
|
+
arel.update(Arel::SqlLiteral.new(@klass.send(:sanitize_sql_for_assignment, updates)))
|
156
158
|
else
|
157
159
|
except(:limit, :order).update_all(updates)
|
158
160
|
end
|
@@ -165,14 +167,14 @@ module ActiveRecord
|
|
165
167
|
# ==== Parameters
|
166
168
|
#
|
167
169
|
# * +id+ - This should be the id or an array of ids to be updated.
|
168
|
-
# * +attributes+ - This should be a hash of attributes
|
170
|
+
# * +attributes+ - This should be a hash of attributes or an array of hashes.
|
169
171
|
#
|
170
172
|
# ==== Examples
|
171
173
|
#
|
172
|
-
# #
|
174
|
+
# # Updates one record
|
173
175
|
# Person.update(15, :user_name => 'Samuel', :group => 'expert')
|
174
176
|
#
|
175
|
-
# #
|
177
|
+
# # Updates multiple records
|
176
178
|
# people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
|
177
179
|
# Person.update(people.keys, people.values)
|
178
180
|
def update(id, attributes)
|
@@ -262,8 +264,8 @@ module ActiveRecord
|
|
262
264
|
# Post.delete_all("person_id = 5 AND (category = 'Something' OR category = 'Else')")
|
263
265
|
# Post.delete_all(["person_id = ? AND (category = ? OR category = ?)", 5, 'Something', 'Else'])
|
264
266
|
#
|
265
|
-
# Both calls delete the affected posts all at once with a single DELETE statement.
|
266
|
-
# If you need to destroy dependent associations or call your <tt>before_*</tt> or
|
267
|
+
# Both calls delete the affected posts all at once with a single DELETE statement.
|
268
|
+
# If you need to destroy dependent associations or call your <tt>before_*</tt> or
|
267
269
|
# +after_destroy+ callbacks, use the +destroy_all+ method instead.
|
268
270
|
def delete_all(conditions = nil)
|
269
271
|
conditions ? where(conditions).delete_all : arel.delete.tap { reset }
|
@@ -314,14 +316,19 @@ module ActiveRecord
|
|
314
316
|
@to_sql ||= arel.to_sql
|
315
317
|
end
|
316
318
|
|
319
|
+
def where_values_hash
|
320
|
+
Hash[@where_values.find_all { |w|
|
321
|
+
w.respond_to?(:operator) && w.operator == :==
|
322
|
+
}.map { |where|
|
323
|
+
[where.operand1.name,
|
324
|
+
where.operand2.respond_to?(:value) ?
|
325
|
+
where.operand2.value : where.operand2]
|
326
|
+
}]
|
327
|
+
end
|
328
|
+
|
317
329
|
def scope_for_create
|
318
330
|
@scope_for_create ||= begin
|
319
|
-
@create_with_value ||
|
320
|
-
if where.is_a?(Arel::Predicates::Equality)
|
321
|
-
hash[where.operand1.name] = where.operand2.respond_to?(:value) ? where.operand2.value : where.operand2
|
322
|
-
end
|
323
|
-
hash
|
324
|
-
end
|
331
|
+
@create_with_value || where_values_hash
|
325
332
|
end
|
326
333
|
end
|
327
334
|
|
@@ -371,7 +378,7 @@ module ActiveRecord
|
|
371
378
|
|
372
379
|
def references_eager_loaded_tables?
|
373
380
|
# always convert table names to downcase as in Oracle quoted table names are in uppercase
|
374
|
-
joined_tables = (tables_in_string(arel.joins(arel)) + [table.name, table.table_alias]).compact.map
|
381
|
+
joined_tables = (tables_in_string(arel.joins(arel)) + [table.name, table.table_alias]).compact.map{ |t| t.downcase }.uniq
|
375
382
|
(tables_in_string(to_sql) - joined_tables).any?
|
376
383
|
end
|
377
384
|
|
@@ -379,7 +386,7 @@ module ActiveRecord
|
|
379
386
|
return [] if string.blank?
|
380
387
|
# always convert table names to downcase as in Oracle quoted table names are in uppercase
|
381
388
|
# ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
|
382
|
-
string.scan(/([a-zA-Z_][\.\w]+).?\./).flatten.map
|
389
|
+
string.scan(/([a-zA-Z_][\.\w]+).?\./).flatten.map{ |s| s.downcase }.uniq - ['raw_sql_']
|
383
390
|
end
|
384
391
|
|
385
392
|
end
|
@@ -50,9 +50,9 @@ module ActiveRecord
|
|
50
50
|
def find_in_batches(options = {})
|
51
51
|
relation = self
|
52
52
|
|
53
|
-
|
54
|
-
|
55
|
-
|
53
|
+
if orders.present? || taken.present?
|
54
|
+
ActiveRecord::Base.logger.warn("Scoped order and limit are ignored, it's forced to be batch order and batch size")
|
55
|
+
end
|
56
56
|
|
57
57
|
if (finder_options = options.except(:start, :batch_size)).present?
|
58
58
|
raise "You can't specify an order, it's forced to be #{batch_order}" if options[:order].present?
|
@@ -73,7 +73,7 @@ module ActiveRecord
|
|
73
73
|
break if records.size < batch_size
|
74
74
|
|
75
75
|
if primary_key_offset = records.last.id
|
76
|
-
records = relation.where(primary_key.gt(primary_key_offset)).
|
76
|
+
records = relation.where(primary_key.gt(primary_key_offset)).to_a
|
77
77
|
else
|
78
78
|
raise "Primary key not included in the custom select clause"
|
79
79
|
end
|
@@ -1,30 +1,38 @@
|
|
1
1
|
require 'active_support/core_ext/object/blank'
|
2
|
+
require 'active_support/core_ext/object/try'
|
2
3
|
|
3
4
|
module ActiveRecord
|
4
5
|
module Calculations
|
5
6
|
# Count operates using three different approaches.
|
6
7
|
#
|
7
8
|
# * Count all: By not passing any parameters to count, it will return a count of all the rows for the model.
|
8
|
-
# * Count using column: By passing a column name to count, it will return a count of all the
|
9
|
+
# * Count using column: By passing a column name to count, it will return a count of all the
|
10
|
+
# rows for the model with supplied column present
|
9
11
|
# * Count using options will find the row count matched by the options used.
|
10
12
|
#
|
11
13
|
# The third approach, count using options, accepts an option hash as the only parameter. The options are:
|
12
14
|
#
|
13
|
-
# * <tt>:conditions</tt>: An SQL fragment like "administrator = 1" or [ "user_name = ?", username ].
|
15
|
+
# * <tt>:conditions</tt>: An SQL fragment like "administrator = 1" or [ "user_name = ?", username ].
|
16
|
+
# See conditions in the intro to ActiveRecord::Base.
|
14
17
|
# * <tt>:joins</tt>: Either an SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id" (rarely needed)
|
15
|
-
# or named associations in the same form used for the <tt>:include</tt> option, which will
|
16
|
-
#
|
18
|
+
# or named associations in the same form used for the <tt>:include</tt> option, which will
|
19
|
+
# perform an INNER JOIN on the associated table(s).
|
20
|
+
# If the value is a string, then the records will be returned read-only since they will have
|
21
|
+
# attributes that do not correspond to the table's columns.
|
17
22
|
# Pass <tt>:readonly => false</tt> to override.
|
18
|
-
# * <tt>:include</tt>: Named associations that should be loaded alongside using LEFT OUTER JOINs.
|
19
|
-
# to already defined associations. When using named associations, count
|
23
|
+
# * <tt>:include</tt>: Named associations that should be loaded alongside using LEFT OUTER JOINs.
|
24
|
+
# The symbols named refer to already defined associations. When using named associations, count
|
25
|
+
# returns the number of DISTINCT items for the model you're counting.
|
20
26
|
# See eager loading under Associations.
|
21
27
|
# * <tt>:order</tt>: An SQL fragment like "created_at DESC, name" (really only used with GROUP BY calculations).
|
22
28
|
# * <tt>:group</tt>: An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
|
23
|
-
# * <tt>:select</tt>: By default, this is * as in SELECT * FROM, but can be changed if you, for example,
|
29
|
+
# * <tt>:select</tt>: By default, this is * as in SELECT * FROM, but can be changed if you, for example,
|
30
|
+
# want to do a join but not
|
24
31
|
# include the joined columns.
|
25
|
-
# * <tt>:distinct</tt>: Set this to true to make this a distinct calculation, such as
|
26
|
-
#
|
27
|
-
#
|
32
|
+
# * <tt>:distinct</tt>: Set this to true to make this a distinct calculation, such as
|
33
|
+
# SELECT COUNT(DISTINCT posts.id) ...
|
34
|
+
# * <tt>:from</tt> - By default, this is the table name of the class, but can be changed to an
|
35
|
+
# alternate table name (or even the name of a database view).
|
28
36
|
#
|
29
37
|
# Examples for counting all:
|
30
38
|
# Person.count # returns the total count of all people
|
@@ -34,12 +42,19 @@ module ActiveRecord
|
|
34
42
|
#
|
35
43
|
# Examples for count with options:
|
36
44
|
# Person.count(:conditions => "age > 26")
|
37
|
-
#
|
38
|
-
#
|
45
|
+
#
|
46
|
+
# # because of the named association, it finds the DISTINCT count using LEFT OUTER JOIN.
|
47
|
+
# Person.count(:conditions => "age > 26 AND job.salary > 60000", :include => :job)
|
48
|
+
#
|
49
|
+
# # finds the number of rows matching the conditions and joins.
|
50
|
+
# Person.count(:conditions => "age > 26 AND job.salary > 60000",
|
51
|
+
# :joins => "LEFT JOIN jobs on jobs.person_id = person.id")
|
52
|
+
#
|
39
53
|
# Person.count('id', :conditions => "age > 26") # Performs a COUNT(id)
|
40
54
|
# Person.count(:all, :conditions => "age > 26") # Performs a COUNT(*) (:all is an alias for '*')
|
41
55
|
#
|
42
|
-
# Note: <tt>Person.count(:all)</tt> will not work because it will use <tt>:all</tt> as the condition.
|
56
|
+
# Note: <tt>Person.count(:all)</tt> will not work because it will use <tt>:all</tt> as the condition.
|
57
|
+
# Use Person.count instead.
|
43
58
|
def count(column_name = nil, options = {})
|
44
59
|
column_name, options = nil, column_name if column_name.is_a?(Hash)
|
45
60
|
calculate(:count, column_name, options)
|
@@ -80,13 +95,15 @@ module ActiveRecord
|
|
80
95
|
calculate(:sum, column_name, options)
|
81
96
|
end
|
82
97
|
|
83
|
-
# This calculates aggregate values in the given column. Methods for count, sum, average,
|
84
|
-
# Options such as <tt>:conditions</tt>,
|
98
|
+
# This calculates aggregate values in the given column. Methods for count, sum, average,
|
99
|
+
# minimum, and maximum have been added as shortcuts. Options such as <tt>:conditions</tt>,
|
100
|
+
# <tt>:order</tt>, <tt>:group</tt>, <tt>:having</tt>, and <tt>:joins</tt> can be passed to customize the query.
|
85
101
|
#
|
86
102
|
# There are two basic forms of output:
|
87
|
-
# * Single aggregate value: The single value is type cast to Fixnum for COUNT, Float
|
88
|
-
#
|
89
|
-
#
|
103
|
+
# * Single aggregate value: The single value is type cast to Fixnum for COUNT, Float
|
104
|
+
# for AVG, and the given column's type for everything else.
|
105
|
+
# * Grouped values: This returns an ordered hash of the values and groups them by the
|
106
|
+
# <tt>:group</tt> option. It takes either a column name, or the name of a belongs_to association.
|
90
107
|
#
|
91
108
|
# values = Person.maximum(:age, :group => 'last_name')
|
92
109
|
# puts values["Drake"]
|
@@ -102,21 +119,30 @@ module ActiveRecord
|
|
102
119
|
# end
|
103
120
|
#
|
104
121
|
# Options:
|
105
|
-
# * <tt>:conditions</tt> - An SQL fragment like "administrator = 1" or [ "user_name = ?", username ].
|
106
|
-
#
|
107
|
-
# * <tt>:
|
108
|
-
#
|
122
|
+
# * <tt>:conditions</tt> - An SQL fragment like "administrator = 1" or [ "user_name = ?", username ].
|
123
|
+
# See conditions in the intro to ActiveRecord::Base.
|
124
|
+
# * <tt>:include</tt>: Eager loading, see Associations for details. Since calculations don't load anything,
|
125
|
+
# the purpose of this is to access fields on joined tables in your conditions, order, or group clauses.
|
126
|
+
# * <tt>:joins</tt> - An SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id".
|
127
|
+
# (Rarely needed).
|
128
|
+
# The records will be returned read-only since they will have attributes that do not correspond to the
|
129
|
+
# table's columns.
|
109
130
|
# * <tt>:order</tt> - An SQL fragment like "created_at DESC, name" (really only used with GROUP BY calculations).
|
110
131
|
# * <tt>:group</tt> - An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
|
111
|
-
# * <tt>:select</tt> - By default, this is * as in SELECT * FROM, but can be changed if you for example
|
112
|
-
# include the joined columns.
|
113
|
-
# * <tt>:distinct</tt> - Set this to true to make this a distinct calculation, such as
|
132
|
+
# * <tt>:select</tt> - By default, this is * as in SELECT * FROM, but can be changed if you for example
|
133
|
+
# want to do a join, but not include the joined columns.
|
134
|
+
# * <tt>:distinct</tt> - Set this to true to make this a distinct calculation, such as
|
135
|
+
# SELECT COUNT(DISTINCT posts.id) ...
|
114
136
|
#
|
115
137
|
# Examples:
|
116
138
|
# Person.calculate(:count, :all) # The same as Person.count
|
117
139
|
# Person.average(:age) # SELECT AVG(age) FROM people...
|
118
|
-
# Person.minimum(:age, :conditions => ['last_name != ?', 'Drake']) # Selects the minimum age for
|
119
|
-
#
|
140
|
+
# Person.minimum(:age, :conditions => ['last_name != ?', 'Drake']) # Selects the minimum age for
|
141
|
+
# # everyone with a last name other than 'Drake'
|
142
|
+
#
|
143
|
+
# # Selects the minimum age for any family without any minors
|
144
|
+
# Person.minimum(:age, :having => 'min(age) > 17', :group => :last_name)
|
145
|
+
#
|
120
146
|
# Person.sum("2 * age")
|
121
147
|
def calculate(operation, column_name, options = {})
|
122
148
|
if options.except(:distinct).present?
|
@@ -137,19 +163,17 @@ module ActiveRecord
|
|
137
163
|
def perform_calculation(operation, column_name, options = {})
|
138
164
|
operation = operation.to_s.downcase
|
139
165
|
|
166
|
+
distinct = nil
|
167
|
+
|
140
168
|
if operation == "count"
|
141
169
|
column_name ||= (select_for_count || :all)
|
142
170
|
|
143
|
-
|
144
|
-
if joins.present? && joins =~ /LEFT OUTER/i
|
171
|
+
if arel.joins(arel) =~ /LEFT OUTER/i
|
145
172
|
distinct = true
|
146
173
|
column_name = @klass.primary_key if column_name == :all
|
147
174
|
end
|
148
175
|
|
149
|
-
distinct = nil if column_name
|
150
|
-
distinct ||= options[:distinct]
|
151
|
-
else
|
152
|
-
distinct = nil
|
176
|
+
distinct = nil if column_name =~ /\s*DISTINCT\s+/i
|
153
177
|
end
|
154
178
|
|
155
179
|
distinct = options[:distinct] || distinct
|
@@ -256,7 +280,7 @@ module ActiveRecord
|
|
256
280
|
|
257
281
|
def select_for_count
|
258
282
|
if @select_values.present?
|
259
|
-
select = @select_values.join(", ")
|
283
|
+
select = @select_values.join(", ")
|
260
284
|
select if select !~ /(,|\*)/
|
261
285
|
end
|
262
286
|
end
|