Reputation: 2687
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:
In relation to class Scope - the readme just has:
class Scope
i.e. it doesn't have the extra "< Scope"
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
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
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