views:

368

answers:

2

My Account model has the following two associations:

has_many  :expenses, 
          :order => 'expenses.dated_on DESC', 
          :dependent => :destroy

has_many  :recent_expenses, 
          :class_name => 'Expense', 
          :conditions => "expenses.dated_on <= '#{Date.today}'",
          :order => 'dated_on DESC', 
          :limit => 5

In one of my views I'm rendering recent expenses like so:

<% @account.recent_expenses.each do |expense| %>
...
<% end %>

On my development machine, on the staging server (which runs in production mode) and also on the production console, @account.recent_expenses returns the correct list. However, on our live production server, the most recent expenses are not returned.

If I replace @account.recent_expenses with @account.expenses in the view, the most recent expenses are displayed, so my guess is that the #{Date.today} part of the conditions clause is somehow being cached the first time it is executed. If I restart the production Mongrel cluster, all the latest expenses are returned correctly.

Can anyone think why this would occur and how might I change the :recent_expenses query to prevent this from happening?

I'm using Rails 2.1.0.

+5  A: 

Rails is building the query when loaded and then will re-use that query every time you call @account.recent_expenses which is exactly what you're experiencing.

If you're using Rails 2.1 you can use named_scope to achieve what you're looking for.

in your Expense model, put the following:

named_scope :recent, lambda { {:conditions => ["expenses.dated_on <= ?", Date.today], :order => 'dated_on DESC', :limit => 5 } }

The lambda is used to ensure rails rebuilds the query each time.

from your Account remove:

has_many :recent_expenses ...

and then call:

<% @account.expenses.recent.each do |expense| %>
...
<% end %>
Andrew
+3  A: 

Like Andrew said, association macros are read at application startup, so any dynamic bits (like Date.today) are parsed at that point. His solution works if you're on Rails 2.1 or later; for earlier versions, you can use the has_finder gem, or just create the following method on the Expense model:

def self.recent
  find(:all, :conditions => ['dated_on <= ?', Date.today], :limit => 5, :order => 'dated_on DESC')
end

Class methods like this are exposed to associations, and are properly scoped - the difference between them and named_scopes is that you can't chain them.

Ben Scofield