Элегантный способ абстрагировать before_actions через контроллеры? - PullRequest
0 голосов
/ 07 сентября 2018

В моем Rails API есть ряд контроллеров, которые очень похожи - они имеют только базовые действия CRUD и различаются только по форме базовых данных, которые они хранят.

То, как я реализую авторизацию, в каждом контроллере у меня есть несколько вызовов before_action, которые проверяют разрешения на соответствующем уровне для заданных действий CRUD - эти проверки разрешений в буквальном смысле дубликаты, за исключением того, что каждый из них принимает разные имена переменная экземпляра - например, можно сказать

before_action -> { is_app_admin?(@app_name) } #where @app_name is the actual name of the app.

Теперь, если каким-то образом сам контроллер может принять параметр, я мог бы поместить их перед проверками в ApiController и не повторять их. Или я мог бы изменить имя переменной во всех контроллерах на что-то общее, например @app_name, но в самих контроллерах, что приводит к менее читаемому коду.

Существует ли стандартный способ абстрагирования дубликата кода в сценарии этого типа?

Ответы [ 3 ]

0 голосов
/ 07 сентября 2018

Имейте в виду, что before_action - это не специальный синтаксис, это просто метод класса, как и любой другой. Это означает, что вы можете написать метод класса, который вызывает before_action:

def self.ensure_app_admin_in(var)
  before_action ->{ is_app_admin?(instance_variable_get(var)) }
end

Добавьте это в модуль, контроллер, ApplicationController или где угодно, и затем в ваших контроллерах скажите:

class Controller1
  ensure_app_admin_in :@app_name
  #...
end

class Controller2
  ensure_app_admin_in :@my_other_app_name
  #...
end
0 голосов
/ 07 сентября 2018

Вы можете создать модуль и поместить в него аналогичные контроллеры с помощью base_controller, аналогичного application_controller, который будет иметь ваши before_actions, и только унаследованные от него контроллеры будут использовать их.

Например, если у вас есть контроллеры администратора:

class Admin::BaseController < ApplicationController
  before_action :authorize_admin!

  def authorize_admin!
    redirect_to root_path unless user.admin?
  end
end

class Admin::UsersController < Admin::BaseController
  def index
  end
end

Тогда в ваших маршрутах вы можете либо добавить пространство имен, либо добавить модуль в маршрут следующим образом:

resources :users, module: 'admin'

Затем поместите ваши административные контроллеры в app/controllers/admin, а ваши представления в app/views/admin/users.

0 голосов
/ 07 сентября 2018

Существует ли стандартный способ абстрагирования дубликата кода в сценарии такого типа?

Да. Это, ну, абстракция . Скрыть это переменное имя в методе со значимым именем. Если, например, у вас есть эти:

class Controller1
  before_action -> { is_app_admin?(@app_name) }
end

class Controller2
  before_action -> { is_app_admin?(@my_other_app_name) }
end

Тогда вот что вы могли бы сделать:

class Controller1
  before_action -> { is_app_admin?(app_name_for_authorization) }

  private 

  def app_name_for_authorization
    @app_name
  end
end

class Controller2
  before_action -> { is_app_admin?(app_name_for_authorization) }

  private 

  def app_name_for_authorization
    @my_other_app_name
  end
end

Действия до этого теперь идентичны, и вы можете перетащить их в родительский класс или извлечь в качестве задачи.

...