ActiveRecord::Base.with_scope { :only => 'in your model' } - Rails 2.0 a feature a day #4
Today's (un)feature covers the oft-abused and misunderstood with_scope method. The excellent err.the_blog had a post on how you should be using with_scope (yes, only use it in your ActiveRecord models, not in your controllers or filters). Yes that blog post may have been written a long time back but it's still applicable to Rails 2.0.
With Rails 2.0, the with_scope method is now protected, meaning that you can no longer use it in your controllers (unless you use the #send(:with_scope), or #send!(:with_scope) in Ruby 1.9). I'm not even going to show you how you could use it in your controllers since you really shouldn't (unless you know what you are doing).
Here's what with_scope allows you to do:
class SiteOwner < ActiveRecord::Base
# Returns list of distinct active sites.
def active_sites(*args)
with_scope :find => { :select => 'DISTINCT(site)', :conditions => { :active => true } } do
find(*args).collect(&:site)
end
end
end
And even better example, stolen from the err.the_blog post mentioned above, is re-using scopes:
class Movie < ActiveRecord::Base
def self.find_playing(*args)
with_playing do
find(*args)
end
end
def self.find_playing_by_id
with_playing do
find_by_id(*args)
end
end
def self.with_playing
with_scope :find => { :conditions => [ state = ? AND visible = ?, NOW_PLAYING, true ] } do
yield
end
end
end
I tend to use that a lot combined with method_missing magic (also from the err.the_blog post) to allow me to do things like Movie.find_playing_by_name:
def self.method_missing(method, *args, &block)
if method.to_s =~ /^find_(all_)?playing_by/
with_playing do
super(method.to_s.sub('playing_', ''), *args, &block)
end
else
super(method, *args, &block)
end
end
If you prefer to get these for free, check out the the Rails scope-out plugin implements many of the ideas in the err.the_blog post.
About the contributor, Josh Peek
Josh Peek (WorkingWithRails profile) was the man behind the "protection" of with_scope. This is not Josh's greatest contribution to Rails, of course - Josh has been a very productive and active Rails contributor since the very beginning. A winner of one of the monthly Rails Hackfest, you can read up on the ever-elusive (I say "elusive" because he doesn't have a blog - it's just patch after patch after patch) Josh Peek in this post-Rails Hackfest interview.
Josh is also the man behind the deprecation and "plugin-izing" of dynamic scaffolding and pagination (yes, those are gone from Rails 2.0) among a great number of other patches and test coverage improvements. Right now, Josh appears to be working on refactoring ActionView.
Outro
Apologies for the late-on-arrival Rails 2.0 a feature a day posts, but such is life. Feature #5 follows immediately after :)