Agile Web Development

Build it. Launch it. Love it.

scope_out

Author:John Andrews
License:Distributes under the same terms as Ruby
======

Usage:

  class Person < ActiveRecord::Base
    scope_out :women, :conditions => ["people.sex = ?", "F"]
  end

The above code creates three class methods: find_women, with_women, and calculate_women. It is equivalent to doing the following:

  class Person < ActiveRecord::Base
    def Person.with_women
      with_scope :find => {:conditions => ['people.sex => ?', "F"]} do
        yield
      end
    end

    def Person.find_women(*args)
      with_women {find(*args)}
    end

    def Person.calculate_women(*args)
      with_women {calculate(*args)}
    end
  end

with_x

Person.with_women acts just like with_scope, except the scope is already defined. For example:

  Person.with_women do
    Person.find(:all, :order => 'people.age desc')
  end

find_x

Person.find_women acts just like find, except that it is scoped with with_women:

  Person.calculate_women(:first, :include => :pets)
  # equivalent to Person.find(:first, :conditions => ["people.sex = ?", "F"], :include => :pets)

calculate_x

Person.calculate_active (you guessed it) is exactly like Person.calculate, but scoped with ‘with_women’

  Person.calculate_active(:count, :all)
  # is the same as Person.calculate(:count, :all, :conditions => ["person.sex = ?", "F"])

Association Finders

scope_out also creates a module called AssociationMethods inside the class that defines the scope. Using this, you can extend your associations using the same scopes. Let’s define a second class:

  class Person < ActiveRecord::Base
    has_many :pets, :extend => Pet::AssociationMethods
    scope_out :women, :conditions => ["people.sex = ?", "F"]
  end

  class Pet < ActiveRecord::Base
    belongs_to :person
    scope_out :cats, :conditions => ["pets.type = ?", "Feline"]
  end

  woman = Person.find_women(:first)
  her_pets = woman.pets
  her_cats = woman.pets.cats

  # cats is cached on the association, so this doesn't cause another call to the database
  # unless you do woman.pets.cats(:reload)
  puts "Cat Lady!" if woman.pets.cats.length > 4

Flexible Syntax

All of the following define the same scope:

  class Ticket
    scope_out :active
    scope_out :active, :field => 'active', :value => true
    scope_out :active, :conditions => ["active = ?", true]
    scope_out :active, :conditions => {:active => true} # rails >= 1.2
  end

If you want to use a dynamic condition (which will be evaluated each time the scope is called), you can pass the options hash as a block.

  class Person < ActiveRecord::Base
    scope_out :adults do
      {:conditions => ["people.birthdate < ?", 18.years.ago],
       :order => 'people.birthdate asc' }
    end
  end

Vitals

Home http://code.google.com/p/scope-out-rails
Repository http://scope-out-rails.googlecode.com/svn/trunk/
License Rails' (MIT)
Tags Tag_red activerecord scope
Rating (13 votes)
Owner John Andrews
Created 16 January 2007

Comments

  • Avatar
    17 January 2007

    Neat idea.

    One question:

    Does the macro scope_out, or more specifically, the parameters given to the macro, get run at "run time" or at class definition time?

    To shed some more light on my question, consider the following example:

    scope_out :yesterday, :conditions =&gt; ['shown_at &gt;= :yesterday and shown_at &lt; :today', {:yesterday =&gt; (Date.today-1), :today =&gt; Date.today}]
    

    Does the code Date.today get run once as the class is defined, ot will it get run every time I use MyClass.find_yesterday(:all)

  • John Andrews
    17 January 2007

    Keith, Current implementation would evaluate it once at class definition time. I'm working on a way to make this dynamic. I'll post an update as soon as I get this worked out.

  • Avatar
    John Andrews
    7 February 2007

    UPDATE: If you want to use a dynamic condition (which will be evaluated each time the scope is called), you can pass the options hash as a block. Please see the description above under the heading "Flexible Syntax" for an example.

  • Avatar
    andy
    2 November 2007

    This plugin seems to have a problem with the :group option for find.

    ArgumentError (Unknown key(s): group):

    /var/lib/gems/1.8/gems/activesupport-1.4.4/lib/active_support/core_ext/hash/keys.rb:48:in `assert_valid_keys'
    
  • Avatar
    Tim Watson
    2 December 2007

    scope_out through association possible?

    I love scope_out, its a life saver, but is it possible to do something like?

    ModelA.find(:all, :include => :model_b.active)

    Where active is a scope_out defined in ModelB ?

Add a comment