Объединение полиморфных областей модели Mongoid в базовом классе?

У меня есть простой полиморфизм, где каждый подкласс имеет область действия dead, каждый из которых реализован немного по-разному. Я хотел бы иметь возможность собрать их все вместе из метода класса dead из базового класса:

class Animal
  include Mongoid::Document
  field :birthday, type: DateTime

  def self.dead
    descendants.map(&:dead)
  end
end

class Dog < Animal
  scope :dead, ->{ where(birthday: { :$lt => Time.now - 13.years }) }
end

class GuineaPig < Animal
  scope :dead, ->{ where(birthday: { :$lt => Time.now - 4.years }) }
end

class Turtle < Animal
  scope :dead, ->{ where(birthday: { :$lt => Time.now - 50.years }) }
end

Как определено, метод Animal::dead возвращает массив с критериями области действия каждой модели-потомка:

>> Animal.dead
=> [#<Mongoid::Criteria
  selector: {"birthday"=>{:$lt=>2000-08-23 14:39:24 UTC}}
  options:  {}
  class:    Dog
  embedded: false>
, #<Mongoid::Criteria
  selector: {"birthday"=>{:$lt=>2009-08-23 14:39:24 UTC}}
  options:  {}
  class:    GuineaPig
  embedded: false>
, #<Mongoid::Criteria
  selector: {"birthday"=>{:$lt=>1963-08-23 14:39:24 UTC}}
  options:  {}
  class:    Turtle
  embedded: false>
]

Если я хочу подсчитать всех своих мертвых животных, я должен сделать что-то вроде этого:

Animal.dead.map(&:count).reduce(:+)

Я бы предпочел, чтобы мой метод Animal::dead возвращал обычные Mongoid::Criteria комбинированных областей (объединенных по ИЛИ) критериев dead каждого потомка, поэтому я мог бы просто сделать

Animal.dead.count

Есть идеи, как это можно реализовать?

Если бы я использовал DataMapper, у него была бы хорошая функция, позволяющая комбинировать области действия/"ИЛИ" вместе. используя + или | (оператор объединения). Я не смог определить, есть ли у Mongoid такая функция, но если да, то я думаю, что это решит мою проблему.

Вот краткая спецификация RSpec того, что мне нужно:

describe Animal.dead do
  it { should respond_to(:count, :all, :first, :destroy) }
end

describe Animal do
  before do
    Animal.all.destroy
    # create 1 dead dog, 2 dead guinea pigs, 3 dead turtles (total 6)
    1.times{ Dog.create(birthday: Time.now - 20.years) }
    2.times{ GuineaPig.create(birthday: Time.now - 5.years) }
    3.times{ Turtle.create(birthday: Time.now - 100.years) }
    # create 3 alive dogs
    3.times{ Dog.create(birthday: Time.now - 6.years) }
  end

  it 'should combine descendant animal dead scopes' do
    expect(Animal.dead.count).to eq(6)
  end
end

Я использую Rails, так что вы можете предположить, что у меня есть ActiveSupport и все другие доступные помощники.


person Abe Voelker    schedule 23.08.2013    source источник


Ответы (1)


У меня есть временное решение, которое, кажется, работает:

class Animal
  include Mongoid::Document
  field :birthday, type: DateTime

  def self.dead
    self.or(*descendants.map{|d| d.dead.type(d).selector})
  end
end

Тем не менее, это кажется хакерским. Я оставлю вопрос открытым на некоторое время, если у кого-то есть более чистые предложения.

person Abe Voelker    schedule 23.08.2013