Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails scope find with current user

I'm using Rails 3 with Devise for user auth. Let's say I have a User model, with Devise enabled, and a Product model, and that a User has_many Products.

In my Products controller I'd like my find method to be scoped by current_user, ie.

@product = current_user.products.find(params[:id])

unless the user is an admin user, i.e. current_user.admin?

Right now, I'm running that code in almost every method, which seems messy:

if current_user.admin?
  @product = Product.find(params[:id])
else
  @product = current_user.products.find(params[:id])
end

Is there a more elegant/standard way of doing this?

like image 762
ideaoforder Avatar asked Nov 18 '11 17:11

ideaoforder


3 Answers

I like to do this as follows:

class Product

  scope :by_user, lambda { |user|
    where(:owner_id => user.id) unless user.admin?
  }

end

this allows you to write the following in your controller:

Product.by_user(current_user).find(params[:id])
like image 153
nathanvda Avatar answered Nov 15 '22 00:11

nathanvda


If you're running this code in a lot of your controllers, you should probably make it a before filter, and define a method to do that in your ApplicationController:

before_filter :set_product, :except => [:destroy, :index]

def set_product
  @product = current_user.admin? ? Product.find(params[:id]) : current_user.products.find(params[:id]) 
end

I don't know what you use to determine if a user is an admin or not (roles), but if you look into CanCan, it has an accessible_by scope that accepts an ability (an object that controls what users can and can't do) and returns records that user has access to based on permissions you write yourself. That is probably really what you want, but ripping out your permissions system and replacing it may or may not be feasible for you.

like image 5
Brett Bender Avatar answered Nov 15 '22 01:11

Brett Bender


You could add a class method on Product with the user sent as an argument.

class Product < ActiveRecord::Base
  ...

  def self.for_user(user)
    user.admin? ? where({}) : where(:owner_id => user.id)
  end

Then you can call it like this:

Product.for_user(current_user).find(params[:id])

PS: There's probably a better way to do the where({}).

like image 2
mbillard Avatar answered Nov 15 '22 02:11

mbillard