Friday 8 July 2011

Ruby on Rails 3: chaining scopes with lambda's

Today we ran into a really strange bug: the bugreport stated that a blog post was shown in the blog list approximately 15 minutes after creation. Which is really weird, because we've dealt with time zone misery before, but that always applies to hours, not a quarter of an hour. After analysis, we've found out that a chained scope caused the trouble (note: the scopes were simplified for better readability):

class Blog::Post
  scope :published_posts, lambda { where("publication_time < ?", DateTime.now) }
  scope :published_non_rotator_posts, published_posts.where("rotator_position IS NULL")
end
Scopes are evaluated at the moment you call them. The published_posts scope uses a lambda to ensure that DateTime.now is evaluated on each call, instead of being evaluated to the same DateTime value for every call. For the published_non_rotator_posts it's the same. This scope is also evaluated on the first call. Since it doesn't use a lambda expression, the outcome of the chained published_posts scope will have the same value on every next call! The correct code is:
class Blog::Post
  scope :published_posts, lambda { where("publication_time < ?", DateTime.now) }
  scope :published_non_rotator_posts, lambda { published_posts.where("rotator_position IS NULL") }
end
So: when chaining a lambda scope you must also wrap it with a lambda! thanx to this slash dot dash article.

No comments:

Post a Comment