ActiveRecord Rails 3 Scope vs Class Method
-
29-10-2019 - |
Question
Je suis nouveau dans la nouvelle interface de requête d'ACTIVERECORD, donc je trouve toujours les choses.
J'espérais que quelqu'un pourrait expliquer la différence entre l'utilisation d'un scope
dans un modèle ActiveRecord et simplement utiliser une méthode de classe (c'est-à-dire self.some_method
)
D'après ce que je peux rassembler, une portée devrait toujours retourner une relation, alors qu'une méthode de classe ne doit pas nécessairement à le faire. Est-ce vrai?
Par exemple, j'ai pensé qu'il serait logique de faire quelque chose comme:
class Person
scope :grouped_counts, group(:name).count
end
Mais cela ne fonctionne pas. J'obtiens cette erreur:
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
Cela fonctionne cependant comme une méthode de classe
def self.grouped_counts
group(:name).count
end
Je suis intéressé à connaître les réflexions des gens sur le moment d'utiliser des lunettes et quand utiliser les méthodes de classe. Ai-je raison de supposer qu'une portée doit toujours renvoyer une relation, mais une méthode de classe peut renvoyer ce qu'elle veut?
La solution
Il y avait plus de différence dans Rails 2.x, car Named_scopes n'a pas exécuté vos requêtes (afin que vous puissiez les enchaîner), tandis que les méthodes de classe ont généralement exécuté les requêtes (afin que vous ne puissiez pas les enchaîner), sauf si vous avez enveloppé manuellement votre requête dans un scoped(...)
appel.
Dans Rails 3, tout renvoie un ActiveRecord::Relation
Jusqu'à ce que vous ayez besoin des résultats réels, les lunettes peuvent donc être enchaînées contre les méthodes de classe et vice versa (tant que les méthodes de classe reviennent ActiveRecord::Relation
objets, pas un autre type d'objet (comme un compte)).
Généralement, j'utilise scope
Entrées pour que les one-liners simples pour filtrer mon ensemble de résultats. Cependant, si je fais quelque chose de compliqué dans une "portée" qui peut nécessiter une logique détaillée, des lambdas, plusieurs lignes, etc., je préfère utiliser une méthode de classe. Et comme vous l'avez attrapé, si j'ai besoin de retourner des comptes ou quelque chose comme ça, j'utilise une méthode de classe.
Autres conseils
Comme Dylan mentionné dans sa réponse, une différence entre la portée et la méthode de la classe est que les portées sont évaluées lorsque la classe est chargée. Cela peut conduire à un résultat inattendu.
Par exemple,
class Post < ActiveRecord::Base
scope :published_earlier, where('published_at < ?', Date.today)
end
est sujet à l'erreur. La bonne façon est d'utiliser un lambda
class Post < ActiveRecord::Base
scope :published_earlier, -> { where('published_at < ?', Date.today) }
end
Le bloc Lambda est évalué paresseusement. Donc date.today est exécuté lorsque vous appelez la portée, pas lorsque la classe est évaluée.
Si vous utilisez une méthode de classe, vous n'avez pas besoin d'utiliser Lambda.
class Post < ActiveRecord::Base
def self.published_earlier
where('published_at < ?', Date.today)
end
end
Parce qu'avec la méthode de classe, le code est exécuté au moment de l'appel de la méthode.