Rails: Complex Queries with Named Scopes

I've recently come to realise the massive power of ActiveRecord's named_scopes. The revelation came whilst I was researching how to scope, by their permissions, the records a @current_user can view or edit.

I'd looked at all sorts of plugins and complex authorization configurations, before realising that named scopes have almost limitless potential for building such complex queries.

What are named scopes?

As demonstrated by Ryan Bates' screencast, named scopes let you declare finder conditions as methods on your model object.

For example, you could get all active users using

class User < ActiveRecord::Base

  named_scope :active, :conditions => { :active => true }


# To select all active users


The real power of named scopes, though, lies in their ability to be chained. As they are just methods on your model class, they can be combined, and ActiveRecord will take care of building the relevant conditions and joins:

ruby# app/models/user.rb

class User < ActiveRecord::Base

named_scope :active => :conditions => { :active => true }

named_scope :admin => :conditions => { :admin => true }

namedscope :recentlylogged_in,

          lambda { { :conditions => ['logged_in_at >= ?', 7.days.ago]  } }


To get all active users who have recently logged in:


To get all active admin users who have recently logged in


Using Named Scopes to Build Complex Queries

With chaining, you can build some pretty complex queries with ease. However, it would be great if we were able to declare some conditions at run-time, rather than when the model class is loaded.

The lambda function lets you do just that, taking a set of arguments and inserting them into your query:

class User < ActiveRecord::Base

  named_scope :admin, :conditions => { :admin => true }

  named_scope :last_logged_in, lambda { |time_ago|

    time_ago ||= 7.days.ago

    { :conditions => ['logged_in_at >= ?', time_ago] }



# By default, we'll get users who have logged in in the last 7 days:


# To get users who have logged in during the past 4 weeks:


# To get admin users who have logged in in the past 6 months


Combining `lambda` with `named_scope` lets you build complex attribute queries with runtime arguments. As an example, I've reworked my the code from my [previous posts](http://blog.chrisblunt.com/rails-building-complex-search-filters-with-activerecord-and-ez_where/) to make use of named_scope:

ruby# app/models/user.rb

class User < ActiveRecord::Base

namedscope :namelike, lambda { |terms|

# ensure terms is an array

terms = [*terms]

composed_conditions = EZ::Caboose::Condition.new :users

terms.each do |term|

  term = ['%', term, '%'].join

  condition = EZ::Caboose::Condition.new :users

  condition.append ['first_name ILIKE ?', term], :or

  condition.append ['last_name ILIKE ?', term], :or

  composed_conditions << condition


{ :conditions => composed_conditions.to_sql }



With this small chunk of code (and a helping hand from the Caboose EZ Condition plugin), you can now search for Users by matching their first or last names with the supplied terms, e.g:

ruby# app/models/user.rb

class User < ActiveRecord

# ...

namedscope :visibleto lambda { |user|

{ :conditions => ['department_id IN ?', user.managed_department_ids] }



User.lastloggedin(4.weeks.ago).namelike(["joe", "bloggs"]).visibleto(@currentuser).orderedby(:name)```

Named scopes are undoubtedly one of the most powerful tools available to ActiveRecord. I can't help but think I'm still just scratching the surface of their potential, but already I've been able to refactor pages of code and build powerful, Rails-friendly custom queries.

Are you using named scopes in your code? Feel free to discuss in the comments!