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.

Files changed (55) hide show
  1. data/CHANGELOG +6 -1
  2. data/README.rdoc +9 -9
  3. data/lib/active_record/aggregations.rb +64 -51
  4. data/lib/active_record/association_preload.rb +11 -9
  5. data/lib/active_record/associations.rb +300 -204
  6. data/lib/active_record/associations/association_collection.rb +7 -2
  7. data/lib/active_record/associations/belongs_to_association.rb +9 -5
  8. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +7 -6
  9. data/lib/active_record/associations/has_many_association.rb +6 -6
  10. data/lib/active_record/associations/has_many_through_association.rb +4 -3
  11. data/lib/active_record/associations/has_one_association.rb +7 -7
  12. data/lib/active_record/attribute_methods/time_zone_conversion.rb +2 -1
  13. data/lib/active_record/attribute_methods/write.rb +2 -2
  14. data/lib/active_record/autosave_association.rb +54 -72
  15. data/lib/active_record/base.rb +167 -108
  16. data/lib/active_record/callbacks.rb +43 -35
  17. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +8 -11
  18. data/lib/active_record/connection_adapters/abstract/database_limits.rb +1 -1
  19. data/lib/active_record/connection_adapters/abstract/query_cache.rb +0 -8
  20. data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
  21. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +8 -6
  22. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +5 -3
  23. data/lib/active_record/connection_adapters/mysql_adapter.rb +5 -1
  24. data/lib/active_record/connection_adapters/postgresql_adapter.rb +9 -5
  25. data/lib/active_record/connection_adapters/sqlite_adapter.rb +5 -5
  26. data/lib/active_record/dynamic_finder_match.rb +3 -3
  27. data/lib/active_record/dynamic_scope_match.rb +1 -1
  28. data/lib/active_record/errors.rb +9 -5
  29. data/lib/active_record/fixtures.rb +36 -22
  30. data/lib/active_record/locale/en.yml +2 -2
  31. data/lib/active_record/migration.rb +36 -36
  32. data/lib/active_record/named_scope.rb +23 -11
  33. data/lib/active_record/nested_attributes.rb +3 -3
  34. data/lib/active_record/observer.rb +3 -3
  35. data/lib/active_record/persistence.rb +44 -29
  36. data/lib/active_record/railtie.rb +5 -8
  37. data/lib/active_record/railties/databases.rake +1 -1
  38. data/lib/active_record/reflection.rb +52 -52
  39. data/lib/active_record/relation.rb +26 -19
  40. data/lib/active_record/relation/batches.rb +4 -4
  41. data/lib/active_record/relation/calculations.rb +58 -34
  42. data/lib/active_record/relation/finder_methods.rb +21 -12
  43. data/lib/active_record/relation/query_methods.rb +26 -31
  44. data/lib/active_record/relation/spawn_methods.rb +17 -5
  45. data/lib/active_record/schema.rb +1 -1
  46. data/lib/active_record/schema_dumper.rb +12 -12
  47. data/lib/active_record/serialization.rb +1 -1
  48. data/lib/active_record/serializers/xml_serializer.rb +1 -1
  49. data/lib/active_record/session_store.rb +9 -9
  50. data/lib/active_record/test_case.rb +2 -2
  51. data/lib/active_record/timestamp.rb +31 -32
  52. data/lib/active_record/validations/associated.rb +4 -3
  53. data/lib/active_record/validations/uniqueness.rb +15 -11
  54. data/lib/active_record/version.rb +1 -1
  55. 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", "ActiveRecord::QueryCache"
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 a SQL file"
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 allows you 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 took an Active Record object
9
- # and created input fields for all of the attributes depending on their type
10
- # and displayed the associations to other objects.
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
- # You can find the interface for the AggregateReflection and AssociationReflection
13
- # classes in the abstract MacroReflection class.
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). Example:
42
+ # Returns the AggregateReflection object for the named +aggregation+ (use the symbol).
43
43
  #
44
- # Account.reflect_on_aggregation(:balance) # returns the balance AggregateReflection
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 that
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
- # <tt>composed_of :balance, :class_name => 'Money'</tt> will return <tt>:balance</tt>
93
- # <tt>has_many :clients</tt> will return <tt>:clients</tt>
94
- def name
95
- @name
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
- # <tt>composed_of :balance, :class_name => 'Money'</tt> will return <tt>:composed_of</tt>
100
- # <tt>has_many :clients</tt> will return <tt>:has_many</tt>
101
- def macro
102
- @macro
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
- # <tt>composed_of :balance, :class_name => 'Money'</tt> will return <tt>{ :class_name => "Money" }</tt>
107
- # <tt>has_many :clients</tt> will return +{}+
108
- def options
109
- @options
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
- # <tt>composed_of :balance, :class_name => 'Money'</tt> will return the Money class
114
- # <tt>has_many :clients</tt> will return the Client class
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
- # <tt>composed_of :balance, :class_name => 'Money'</tt> will return <tt>'Money'</tt>
121
- # <tt>has_many :clients</tt> will return <tt>'Client'</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> do not call +klass.new+ or +klass.create+ to instantiate
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 one of +has_many+ or
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>, it will take place when:
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. Example:
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 are JOINS and no explicit SELECT.
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.present? ? to_a.many? : size > 1
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 #=> SELECT * FROM comments WHERE post_id = 1
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. See conditions in the intro.
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 our conditions, but limit it to 5 ordered by date
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 to be set on the object, or an array of hashes.
170
+ # * +attributes+ - This should be a hash of attributes or an array of hashes.
169
171
  #
170
172
  # ==== Examples
171
173
  #
172
- # # Updating one record:
174
+ # # Updates one record
173
175
  # Person.update(15, :user_name => 'Samuel', :group => 'expert')
174
176
  #
175
- # # Updating multiple records:
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 || @where_values.inject({}) do |hash, where|
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(&:downcase).uniq
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(&:downcase).uniq - ['raw_sql_']
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
- 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
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)).all
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 rows for the model with supplied column present
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 ]. See conditions in the intro to ActiveRecord::Base.
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 perform an INNER JOIN on the associated table(s).
16
- # If the value is a string, then the records will be returned read-only since they will have attributes that do not correspond to the table's columns.
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. The symbols named refer
19
- # to already defined associations. When using named associations, count returns the number of DISTINCT items for the model you're counting.
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, want to do a join but not
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 SELECT COUNT(DISTINCT posts.id) ...
26
- # * <tt>:from</tt> - By default, this is the table name of the class, but can be changed to an alternate table name (or even the name
27
- # of a database view).
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
- # Person.count(:conditions => "age > 26 AND job.salary > 60000", :include => :job) # because of the named association, it finds the DISTINCT count using LEFT OUTER JOIN.
38
- # Person.count(:conditions => "age > 26 AND job.salary > 60000", :joins => "LEFT JOIN jobs on jobs.person_id = person.id") # finds the number of rows matching the conditions and joins.
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. Use Person.count instead.
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, minimum, and maximum have been added as shortcuts.
84
- # Options such as <tt>:conditions</tt>, <tt>:order</tt>, <tt>:group</tt>, <tt>:having</tt>, and <tt>:joins</tt> can be passed to customize the query.
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 for AVG, and the given column's type for everything else.
88
- # * Grouped values: This returns an ordered hash of the values and groups them by the <tt>:group</tt> option. It takes either a column name, or the name
89
- # of a belongs_to association.
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 ]. See conditions in the intro to ActiveRecord::Base.
106
- # * <tt>:include</tt>: Eager loading, see Associations for details. Since calculations don't load anything, the purpose of this is to access fields on joined tables in your conditions, order, or group clauses.
107
- # * <tt>:joins</tt> - An SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id". (Rarely needed).
108
- # The records will be returned read-only since they will have attributes that do not correspond to the table's columns.
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 want to do a join, but not
112
- # include the joined columns.
113
- # * <tt>:distinct</tt> - Set this to true to make this a distinct calculation, such as SELECT COUNT(DISTINCT posts.id) ...
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 everyone with a last name other than 'Drake'
119
- # Person.minimum(:age, :having => 'min(age) > 17', :group => :last_name) # Selects the minimum age for any family without any minors
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
- joins = arel.joins(arel)
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.to_s =~ /\s*DISTINCT\s+/i
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