ActiveRecord, сохраняя область видимости инкапсулированной - PullRequest
2 голосов
/ 20 января 2011

У меня есть две модели, foo и bar, foo имеет много bars.

Bar - это событие, которое происходит в течение определенного периода времени, поэтому я быкак метод или область действия, которые возвращают ActiveRecord::Relation, представляющий foos, которые имеют в настоящее время активные бары.

Это достаточно просто в классе Foo с областью действия:

class Foo < ActiveRecord::Base
has_many :bars

scope :has_current_bars, joins(:bars).where('bar.foo_id IS NOT NULL').where('bar.starts_at <= ?', DateTime.now).where('bar.ends_at >= ?', DateTime.now)

Мне не нравится в этом то, что foo нужно очень много знать о внутренностях bar.

Можно ли это переписать, возможно, добавив область действия на bar, так что foo не нужно знать о bar атрибутах?

Ответы [ 3 ]

1 голос
/ 20 января 2011

Абсолютно.Вы можете и должны переместить область действия на Bar.

class Bar < ActiveRecord::Base
  belongs_to :foo

  scope :current, where('starts_at <= ? AND ends_at >= ?', DateTime.now, DateTime.now)
end

foo = Foo.first
foo.bars.current # Will return all of foo's bars which match the scope

# EDIT:
bars.current.map(&:foo) # Will return all foos that have current bars
0 голосов
/ 13 апреля 2014

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

https://github.com/ElMassimo/queryable

Он заботится о том, чтобы сделать ваши области видимости связанными, и делегировать методы, подобные каждому, и сопоставить их с фактическим запросом.

В этом случае у вас может быть два объекта запроса, FooQuery и BarQuery, и заставить эти объекты взаимодействовать, чтобы каждый объект запроса заботился о инкапсуляции логики, связанной с соответствующей моделью.

0 голосов
/ 28 августа 2013
class Foo < ActiveRecord::Base
  has_many :bars

  def self.has_current_bars
    joins(:bars).merge(Bar.current)
  end

  # or
  scope :has_current_bars, joins(:bars).merge(Bar.current)
end

class Bar < ActiveRecord::Base
  scope :current, where('bar.starts_at <= ?', DateTime.now).where('bar.ends_at >= ?', DateTime.now)
end

foos = Foo.has_current_bars
...