所以我决定尝试pundit用户授权解决方案。我想知道如何在视图中使用policy
帮助器,其中实例变量可能为nil,如下面的简单情况:
应用程序/视图/项目/ index.html.slim
h1 Projects
(...)
- if policy(@project).create?
= link_to 'New Project', new_project_path
应用程序/控制器/ projects_controller.rb
(...)
def index
@projects = Project.all
end
(...)
应用程序/策略/ project_policy.rb
class ProjectPolicy < Struct.new(:user, :project)
def create?
user.has_role? :admin
end
我想展示一个&#34;新项目&#34;在Projects#index页面上链接,但是我在这个视图中没有@project实例变量,收到错误:
项目中的Pundit :: NotDefinedError #index
无法找到
的政策NilClassPolicy
错误显然是因为我传递的@project
实例变量是nil,因此有一个NilClass,显然我没有必要授权。
我找到了2个解决此问题的方法,使其正确运行,但似乎没有一个是合适的:
policy(@projects[0])
@project = Project.new
(或直接与上面类似的视图:policy(Project.new)
)第一个解决方案将导致@projects数组中的相同错误为空,而第二个解决方案将创建冗余实例变量。所有策略帮助程序需要知道的是我要在哪个类上强制执行授权逻辑。
有关正确实现目标的任何建议吗?
答案 0 :(得分:5)
你提出的第二个解决方案是我的工作。
- if policy(@project).create?
= link_to 'New Project', new_project_path
此处针对新记录的策略检查应使用Project.new
(无论是否已分配给实例变量)。
- if policy(Project.new).create?
= link_to 'New Project', new_project_path
无论如何,必须将Project
的实例传递给policy
帮助程序,以便Pundit派生策略类ProjectPolicy
来执行create?
检查查找。当您传递nil
时,这就是您看到Pundit派生NilClassPolicy
的原因。
答案 1 :(得分:3)
通常,单个实例与该类中的所有其他实例具有相同的策略 - 这实际上是Pundit正常工作的方式。在这种情况下,您真的不关心属于特定实例的策略;相反,你正在寻找一类物体的政策。
policy
方法使用this find method来标识对象的策略类。
def find
if object.respond_to?(:policy_class)
object.policy_class
elsif object.class.respond_to?(:policy_class)
object.class.policy_class
else
klass = if object.respond_to?(:model_name)
object.model_name
elsif object.class.respond_to?(:model_name)
object.class.model_name
elsif object.is_a?(Class)
object
else
object.class
end
"#{klass}Policy"
end
end
您可以将任何对象传递给策略方法。由于类是对象,因此可以传入类。在find
方法中,第一个条件检查您传递的对象是否响应policy_class
,因此您可以在Project
中定义方法} class policy_class
并让该方法返回ProjectPolicy
类。
class Project < ActiveRecord::Base
def self.policy_class
ProjectPolicy
end
end
如果您没有定义policy_class方法,那么前两个条件将失败,然后find方法将尝试构造策略类名称。它首先查看object.model_name。如果你在Rails中并且你的模型扩展了ActiveModel :: Naming(ActiveRecord :: Base那么),它就会响应model_name
,如果你正在做典型的Rails-ey的东西,那个名称正是你想要的:它将返回"Project"
。然后,find方法将附加&#34; Policy&#34;到最后要做"ProjectPolicy"
。如果它是某个其他对象,则使用该对象的名称,这也适用于大多数情况。
或者,您可以传递任何表示您希望策略对象类型的原型对象。这意味着您可以使用Policy.new
创建一个,或者您可以从@policies
数组中获取一个。正如你所说,这些选择有缺点。
您可以选择很多选项。没有特别的&#34;正确的&#34;办法。我喜欢在这样的情况下传递类,因为它具有最大的语义意义。