Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Timecop gem For Scopes

I'm spec'ing a scope in a Rails 3.0 app as follows:

class DrawingList < ActiveRecord::Base
  scope :active_drawings, where('start_date <= ? AND end_date >= ?', Date.today, Date.today)
end

In my spec, I want to do:

before do
  @list = DrawingList.create #things that include begin and end dates
end

it "doesn't find an active drawing if they are out of range" do
  pending "really need to figure out how to work timecop in the presence of scopes"
  Timecop.travel(2.days)
  puts Date.today.to_s
  DrawingList.active_drawings.first.should be_nil
end

As you might imagine, the puts really shows that Date.today is two days hence. However, the scope is evaluated in a different context, so it uses the old "today". How does one get today evaluated in a context that Timecop can affect.

Thanks!

like image 909
Steve Ross Avatar asked Jan 13 '12 17:01

Steve Ross


1 Answers

This is a really common mistake. As you've written in the date used by the scope is the date as it was when the code was loaded. Were you to run this in production where code is only reloaded if you restart the app (unlike development where it is reloaded on each request), you'd get the right results on the day you restarted the app, but the next day the results would be out by one day, the day after by 2 days etc.

The correct way of defining a scope like that is

scope :active_drawings, lambda { where('start_date <= ? AND end_date >= ?', Date.today, Date.today)}

The lambda ensures that those dates are evaluated each time the scope is used.

like image 181
Frederick Cheung Avatar answered Oct 27 '22 23:10

Frederick Cheung