basheps
basheps

Reputation: 10604

Rails 3.1 named_scope

What is the Rails 3.1 of writing the code below:

named_scope :min_2_items_last_90_days, {
    :include => { :orders => :order_items },
    :conditions => ['orders.created_at >= ?', 90.days.ago],
    :group   => 'people.id',
    :having => 'COUNT(order_items.id) >= 2'
  }

Upvotes: 4

Views: 1711

Answers (2)

Marek Příhoda
Marek Příhoda

Reputation: 11198

scope :min_2_items_last_90_days, lambda { where('orders.created_at >= ?', 90.days.ago).includes(:orders => :order_items).group('people.id').having('COUNT(order_items.id) >= 2') }

NB (because it's easy to forget): Using lambda ensures that the conditions are re-evaluated each time the scope is called (see also the docs on scope). And the re-evaluation is needed here due to the 90.days.ago expression - you definitively want 90.days.ago to be evaluated each time the scope is called. Without the lambda, no re-evaluation would happen, and the 90.days.ago expression would be evaluated (only) when the server starts.

Upvotes: 2

Frederick Cheung
Frederick Cheung

Reputation: 84114

While writing it as

scope :min_2_items_last_90_days, where(...)

is syntactically correct, it probably (like your original code) doesn't do quite what you think.

In both cases the 90.days.ago is evaluated once only, when the class is loaded, so the 90 days will always be 90 days before you app was last restarted. If you haven't restart your app for 10 days you'd actually be looking at stuff created in the last 100 days. You won't notice this in development because your source code is continuously being reloaded (and thus the 90.days is reevaluated).

Instead you should do

scope :min_2_items_last_90_days, lambda { where('orders.created_at >= ?', 90.days.ago).includes(...) ... }

which ensures that the conditions will be re-evaluated every time you use the scope.

Upvotes: 7

Related Questions