Metoda sum

Czy wy też macie czasem problem z poprawnym działaniem metody sum?

#v = transfers_between(start_day , end_day).sum {|t| t.value_by_category(self)} tt = transfers_between(start_day , end_day) sum = 0 tt.each do |t| sum += t.value_by_category(self) end v = sum
Pierwsza linijka nie działa (dlatego jest zakomentowana) i muszę używać tych kilku, które napisałem niżej.
Jakiś pomysł czego to może być przyczyna?

Spróbuj zrobić:

transfers_between(start_day , end_day).inject(0) { |sum, t| sum += t.value_by_category(self) }

i powiedz jaki jest rezultat.

Jeśli zadeklaruję na początku sum = 0 to jak najbardziej działa. Ale co z metodą sum?

Właściwie nie wiadomo, co jest w transfers_between(start_day, end_day).

Są dwie metody sum. Jedna dla typu Enumerable, druga dla typu ActiveRecord. Pierwsza robi dokładnie to, co wkleił jszumiec, a druga, z tego co pamiętam, wywołuje zapytanie SQL-owe z SUM() dla danego pola.

Wklej (z script/console czy tam breakpoint):

transfers_between(start_day, end_day)
transfers_between(start_day, end_day).class
transfers_between(start_day, end_day).collect { |x| t.value_by_category(self) }

[code]>> Category.find(4).transfers_between()
=> [#<Transfer:0xb73ffd74 @attributes={“id”=>“6”, “day”=>“2007-04-09”, “description”=>“dfs”, “user_id”=>“1”}>,
#<Transfer:0xb73ffc70 @attributes={“id”=>“14”, “day”=>“2007-04-08”, “description”=>“Cosik”, “user_id”=>“1”}>,
#<Transfer:0xb73ffba8 @attributes={“id”=>“15”, “day”=>“2007-04-13”, “description”=>“abcd”, “user_id”=>“1”}>]

Category.find(4).transfers_between().class
=> Array

Category.find(4).transfers_between().collect { |t| t.value_by_category(self) }
=> [0, 0, 0]
Ale w konsoli inaczej dziala self więc:

Category.find(4).transfers_between().collect { |t| t.value_by_category( Category.find(4) ) }
=> [1001, 2500, 1]

Category.find(4).transfers_between().sum { |t| t.value_by_category( Category.find(4) ) }
ArgumentError: wrong number of arguments (1 for 2)
from …/activerecord-1.15.2/lib/active_record/associations/has_many_through_association.rb:110:in calculate' from .../activerecord-1.15.2/lib/active_record/associations/has_many_through_association.rb:110:in method_missing’
from …/activerecord-1.15.2/lib/active_record/base.rb:946:in with_scope' from .../activerecord-1.15.2/lib/active_record/associations/has_many_through_association.rb:110:in method_missing’
from /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.2/lib/active_record/associations/has_many_through_association.rb:102:in `sum’
from (irb):23
from :0

[2,3,4].sum
=> 9
[2,3,4].sum { |a| a+2 }
=> 15[/code]

No cóż.

Rzucając okiem na has_many_through_association.rb:102 widzimy, że wywoływane jest sum od ActiveRecorda, a nie sum od Enumerable. Ponieważ jest to has_many, jest ono nadpisywane przez ActiveRecorda, który chce wywoływać SQL-a.

Właściwie to niewiele możesz z tym zrobić.

Ot tak, na szybko i brzydko:

Category.find(4).transfers_between().collect { |t| t.value_by_category( Category.find(4)) }.sum

Nie jestem też pewien, czy tego rodzaju hack nie da pożądanego efektu:

Category.find(4).transfers_between().to_a.sum { |t| t.value_by_category(Category.find(4)) }

Tyle.

Oba działają, pierwszy rozumiem dlaczego a drugi niezupełnie :slight_smile:

Metoda to_a tworzy Array z danego obiektu. W tym wypadku tworzy Array z Array, z tym że pierwszy Array przez to, iż był utworzony za pomocą has_many miał nadpisaną metodę sum na wywoływanie SQL-a, podczas gdy drugi Array wywołuje już oryginalne sum.

To jest bardzo nieeleganckiego rozwiązanie, i pewnie po tygodniu zapomnisz o co właściwie chodziło. Rekomendowałbym pierwsze. :slight_smile:

Właśnie to zastosowałem :slight_smile: