Skip to content

Commit a9aeba6

Browse files
Merge branch 'deprecate-calculations-with-block'
Follow up of the discussion from the original merge commit: rails@f9cb645#commitcomment-1414561 We want to avoid people's mistakes with methods like count and sum when called with a block, that can easily lead to code performing poorly and that could be way better written with a db query. Please check the discussion there for more background. Closes rails#8268
2 parents ae934ae + fd1c457 commit a9aeba6

File tree

6 files changed

+25
-49
lines changed

6 files changed

+25
-49
lines changed

activerecord/CHANGELOG.md

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
## Rails 4.0.0 (unreleased) ##
22

3+
* Deprecate calling `Relation#sum` with a block. To perform a calculation over
4+
the array result of the relation, use `to_a.sum(&block)`.
5+
6+
*Carlos Antonio da Silva*
7+
38
* Fix postgresql adapter to handle BC timestamps correctly
49

510
HistoryEvent.create!(:name => "something", :occured_at => Date.new(0) - 5.years)
@@ -736,13 +741,6 @@
736741

737742
*Marc-André Lafortune*
738743

739-
* Allow blocks for `count` with `ActiveRecord::Relation`, to work similar as
740-
`Array#count`:
741-
742-
Person.where("age > 26").count { |person| person.gender == 'female' }
743-
744-
*Chris Finne & Carlos Antonio da Silva*
745-
746744
* Added support to `CollectionAssociation#delete` for passing `fixnum`
747745
or `string` values as record ids. This finds the records responding
748746
to the `id` and executes delete on them.

activerecord/lib/active_record/associations.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ def association_instance_set(name, association)
234234
# others.size | X | X | X
235235
# others.length | X | X | X
236236
# others.count | X | X | X
237-
# others.sum(args*,&block) | X | X | X
237+
# others.sum(*args) | X | X | X
238238
# others.empty? | X | X | X
239239
# others.clear | X | X | X
240240
# others.delete(other,other,...) | X | X | X

activerecord/lib/active_record/associations/collection_association.rb

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -161,15 +161,6 @@ def destroy_all
161161
end
162162
end
163163

164-
# Calculate sum using SQL, not Enumerable.
165-
def sum(*args)
166-
if block_given?
167-
scope.sum(*args) { |*block_args| yield(*block_args) }
168-
else
169-
scope.sum(*args)
170-
end
171-
end
172-
173164
# Count all records using SQL. If the +:counter_sql+ or +:finder_sql+ option is set for the
174165
# association, it will be used for the query. Otherwise, construct options and pass them with
175166
# scope to the target class's +count+.

activerecord/lib/active_record/relation/calculations.rb

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
require 'active_support/deprecation'
2+
13
module ActiveRecord
24
module Calculations
35
# Count the records.
@@ -13,16 +15,9 @@ module Calculations
1315
#
1416
# Person.count(:age, distinct: true)
1517
# # => counts the number of different age values
16-
#
17-
# Person.where("age > 26").count { |person| person.gender == 'female' }
18-
# # => queries people where "age > 26" then count the loaded results filtering by gender
1918
def count(column_name = nil, options = {})
20-
if block_given?
21-
self.to_a.count { |item| yield item }
22-
else
23-
column_name, options = nil, column_name if column_name.is_a?(Hash)
24-
calculate(:count, column_name, options)
25-
end
19+
column_name, options = nil, column_name if column_name.is_a?(Hash)
20+
calculate(:count, column_name, options)
2621
end
2722

2823
# Calculates the average value on a given column. Returns +nil+ if there's
@@ -56,13 +51,13 @@ def maximum(column_name, options = {})
5651
# +calculate+ for examples with options.
5752
#
5853
# Person.sum('age') # => 4562
59-
# # => returns the total sum of all people's age
60-
#
61-
# Person.where('age > 100').sum { |person| person.age - 100 }
62-
# # queries people where "age > 100" then perform a sum calculation with the block returns
6354
def sum(*args)
6455
if block_given?
65-
self.to_a.sum(*args) { |item| yield item }
56+
ActiveSupport::Deprecation.warn(
57+
"Calling #sum with a block is deprecated and will be removed in Rails 4.1. " \
58+
"If you want to perform sum calculation over the array of elements, use `to_a.sum(&block)`."
59+
)
60+
self.to_a.sum(*args) {|*block_args| yield(*block_args)}
6661
else
6762
calculate(:sum, *args)
6863
end

activerecord/test/cases/associations/has_many_associations_test.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1658,6 +1658,12 @@ def test_collection_association_with_private_kernel_method
16581658
assert_deprecated { klass.has_many :foo, :counter_sql => 'lol' }
16591659
end
16601660

1661+
test "sum calculation with block for array compatibility is deprecated" do
1662+
assert_deprecated do
1663+
posts(:welcome).comments.sum { |c| c.id }
1664+
end
1665+
end
1666+
16611667
test "has many associations on new records use null relations" do
16621668
post = Post.new
16631669

activerecord/test/cases/calculations_test.rb

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -383,30 +383,16 @@ def test_count_with_from_option
383383
Company.where(:type => "Firm").from('companies').count(:type)
384384
end
385385

386-
def test_count_with_block_acts_as_array
387-
accounts = Account.where('id > 0')
388-
assert_equal Account.count, accounts.count { true }
389-
assert_equal 0, accounts.count { false }
390-
assert_equal Account.where('credit_limit > 50').size, accounts.count { |account| account.credit_limit > 50 }
391-
assert_equal Account.count, Account.count { true }
392-
assert_equal 0, Account.count { false }
393-
end
394-
395-
def test_sum_with_block_acts_as_array
396-
accounts = Account.where('id > 0')
397-
assert_equal Account.sum(:credit_limit), accounts.sum { |account| account.credit_limit }
398-
assert_equal Account.sum(:credit_limit) + Account.count, accounts.sum{ |account| account.credit_limit + 1 }
399-
assert_equal 0, accounts.sum { |account| 0 }
400-
end
401-
402386
def test_sum_with_from_option
403387
assert_equal Account.sum(:credit_limit), Account.from('accounts').sum(:credit_limit)
404388
assert_equal Account.where("credit_limit > 50").sum(:credit_limit),
405389
Account.where("credit_limit > 50").from('accounts').sum(:credit_limit)
406390
end
407391

408-
def test_sum_array_compatibility
409-
assert_equal Account.sum(:credit_limit), Account.sum(&:credit_limit)
392+
def test_sum_array_compatibility_deprecation
393+
assert_deprecated do
394+
assert_equal Account.sum(:credit_limit), Account.sum(&:credit_limit)
395+
end
410396
end
411397

412398
def test_average_with_from_option

0 commit comments

Comments
 (0)