Ambito di ActiveRecord Rails 3 vs metodo di classe
-
29-10-2019 - |
Domanda
Sono nuovo nella nuova interfaccia di query di ActiveRecord, quindi sto ancora cercando di capire le cose.
Speravo che qualcuno potesse spiegare la differenza tra l'utilizzo di un scope
in un modello ActiveRecord e il semplice utilizzo di un metodo di classe (ad esempio self.some_method
)
Da quello che posso raccogliere, ci si aspetta sempre che uno scope restituisca una relazione, mentre un metodo di classe non deve necessariamente farlo.È vero?
Ad esempio, ho pensato che avrebbe avuto senso fare qualcosa del tipo:
class Person
scope :grouped_counts, group(:name).count
end
Ma questo non funziona.Ottengo questo errore:
ArgumentError: Unknown key(s): communicating, failed, matched, unmatched
from /Users/bradrobertson/.rvm/gems/ruby-1.9.2-p180@influitive/gems/activesupport-3.0.5/lib/active_support/core_ext/hash/keys.rb:43:in `assert_valid_keys'
from /Users/bradrobertson/.rvm/gems/ruby-1.9.2-p180@influitive/gems/activerecord-3.0.5/lib/active_record/relation/spawn_methods.rb:110:in `apply_finder_options'
from /Users/bradrobertson/.rvm/gems/ruby-1.9.2-p180@influitive/gems/activerecord-3.0.5/lib/active_record/named_scope.rb:110:in `block in scope'
from (irb):48
from /Users/bradrobertson/.rvm/gems/ruby-1.9.2-p180@influitive/gems/railties-3.0.5/lib/rails/commands/console.rb:44:in `start'
from /Users/bradrobertson/.rvm/gems/ruby-1.9.2-p180@influitive/gems/railties-3.0.5/lib/rails/commands/console.rb:8:in `start'
from /Users/bradrobertson/.rvm/gems/ruby-1.9.2-p180@influitive/gems/railties-3.0.5/lib/rails/commands.rb:23:in `<top (required)>'
from script/rails:6:in `require'
from script/rails:6:in `<main>'
r
Funziona comunque come un metodo di classe
def self.grouped_counts
group(:name).count
end
Mi interessa conoscere i pensieri delle persone su quando usare gli scope e quando usare i metodi di classe.Ho ragione nel presumere che uno scope debba sempre restituire una relazione, ma un metodo di classe può restituire quello che vuole?
Soluzione
C'era più differenza in Rails 2.x, dal momento che named_scopes non eseguiva le tue query (quindi puoi concatenarle), mentre i metodi di classe generalmente eseguivano le query (quindi non puoi concatenarle), a meno che tu nonha racchiuso la tua query in una chiamata scoped(...)
.
In Rails 3, tutto restituisce un ActiveRecord::Relation
fino a quando non servono i risultati effettivi, quindi gli ambiti possono essere concatenati con metodi di classe e viceversa (purché i metodi di classe restituiscano oggetti ActiveRecord::Relation
, non qualche altro tipo di oggetto (come un conteggio)).
In genere, utilizzo le voci scope
per semplici battute per filtrare il mio set di risultati.Tuttavia, se sto facendo qualcosa di complicato in un "ambito" che potrebbe richiedere una logica dettagliata, espressioni lambda, più righe, ecc., Preferisco utilizzare un metodo di classe.E come hai capito, se ho bisogno di restituire conteggi o qualcosa del genere, utilizzo un metodo di classe.
Altri suggerimenti
Come Dylan ha accennato nella sua risposta, una differenza tra l'ambito e il metodo della classe è che gli ambiti vengono valutati quandola classe viene caricata.Ciò potrebbe portare a risultati imprevisti.
Ad esempio,
class Post < ActiveRecord::Base
scope :published_earlier, where('published_at < ?', Date.today)
end
è soggetto a errori.Il modo corretto è usare un lambda
class Post < ActiveRecord::Base
scope :published_earlier, -> { where('published_at < ?', Date.today) }
end
Il blocco Lambda viene valutato pigramente.Quindi Date.today viene eseguito quando chiami l'ambito, non quando la classe viene valutata.
Se utilizzi un metodo di classe, non è necessario utilizzare lambda.
class Post < ActiveRecord::Base
def self.published_earlier
where('published_at < ?', Date.today)
end
end
Perché con il metodo di classe, il codice viene eseguito al momento della chiamata al metodo.