Scope Out ActiveRecord Conditions
Most Rails applications often need to work with ActiveRecord conditions. For example, your application might need to find recent items, inactive users, or non-expired coupon codes. To keep your code DRY, you do not want to be doing this all over your controllers:
Jamis Buck has written about this; he prefers defining methods on the associations.
For example:
(1) To make this scope available to all models which have many users, you have to define it on all the associations or extend all associations with a module.
(2) This provides no easy way to nest scopes, such as finding active users who are female. (That is, without going back to passing in the :conditions option over and over again.)
(3) If I want to know what criteria makes a user active/inactive, I want to look only in the User model.
There are definitely some challenges with this. As Jamis mentioned, you can define the conditions on the model and still access it through the association, such as:
The solution: John Andrews has written a plugin, Scope Out, which you need to use in your models. Take a look at the examples on the project page, but in summary:
(1) Your conditions are defined in ONE place.
(2) You can extend associations if you want caching.
(3) The 'raw' with_scope is available, making applying multiple conditions easy.
(4) The find and calculate methods are both available.
It's a great plugin and has been a great benefit to all our projects.
Documentation and code: Scope Out
class UsersController < ApplicationController def list @users = User.find(:all, :conditions => {:active => true}) end endIn addition, shouldn't your model define what makes a user active or not?
Jamis Buck has written about this; he prefers defining methods on the associations.
For example:
class Group < ActiveRecord::Base has_many :users do def active(reload = false) @active = nil if reload @active ||= find(:all, :conditions => ["active = ?", true]) end end endNow you can find active users in a group through: Group.find(1).users.active. Defining the scope on the association is cool, especially so you can cache the result, but I have a few problems with it.
(1) To make this scope available to all models which have many users, you have to define it on all the associations or extend all associations with a module.
(2) This provides no easy way to nest scopes, such as finding active users who are female. (That is, without going back to passing in the :conditions option over and over again.)
(3) If I want to know what criteria makes a user active/inactive, I want to look only in the User model.
There are definitely some challenges with this. As Jamis mentioned, you can define the conditions on the model and still access it through the association, such as:
class User < ActiveRecord::Base def find_active(options = {}) find(:all, options.merge(:conditions => {:active => true})) end end # this works now Group.find(1).users.find_activeThis seems like a simple solution, but what about the caching? Or nesting conditions? Clearly, any way of doing this has some downsides.
The solution: John Andrews has written a plugin, Scope Out, which you need to use in your models. Take a look at the examples on the project page, but in summary:
(1) Your conditions are defined in ONE place.
(2) You can extend associations if you want caching.
(3) The 'raw' with_scope is available, making applying multiple conditions easy.
(4) The find and calculate methods are both available.
class User < ActiveRecord::Base scope_out :active, :conditions => {:active => true} end class Group < ActiveRecord::Base has_many :users, :extend => User::AssociationMethods end # All of these work # Basic example User.find_active(:all, :order => 'country') # Using the with_ (great for combining with other 'withs' User.with_active { User.find(:all) } # Calculations User.calculate_active(:count, :all) # Accessing from associations (with caching) Group.find(:first).users.active
It's a great plugin and has been a great benefit to all our projects.
Documentation and code: Scope Out
Posted on 2007-02-10 | permalink | del.icio.us
Blog Archive
