ActiveRecord::Base.with_scope { :only => ‘in your model’ } – Rails 2.0 a feature a day #4

In: Ruby on Rails

16 Dec 2007

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 :)

4 Responses to ActiveRecord::Base.with_scope { :only => ‘in your model’ } – Rails 2.0 a feature a day #4

Avatar

Fred

January 10th, 2008 at 5pm

Question: Where did you first learn of the existence of with_scope? It’s not mentioned in AWDWR. Were you trolling through the Rails source, reading the Rdoc like a novel, or what?

I’m interested in the development methods others use. I learned a long time ago that an important part of development is scrutinizing the documentation in great detail. And to avoid using undocumented API’s. With_scope is nearly undocumented, although it is in the rdoc. I was surprised to learn that this method call I’d never heard of was already being abused…

Avatar

Chu Yeow

January 10th, 2008 at 6pm

You said it yourself, it is in the RDoc. That is hardly undocumented and unfortunately with_scope IS part of the documented API. It was simply misused by some. Anyway, I read the Rails source and embedded RDocs quite often since I use edge Rails and often submit patches to Rails.

But I didn’t first learn of it from there – I think I either saw it on Ryan Daigle’s blog or in the errtheblog post I was talking about.

Avatar

Fred

January 11th, 2008 at 2pm

Thanks for the info. I tend to dislike reading gobs of extracted documentation, it tends to be very patchy and with logical connections missing.

My psychology on these things comes from building lots of software in teams, software we documented for our customers. Meanwhile, once something was documented we hardly ever dared violate the docs, otherwise the customers would scream. Meanwhile we were fixing and enhancing things behind the wall of the documented API and UI.

I guess this mindset comes from the world of proprietary software. In the open source, or at least Rails, world, I think I’m actually expected to know the source. Which partially defeats the purpose of using software written by others. On the other hand, Linux and Ruby are 2 examples of open source software where you can read the docs to learn what you need to know.

Rails is a change of culture, like moving to a new country, and I am going to have to get used to it. It’s a little awkward since I have non-Rails projects too. A leg in each camp.

Thanks for the info.
Fred

Avatar

Tecker.LOG » Blog Archive » ActiveRecord::Base.with_scope { :only => ‘in your model’ }?????Rails 2.0?? #4(??)

June 3rd, 2008 at 11pm

[…] ???ActiveRecord::Base.with_scope { :only => ‘in your model’ } – Rails 2.0 a feature a day #4 […]