Политики Pundit с двумя входными параметрами

Я новичок в Rails, и у меня есть проблема со следующими политиками (используя Pundit): я хотел бы сравнить два объекты: @record и @foo, как вы можете видеть здесь:

class BarPolicy < ApplicationPolicy
  def show?
    @record.foo_id == @foo
  end
end

Я не могу найти хороший способ передать второй параметр методам эксперта (@foo).

Я хотел бы сделать что-то вроде:

class BarsController < ApplicationController
  def test
    authorize bar, @foo, :show? # Throws ArgumentError
    ...
  end
end

Но метод авторизации Pundit допускает только два параметра. Есть ли способ решить эту проблему?

Спасибо!


person Rowandish    schedule 29.01.2015    source источник


Ответы (2)


Я нашел ответ на здесь.

Вот мой способ:

Добавьте функцию pundit_user в ApplicationController:

class ApplicationController < ActionController::Base
include Pundit
def pundit_user
    CurrentContext.new(current_user, foo)
end

Создайте класс CurrentContext:

/lib/pundit/current_context.rb
class CurrentContext
  attr_reader :user, :foo

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

Обновите метод инициализации Pundit.

class ApplicationPolicy
  attr_reader :user, :record, :foo

  def initialize(context, record)
    @user = context.user
    @foo = context.foo
    @record = record
  end
end
person Rowandish    schedule 30.01.2015
comment
Да, это правильный способ задокументировать это. - person Arup Rakshit; 29.09.2017
comment
Какой смысл писать столько кода только для того, чтобы передать параметр одному методу политики, когда никакие другие методы этого не требуют. - person Vedmant; 21.05.2019
comment
Я согласен, к сожалению, некоторые пуристы определили, что это запах кода, но он просто сбивает с толку простых смертных, таких как я. - person Dylan Pierce; 23.04.2020

Полагаться на что-то большее, чем текущий пользователь и модель домена, — это запах кода, но если это действительно необходимо, вы можете использовать собственный метод запроса с любым количеством параметров и вызвать исключение, если требования не выполняются:

class BarPolicy < ApplicationPolicy
  def authorize_test?(foo)
    raise Pundit::NotAuthorizedError, "not authorized to test" unless record.foo_id == foo
  end
end

class BarsController < ApplicationController
  def test
    skip_authorization && BarPolicy.new(current_user, @record).authorize_test?(@foo)
    ...
  end
end

Часть skip_authorization && не требуется, если after_action :verify_authorized не используется, я просто хотел показать однострочник, который можно использовать в этом случае, чтобы избавиться от неавторизованного исключения, но при этом иметь требование авторизовать действие.

person István Ujj-Mészáros    schedule 13.05.2020