Extraño comportamiento del sum en un Ar ray devuelto por un has_many de rails

Hola gente,

Llevo un rato dándole vueltas a este asunto y no logro encontrar donde
está el error/mal entendimiento/mal uso/bug/feature…

Resulta que extraigo todos los elementos de una relación has_many y le
quiero pasar un sum para sumar uno de los campos de los objetos del
array tal que
así:
Tengo el modelo Cart con estos has_many
has_many :carts_events, :dependent => :destroy
has_many :events, :through => :carts_events

Y el modelo Evento que tiene un atributo price_cents:integer

Fijaros en el through pero como veremos más abajo ocurre de igual modo
si uso el array del carts_events que no tiene through

Invoco al sum tal que
así: cart.events.sum{ |e| e.price_events }

Y me suelta este error:
ArgumentError: wrong number of arguments (1 for 2)
from
/Library/Ruby/Gems/1.8/gems/activerecord-2.2.2/lib/active_record/associations/association_collection.rb:368:in
calculate' from /Library/Ruby/Gems/1.8/gems/activerecord-2.2.2/lib/active_record/associations/association_collection.rb:368:insend’
from
/Library/Ruby/Gems/1.8/gems/activerecord-2.2.2/lib/active_record/associations/association_collection.rb:368:in
method_missing' from /Library/Ruby/Gems/1.8/gems/activerecord-2.2.2/lib/active_record/base.rb:2003:inwith_scope’
from
/Library/Ruby/Gems/1.8/gems/activerecord-2.2.2/lib/active_record/associations/association_proxy.rb:202:in
send' from /Library/Ruby/Gems/1.8/gems/activerecord-2.2.2/lib/active_record/associations/association_proxy.rb:202:inwith_scope’
from
/Library/Ruby/Gems/1.8/gems/activerecord-2.2.2/lib/active_record/associations/association_collection.rb:366:in
method_missing' from /Library/Ruby/Gems/1.8/gems/activerecord-2.2.2/lib/active_record/associations/association_collection.rb:151:insum’
from (irb):19

Ahora un montón de comprobaciones:

Event.all.sum{ |e| e.price_cents }
=> 300

BIEN!

cart.events.class
=> Array

Event.all.class
=> Array

BIEN!

cart.events
=> [#<Event id: 491222053, name: “Event 1”, permalink: “event-1”,
description: “Just an Event”, price_cents: 100, created_at:
“2009-01-22 00:47:38”, updated_at: “2009-01-22 00:47:38”>, #<Event id:
491222054, name: “Event 2”, permalink: “event-2”, description: “Just
another Event”, price_cents: 200, created_at: “2009-01-22 00:47:38”,
updated_at: “2009-01-22 00:47:38”>]

Event.all
=> [#<Event id: 491222053, name: “Event 1”, permalink: “event-1”,
description: “Just an Event”, price_cents: 100, created_at:
“2009-01-22 00:47:38”, updated_at: “2009-01-22 00:47:38”>, #<Event id:
491222054, name: “Event 2”, permalink: “event-2”, description: “Just
another Event”, price_cents: 200, created_at: “2009-01-22 00:47:38”,
updated_at: “2009-01-22 00:47:38”>]

BIEN!

Event.all == cart.events
=> true

BIEN!

?> cart.events.ancestors
=> [Event(id: integer, name: string, permalink: string, description:
text, price_cents: integer, created_at: datetime, updated_at:
datetime), ActiveRecord::Base, ActiveRecord::Serialization,
ActiveRecord::Calculations, ActiveRecord::Reflection,
ActiveRecord::Transactions, ActiveRecord::Aggregations,
ActiveRecord::AssociationPreload, ActiveRecord::NamedScope,
ActiveRecord::Associations, ActiveRecord::Timestamp,
ActiveRecord::Observing, ActiveRecord::Callbacks, ActiveRecord::Dirty,
ActiveRecord::AttributeMethods, ActiveRecord::Locking::Pessimistic,
ActiveRecord::Locking::Optimistic, ActiveSupport::Callbacks,
ActiveRecord::Validations, Object,
ActiveSupport::Dependencies::Loadable, InstanceExecMethods,
Base64::Deprecated, Base64, Kernel]

Event.all.ancestors
NoMethodError: undefined method `ancestors’ for #Array:0x21da568
from (irb):28

OUCH!.. digo FAIL!

Para ver que el through no está causando el error:

cart.carts_events.sum{ |ce| ce.event.price_event }
ArgumentError: wrong number of arguments (1 for 2)
from
/Library/Ruby/Gems/1.8/gems/activerecord-2.2.2/lib/active_record/associations/association_collection.rb:368:in
calculate' from /Library/Ruby/Gems/1.8/gems/activerecord-2.2.2/lib/active_record/associations/association_collection.rb:368:insend’
from
/Library/Ruby/Gems/1.8/gems/activerecord-2.2.2/lib/active_record/associations/association_collection.rb:368:in
method_missing' from /Library/Ruby/Gems/1.8/gems/activerecord-2.2.2/lib/active_record/base.rb:2003:inwith_scope’
from
/Library/Ruby/Gems/1.8/gems/activerecord-2.2.2/lib/active_record/associations/association_proxy.rb:202:in
send' from /Library/Ruby/Gems/1.8/gems/activerecord-2.2.2/lib/active_record/associations/association_proxy.rb:202:inwith_scope’
from
/Library/Ruby/Gems/1.8/gems/activerecord-2.2.2/lib/active_record/associations/association_collection.rb:366:in
method_missing' from /Library/Ruby/Gems/1.8/gems/activerecord-2.2.2/lib/active_record/associations/association_collection.rb:151:insum’
from (irb):37

FAIL!

Pero es que además:

cart.events.sum
ArgumentError: wrong number of arguments (1 for 2)
from
/Library/Ruby/Gems/1.8/gems/activerecord-2.2.2/lib/active_record/associations/association_collection.rb:370:in
calculate' from /Library/Ruby/Gems/1.8/gems/activerecord-2.2.2/lib/active_record/associations/association_collection.rb:370:insend’
from
/Library/Ruby/Gems/1.8/gems/activerecord-2.2.2/lib/active_record/associations/association_collection.rb:370:in
method_missing' from /Library/Ruby/Gems/1.8/gems/activerecord-2.2.2/lib/active_record/base.rb:2003:inwith_scope’
from
/Library/Ruby/Gems/1.8/gems/activerecord-2.2.2/lib/active_record/associations/association_proxy.rb:202:in
send' from /Library/Ruby/Gems/1.8/gems/activerecord-2.2.2/lib/active_record/associations/association_proxy.rb:202:inwith_scope’
from
/Library/Ruby/Gems/1.8/gems/activerecord-2.2.2/lib/active_record/associations/association_collection.rb:366:in
method_missing' from /Library/Ruby/Gems/1.8/gems/activerecord-2.2.2/lib/active_record/associations/association_collection.rb:153:insum’
from (irb):38

No entiendo… recibe un argumento y esperaba 2 :?

En la línea donde peta pone esto:
@reflection.klass.send(method, *args)

No he querido meterme ahí a debuggear por saber si esque la
soluciónla tengo delante y no la veo.

Qué me decís?

Saludos
f.

cart.events.class

=> Array

Aquí ActiveRecord te engaña… te dice que te devuelve un Array, pero en
realidad te está devolviendo otra cosa (no recuerdo de memoria, pero
algo que ver con la association), así que realmente no tiene lo mismo
que un Array

En concreto, el sum es diferente y no admite un bloque, sino un nombre
de campo. Lo que tú quieres es

cart.events.sum(:price_events)

saludos,

[1] sum de AR
http://api.rubyonrails.com/classes/ActiveRecord/Calculations/ClassMethods.html#M001920


javier ramírez

…i do ruby on rails development in madrid, spain, at
http://www.aspgems.com
…you can find out more about me on http://formatinternet.wordpress.com
and http://workingwithrails.com/person/5987-javier-ramirez

2009/1/22 javier ramirez [email protected]:

En concreto, el sum es diferente y no admite un bloque, sino un nombre
de campo. Lo que tú quieres es

cart.events.sum(:price_events)

Macagüen en los mil recórcholis!!

saludos,

[1] sum de AR
Peak Obsession

:wink:

Gracias… trasnochador!!

El día 22 de enero de 2009 3:49, Guillermo Álvarez Fernández
[email protected]
escribió:>

El 22/01/2009, a las 2:16, Fernando G. escribió:

Invoco al sum tal que así:
cart.events.sum{ |e| e.price_events }

cart.events devuelve un asociation_proxy
cart.events.all devuelve todos los elementos de la asociación.

cart.events.all.sum {|e| e.price_cents} Sumas en ruby el resultado de todos
los métodos.
cart.events.sum(:price_cents) Dejas que haga la suma mysql.

Genial… buena
aclaración

Ahora un montón de comprobaciones:

Event.all.sum{ |e| e.price_cents }

=> 300

Cuidado con esto. Estás sacando todos los eventos de la aplicación.

Sipi… era una prueba

cart.events #Select * from events where cart.id = 1

Sin embargo
cart.events.first #Select * from events where cart.id = 1 LIMIT 1

¿Adivina rails para que vas a usar y hace la consulta?

Tengo que estudiarme ese aspecto de active record. A día de hoy no se muy
bien como lo hace.

Eres un estudioso :slight_smile:

Qué pasa que aquí nadie duerme… a lavarse los dientes y a la cama todol
mundo.

O esque estáis todos en el hacklab este del patio maravillas?
http://twitter.com/isaachacksimov

f.

El 22/01/2009, a las 2:16, Fernando G. escribió:

Invoco al sum tal que así:
cart.events.sum{ |e| e.price_events }

cart.events devuelve un asociation_proxy
cart.events.all devuelve todos los elementos de la asociación.

cart.events.all.sum {|e| e.price_cents} Sumas en ruby el resultado de
todos los métodos.
cart.events.sum(:price_cents) Dejas que haga la suma mysql.

Ahora un montón de comprobaciones:

Event.all.sum{ |e| e.price_cents }
=> 300

Cuidado con esto. Estás sacando todos los eventos de la aplicación.

cart.events.class
=> Array
Event.all.class
=> Array

Esto es cosa de la magia de ruby. Ese Array efectivamente despista
mucho.
Fijate en una cosa curiosa.

cart.events #Select * from events where cart.id = 1

Sin embargo
cart.events.first #Select * from events where cart.id = 1 LIMIT 1

¿Adivina rails para que vas a usar y hace la consulta?

Tengo que estudiarme ese aspecto de active record. A día de hoy no se
muy bien como lo hace.


Guillermo Álvarez Fernández
[email protected]
http://cientifico.net