如何使用 Pundit 解决 NoMethodError
How to solve NoMethodError with Pundit
我不知道我是不是哪里做错了,但好像是这样。
我使用 Pundit 进行授权,我现在已经用它设置了几个模型。
我有一个只能由管理员创建的类别模型。此外,我也不希望用户看到 show/edit/destroy 视图。我只是想让管理员访问它。到目前为止一切顺利。
将在下面添加一些代码:
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
categories_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
也在那里设置。
授权本身有效,如果我使用管理员帐户登录,我可以访问/categories/*。如果我注销,我会得到以下信息:NoMethodError at /categories
undefined method
admin?'对于 nil:NilClass`
在写问题时我想我发现了问题 - 我猜 Pundit 寻找一个 nil 的用户,因为我没有登录。解决这个问题的正确方法是什么?
此致
最常见的方法是将用户从未登录用户无法访问的页面重定向。只需在您的控制器中添加一个之前的操作:
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
方法,其中 returns 用户实例或 nil。请将 :home 更改为您想要重定向用户的任何位置)
有多种方法可以实现您想要的。
最明显(但有点脏)和最直接的方法是在每种情况下添加对用户存在的检查:
user && user.admin?
它不会因 nil
错误而失败,因为条件的第二部分不会被执行。但它看起来不太好,对吧?特别是如果您必须将其复制到 CategoryPolicy
.
中的所有方法
您可以做的是通过创建一个 GuestUser
class 来使 Pundit "think" 成为您通过的 User
用 false
到 admin?
方法 (https://en.wikipedia.org/wiki/Null_object_pattern):
In object-oriented computer programming, a null object is an object with no referenced value or with defined neutral ("null") behavior. The null object design pattern describes the uses of such objects and their behavior (or lack thereof)
并在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
这样您就不必更改任何其他代码,因为您传递的模型会响应策略 (admin?
) 预期的方法。您可能希望在其他地方(而不是在策略文件中)定义此 GuestUser
,具体取决于您是否希望应用程序的其他部分重用该行为。
您也可以继续使用 P. Boro 回答中的重定向方法。它在某种意义上不太灵活,但如果除了重定向所有未登录的用户之外不需要任何东西,它完全可以正常工作。
我不知道我是不是哪里做错了,但好像是这样。
我使用 Pundit 进行授权,我现在已经用它设置了几个模型。
我有一个只能由管理员创建的类别模型。此外,我也不希望用户看到 show/edit/destroy 视图。我只是想让管理员访问它。到目前为止一切顺利。
将在下面添加一些代码:
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
categories_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
也在那里设置。
授权本身有效,如果我使用管理员帐户登录,我可以访问/categories/*。如果我注销,我会得到以下信息:NoMethodError at /categories
undefined method
admin?'对于 nil:NilClass`
在写问题时我想我发现了问题 - 我猜 Pundit 寻找一个 nil 的用户,因为我没有登录。解决这个问题的正确方法是什么?
此致
最常见的方法是将用户从未登录用户无法访问的页面重定向。只需在您的控制器中添加一个之前的操作:
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
方法,其中 returns 用户实例或 nil。请将 :home 更改为您想要重定向用户的任何位置)
有多种方法可以实现您想要的。
最明显(但有点脏)和最直接的方法是在每种情况下添加对用户存在的检查:
user && user.admin?
它不会因
nil
错误而失败,因为条件的第二部分不会被执行。但它看起来不太好,对吧?特别是如果您必须将其复制到CategoryPolicy
. 中的所有方法
您可以做的是通过创建一个
GuestUser
class 来使 Pundit "think" 成为您通过的User
用false
到admin?
方法 (https://en.wikipedia.org/wiki/Null_object_pattern):In object-oriented computer programming, a null object is an object with no referenced value or with defined neutral ("null") behavior. The null object design pattern describes the uses of such objects and their behavior (or lack thereof)
并在
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
这样您就不必更改任何其他代码,因为您传递的模型会响应策略 (
admin?
) 预期的方法。您可能希望在其他地方(而不是在策略文件中)定义此GuestUser
,具体取决于您是否希望应用程序的其他部分重用该行为。您也可以继续使用 P. Boro 回答中的重定向方法。它在某种意义上不太灵活,但如果除了重定向所有未登录的用户之外不需要任何东西,它完全可以正常工作。