Как рельсы могут получить доступ к ресурсу в контроллере 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 без двойного обращения к базе данных?


person user341493    schedule 12.03.2020    source источник


Ответы (2)


Поскольку вы спрашиваете о «пути рельсов», именно так вы бы настроили это в «обычных старых рельсах» без InheritedResources.

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

Да, вы могли бы написать один метод обратного вызова с тремя ветвями кода, но это имеет меньшую циклическую сложность, и каждый метод выполняет одну работу.

person max    schedule 13.03.2020

Оказывается, у контроллеров рельсов есть 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
person user341493    schedule 12.03.2020
comment
Скорее всего, это метод InheritedResources, а не Rails. - person max; 13.03.2020
comment
И не делай этого. ActiveRecord::RecordNotFound будет поднято, если переданный идентификатор не является допустимым идентификатором, что произойдет в вашем приложении, если ресурс будет удален, и кто-то перейдет по старой ссылке. Вместо того, чтобы возвращать страницу с ошибкой и 404, вы теперь визуализируете страницу показа с новым (пустым) ресурсом и получите множество нулевых ошибок. - person max; 13.03.2020