Как решить NoMethodError с Pundit - PullRequest
       35

Как решить NoMethodError с Pundit

0 голосов
/ 10 октября 2018

Я не знаю, делаю ли я здесь что-то не так, но мне кажется, что

Я использую Pundit для авторизации и настроил несколько моделей сейчас.

У меня есть модель категории, которая может быть создана только администраторами.Кроме того, я не хочу, чтобы пользователи также видели представления / редактировать / уничтожать.Я просто хочу, чтобы это было доступно администраторам.Пока все хорошо.

Добавим немного кода ниже:

category_policy.rb

class CategoryPolicy < ApplicationPolicy
  def index?
    user.admin?
  end

  def create?
    user.admin?
  end

  def show?
    user.admin?
  end

  def new?
    user.admin?
  end

  def update?
    return true if user.admin?
  end

  def destroy?
    return true if user.admin?
  end
end

category_controller.rb

class CategoriesController < ApplicationController
  layout 'scaffold'

  before_action :set_category, only: %i[show edit update destroy]

  # GET /categories
  def index
    @category = Category.all
    authorize @category
  end

  # GET /categories/1
  def show
    @category = Category.find(params[:id])

    authorize @category
  end

  # GET /categories/new
  def new
    @category = Category.new
    authorize @category
  end

  # GET /categories/1/edit
  def edit
    authorize @category
  end

  # POST /categories
  def create
    @category = Category.new(category_params)
    authorize @category
    if @category.save
      redirect_to @category, notice: 'Category was successfully created.'
    else
      render :new
    end
  end

  # PATCH/PUT /categories/1
  def update
    authorize @category
    if @category.update(category_params)
      redirect_to @category, notice: 'Category was successfully updated.'
    else
      render :edit
    end
  end

  # DELETE /categories/1
  def destroy
    authorize @category
    @category.destroy
    redirect_to categories_url, notice: 'Category was successfully destroyed.'
  end

  private

  # Use callbacks to share common setup or constraints between actions.
  def set_category
    @category = Category.find(params[:id])
  end

  # Only allow a trusted parameter "white list" through.
  def category_params
    params.require(:category).permit(:name)
  end
end

application_policy.rb

class ApplicationPolicy
  attr_reader :user, :record

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

  def index?
    false
  end

  def create?
    create?
  end

  def new?
    create?
  end

  def update?
    false
  end

  def edit?
    update?
  end

  def destroy?
    false
  end

  class Scope
    attr_reader :user, :scope

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

    def resolve
      scope.all
    end
  end
end

Я включил Pundit в мой ApplicationController и rescue_from Pundit::NotAuthorizedError, with: :forbidden он также там настроен.

Сама авторизация работает, если я вошел в систему с учетной записью администратора, которая у меня естьдоступ к / категориям / *.Если я выхожу из системы, я получаю следующее: NoMethodError at /categories undefined method admin? 'for nil: NilClass` При написании вопроса я думаю, что нашел проблему - я думаю, что Pundit ищет пользователя с нулевым значением, потому что я не вошел в систему. Как будет выглядеть правильный подход к решению этой проблемы?

С наилучшими пожеланиями

Ответы [ 2 ]

0 голосов
/ 10 октября 2018

Есть несколько способов достижения того, что вы хотите.

  1. Самый очевидный (но довольно грязный) и простой способ сделать это - добавить проверку для пользователя.Присутствие в каждом условии:

    user && user.admin?

    Это не приведет к ошибке nil, так как вторая часть условия не будет выполнена.Но это выглядит не очень хорошо, правда?Особенно, если вам нужно скопировать это на все методы, которые вы используете в CategoryPolicy.

  2. Что вы можете сделать вместо этого, это заставить Pundit «думать», что вы передали Userпутем создания класса GuestUser, который отвечает методом false на admin? (https://en.wikipedia.org/wiki/Null_object_pattern):

    В объектно-ориентированном компьютерном программировании нулевой объект - это объект без ссылочного значения илис определенным нейтральным («нулевым») поведением. Шаблон проектирования пустых объектов описывает использование таких объектов и их поведение (или его отсутствие)

    И используйте его, когда user равно nil.На практике это будет выглядеть так:

    class ApplicationPolicy
      attr_reader :user, :record
    
      def initialize(user, record)
        @user = user || GuestUser.new
        @record = record
      end
    
      # ...
    end
    
    class GuestUser
      def admin?
        false
      end
    end
    

    Таким образом, вам не придется изменять любой другой ваш код, поскольку модель, которую вы передали, реагирует на метод, ожидаемый политикой (* 1032).*). Вы можете определить это GuestUser где-то еще (не в файле политики), в зависимости от того, хотите ли вы, чтобы другие части приложения использовали это поведение повторно.

  3. Вы можететакже приступить к перенаправлению подхода от P. Boro answeр.В некотором смысле он менее гибок, но может полностью работать, если вам ничего не нужно, кроме перенаправления всех не авторизованных пользователей.

0 голосов
/ 10 октября 2018

Самым распространенным подходом является перенаправление пользователей со страниц, которые недоступны не зарегистрированным пользователям.Просто добавьте в свой контроллер действие before:

class CategoriesController < ApplicationController
  before_action :redirect_if_not_logged_in

  <...>

  private

  def redirect_if_not_logged_in
    redirect_to :home unless current_user
  end
end

(здесь я предполагаю, что у вас есть метод current_user, который возвращает пользовательский экземпляр или ноль. Пожалуйста, измените: домой, куда вы хотите перенаправить пользователей)

...