web-dev-qa-db-ja.com

ActiveRecord Rails 3スコープvsクラスメソッド

私はActiveRecordの新しいクエリインターフェイスを初めて使用するので、まだ理解していません。

私は、誰かがActiveRecordモデルでscopeを使用することと、クラスメソッド(つまり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

スコープをいつ使用し、いつクラスメソッドを使用するかについての人々の考えを知りたいです。スコープは常にリレーションを返す必要があると思いますが、クラスメソッドは何でも返すことができますか?

50
brad

Rails 2.x、named_scopesはクエリを実行しなかったため(連鎖できるように)、クラスメソッドは一般にクエリを実行したため(連鎖できないため)、さらに多くの違いがありました。クエリを手動でscoped(...)呼び出しでラップした場合を除きます。

Rails 3では、実際の結果が必要になるまですべてがActiveRecord::Relationを返すため、スコープをクラスメソッドに対してチェーンでき、その逆も可能です(クラスメソッドがActiveRecord::Relationオブジェクトであり、他のオブジェクトタイプ(カウントなど)ではありません)。

通常、単純な1行のscopeエントリを使用して、結果セットをフィルタリングします。ただし、詳細なロジック、ラムダ、複数行などが必要になる可能性のある「スコープ」で複雑なことを行う場合は、クラスメソッドを使用することをお勧めします。そして、あなたがキャッチしたように、カウントなどを返す必要がある場合は、クラスメソッドを使用します。

83
Dylan Markow

Dylan が彼の答えで言及したように、スコープとクラスメソッドの1つの違いは、クラスが読み込まれるときにスコープが評価されることです。これにより、予期しない結果が生じる可能性があります。

例えば、

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

エラーが発生しやすい。正しい方法はラムダを使用することです

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

Lambdaブロックは遅延評価されます。したがって、Date.todayは、クラスが評価されるときではなく、スコープを呼び出すときに実行されます。

クラスメソッドを使用する場合は、ラムダを使用する必要はありません。

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

クラスメソッドの場合、コードはメソッド呼び出し時に実行されるためです。

12
Zack Xu