Render engine в макете приложения
Фон
Я создаю приложение, состоящее из ядра и нескольких модулей. Модули являются рельсовыми двигателями и обеспечивают фактическую функциональность, поскольку сам ядро действует только как хост.
Двигатели размещаются с /lib
и монтируются по их соответствующим путям.
coreApp
└──lib
├── module1
├── module2
└── etc
Затем модули монтируются таким образом
mount Module1::Engine => "/module1", :as => "module1"
mount Module2::Engine => "/module2", :as => "module2"
Ядро также отвечает за обработку сеанса, хотя сам вход выполняется с помощью модуля.
Проблема
Мне еще предстоит найти отличный способ совместного использования макета приложения с двигателями. На данный момент это то, как я делаю макет доступным для двигателей:
coreApp
└── app
└── views
└── layouts
├── application.html.erb
└── core.html.erb
Файл core.html.erb
содержит только
<%= render :template => 'layouts/application' %>
Затем он включается в каждый модуль, подобный этому
module Module1
class ApplicationController < ActionController::Base
layout "core"
end
end
Хотя он не особенно изящный, он отлично работает, и содержимое модуля отображается там, где оператор yield
в макете приложения.
Проблемы заключаются в следующем:
1. Таблицы стилей, специфичные для модуля, не включены в заголовок
Мне нужен способ включить таблицы стилей активного модуля.
2. Заголовок нуждается в доступе к информации о зарегистрированном пользователе
Заголовок содержит информацию о зарегистрированном пользователе, например
Logged in as <%= @user[:realname] %>
Это происходит от ядер home_controller
def index
@user = User.find_by_id(session[:user])
end
Но когда я пытаюсь получить доступ к модулю, я получаю следующую ошибку
NoMethodError in Module1/home#index
You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.[]
Очевидно, ссылаясь на @user
.
Вопрос
Как это можно решить как изящно и сухим, насколько это возможно, без чрезмерного вмешательства на стороне двигателя?
У меня есть многого, но я не могу понять, как это решить. Это может быть полное отсутствие понимания того, как работают рельсы, поэтому есть хороший шанс, что этот вопрос даже не имеет смысла для тех, кто хорошо знает рельсы.
Прокомментируйте, если что-то неясно или неоднозначно, и я попытаюсь уточнить.
Ответы
Ответ 1
Я успешно использовал макеты моего родительского приложения в своих машинах. Во-первых, на основе Раздела 4.3.2 Rails Guides (Engines), чтобы получить доступ к родительским приложениям. Параметры ApplicationController (например, session
, поскольку вы используете выше), вам необходимо заменить двигатель application_controller.rb
на тот, который у вас есть:
module Module1
class ApplicationController < ActionController::Base
layout "core"
end
end
:
class Module1::ApplicationController < ::ApplicationController
end
Это наследует родительское приложение ApplicationController и все его переменные.
Во-вторых, вам нужно будет удалить файл app/views/layouts/application.html.erb
из представлений вашего механизма, поскольку он не понадобится, поскольку вы используете родительское приложение.
Теперь, когда вы визуализируете представление Module1 из родительского приложения, будет использоваться макет родительского приложения, и все переменные session[]
будут оценены правильно.
Не забудьте добавить слова "main_app". перед каждой ссылкой в ваших макетах, в противном случае он попытается найти пути в движке вместо родительского приложения. Например, если макет в родительском приложении содержит ссылку на some_path
(это представление в родительском приложении), при показе представления в движке, который использует этот макет, попробуйте и посмотрите some_path
в Engine вместо родительского приложения. Вам нужно будет изменить ссылку на main_app.some_path
, чтобы она работала.
Надеюсь, что это поможет.
Ответ 2
Используйте layout 'layouts/application'
И если вы не хотите использовать main_app.your_path, вы также можете добавить:
module YourEngine
module ApplicationHelper
def method_missing(method, *args, &block)
if (method.to_s.end_with?('_path') || method.to_s.end_with?('_url')) && main_app.respond_to?(method)
main_app.send(method, *args)
else
super
end
end
end
end
Ответ 3
Я также сделал то же самое в своем приложении. Все, что я сделал, это:
- Удалить макет двигателя в /app/view/layouts/
-
Измените приложение application_controller на
module EngineModule
class ApplicationController < ::ApplicationController
layout 'layouts/application'
end
end
-
В ваших представлениях, если вы хотите обратиться к любому пути, например, login_path
, его можно передать через main_app.login_path