Search code examples
ruby-on-railsactiverecordruby-on-rails-3named-scopearel

How do you scope ActiveRecord associations in Rails 3?


I have a Rails 3 project. With Rails 3 came Arel and the ability to reuse one scope to build another. I am wondering if there is a way to use scopes when defining a relationship (e.g. a "has_many").

I have records which have permission columns. I would like to build a default_scope that takes my permission columns into consideration so that records (even those accessed through a relationship) are filtered.

Presently, in Rails 3, default_scope (including patches I've found) don't provide a workable means of passing a proc (which I need for late variable binding). Is it possible to define a has_many into which a named scope can be passed?

The idea of reusing a named scope would look like:

Orders.scope :my_orders, lambda{where(:user_id => User.current_user.id)}
has_many :orders, :scope => Orders.my_orders

Or implicitly coding that named scope in the relationship would look like:

has_many :orders, :scope => lambda{where(:user_id => User.current_user.id)}

I'm simply trying to apply default_scope with late binding. I would prefer to use an Arel approach (if there is one), but would use any workable option.

Since I am referring to the current user, I cannot rely on conditions that aren't evaluated at the last possible moment, such as:

has_many :orders, :conditions => ["user_id = ?", User.current_user.id]

Solution

  • I suggest you take a look at "Named scopes are dead"

    The author explains there how powerful Arel is :)

    I hope it'll help.

    EDIT #1 March 2014

    As some comments state, the difference is now a matter of personal taste.

    However, I still personally recommend to avoid exposing Arel's scope to an upper layer (being a controller or anything else that access the models directly), and doing so would require:

    1. Create a scope, and expose it thru a method in your model. That method would be the one you expose to the controller;
    2. If you never expose your models to your controllers (so you have some kind of service layer on top of them), then you're fine. The anti-corruption layer is your service and it can access your model's scope without worrying too much about how scopes are implemented.