ActiveRecord的Rails 3范围与类的方法

我是新来的ActiveRecord的新查询接口,所以我仍然搞清楚事情。

我希望有人可以解释在ActiveRecord模型中使用scope和仅使用类方法(即self.some_method )之间的self.some_method

从我所能得到的结果来看,一个范围总是被期望返回一个关系,而一个类方法并不一定非要。 这是真的?

例如,我认为这样做是有道理的:

 class Person scope :grouped_counts, group(:name).count end 

但是这不起作用。 我得到这个错误:

 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 

它不过是一个类方法

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

我有兴趣了解人们何时使用范围以及何时使用类方法的想法。 我是否正确地认为范围必须总是返回一个关系,但是类方法可以返回任何想要的结果?

在Rails 2.x中有更多的不同,因为named_scopes没有执行你的查询(所以你可以链接它们),而类方法通常执行查询(所以你不能链接它们),除非你手动包装你的查询在scoped(...)调用。

在Rails 3中,一切都会返回一个ActiveRecord::Relation直到你需要实际的结果,所以范围可以和类方法链接,反之亦然(只要类方法返回ActiveRecord::Relation对象,而不是其他对象types一个计数))。

一般来说,我使用scope条目简单的一行来筛选我的结果集。 但是,如果我在可能需要详细逻辑,lambdas,多行等的“范围”中做任何复杂的操作,我更喜欢使用类方法。 而当你发现,如果我需要返回计数或类似的东西,我使用类方法。

正如Dylan在他的回答中暗示的那样,范围和类方法之间的一个区别是当类加载时评估范围。 这可能会导致意想不到的结果。

例如,

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

很容易出错。 正确的方法是使用lambda

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

Lambda块被懒惰评估。 所以Date.today是在你调用作用域时运行的,而不是在类被评估的时候运行。

如果你使用类方法,那么你不需要使用lambda。

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

因为使用类方法,代码在方法调用时运行。