Класс ActiveRecord Rails 3 vs class method

Я новичок в новом интерфейсе запросов ActiveRecord, поэтому я все еще разбираюсь.

Я надеялся, что кто-то может объяснить разницу между использованием scope в модели ActiveRecord и просто использованием метода класса (т.е. 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/[email protected]/gems/activesupport-3.0.5/lib/active_support/core_ext/hash/keys.rb:43:in `assert_valid_keys'
    from /Users/bradrobertson/.rvm/gems/[email protected]/gems/activerecord-3.0.5/lib/active_record/relation/spawn_methods.rb:110:in `apply_finder_options'
    from /Users/bradrobertson/.rvm/gems/[email protected]/gems/activerecord-3.0.5/lib/active_record/named_scope.rb:110:in `block in scope'
    from (irb):48
    from /Users/bradrobertson/.rvm/gems/[email protected]/gems/railties-3.0.5/lib/rails/commands/console.rb:44:in `start'
    from /Users/bradrobertson/.rvm/gems/[email protected]/gems/railties-3.0.5/lib/rails/commands/console.rb:8:in `start'
    from /Users/bradrobertson/.rvm/gems/[email protected]/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

Мне интересно знать мысли людей о том, когда использовать области и когда использовать методы класса. Правильно ли я предполагаю, что область должна всегда возвращать отношение, но метод класса может возвращать то, что он хочет?

Ответ 1

В Rails 2.x было больше различий, так как named_scopes не выполнял ваши запросы (чтобы вы могли их сцепить), тогда как методы класса обычно выполняли запросы (поэтому вы не могли их связать), если только вы не вручную завернул ваш запрос в вызов scoped(...).

В Rails 3 все возвращает ActiveRecord::Relation до тех пор, пока вам не понадобятся фактические результаты, поэтому области могут быть привязаны к методам класса и наоборот (если методы класса возвращают объекты ActiveRecord::Relation, а не какой-либо другой тип объекта ( как счет)).

Как правило, я использую записи scope для простых однострочных фильтров для фильтрации моего результирующего набора. Однако, если я делаю что-то сложное в "области", для которой может потребоваться подробная логика, lambdas, несколько строк и т.д., Я предпочитаю использовать метод класса. И когда вы поймали, если мне нужно вернуть счет или что-то в этом роде, я использую метод класса.

Ответ 2

Как Dylan, о котором упоминается в его ответе, одно различие между методом scope и class заключается в том, что области вычисляются при загрузке класса. Это может привести к неожиданному результату.

Например,

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

Лямбда-блок лениво оценивается. Поэтому Date.today запускается, когда вы вызываете область действия, а не когда класс оценивается.

Если вы используете метод класса, вам не нужно использовать lambda.

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

Поскольку с помощью метода класса код запускается во время вызова метода.