访问控制器 before_action 中的资源的 "rails way" 是什么

What's the "rails way" to access a resource in a controller's before_action

我正在使用 Pundit 来授权我控制器中的操作。我的第一次尝试是在 after_action 钩子中授权模型:

class CompaniesController < InheritedResources::Base
  after_action :authorize_company, except: :index

  def authorize_company
    authorize @company
  end

这让我可以使用定义 @company 的默认控制器操作,这样我就不会访问数据库两次。但是,这对破坏性操作不利,因为它不会在 之后授权操作我已经搞砸了数据库。

所以,我改为使用 before_action 挂钩:

class CompaniesController < InheritedResources::Base
  before_action :authorize_company, except: :index

  def authorize_company
    @company = Company.find(params.require(:id))
    authorize @company
  end

现在,我不允许未经授权的人删除资源等...但我正在访问数据库两次。有没有访问 @company 而不访问数据库两次的方法?

事实证明 rails 控制器有一个 resource 如果模型存在并且 build_resource 用于像 new.

这样的操作
class CompaniesController < InheritedResources::Base
  before_action :authorize_company, except: :index

  private

    def authorize_company
      authorize resource
    rescue ActiveRecord::RecordNotFound
      authorize build_resource
    end
end

既然你要求 "rails way" 这就是你在没有 InheritedResources 的 "plain old rails" 中设置它的方式。

class CompaniesController < ApplicationController
  before_action :authorize_company, except: [:new, :index]

  def new
    @company = authorize(Company.new)
  end

  def index
    @companies = policy_scope(Company)
  end

  # ...

  private

  def authorize_company
    @company = authorize(Company.find(params[:id]))
  end
end

如果您真的想要使用回调,您可以这样做:

class CompaniesController < ApplicationController
  before_action :authorize_company, except: [:new, :index]
  before_action :authorize_companies, only: [:index]
  before_action :build_company, only: [:new]

  # ...

  private

  def authorize_company
    @company = authorize(Company.find(params[:id]))
  end

  def authorize_companies
    @companies = policy_scope(Company)
  end

  def build_companies
    @company = authorize(Company.new)
  end
end

是的,您可以使用三个代码分支编写一个回调方法,但这具有较低的循环复杂度,并且每个方法只完成一项工作。