Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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] 
like image 703
Mario Avatar asked Mar 09 '10 20:03

Mario


People also ask

What is scope in ActiveRecord?

Scopes are used to assign complex ActiveRecord queries into customized methods using Ruby on Rails. Inside your models, you can define a scope as a new method that returns a lambda function for calling queries you're probably used to using inside your controllers.

What is difference between Has_one and Belongs_to?

They essentially do the same thing, the only difference is what side of the relationship you are on. If a User has a Profile , then in the User class you'd have has_one :profile and in the Profile class you'd have belongs_to :user .

What is ActiveRecord in Ruby on Rails?

Active Record is the M in MVC - the model - which is the layer of the system responsible for representing business data and logic. Active Record facilitates the creation and use of business objects whose data requires persistent storage to a database.


2 Answers

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.
like image 66
Franck Verrot Avatar answered Sep 22 '22 14:09

Franck Verrot


How about association extensions?

class Item < ActiveRecord::Base   has_many :orders do     def for_user(user_id)       where(user_id: user_id)     end   end end  Item.first.orders.for_user(current_user) 

UPDATE: I'd like to point out the advantage to association extensions as opposed to class methods or scopes is that you have access to the internals of the association proxy:

proxy_association.owner returns the object that the association is a part of. proxy_association.reflection returns the reflection object that describes the association. proxy_association.target returns the associated object for belongs_to or has_one, or the collection of associated objects for has_many or has_and_belongs_to_many.

More details here: http://guides.rubyonrails.org/association_basics.html#association-extensions

like image 43
Peter P. Avatar answered Sep 19 '22 14:09

Peter P.