我怎么能重构一个Pundit政策让它变得更干?

时间:2018-05-17 14:47:59

标签: ruby-on-rails ruby dry pundit

我很高兴将Pundit与Rails一起使用,并为我的Artist模型制定了一项新政策,该政策正如我所期望的那样,但我不清楚是否有一个很好的方法来重构它以实现它更干。具体来说,似乎我在authorize @artist中调用了artists_controller.rb次太多次,并且artist_policy.rb中有很多代码重复。

对于上下文,艺术家有一个名字,例如" Claude Monet"就是这样。

这是我的artist_policy.rb: https://gist.github.com/leemcalilly/799d5f9136b92fcf92c6074e6a28bfdb

而且,我的application_policy.rb: https://gist.github.com/leemcalilly/09d37a42c6f2500f98be3f1518c945e9

这是我的artists_controller.rb: https://gist.github.com/leemcalilly/c0dd8f33416b002f3b4c9a7baf0a3a75

并且,models / artist.rb: https://gist.github.com/leemcalilly/c190322af41f3e91739b53391d8b7834

我目前的工作方式是否正常,因为我清除表达每个策略(与集成测试中的某些重复代码相同的方式),或者我应该重构这个?如果是这样,人们是否有一种标准的方式来构建我所缺少的Pundit政策?

1 个答案:

答案 0 :(得分:4)

  

我似乎在artists_controller.rb

中多次调用authorize

老实说,我认为你在那里拥有的很好。

您可以通过以下几种方式 尝试巧妙地解决此问题,并自动调用artist_policy.rb"对于每个控制器动作,但(警告:基于意见的答案)从过去的经验中我发现,这种使其更干燥的尝试增加了显着的混乱。特别是当您最终编写一些不需要授权或需要以不寻常的方式授权的控制器操作时。

  

我的class ArtistPolicy < ApplicationPolicy attr_reader :user, :artist def initialize(user, artist) @user = user @artist = artist end def create? if user.admin? || user.moderator? || user.contributor? true elsif user.banned? false end end def update? if user.admin? || user.moderator? || user.contributor? && user.id == @artist.user_id true elsif user.banned? false end end def destroy? if user.admin? || user.moderator? || user.contributor? && user.id == @artist.user_id true elsif user.banned? false end end end

中有很多代码重复

一步一步......这是原作:

initialize

没有必要定义您自己的record方法,只要您乐意引用更通用的变量名称:artist,而不是{{1 (应该在ApplicationPolicy中定义):

class ArtistPolicy < ApplicationPolicy
  def create?
    if user.admin? || user.moderator? || user.contributor?
      true
    elsif user.banned?
      false
    end
  end

  def update?
    if user.admin? || user.moderator? || user.contributor? && user.id == record.user_id
      true
    elsif user.banned?
      false
    end
  end

  def destroy?
    if user.admin? || user.moderator? || user.contributor? && user.id == record.user_id
      true
    elsif user.banned?
      false
    end
  end
end

接下来,在这种情况下,从一个策略规则引用另一个策略规则是可以的 - 只要它们同样适用于用户类型:

class ArtistPolicy < ApplicationPolicy
  def create?
    if user.admin? || user.moderator? || user.contributor?
      true
    elsif user.banned?
      false
    end
  end

  def update?
    if user.admin? || user.moderator? || user.contributor? && user.id == record.user_id
      true
    elsif user.banned?
      false
    end
  end

  def destroy?
    update?
  end
end

接下来,请注意创建操作的record.user_id 登录用户!所以你可以进一步简化这个:

class ArtistPolicy < ApplicationPolicy
  def create?
    if user.admin? || user.moderator? || user.contributor? && user.id == record.user_id
      true
    elsif user.banned?
      false
    end
  end

  def update?
    create?
  end

  def destroy?
    create?
  end
end

最后,该方法的逻辑实际上是错误的。 (你本可以通过测试选择它...)如果用户是管理员他们被禁止,那么你仍然希望这返回false,而不是{{1} }。考虑到这一点,我们可以再次修复+简化代码:

true