Méthode ActiveRecord Rails 3 scope vs class

Je suis nouveau dans la nouvelle interface de requête d’ActiveRecord, alors je continue à comprendre.

J’espérais que quelqu’un pourrait expliquer la différence entre l’utilisation d’une scope dans un modèle ActiveRecord et l’utilisation d’une méthode de classe (ie self.some_method )

D’après ce que je peux rassembler, une scope doit toujours renvoyer une relation, alors qu’une méthode de classe ne doit pas nécessairement l’être. 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. Je reçois 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 `' from script/rails:6:in `require' from script/rails:6:in `' 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 idées des gens sur le moment d’utiliser les scopes et quand utiliser les méthodes de classe. Ai-je raison de supposer qu’une scope doit toujours renvoyer une relation, mais une méthode de classe peut renvoyer ce qu’elle veut?

Il y avait plus de différence dans Rails 2.x, puisque named_scopes n’exécutait pas vos requêtes (vous pouviez donc les enchaîner), alors que les méthodes de classe exécutaient généralement les requêtes (vous ne pouviez donc pas les enchaîner), sauf si vous aviez manuellement effectué votre requête. dans un appel de scoped(...) .

Dans Rails 3, tout retourne un ActiveRecord::Relation jusqu’à ce que vous ayez besoin des résultats réels, donc les scopes peuvent être enchaînées contre les méthodes de classe et vice versa (tant que les méthodes de classe renvoient des objects ActiveRecord::Relation , pas un autre type d’object un compte)).

En règle générale, j’utilise les entrées d’ scope pour les lignes simples pour filtrer mon jeu de résultats. Cependant, si je fais quelque chose de compliqué dans un “scope” qui peut nécessiter une logique détaillée, lambdas, plusieurs lignes, etc., je préfère utiliser une méthode de classe. Et comme vous avez attrapé, si j’ai besoin de retourner des chiffres ou quelque chose comme ça, j’utilise une méthode de classe.

Comme l’a fait remarquer Dylan dans sa réponse, une différence entre la méthode de l’étendue et de la classe est que les scopes sont évaluées lorsque la classe est chargée. Cela peut entraîner un résultat inattendu.

Par exemple,

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

est enclin à l'erreur. La manière correcte 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 scope, 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 

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