Mel
Mel

Reputation: 2687

Rails 5 - Pundit Scopes

I am trying to learn how to use pundit with my Rails 5 app.

I'm trying to follow the approach in this article.

https://learn.co/lessons/devise_pundit_readme

I am struggling to find a working result from this approach. Specifically, the line in my index where I try to use the defined scope, being:

<% policy_scope(@user.proposals).each do |prop| %>

is returning this error:

undefined method `proposals' for nil:NilClass

My setup is:

I have models for user and proposal. The associations are:

User:

  has_many :proposals

Proposal:

belongs_to :user

Application Controller:

include Pundit

Proposals controller:

def index
    @proposals = Proposal.all
    # @bips = @proposal.bips
    authorize @proposals
  end

Proposals policy:

  class ProposalPolicy < ApplicationPolicy
      class Scope < Scope
         def resolve
           if user.has_role?(:admin)
             scope.all
           else
             scope.where(:current_state == :draft)
           end
         end
       end

      def index?
        true
      end

Proposal index:

<% policy_scope(@user.proposals).each do |prop| %>
    <%= prop.title %>
    <%= prop.user.full_name %>
    <%= prop.created_at %>
    <%= link_to 'More', proposal_path(prop) %>
<% end %>

When I try this, I get an error - highlighting an issue with this line of my index:

  <% policy_scope(@user.proposals).each do |prop| %>

Given this is exactly the same as the article I linked shows me to do, I'm lost for why it doesnt work.

The error says:

undefined method `proposals' for nil:NilClass

Key differences between the article I linked and the Pundit documents are that the Pundit readme says:

  1. In relation to class Scope - the readme just has:

    class Scope

i.e. it doesn't have the extra "< Scope"

  1. The readme has this in the Post Policy (I have it in my application policy - but have tried adding it to the Proposal Policy as well):

    attr_reader :user, :scope

  2. The readme has this in the Post Policy (I have it in my application policy - but have tried adding it to the Proposal Policy as well):

    def initialize(user, scope) @user = user @scope = scope end

I tried doing it the way the readme in the pundit docs shows - but I get the same error as when I try following the article.

I tried changing the resolve method so that it always returns scope.all:

def resolve
       if user.has_role?(:admin)
         scope.all
       else
        scope.all
       end

But I still get the same error.

Can anyone see what I need to do in order to use Pundit scopes?

Reading back through my related question earlier this year - I can see that neither the readme on the pundit gem nor the tutorial article I linked appear to be correct: Rails 4 - Pundit - scoped policy for index

I can't get any of the solutions from the linked SO post to work - but the concepts in those answers are more thorough than the gem readme and the tutorial.

NEXT ATTEMPT

Something is fishy with the way pundit finds the user.

I changed the resolve method to something really simple - checking if the first name value of a user is equal to a string (which is a record my user table for the first name of the user I'm logged in as):

class ProposalPolicy < ApplicationPolicy
  class Scope #< Scope
    # class Scope
    attr_reader :user, :scope

    def initialize(user, scope)
      user  = user
      scope = scope
    end

     def resolve
       if user.first_name == "Jane"
         scope.all
       else
         scope.where(:current_state == :draft)
       end

     end
   end

When I try this, I get an error that says:

undefined method `first_name' for nil:NilClass

I don't think pundit knows how to find the user.

When I put a byebug in my proposals index action, I get:

user.inspect
*** NameError Exception: undefined local variable or method `user' for #<ProposalsController:0x007fa3e8cf8698>

(byebug) @user
nil


(byebug) current_user
#<User id: 43, first_name: "Jane",

So - something is wrong with how I am trying to have pundit recognise a user. I've combed through hundreds of repos on GitHub to try to find examples of how people are getting this to work. I can't find anything different to all of the options I tried in my linked post.

I'm not sure if it's strange, but I can do:

(byebug) policy_scope(Proposal)
  Proposal Load (0.4ms)  SELECT "proposals".* FROM "proposals"
#<ActiveRecord::Relation [#<Proposal id: 17, user_id: 43, title: "asdf", description: "adsf", byline: "asdf", nda_required: true, created_at: "2016-11-16 00:28:31", updated_at: "2016-11-28 01:16:47", trl_id: 1>]>

The user_id 43 has a first_name attribute which is Jane. I thought this might have been a sign that something was working, but when I change the resolve method to ask for "John" instead of "Jane", I get the same result:

(byebug) policy_scope(Proposal)
  Proposal Load (0.8ms)  SELECT "proposals".* FROM "proposals"
#<ActiveRecord::Relation [#<Proposal id: 17, user_id: 43, title: "asdf", description: "adsf", byline: "asdf", nda_required: true, created_at: "2016-11-16 00:28:31", updated_at: "2016-11-28 01:16:47", trl_id: 1>]>

Obviously, this time it's not correct, because the first name of the user that this proposal belongs to is not John.

Can anyone see what I need to do to get Pundit to recognise the user?

Upvotes: 2

Views: 1768

Answers (1)

Mel
Mel

Reputation: 2687

So, after another session on codementor.io, I don't have a solution but I have learned that I can't use Pundit scopes with the statesman state_machine gem. I either need to find another authorisation tool or use a different state machine tool.

I have also learned that the pundit readme has several implicit assumptions in the examples which make them unsuitable for use.

If I manage to learn another state machine and give things another try with Pundit, I'll post again to share what I've learned about how to use pundit scopes. Onwards.

Upvotes: 0

Related Questions