2014-09-13 2 views
0

У моего приложения есть постоянные пользователи и администраторы. Она имеет несколько помощников рендеринга, которые отображают виджеты:Рельсы: динамически включать помощников за запрос во время выполнения

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. AdminHelper не загружен из-за установки config.action_controller.include_all_helpers - установив его на false. Рельсы будут загружать только хелпер с тем же именем, что и контроллер.

Когда обычный пользователь вызывает метод 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_navigation.html.erb, основанные на admin_signed_in?.Ваше мнение включает в себя «<% = визуализировать» навигацию «%>» и не беспокоиться о внутренних деталях.

Если у вас достаточно кода, что вышеприведенное начинает казаться громоздким, тогда вы должны посмотреть на Cells for Rails. драгоценный камень, который упрощает сборку инкапсулированных компонентов дисплея - комбинации логики контроллера и кода просмотра.

+0

Спасибо за подробный ответ! Я бы предпочел не проверять роль пользователя в каждом отдельном виджете - это довольно избыточно и назойливо, когда у вас их десятки. Для моих взглядов и партитур я просто 'prepend_view_path '', если admin_signed_in?' - таким образом, я могу переопределить их на индивидуальной основе, если это необходимо, все, что мне не хватает, - это также элегантный метод для решения с помощниками. – janfoeh

+0

Если у вас есть десятки компонентов, я бы попытался найти то, что мог бы определить как базовый класс. Затем, в базовом классе, я бы сделал что-то вроде 'm =" admin _ # {method} "; return self.call (m), если admin_signed_in? && self.respond_to? (m) 'внутри него. Это похоже на то, что вы могли бы сделать с [ячейками] (https://github.com/apotonick/cells) красиво. –

+0

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

Смежные вопросы