Можно ли добавить предложения SQL, связанные с методом `scope`, в метод/предложение `where`?

Я использую Ruby on Rails 3.2.2 и экспериментирую с драгоценным камнем Squeel. Я хотел бы знать, можно ли (каким-то образом, с помощью драгоценного камня Squeel или нет) «добавить» предложения SQL, связанные с методом scope, «непосредственно» в предложении where. То есть у меня есть:

class Article < ActiveRecord::Base
  # Note: This is a scope method.
  def self.created_by(user)
    where(:user_id => user.id)
  end

  # I would like to use a scope method like the following.
  #
  # Note: Code in the following method doesn't work, but it should help
  # understanding what I mean.
  def self.scope_method_name(user)
    where{ created_by(user) | ... & ... }
  end
end

Итак, когда я запускаю Article.scope_method_name(@current_user).to_sql, он должен возвращать что-то вроде следующего:

SELECT articles.* FROM articles WHERE articles.user_id = 1 OR ... AND ...

Я попробовал просеиватели, но они (по крайней мере, для меня) предназначены для использования исключительно< /em> в других операторах Squeel. То есть, если я укажу просеиватель, то я не могу использовать его для области видимости ActiveRecords, потому что этот просеиватель возвращает объект Squeel::Nodes::Predicate вместо ActiveRecord::Relation.


person user502052    schedule 25.06.2012    source источник


Ответы (2)


Вы должны перейти к более необработанному AREL для операций ИЛИ.

def self.scope_method_name(user)
  t = arel_table
  where(
    (t[:user_id].eq(user.id).or(
    t[:blah].eq('otherthing')
      ).and([:bleh].eq('thirdthing'))
    )
end

Или что-то вдоль этих линий.

person Mike Auclair    schedule 26.06.2012

Вы можете связать области, такие как Article.by_author(user).by_editor(), но это объединяет все условия с помощью AND. Итак, чтобы обойти это, вы можете написать отдельные области (не связывая их в цепочку), используя Squeel, например:

class Article < ActiveRecord::Base

  scope :by_author, ->(user) { where{author_id == user.id} }
  scope :by_editor, ->(user) { where{editor_id == user.id} }
  scope :by_title, ->(token) { where{title =~ "%#{token}%"} }
  scope :by_author_or_editor, ->(user) { where{(author_id == user.id)|(editor_id == user.id)} }
  scope :by_author_or_editor_and_title, ->(user, token) { where{((author_id == user.id)|(editor_id == user.id))&(title =~ "%#{token}%")} }
end

Или вы можете использовать сита:

class Article < ActiveRecord::Base

  sifter :sift_author do |user|
    author_id == user.id
  end

  sifter :sift_editor do |user|
    editor_id == user.id
  end

  sift :sift_title do |token|
    title =~ "%#{token}%"
  end

  scope :by_author, ->(user) { where{sift :sift_author, user} }
  scope :by_editor, ->(user) { where{sift :sift_editor, user} }
  scope :by_title, ->(token) { where{sift :sift_title, token} }
  scope :by_author_or_editor, -> (user) { where{(sift :sift_author, user)|(sift :sift_editor, user)} }
  scope :by_author_or_editor_and_title, ->(user, token) { where{((sift :sift_author, user)|(sift :sift_editor, user))&(sift :sift_title, token)} }
end

Это дает вам ваши области, которые возвращают ActiveRecord::Relation, поэтому вы теоретически можете их связать.

person erroric    schedule 14.08.2013