In one hand, I have a mountable engine let's say Front Front contain my assets and couple of pages It's isolated from MainApp. I don't want it to touch the main app.
In the other hand I want my MainApp using layout and partial of the Front. So I setup the layout this way :
class ApplicationController < ActionController::Base
layout 'front/application'
end
But the front/application refer to engine partial directly, because of isolation, like this
render 'header' # front/ prefix is not required
So the MainApp views try to load app/views/application/header instead of app/views/front/application/header
To fixe this I put a prepend_view_path like this :
class ApplicationController < ActionController::Base
layout 'front/application'
before_filter :prepend_front
protected
def prepend_front
prepend_view_path "app/views/front"
end
end
But this doesn't work because engine path point into the vendor. The engine add it self this to the prepend path list : ~/main_app/vendor/private_gems/front-0.0.2/app/views And my preprend_front method create this : ~/main_app/app/views/front
I tryed to prepend by force the correct path (but it looks so dirty) :
prepend_view_path "#{Rails.root}/vendor/private_gems/front-0.0.2/app/views/front"
I doesn't work, just crash the app ...
And I'm stuck here. Maybe my design is wrong?
The answer from Jack is perfect except if you want to do this inside the Rails engine (for example if your engine itself has 'themes' that require a different load path). In this case the prepend_path and append_path are not appropriate since you are going to want to insert your new load path before the engines default load path but after the applications load path.
A solution to this (only tested in Rails 3.2) is to add the following to your engines /lib/my_engine.rb file:
config.after_initialize do
my_engine_root = MyEngine::Engine.root.to_s
paths = ActionController::Base.view_paths.collect{|p| p.to_s}
paths = paths.insert(paths.index(my_engine_root + '/app/views'), my_engine_root + '/app/views/themes/my_theme')
ActionController::Base.view_paths = paths
end
The new load path my_engine_root + '/app/views/themes/my_theme'
will now be just before your engines standard load path my_engine_root + '/app/views'
By default, Rails looks in views/ for the file, but when it doesn't find it, it'll look inside the engine's views/. This means you'll have to append the views path so that Rails will look in main app's views/ and then the modified path in your engine, and finally the engine's views/. This would then be the stack:
- main app views/
- engine modified path (views/front/ inside engine)
- engine views/
To do this, go into application_controller.rb
class ApplicationController < ActionController::Base
before_filter :set_views_path
def set_views_path
append_view_path FnetIdentityRailsEngine::Engine.root.join('app', 'views', 'front')
end
end
If you prepended your views path with the modified path in the engine, you wouldn't get the ability to override engine views in the main app. This is why you'd want to append.
Finally I remove the isolate property. I moved my views one folder up, so out of the engine_app folders.
The only final ajustement was to include EngineApp::Engine.url_helper into the MainApp application controller.
The prepend stack look like this :
- Main App prepended path
- Main App views
- Engine views
So it's based on the engine, then add main app stuff on the top.
来源:https://stackoverflow.com/questions/9769337/rails-prepend-view-path-of-mountable-engine