Has Finder Functionality

by: Ryan Daigle | posted: March 24th, 2008

It looks like Nick Kallen’s wildly popular has_finder plugin will be making its way into Rails 2.x in the form of named_scope. Observe:

class User < ActiveRecord::Base
  named_scope :active, :conditions => {:active => true}
  named_scope :inactive, :conditions => {:active => false}
  named_scope :recent, lambda { { :conditions => ['created_at > ?', 1.week.ago] } }
end

# Standard usage
User.active    # same as User.find(:all, :conditions => {:active => true})
User.inactive # same as User.find(:all, :conditions => {:active => false})
User.recent   # same as User.find(:all, :conditions => ['created_at > ?', 1.week.ago])

# They're nest-able too!
User.active.recent
  # same as:
  # User.with_scope(:conditions => {:active => true}) do
  #   User.find(:all, :conditions => ['created_at > ?', 1.week.ago])
  # end

All the goodness you’ve come to love in has_finder is now available as named_scope – plus you get some extra goodies too. User.all is given to you for free as an alias for User.find(:all).

Advanced

For those with more discriminating needs, don’t forget some of these has_finder tidbits:

Passing Arguments

Pass in arguments to your named scopes to specify conditions (or other props) at run-time.

class User < ActiveRecord::Base
  named_scope :registered, lambda { |time_ago| { :conditions => ['created_at > ?', time_ago] }
end

User.registered 7.days.ago # same as User.find(:all, :conditions => ['created_at > ?', 7.days.ago])

Named Scope Extensions

Extend named scopes (in a similar fashion to association extensions).

class User < ActiveRecord::Base
  named_scope :inactive, :conditions => {:active => false} do
    def activate
      each { |i| i.update_attribute(:active, true) }
    end
  end
end

# Re-activate all inactive users
User.inactive.activate

Anonymous Scopes

You can also pass around scopes as first class objects using scoped (a named scoped provided to you for free) as a way to build hairy queries on the fly.

# Store named scopes
active = User.scoped(:conditions => {:active => true})
recent = User.scoped(:conditions => ['created_at > ?', 7.days.ago])

# Which can be combined
recent_active = recent.active

# And operated upon
recent_active.each { |u| ... }

named_scope is a truly great feature. If you haven’t started using it yet, do so. You won’t know how you lived without it. Major thanks goes out to Nick.