Rails 4 - Pundit - scoped policy for index

后端 未结 3 1212
萌比男神i
萌比男神i 2021-01-14 17:24

I am trying to learn how to use Pundit with my Rails 4 app.

I have the following models:

class User < ActiveRecord::Base
  has_one :profile
  has_         


        
3条回答
  •  天涯浪人
    2021-01-14 18:28

    In your first example, there's a couple of issues. Firstly, @eoi does not exist, and can't exist. The @eoi variable is set in the controller, and this is a different object. It doesn't work in the same way as your views where this is accessible, so this will never be set.

    Equally, the eoi variable will not be set, as your initialize method is only assigning the user and resource variables, so they're the only two you have access to (unless you rename)

    The scope in the policy works a little differently to how you think it works. The policy itself generally takes the user logged in, and a class, or a record that you are authorising. The scope however, doesn't normally take a record as the second argument. It is a scope, so either an active record sub-class, or a relation. You're not restricted to this however, and you could work around it by supplying a record but do note this is not normal behaviour for Pundit.

    In order to achieve what you're after, you should only have to make a few adjustments:

    class EoiPolicy < ApplicationPolicy
    
      class Scope
        attr_reader :user, :eoi
    
        def initialize(user, eoi)
          @user = user
          @eoi  = eoi
        end
    
        def resolve
          if user.profile.project.id == eoi.project_id
            Eoi.where(project_id: user.profile.project.id)
          elsif user.id == eoi.user_id
            Eoi.where(user_id: user.id)
          else
            nil
          end
        end
      end
    
      def index?
        user.profile.project.id == record.project_id or user.id == record.user_id
      end
    
      def new?
        true
      end
    
      def show?
        user.profile.project.id == record.project_id? or user.id == record.user_id
      end
    
      def edit?
        user.id == record.user.id
      end
    
      def create?
        true 
      end
    
      def update?
        user.id == record.user.id
      end
    
      def destroy?
        user.id == record.user.id
      end
    
    
    end
    

    The main changes here are that the attr_reader :user, :scope is now attr_reader :user, :eoi which will give you access to eoi within that scope.

    Access to this is no longer prefixed with @ as this is in-line with how pundit works.

    Throughout the rest of the policy, @eoi again cannot work, but this has been changed to record (assuming this is what it is in ApplicationPolicy). Please bear in mind the the Scope, and the rest of the policy are two different classes.

    With this setup, you should now be able to simply call policy_scope(@eoi) from within your controller. Note the usage of the @eoi variable here and NOT the Eoi class as before. This is crucial, as without this, you won't have access to things like user_id or project_id as those methods don't exist in the Eoi class, but only a record.

    I've also removed the ? symbols from the end of your if conditions. These are generally used to signify that the method being called returns a boolean, whereas you had them on the end of something that simply returns an integer. I'd imagine you'd actually get an error saying the method doesn't exist but if you've renamed things then you may want to put them back, but as I say that does go against ruby coding styles.

    And on a side-note, using or or and in statements instead of || or && can on the odd occasion behave differently to how you expect. In most scenarios it's fine, but it doesn't technically mean the same thing.

    Hope this all helps, let me know if you have any further issues with it.

提交回复
热议问题