Enumerable sum oddity


#1

Hi.

In this setup “account has_many :consultations”. Can anyone explain
the following behaviour to me? Rails 1.2.2. The “sum” method should
return 0 for an empty array.

a = Account.find(1)
=> #<Account:0x39c80a8 @attributes={…}

a.consultations
=> []

a.consultations.sum(&:foo)
ArgumentError: wrong number of arguments (1 for 2)
from ./script/…/config/…/config/…/vendor/rails/activerecord/
lib/active_record/associations/has_many_through_association.rb:110:in
calculate' from ./script/../config/../config/../vendor/rails/activerecord/ lib/active_record/associations/has_many_through_association.rb:110:insend’
from ./script/…/config/…/config/…/vendor/rails/activerecord/
lib/active_record/associations/has_many_through_association.rb:110:in
method_missing' from ./script/../config/../config/../vendor/rails/activerecord/ lib/active_record/base.rb:946:inwith_scope’
from ./script/…/config/…/config/…/vendor/rails/activerecord/
lib/active_record/associations/has_many_through_association.rb:110:in
method_missing' from ./script/../config/../config/../vendor/rails/activerecord/ lib/active_record/associations/has_many_through_association.rb:102:insum’
from (irb):15

But if I use a hard coded empty array, things work:

[].sum(&:foo)
=> 0

What’s happening? Thanks.

Morten


#2

Hey Morten,

yeah, I feel your pain sharply, because it happened to me too.

It turns out that associations favor the ActiveRecord sum method
(which issues an sql query) over the Enumerable sum method (which
iterates over the collection).

Here’s something that’s not fully tested by me but you can stick it
in your environment.rb and see how it works for you. Once I test it
better I’ll submit it as a patch.

http://pastie.caboo.se/48812

When you don’t pass a block to sum() it’ll end up delegating to the
AR calculate() method, otherwise it iterates.

so:

collection.sum :id # => issues an sql query via calculate()
collection.sum &:id => iterates over the collection due to :id.to_proc

HTH
Trevor


#3

Thanks Trevor. I also got informed that an explicit .to_a will work
(which it did for me):

collection.to_a.sum(&:id)

Br,

Morten