views:

57

answers:

2

I have a number of models that need the same scope. They each have an expiration_date date field that I want to write a scope against.

To keep things DRY, I'd like to put the scope in a module (in /lib) that I'll extend each model with. However, when I call scope within the module, the method is undefined.

To work around this, I use class_eval when the module is included:

module ExpiresWithinScope
  def self.extended(base)
    scope_code = %q{scope :expires_within, lambda { |number_of_months_from_now| where("expiration_date BETWEEN ? AND ?", Date.today, Date.today + number_of_months_from_now) } }
    base.class_eval(scope_code)
  end 
end

I then do extend ExpiresWithinScope in my models.

This approach works, but feels a little hackish. Is there a better way?

+2  A: 

You could do something a little cleaner like this, since scope is a public class method:

module ExpiresWithinScope
  def self.included(base)
    base.scope :expires_within, lambda { |number_of_months_from_now| 
      where("expiration_date BETWEEN ? AND ?", 
        Date.today,
        Date.today + number_of_months_from_now) 
    }
  end 
end

and then in your model

include ExpiresWithinScope
BaroqueBobcat
That works, except you need `base.where`.
r00k
ah, lexical scoping strikes again.
BaroqueBobcat
+1  A: 

With AR3, they finally got somewhere near DataMapper awesomeness, so you can go

module ExpiresWithinScope
  def expires_within(months_from_now)
    where("expiration_date BETWEEN ? AND ?", 
    Date.today,
    Date.today + number_of_months_from_now) 
  end
end

You could also try:

module ExpiresWithinScope
  def expires_within(months_from_now)
    where(:expiration_date => Date.today..(Date.today + number_of_months_from_now))
  end
end

But according to the guide, arel can't handle that as well.

Tass
The first one worked nicely.
r00k