Rails best practices: Using named_scope
27 Apr 2010
Knowledge
The
named_scope method, which was introduced since Rails 2.1, allows us to do finds in a more elegant and comfortable than it was in use also make the code that we make more DRY.
By using named_scope we can apply certain conditions when we do find without having to write it repeatedly.
For example, say I have a Book model that has the attribute published. To get a list of books already published, by just using the find I can write it like this
@books = Book.all(:conditions => ["published = ?", true])By way as above, I will probably write it again and again if I want to use the
@books on different controllers. We can avoid this by adding named_scope on Book model such as the following
class Book < ActiveRecord::Base named_scope :published_only, :conditions => ["published = ?", true] endso I can use it in the controller simply by invoke
@books = Book.published_onlyof the code snippet above we can see that by using
named_scope we have added a class method with the name published_only.
Unlike Book.find, the object that is returned by Book.published_only is not an Array object but rather more like the association that was built by the has_many declaration. For example, we can invoke Book.published_only.first or Book.published_only.count. In addition we can also implementing Enumerable on the object that is returned by the named_scope, for example Book.published_only.each(&block).
Named Scopes can also be called in sequence, for example we have one more named_scope
named_scope :hard_cover_only, :conditions => ["cover = ?", "hard"]so that we can invoke
Book.published_only.hard_cover_onlyNamed Scopes are also available for
has_many associations, eg
class Author > ActiveRecord::Base has_many :books endand
rowling = Author.find(221)then
rowling.published_only.hard_cover_only will return all books written by Rowling that have been published and has a hard cover.
You can also pass in variable by using a lambda
class Book < ActiveRecord::Base
named_scope :language, lambda { |language|
{ :conditions => { :language => language } }
}
end
then Book.language('English') will return a list of English books.
