Rails 4 - Pundit, Scopes: Getting Started

痞子三分冷 提交于 2019-12-12 10:14:59

问题


I am really struggling in my efforts over the past 2+ years to try to learn how to use pundit.

I am trying to write scoped policies, so that different users can receive objects based on the scope class that they fit into.

I have asked several questions on the same topic previously, but I'm not getting any closer to a solution. My recent questions are: here, here, here, here, here and here.

There are several others but these give the general picture, that I am struggling with the fundamentals of how to get started.

I have a million problems with getting started and realise that after 4 years of trying (every day - 10+ hours), I'm not really getting far in my attempts to learn to code, BUT the current problem Im facing is with this error:

wrong number of arguments (given 2, expected 0)

It points to wherever I put the authorize @eoi line in my controller. I previously asked about this error here

I was originally putting in the set_eoi method as follows:

 def set_eoi
      @eoi = Eoi.find(params[:id])
      authorize @eoi
    end

I was using that method in a before action in the controller:

before_action :set_eoi, only: [:show, :edit, :update, :destroy]

I stopped doing that when I found the answer to this post, which says not to use authorize in a before action (even though the Go Rails video tutorial on pundit shows to do this).

Then, I moved the authorize call to the show action itself:

  def show
    authorize @eoi
  end

That gives the same error as using it in the before action.

Please, can someone help with getting started. My current setup is as follows:

Objective

User's have profiles. Those profiles own projects they create. Other user's can express interest in joining a project owned by another user. The user who creates the project should be able to see all of the eois submitted on their own project. A user who submits an eoi on a project, should see their own eoi only, when viewing a project page. Also, every user can see an index of their own eois (doesnt matter which project they submitted the eoi on).

The approach I currently took is to make two policies for the same controller (although I think maybe this issue resolves that this is not a good approach to take).

I have models for User, Profile. Project and Eoi. The associations are:

class User < ActiveRecord::Base
  has_one :profile
  has_many :eois
end

class Profile < ActiveRecord::Base
  belongs_to :user
  has_many :projects, dependent: :destroy
end

class Project < ActiveRecord::Base
  belongs_to :profile
  has_many :eois
end

class Eoi < ActiveRecord::Base
  belongs_to :project
  belongs_to :user
end

My routes file has:

resources :eois#, only: [:index]
  concern :eoiable do
    resources :eois
  end

resources :projects do
    member do
  concerns :eoiable
end

I have an Eoi Policy and a Project Eoi Policy, as follows:

class EoiPolicy < ApplicationPolicy

  class Scope

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

    def resolve
      # selects all the EOI's for a given user
      @scope.where(user_id: @user.id)
    end

  end

  def index?
    true
  end

  def new?
    true
  end

  def show?
#     record.user_id == user.id || user.profile.project_id == record.project_id
    true
  end

  def edit?
    user.id == eoi.user.id?
  end

  def create?
    true
  end

  def update?
    user.id == eoi.user.id?
  end

  def destroy?
    user.id == eoi.user.id?
  end


end


class ProjectEoiPolicy < ApplicationPolicy
  class Scope < Scope
    def resolve(project_id)
      project = Project.find(project_id)
      if project.owner?(@user)
        # if the user is the owner of the project, then get
        # all the eois
        project.eois
      else
        # select all the eois for the project
        # created by this user
        Eoi.for_user(@user.id).for_project(project_id)
      end
    end
  end

end

My eois controller has:

class EoisController < ApplicationController
  before_action :get_project, except: [:index, :show]
  before_action :set_eoi, only: [:show, :edit, :update, :destroy]
  # before_action :load_parent
  # before_action :load_eoi, only: [:show, :edit, :update, :destroy]


  def index
    if params[:project_id]
      @eois = ProjectEoiPolicy::Scope.new(current_user, Eoi).resolve(params[:project_id])
    else
      @eois = policy_scope(Eoi)
    end
  end


  # GET /eois/1
  # GET /eois/1.json
  def show
    authorize @eoi
  end

My application policy has:

class ApplicationPolicy
  attr_reader :user,  :scope

  class Scope
    def initialize(user, scope)
      #byebug        
      @user = user
      # record = record
      @scope = scope
    end

    def resolve
      scope
    end
  end

  def index?
    false
  end

  def show?
    scope.where(:id => record.id).exists?
  end

  def create?
    false
  end

  def new?
    create?
  end

  def update?
    false
  end

  def edit?
    update?
  end

  def destroy?
    false
  end

  def scope
    Pundit.policy_scope!(user, record.class)
  end

When I save all of this and try it, I can render all of the indexes correctly (although I can't when I add authorize @eois to the index action in the eois controller.

I can't get the show page to render. Instead I get an error that says:

ArgumentError at /projects/26/eois/24
wrong number of arguments (given 2, expected 0)

I don't know what this error message means. I am only giving one argument to the authorize method in the show action of the eoi controller - so its strange to me that this error thinks I have given 2.

I already saw this post, where a user seems to have the same error message as me, although the answer in that post highlights some errors in that user's setup that I don't have myself.

Can anyone see where I'm going wrong? To my mind, the index only works because I haven't tried to add authorize @eois to it. The scopes are right for the index purposes. The show action doesnt work at all when I try to authorize it.


回答1:


ok, on the Pundit docs: https://github.com/elabs/pundit

the example seems to suggest that the Policy Scope need to inherit from applicationPolicy's scope:

class PostPolicy < ApplicationPolicy
  class Scope < Scope

so my guess is that you should try this:

class EoiPolicy < ApplicationPolicy
  class Scope < Scope # this is the changed line of code

Likewise, the application policy you have has the attr_readers outside of the Scope where the pundit examples have them inside eg:

class ApplicationPolicy
  # should not be here
  # attr_reader :user,  :scope

  class Scope
    # should be here instead
    attr_reader :user,  :scope

which might get you past the issue where you're deep-diving into the @-var here when you should just need the reader:

def resolve
  # selects all the EOI's for a given user
  # should not use `@scope`
  # @scope.where(user_id: @user.id)
  # instead use `scope` which is the reader
  scope.where(user_id: @user.id)
end

Regardless of whether this fixes your bug... you probably need to do these things :)



来源:https://stackoverflow.com/questions/39382294/rails-4-pundit-scopes-getting-started

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!