Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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'
  }
like image 574
basheps Avatar asked Jan 17 '23 23:01

basheps


2 Answers

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.

like image 98
Frederick Cheung Avatar answered Jan 24 '23 11:01

Frederick Cheung


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.

like image 31
Marek Příhoda Avatar answered Jan 24 '23 13:01

Marek Příhoda