Tuesday 22 December 2009

The named_scope in Rails

The named_scope method allows you to set up shortcuts to narrow searches within set parameters. It is invoked in the model, something like this:
class Computer < ActiveRecord::Base
named_scope :active, :conditions => {:in_use => true}
named_scope :networked, :conditions => "(network_id <> \"\")"
named_scope :recent, lambda { {:conditions => ["created_at > ?", 1.months.ago ] } }
end

Note the use of the lambda. If you simply compared the created_at to the date, you would be comparing it to the date the named_scope was invoked, rather than the current date (you might object that Rails reloads the model each time, so the difference might not be so much, but that does not seem to affect your named_scopes). You might also want to do this if you want to access another model. Say your database has a list of types, and this model has a field for the ID of one of those types. You want to collect all the records by the name of the type, so you need to access the table for the other model to get the ID. But that model might not be loaded when this one is, so you have to delay getting the ID. Or perhaps you want to send parameters to a named_scope through the lambda.
# Use a lambda to access a later loading model
named_scope :blue, lambda {
{ :conditions => ["(colour_id = #{Colour.find_by_name('blue').id})"] }
}
# Use a lambda to allow parameters
named_scope :located, lambda { |loc|
{ :conditions => {:location_id => loc } }
}

You can also use named_scopes for other things, such as ordering.
named_scope :ordered, :order => 'created_at ASC'
Now to get an array of computers in use, just do this:
Computer.active

You can chain named_scopes together, and also with find. Now I can list all the blue computers at location 5, in ascending order of record creation, or all the networked computers running WinXP just like this:
Computer.blue.located(5).ordered
Computer.networked.find_by_os("winxp")


References
http://ryandaigle.com/articles/2008/8/20/named-scope-it-s-not-just-for-conditions-ya-know
http://snippets.aktagon.com/snippets/210-How-to-use-named-scope-in-Rails
http://jitu-blog.blogspot.com/2009/07/looking-into-rails-namedscope.html
http://stackoverflow.com/questions/137630/encapsulating-sql-in-a-namedscope

Notes
A named_space can be used with will_paginate without a problem:
@samples = Sample.outstanding.ordered.paginate :page => params[:page], :per_page => 16

I found that I had to restart my web server when trying these out to get the named_scopes to reload.

Also, if named_scope does not like your condition, it just fails to define a new method. There is no warning or hint about what could be wrong. All you get is a method_missing complaint when you try to invoke it.


Struggling with Ruby: Contents Page

1 comment:

Jo said...

Yeah man, thanks you so much for publishing !!!