跨控制器抽象before_actions的优雅方法?

时间:2018-09-06 21:47:22

标签: ruby-on-rails ruby controller code-duplication

我的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 :(得分:3)

请记住,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

答案 1 :(得分:2)

  

在这种情况下,是否有抽象重复代码的标准方法?

是的。好吧,抽象。在具有有意义名称的方法中隐藏该不同名称。例如,如果您具有以下条件:

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

之前的动作现在是相同的,您可以将其拉到父类或提取为关注点。

答案 2 :(得分:0)

您可以创建一个模块并将类似的控制器放入其中,并使用一个类似于application_controller的base_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中。