Nick Kallen’s named_scope allows you to create readable, powerful class-specific finder scopes.
One thing that might bite new users is how it operates in the Class context. Specifically, Named Scopes created in a parent class, even those using the lambda syntax, are scoped to the parent class.
For example, we might have a generic SQL operation that finds records whose date field falls within a certain range in a parent class, and simply allow any child classes to provide the range.
class HighScore named_scope :for_date, lambda { |time| { :conditions => { :date => time_range_for(time) } } } end class DailyHighScore < HighScore def self.time_range_for time (time.beginning_of_day)..(time.beginning_of_day + 1.day) end end class WeeklyHighScore < HighScore def self.time_range_for time (time.beginning_of_week)..(time.beginning_of_week + 1.week) end end
Wouldn’t it be lovely if we could just rely on the subclass to complete the conditions? Unfortunately, since time_range_for is not defined in the HighScore class, this code will not not work.
The solution is to create individual Named Scopes on the child classes. However, instead of declaring unique Named Scopes, common behavior is isolated in the parent class by providing a Named Scope builder.
class HighScore def self.has_named_scope_for_time_range named_scope :for_date, lambda { |time| { :conditions => { :date => time_range_for(time) } } } end end class DailyHighScore < HighScore def self.time_range_for time (time.beginning_of_day)..(time.beginning_of_day + 1.day) end has_named_scope_for_time_range end class WeeklyHighScore < HighScore def self.time_range_for time (time.beginning_of_week)..(time.beginning_of_week + 1.week) end has_named_scope_for_time_range end
Now all child classes using has_named_scope_for_time_range get a for_date Scope that works just the way they want. An additional advantage of this approach is that the parent class no longer has the extraneous for_date Named Scope.
Leave a Comment