50 votes

ActiveRecord Rails 3 scope vs méthode de classe

Je suis nouveau dans la nouvelle interface de requête d'ActiveRecord et je suis encore en train de comprendre certaines choses.

J'espérais que quelqu'un pourrait expliquer la différence entre l'utilisation d'une scope dans un modèle ActiveRecord et en utilisant simplement une méthode de classe (c'est à dire self.some_method )

D'après ce que je peux comprendre, on s'attend toujours à ce qu'un scope renvoie une relation, alors qu'une méthode de classe ne doit pas nécessairement le faire. Cela est-il vrai ?

Par exemple, je pensais 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

Il fonctionne cependant comme une méthode de classe

def self.grouped_counts
  group(:name).count
end

J'aimerais savoir ce que les gens pensent de l'utilisation des scopes et des méthodes de classe. Ai-je raison de supposer qu'un scope doit toujours retourner une relation, alors qu'une méthode de classe peut retourner ce qu'elle veut ?

83voto

Dylan Markow Points 65796

La différence était plus marquée dans Rails 2.x, puisque les named_scopes n'exécutaient pas vos requêtes (vous pouviez donc les chaîner), alors que les méthodes de classe exécutaient généralement les requêtes (vous ne pouviez donc pas les chaîner), à moins que vous n'enveloppiez manuellement votre requête dans un fichier scoped(...) appeler.

Dans Rails 3, tout renvoie un ActiveRecord::Relation jusqu'à ce que vous ayez besoin des résultats réels, de sorte que les scopes peuvent être enchaînés avec les méthodes de classe et vice versa (tant que les méthodes de classe renvoient ActiveRecord::Relation et non un autre type d'objet (comme un compte)).

En général, j'utilise scope des entrées pour de simples phrases d'une seule ligne pour filtrer mes 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, des lignes multiples, etc., je préfère utiliser une méthode de classe. Et comme vous l'avez pris, si j'ai besoin de renvoyer des comptes ou quelque chose de ce genre, j'utilise une méthode de classe.

12voto

Zack Xu Points 1531

Comme Dylan Comme il l'a mentionné dans sa réponse, une différence entre la portée et la méthode de 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 à des erreurs. La méthode correcte consiste à utiliser un lambda

class Post < ActiveRecord::Base
    scope :published_earlier, -> { where('published_at < ?', Date.today) }
end

Le bloc lambda est évalué paresseusement. Ainsi, Date.today est exécuté lorsque vous appelez la portée, et non pas lorsque la classe est évaluée.

Si vous utilisez une méthode de classe, alors vous n'avez pas besoin d'utiliser lambda.

class Post < ActiveRecord::Base
    def self.published_earlier
        where('published_at < ?', Date.today)
    end
end

Car avec la méthode de classe, le code est exécuté au moment de l'appel de la méthode.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X