Skip to content

Commit

Permalink
Allow blocks for count with ActiveRecord::Relation. Document and test…
Browse files Browse the repository at this point in the history
… that sum allows blocks
  • Loading branch information
chrisfinne committed Dec 16, 2011
1 parent f307f4d commit 4a6b28d
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 3 deletions.
23 changes: 20 additions & 3 deletions activerecord/lib/active_record/relation/calculations.rb
Expand Up @@ -3,12 +3,13 @@

module ActiveRecord
module Calculations
# Count operates using three different approaches.
# Count operates using four different approaches.
#
# * Count all: By not passing any parameters to count, it will return a count of all the rows for the model.
# * 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.
# * Count using options will find the row count matched by the options used.
# * Count using a block will acts as Array#count
#
# The third approach, count using options, accepts an option hash as the only parameter. The options are:
#
Expand Down Expand Up @@ -51,11 +52,21 @@ module Calculations
# Person.count('id', :conditions => "age > 26") # Performs a COUNT(id)
# Person.count(:all, :conditions => "age > 26") # Performs a COUNT(*) (:all is an alias for '*')
#
# Examples for count with a block:
# Person.where(:conditions => "age > 26").count{|person| gender=='female' }

# Note: <tt>Person.count(:all)</tt> will not work because it will use <tt>:all</tt> as the condition.
# Use Person.count instead.
#
# Note: passing a block to the base ActiveRecord Class will not work <tt>Person.count{|person| gender=='female' }</tt>.
# Use <tt>Person.all.count{|person| gender=='female'}</tt>
def count(column_name = nil, options = {})
column_name, options = nil, column_name if column_name.is_a?(Hash)
calculate(:count, column_name, options)
if block_given?
self.to_a.count {|*block_args| yield(*block_args)}
else
column_name, options = nil, column_name if column_name.is_a?(Hash)
calculate(:count, column_name, options)
end
end

# Calculates the average value on a given column. Returns +nil+ if there's
Expand Down Expand Up @@ -84,6 +95,12 @@ def maximum(column_name, options = {})
calculate(:maximum, column_name, options)
end

# Sum operates using two different approaches:
# * Sum using a block will act as Array#sum
#
# Person.where('age > 100').sum{|person| person.age - 100} # => 11
#
# * Sum as a Calculation on a given column.
# Calculates the sum of values on a given column. The value is returned
# with the same data type of the column, 0 if there's no row. See
# +calculate+ for examples with options.
Expand Down
16 changes: 16 additions & 0 deletions activerecord/test/cases/calculations_test.rb
Expand Up @@ -398,6 +398,22 @@ def test_count_with_from_option
assert_equal Company.count(:type, :conditions => {:type => "Firm"}),
Company.count(:type, :conditions => {:type => "Firm"}, :from => 'companies')
end

def test_count_with_block_acts_as_array
accounts = Account.where('id > 0')
assert_equal Account.count, accounts.count{ true }
assert_equal 0, accounts.count{ false }
assert_equal Account.where('credit_limit > 50').size, accounts.count{|account| account.credit_limit > 50}
assert_equal Account.count, Account.count{true}
assert_equal Account.count, Account.count{false}
end

def test_sum_with_block_acts_as_array
accounts = Account.where('id > 0')
assert_equal Account.sum(:credit_limit), accounts.sum{|account| account.credit_limit }
assert_equal Account.sum(:credit_limit) + Account.count, accounts.sum{|account| account.credit_limit + 1 }
assert_equal 0, accounts.sum{|account| 0 }
end

def test_sum_with_from_option
assert_equal Account.sum(:credit_limit), Account.sum(:credit_limit, :from => 'accounts')
Expand Down

0 comments on commit 4a6b28d

Please sign in to comment.