Rails views inheritance - ruby-on-rails

I have a rails application with an engine inside it.
Is it possible for the engine to inherit base application view?
I mean, if I have the #{Rails.root}/app/views path in the engine's view_paths, the views will be searched in:
/app/views/my_engine/controller_name/action
I need instead to put the views in:
/app/views/controller_name/action
It is easy to use an application wide layout using the layout() method inside the engine, but no luck using base views for each action.
Any suggestion?

Solved adding a prefix to the Action View lookup_context
lookup_context.prefixes << 'controller_name'

Related

Helper directory in rails

Will a file in helper directory included in all controllers?. I didn't find any good explanation regarding this. I have 2 custom directories in my controller( like admin, for normal user). Do I have same directory structure at my helper?. Is Helper name same as controller name only for readability?
By default all helper files under app/helpers are included in all controllers. As such, it doesn't matter how you structure what's inside helpers folder. If you really want to enforce controller to only include matching helper then set config.action_controller.include_all_helpers in config to false.
See comment section for details: https://github.com/rails/rails/blob/b5db73076914e7103466bd76bec785cbcfe88875/actionpack/lib/action_controller/metal/helpers.rb
Helper is just a ruby module which is openly available for views and controllers. You should never keep your code in helper if you do not want it to expose to views.
If you want to use helper methods for all your controller and views. Then you can add methods to application helper and include it to application controller. However if you don't want to expose methods to views, then you can use rails concerns. create a methods inside it and include it inside different controllers.
No helper do not name the same name only for readability. you still need to include inside your same name controller to call functions if you want to use it inside controller. But you can still use inside views methods with same name.

Rails engine extending views, not overriding

I am aware that I can override an applications view from within an engine by simply creating the same file within the engine and removing it from the application (eg: 'users/show.html.erb').
However, what I want is to be able to extend the applications view, not override.
Lets say I have a yield inside 'users/show.html.erb' of the main application:
yield :foo
What I want is for the engine to specify the same file 'users/show.html.erb' and to have a content_for block
content_for :foo {}
Thereby, injecting some template data from the engines view, into the applications view.
Obviously, the above won't work as once it has found the template file in the application, it won't look for one in the engine.
Is there a way to make this work?
There is no way to extend views that way in Rails. You can however, accomplish this by using partials. In your engine, write a partial view file users/_show.html.erb and then render it in your app's view:
# app/views/users/show
# will look for a partial called "_show.html.erb" in the engine's and app's view paths.
render partial: 'show'
It's just as easy as your suggestion.
This gem tries to implement partial extension of views, but it works similarly to what I just described: https://github.com/amatsuda/motorhead#partially-extending-views-in-the-main-app

Mountable Engine & Redefining views

I've developed a mountable engine that has partial at xxx/admin/shared/_menu. This partial is responsible for rendering a menu.
My application defines a few controllers, which are part of the backend, and, of course, I want to include them in the menu.
It seems to me that the easiest approach is to create a partial in the engine and then override it in the (containing) application. However, when I try to use a url helper inside of it, it gives me an error.
For example:
<%= admin_posts_url %>
Gives
undefined local variable or method `admin_posts_url ' for...
Is there way to fix it? May be there is another good way to do it?
Rails engine provides main_app helper to be used inside an engine by which you can refer to application's helpers.
Ref : http://guides.rubyonrails.org/engines.html#routes
Try using
main_app.admin_posts_url

How to Include a Helper Method from a Rails Engine (Spree) inside of another Rails Engine (AlchemyCMS)

What I've run into is this:
AlchemyCMS is a Rails Engine for allowing Rails applications to have a Content Management System. It also has a preview page where it can load up an iframe of the example page with the layout. The layout here is the Spree layout. I've modified Alchemy to be able to load up the spree application layout and not its default.
In doing so, it is not loading up the helper methods. I am currently receiving:
undefined local variable or method `title' for #<#<Class:0x007f8dcc359498>:0x007f8de17dd6a8>
Where title is the first helper method in the application.
I've tried 5000 different techniques to try to load in Spree's helper methods into AlchemyCMS and I just can't do it.
Does anyone know how?
Ben,
You could do so by either including Spree's helpers within your application controller or within the base Alchemy controllers.
There is an extension for alchemy and spree together, which does a similar thing here:
https://github.com/magiclabs/alchemy_spree/blob/master/app/controllers/spree/base_controller_decorator.rb
You will just want to go in the opposite direction so instead of decorating a Spree controller to add Alchemy in you would decorate Alchemy controllers to include whichever of Spree's controller helpers you need to use:
https://github.com/spree/spree/blob/master/core/app/controllers/spree/base_controller.rb
In this case you need to include the common controller helpers:
https://github.com/spree/spree/blob/master/core/lib/spree/core/controller_helpers/common.rb
EDIT:
Alchemy::BaseController.class_eval do
include Spree::Core::ControllerHelpers
include Spree::Core::ControllerHelpers::Store
helper Spree::Core::Engine.helpers
end

Rails mountable engine and overriding another engine

I'm in the proces of converting my standard Rails app to an mountable engine. The app is comparable to a standard blogging app and i want every model, controller and view to be extendable hence my choice for a mountable engine.
One of the gems i use is Devise which is as far as i understand a sort of a mountable engine itself. It can be used inside a mountable engine as stated here.
I'm able to use it partially within my engine. Everything is working fine including some Devise controller i override like this one:
# config/routes.rb
Bbronline::Engine.routes.draw do
devise_for :users, class_name: "Bbronline::User", module: :devise,
controllers: { registrations: "bbronline/devise_overrides/registrations"}
...
# controllers/bbronline/devise_overrides/registrations_controller.rb
require_dependency "bbronline/application_controller"
module Bbronline
class DeviseOverrides::RegistrationsController < Devise::RegistrationsController
def new_intermediair
#user = User.new
end
...
The correct view 'views/bbronline/devise_overrides/registrations/new_intermediair.html.haml' is also correctly loading as expected.
However my issue is that the views that i override without a custom controller are not properly loaded. For example the view that should the login view is located in views/bbronline/devise/sessions/new.html.haml and is not loaded. Instead the standard Devise login view gets loaded i.e. devise-2.1.0/app/views/devise/sessions/new.html.erb
Of course i could solve this problem by overriding every controller with my own controller like i did with the registrations_controller above but this seems very ugly. Is overriding every controller the way to do this? Is there a more convenient way to override views from an mountable engine from within another mountable engine?
If you don't want to adjust the config.railties_order in every app that uses your engine, just require 'devise' on top of your lib\my_engine\engine.rb file.
The view_paths are in incorrect order. Checking the view paths of Devise::SessionsController shows:
Devise::SessionsController.view_paths
=> #<ActionView::PathSet:0x007fa1bf0e36f8 #paths= [/Users/harmdewit/Dropbox/Code/projects/brightin/bbr-online/bbr-online-gem/test/‌​dummy/app/views,
/Users/harmdewit/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/devise-2.1.‌​0/app/views,
/Users/harmdewit/Dropbox/Code/projects/brightin/bbr-online/bbr-online-gem/app/vi‌​ews]>
The last path of the mountable engine should come before the middle devise path. The solution is setting the loading priority in application.rb like this:
#test/dummy/config/application.rb (the app that uses my mountable engine)
...
config.railties_order = [Blog::Engine, :main_app, :all]
...
This is also documented in the rails api: http://api.rubyonrails.org/classes/Rails/Engine.html#label-Loading+priority
Thanks to José Valim for pointing in the right direction.
I need more information. Which controller are you defining and from which controller is it inheriting from? Which view is being rendered and which one did you expect to render? Also, .view_paths is your friend so try in your rails console the following:
Devise::SessionsController.view_paths
YourApp::SomeDeviseController.view_paths
This will give you a better idea of where each controller is searching for templates.

Resources