Dowemo
0 0 0 0


Question:

I have the following scopes defined in my model:

scope :upcoming, -> { where(:start_time.gt => Time.now).asc(:start_time) }
scope :in_progress, -> {
   now = Time.now
   where(:start_time.lte => now).where(:end_time.gte => now).asc(:start_time)
}

I want to create another scope that combines the results of both of those scopes called current. I tried something like this:

scope :current, -> { self.in_progress | self.upcoming }

But this just ends up treating them both like arrays and concatenating them. The problem with this is that when I try to call my scope with Model.current, I get the following error message:

NoMethodError: undefined method `as_conditions' for #<Array:0xaceb008>

This is because it converted the Mongoid Criteria object to an array, but I don't want that. I want the object to stay as a Mongoid Criteria object.

What I really want is the union of the in_progress set and the upcoming set.

Any ideas? Thanks.


Best Answer:


You can try to compose your criteria using Mongoid's query methods and dereferencing into the criteria's selector, but I wouldn't necessarily recommend this -- see below for an example. I second the recommendation to craft your third scope. Remember that these scopes correspond to db queries that you want to be efficient, so it is probably worth your time to examine and understand the resulting and underlying MongoDB queries that are generated.

Model

class Episode
  include Mongoid::Document
  field :name, type: String
  field :start_time, type: Time
  field :end_time, type: Time
  scope :upcoming, -> { where(:start_time.gt => Time.now).asc(:start_time) }
  scope :in_progress, -> {
     now = Time.now
     where(:start_time.lte => now).where(:end_time.gte => now).asc(:start_time)
  }
  scope :current, -> { any_of([upcoming.selector, in_progress.selector]) }
  scope :current_simpler, -> { where(:end_time.gte => Time.now) }
end

Test

require 'test_helper'
class EpisodeTest < ActiveSupport::TestCase
  def setup
    Episode.delete_all
  end
  test "scope composition" do
    #p Episode.in_progress
    #p Episode.upcoming
    #p Episode.current
    #p Episode.current_simpler
    in_progress_name = 'In Progress'
    upcoming_name = 'Upcoming'
    Episode.create(:name => in_progress_name, :start_time => Time.now, :end_time => 1.hour.from_now)
    Episode.create(:name => upcoming_name, :start_time => 1.hour.from_now, :end_time => 2.hours.from_now)
    assert_equal([in_progress_name], Episode.in_progress.to_a.map(&:name))
    assert_equal([upcoming_name], Episode.upcoming.to_a.map(&:name))
    assert_equal([in_progress_name, upcoming_name], Episode.current.to_a.map(&:name))
    assert_equal([in_progress_name, upcoming_name], Episode.current_simpler.to_a.map(&:name))
  end
end



Copyright © 2011 Dowemo All rights reserved.    Creative Commons   AboutUs