has_many Association Pitfall

Ran into an interesting pitfall at work today with a has_many association. I had was using a condition where it was constraining the returned objects by date.

The wrong way

For example: Say I have a Post class that has many comments. And I want to retrieve all of new comments only, let’s say the last 5 days worth of comments.

This is how I initially would’ve written it:

class Post < AR::Base
  has_many :comments, :conditions => ["created_at >= ?", (Date.today - 5.days)]
end
class Comment < AR::Base
  belongs_to :post
end

The problem with this though is when rails boots up the condition gets initialized. If I came back to my app 5 days later it wouldn't be correct because the condition would still be what ever it was when I started the application.

The right way/scopes to the rescue

class Post < AR::Base
  has_many :comments
end
class Comment < AR::Base
  belongs_to :post
  
  scope :most_recent_posts, lambda { where("created_at >= ?", (Date.today - 5.days)) }
end

How to use

post = Post.find(1)
post.comments.most_recent_posts # => returns the most recent posts (actually returns the Relation)

This acts as a filter tact on the comments association. And since the Date.today is wrapped in the lambda function it will always be evaluated when the query is executed and thus always return the correct results.

Edit

There's one other gotcha for this. In my original problem I had a couple of has_many associations with different conditions on it. When I started using the better approach with scopes I had created a default scope. But when I tried to apply my other condition to that it didn't work. My guess is that rails was getting confused to which conditions to use and caused the application to barf.