Skip to main content

Pundit Scope and has_many through

Rails • Asked by Wouter van den Beld

Hi!

I can use a little help to get on the right track.

I have 3 models

class User < ActiveRecord::Base
  has_many :associations
  has_many :items, through: :associations
end

class Item < ActiveRecord::Base
   has_many :associations
   has_many :users, through: :associations
end

class Association < ActiveRecord::Base
    belongs_to :user
    belongs_to :item
end

Now i want to allow my users only to view and edit the item if they have a link trough associations.
so in my ItemPolicy i have the following scope

 class Scope < Scope
    def resolve
     if user.admin?
        scope.all
      else
        scope.where(:company_id => user.associations.select(:item_id))
      end
    end
  end

I setup my items_controller

def index
  @items = policy_scope(Item.includes(:company).all)
  authorize @items
end

and this works. i only see the items where i have a association. Now i want to prevent users to edit the urllike myapp.com/item/2 and still be able to view item 2 if there is no association.

So i setup my show in the controller:

def show
    @item = policy_scope(Item.find(params[:id]))
    authorize @item
end

but now i get undefined method `where' error. why does the same thing work for index and not for show?
any ideas?

also i would like your opinion if this is the right way to go.


I think that's because on the index you want an array of records, but on show you just want a single record.

When you pass in the array into policy_scope, it's going to call the .where(company_id: X) method on it. This works when you return Item.all but when you pass in just an Item record, that does not have a where method because it's a single instance of the Item class.

In your show action, you can change it to:

def show
    @item = Item.find(params[:id])
    authorize @item
end

And this will load the record and Pundit will know to authorize it against the right method in the policy without hitting the Scope.


Login or Create An Account to join the conversation.

Subscribe to the newsletter

Join 27,623+ developers who get early access to new screencasts, articles, guides, updates, and more.

    By clicking this button, you agree to the GoRails Terms of Service and Privacy Policy.

    More of a social being? We're also on Twitter and YouTube.