2014-09-13 51 views
0

我的應用程序具有普通用戶和管理員。它有一個顯示部件一些渲染助手:這些小部件的Rails:在運行時動態包含每個請求的幫助程序

module ApplicationHelper 
    def widget 
    "widget" 
    end 
end 

對於管理員,也有擴展版本:

module AdminHelper 
    def widget 
    "admin-widget" 
    end 
end 

該應用程序被配置爲不通過config.action_controller.include_all_helpers = false包括默認情況下所有的助手。

當管理員查看一個頁面 - 相同的控制器,相同的視圖模板 - ,我想通過重載助手與他們的AdminHelper版本來渲染它的擴展版本。如果當前登錄的用戶是管理員,我如何在每個請求的基礎上將ApplicationHelper與AdminHelper重載?

我已經試過

class ApplicationController < ActionController::Base 
    before_action :include_backend_helpers 

    protected 

    def include_backend_helpers 
    self.class.helper "pica/backend" if admin_signed_in? 
    end 
end 

但除非是ApplicationController的請求之間重新加載,這並不工作。

回答

2

嘗試動態加載代碼來覆蓋小部件是錯誤的方法。相反,widget方法應該決定在逐個請求的基礎上顯示哪個版本。在我們考慮一些方法來做到這一點之前,讓我們解釋爲什麼模塊覆蓋不起作用。


ApplicationController在您的Rails應用程序啓動時加載。這將自動包含ApplicationHelper模塊,該模塊定義了widget方法。由於config.action_controller.include_all_helpers設置,AdminHelper未被加載 - 通過將其設置爲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部分,包括無論是_user_navigation.html.erb或基於admin_signed_in?_admin_navigation.html.erb。您的視圖包括'<%=渲染「導航」%>」,並且不擔心內部細節。

如果你有足夠的代碼,上面開始感到笨重,那麼你應該看看Cells for Rails。這是一個gem,可以更容易地構建封裝的顯示組件 - 控制器邏輯和視圖代碼的組合。

+0

謝謝你的詳細答案!我不希望檢查用戶在每個小部件中的角色 - 這是相當多餘和麻煩的時候你有幾十個,對於我的觀點和部分,我只是'prepend_view_path''如果admin_signed_in?' - 這樣我可以根據需要在個別基礎上覆蓋它們;我所缺少的是一個類似的優雅的交易方法與幫手。 – janfoeh 2014-09-13 16:42:03

+0

如果你有幾十個組件,我會嘗試找到一些我可以定義爲基類的東西。然後,在基類中,我會做類似'm =「admin _#{method}」;返回self.call(m)如果admin_signed_in? && self.respond_to?(m)'在裏面。這似乎是你可以用[cells](https://github.com/apotonick/cells)很好地完成的事情。 – 2014-09-13 17:44:48

+0

我會給這個想法一個旋轉,謝謝。對於這個項目,我已經考慮過Cells,並且出於概念上的原因將它解僱了,但我會再次嘗試。 – janfoeh 2014-09-13 18:26:39