Wednesday, September 30, 2009

why your named_scope isn't behaving the way you want

Let me go into this as briefly as possible.  I had the following named_scope in one of my rails models:

class Alert < ActiveRecord::Base
  named_scope :in_the_past,
                ["start_datetime <= ?",]
Seems straightforward, right? I thought so too. I used this "in_the_past" method to replace a poorly written load-and-filter algorithm. This would use much less memory and be much quicker. Only one problem it didn't work. It passed all the unit tests. It seemed to work when it was deployed. But over the next few days more and more people were calling me saying they weren't seeing the alerts they expected to. I tried to figure out the problem (maybe related to timezones? or bad data?), and every time I deployed a fix all my users would tell me it looked good again; but then a day or so later everyone was complaining once more. I finally got fed up with it and went back to the old way (loading a collection and filtering out the items in the past using a "select" iterator), cursing the day I'd ever started down this road. And then today, I saw this in a blog post from pivotal labs: Public Service Announcement: your named_scope's get evaluated when the class is loaded, not when an instance is created. I felt like I'd been hit by a truck. The "" call was being evaluated at server startup, so for the first few minutes it seemed fine. However, as the amount of time since class-load increased, the "in the past" scope was missing more and more of the items that were actually now in the past because it's measuring stick was stuck at the time of creation. As a result I dug through my codebase and found no less than 7 scopes to refactor into using a lambda instead so that the system time would be loaded at time of execution instead:
class Alert < ActiveRecord::Base
  named_scope :in_the_past,lambda{
                   ["start_datetime <= ?",]}
Thanks to those pros at Pivotal Labs!

1 comment:

Andrés Mejía said...

Pure gold. Thanks for posting!