Revealing intent in your controllers with named_scope
I have been thinking lately about how many Rails controller actions I run into that are hard to read and do not reveal the actual intent of the person that wrote the software. This can be extremely frustrating for a developer working with code that they did not originally write.
Consider the following example:We have an Article model that has an Article Type and belongs to a User. Any time a User creates/updates an Article, a Boolean "approved" attribute is set to false.The Article migration file might look something like this:class CreateArticles < ActiveRecord::MigrationSo a simple requirement in this case might be for the site Administrator to be able to "approve" articles. The business refers to unapproved articles as "pending". To do this, we would need to find all of the Articles that have an "approved" value of false. We would also need to order the results by date to return the most recent article changes first.Here is an example of how one could accomplish this query in the controller:
def self.up
create_table :articles do |t|
t.string :title, :limit => 155, :null => false
t.text :content, :null => false
t.integer :user_id, :null => false
t.integer :article_type_id, :null => true
t.boolean :approved, :default => false, :null => false
t.timestamps
end
end def self.down
drop_table :articles
end
end
class PendingArticlesController < ApplicationControllerThere is nothing really wrong with the above find but there are a few things we can do to improve the code by revealing more intent and using the Projects Ubiquitous Language.Enter named_scope:
def index
@articles = Article.find(:all, :include => [:user, :article_type], :conditions => ["approved = ?", false], :order => 'updated_at DESC')
end
end
From the Rails documentation, named_scope "Adds a class method for retrieving and querying objects. A scope represents a narrowing of a database query"By using named_scope, we can refactor the controller code as follows:Introduce named_scope into the Article model
class Article < ActiveRecord::Base
named_scope :pending, :include => [:user, :article_type], :conditions => ["approved = ?", false], :order => 'updated_at DESC'
end
We can now update our controller code to use our new named_scope
class PendingArticlesController < ApplicationController
def index
@pending_articles = Article.pending
end
end
Hopefully this small tip helps.

