Select in scope ActiveRecord e successiva sum

Ciao,
nel precedente thread ho avuto l’occasione di provare questo codice

class Product < ActiveRecord::Base

quantity:integer

amount:decimal

def self.with_total_amount
select(’*, quantity * amount as total_amount’)
end

def self.sum_of_total_amounts
sum(:total_amount)
end
end

Product.with_total_amount.sum_of_total_amounts

che però non funziona in quanto .sum verifica direttamente se
l’argomento
passatogli è una colonna della tabella, a prescindere dagli alias
precedentemente introdotti.

Sono l’unico che si aspettava che quel codice avrebbe funzionato? ^_^’

Questa versione invece funziona:

def self.sum_of_total_amounts
sum(‘quantity * amount’)
end

Sarebbe sensato che .sum verificasse la presenza di alias creati fino a
quel punto della scope chain in modo da rendersi conto che in (SELECT *,
quantity * amount as total_amount FROM “products”) è presente
l’attributo
sul quale poter wrappare la funzione matematica?

Sarebbe sensato che .sum verificasse la presenza di alias creati fino a
quel punto della scope chain […]

IMHO ActiveRecord wrappa Arel troppo semplicisticamente, castrando
quella
che a mio parere è una libreria fenomenale. Non capisco neanche perché
abbiano definito dei metodi come “sum”, “average” etc. implementandoli
usando Arel “di nascosto” invece che fare dei semplici “syntactic
sugars”
che ti restituiscono la relazione; forse per retrocompatibilità? Non
ricordo neanche se erano definiti in ActiveRecord prima di Arel… mah.

Mi spiego meglio: volendo calcolare la somma del quadrato degli id degli
utenti (vai a capire perchè poi :stuck_out_tongue: ):

SELECT SUM("users"."id" * "users"."id") AS id_squares_sum FROM 

“users”

Usando Arel possiamo comporla in questo modo (a me ricorda molto jQuery
e
mi piace un sacco):

users = Arel::Table.new(:users)

users.project(
  (users[:id] * users[:id]).
    sum.
    as('id_squares_sum')
)

Usando il wrapper di Arel di ActiveRecord dovremmo scriverla in questo
modo:

users = User.arel_table
User.
  select(
    (users[:id] * users[:id]).
      sum.
      as('id_squares_sum')
  ).
to_a.first.id_squares_sum

La precendente dichiarazione ha i seguenti problemi:

  • Bisogna definire una variabile estemporanea per comodità, cosa che
    spesso
    mi ferma da usare Arel poiché questa cosa mi costringe a rinunciare alla
    possibilità del oneline
  • Bisogna fare un to_a prima della first altrimenti viene lanciata
    un’eccezione per un ORDER BY che viene aggiunta a buffo da ActiveRecord,
    che ti mette l’ordinamento per id anche in questo caso in cui potrebbe
    benissimo sapere che non ce lo deve mettere; cosa che invece
    ActiveRecord
    ha cura di fixare per la sum di ActiveRecord::Calculations. Questo per
    dire
    quanto Arel è meno considerato.

A me piacerebbe invece qualcosa più simile a questo:

User.
  select(
    (User[:id] * User[:id]).
      sum.
      as('id_squares_sum')
).first.id_squares_sum

Che ne dite?

Maurizio De Santis

Il giorno 23 gennaio 2015 18:32, maurizio de magnis <
[email protected]> ha scritto: