如何让专家政策更干?

How to make pundit policies more DRY?

在我的一个项目中,我开始使用专家 gem,我有一个非常简单的策略,如下所示:

class CompanyPolicy < ApplicationPolicy
  def index?
    true if user.is_a? Administrator
  end

  def new?
    true if user.is_a? Administrator
  end

  def create?
    new?
  end

  def edit?
    true if user.is_a? Administrator
  end

  def update?
    edit?
  end
end

问题是我怎样才能避免重复这个:

true if user.is_a? Administrator

我实际上认为您不需要删除它。通过重复这一点,您明确表示该用户必须是管理员才能访问该方法。如果你确实想要,你可以创建一个私有方法。

class CompanyPolicy < ApplicationPolicy
  def index?
    admin?
  end

  def new?
    admin?
  end

  def create?
    new?
  end

  def edit?
    admin?
  end

  def update?
    edit?
  end

  private 
     def admin?
        user.is_a? Administrator
     end
end

我猜这是个人喜好问题。

我的技巧是这样的:

class ApplicationPolicy

  private

  def self.permit_owner_to(*actions)
    actions.each do |action|
      define_method("#{action}?") do
        owner?
      end
    end
  end

  def owner?
    # owner logic
  end

end

并在其他政策中使用

class ItemPolicy < ApplicationPolicy

  permit_owner_to :show, :update, :destroy, :confirm

end

你可以使用 alias_method.

class CompanyPolicy < ApplicationPolicy
  def index?
    user.is_a? Administrator
  end

  alias_method :create?, :index?
  alias_method :update?, :index?
end

您有一个基础 class ApplicationPolicy,它可能已经包含:

def new?
  create?
end

def edit?
  update?
end

所以你不需要在你的子程序中重复这些方法class。

.is_a? returns truefalse 所以不需要明确 return true if true.

这样简洁多了是吗? :)

我结合上面的答案得出以下结论:

class ApplicationPolicy
  attr_reader :user

  def initialize(user)
    @user = user
  end

  def self.permit(roles, options)
    return if options[:to].none?

    options[:to].each do |action|
      define_method("#{action}?") do
        return @user.roles? Array.wrap(roles) if options[:when].blank?

        send(options[:when]) and @user.roles? Array.wrap(roles)
      end
    end
  end
end

允许这样使用它:

class CommentPolicy < ApplicationPolicy
  attr_reader :user, :record

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

  permit %i[admin member], to: %i[show edit destroy update], when: :created_by_user

  def created_by_user
    @record.user == @user
  end
end

permit :admin, to: %i[index update edit]

同样有效

我在 user 模型中的角色方法如下所示:

def roles?(user_roles)
    user_roles.each do |role|
      return true if role?(role)
    end
    false
  end

  def role?(role)
    roles.any? { |r| r.name.underscore.to_sym == role }
  end