Rails:在运行时动态地包含每个请求的帮助程序

时间:2014-09-13 09:31:19

标签: ruby-on-rails ruby-on-rails-4 actioncontroller

我的应用有常规用户和管理员。它有一些显示小部件的渲染助手:

module ApplicationHelper
  def widget
    "widget"
  end
end

对于管理员,这些小部件有扩展版本:

module AdminHelper
  def widget
    "admin-widget"
  end
end

默认情况下,应用程序配置为不包括所有帮助程序{.1}}。

当管理员查看页面 - 同一个控制器,相同的视图模板 - 时,我想通过使用AdminHelper版本重载帮助程序来呈现它的扩展版本。如果当前登录的用户是管理员,如何在每个请求的基础上使用AdminHelper重载ApplicationHelper?

我试过了

config.action_controller.include_all_helpers = false

但除非在请求之间重新加载ApplicationController,否则这不起作用。

1 个答案:

答案 0 :(得分:2)

尝试动态加载代码以覆盖窗口小部件是错误的方法。相反,widget方法应决定在逐个请求的基础上显示哪个版本。在我们看一些方法之前,让我们解释为什么模块覆盖不起作用。


当您的Rails应用程序启动时加载

ApplicationController。这会自动包含ApplicationHelper模块,该模块定义widget方法。由于AdminHelper设置,未加载config.action_controller.include_all_helpers - 通过将其设置为false Rails将仅加载与控制器同名的帮助程序。

当普通用户调用widget方法时,将返回字符串"widget"。但管理员访问网站后 include_backend_helpers回调会加载您的AdminHelper模块,覆盖widget方法。管理员获取了返回的字符串"admin-widget" - 但该网站的每个未来访问者也是如此! widget方法不基于用户类型进行区分。

你可以通过在每个请求之后重新加载ApplicationController来解决这个问题,但这需要时间并且会使性能陷入困境。你might be able to unload the module,但这似乎是一种充满困难的方法。您还可以尝试在每个请求上重新加载帮助程序:

def include_backend_helpers
  if admin_signed_in?
    self.class.helper "admin/backend"
  else
    self.class.helper "user/backend"
  end
end

但我不确定这会起作用,我不会推荐它。


相反,似乎小部件的工作是决定什么是适合显示。例如,如果这是一个导航组件,它应该足够智能,只显示管理员链接。您仍然可以将其分解为单独的方法:

module ApplicationHelper
  def widget
    if admin_signed_in?
      user_widget
    else
      admin_widget
    end
  end

  private
  def user_widget
    "widget"
  end

  def admin_widget
    "admin-widget"
  end
end

您可以对Rails部分使用类似的方法 - 您可以使用基于_navigation.html.erb的{​​{1}}部分_user_navigation.html.erb_admin_navigation.html.erb。您的视图包含“<%= render”导航“%>”,并且不会担心内部细节。

如果您有足够的代码使上述内容开始变得难以处理,那么您应该查看Cells for Rails。它是一个宝石,可以更容易地构建封装的显示组件 - 控制器逻辑和视图代码的组合。