Mountable Engine & Redefining views - ruby-on-rails

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

Related

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

Rails: best practice to load a helper in controller

I was hired some days ago to update a Rails 4 app. In general the rspecs and the code look good however in the top of some controller a found this line:
delegate :edit_app_path, :new_app_payment_path, to: :view_context
Searching in the net, I found that the line is a way to load methods from the helpers inside a controller through a new instance of ActionView::Base class. I mean, is a way to do it instead of the classic:
include MyHelper
in the controller. My question is: is this really a good practice? is faster? AFAIK, view_context will load a new class with all the helpers and all the context of the view instead of one helper if I use the classic "include MyHelper". By the way :edit_app_path and :new_app_payment_path methods are in the same helper.
Should I remove the line?
Using view_context allows the controller to be blissfully ignorant of where the path is defined. If the helper file structure is refactored in future, the controller will continue humming along without requiring change.
Performance wise, I doubt the impact will be significant since all the code will have been loaded. Rails (and the Ruby standard library) creates new objects all the time.

render doesn't know where to look for partials (from helper code)

When I try to render a partial from a helper, it fails with this (condensed) error message:
Missing partial /_cube_icon with [...]. Searched in:
Note that the list of searched directories is empty!
In contrast, when using render in a view, it knows where to look:
Searched in: * "/Users/Lars/GitHub/algdb/app/views"
In the helper code, I use ActionController::Base.helpers.render(). Should I use some other render function? How do I tell it where to look for partials? Could I have set up the project wrong somehow?
This is Rails 4.2.4 ยท Ruby 2.3.1
OK, I figured it out:
I was calling render from code in the helper directory, but not from a function in a standard SomethingHelper module.
When following that convention, things started working.
Partial files normally reside within the app/views directory and are not located in the helpers directory in Rails as you can see from the error message you are receiving. To solve your problem, I would move the _cube_icon file into the app/views directory and organize your code there. I recommend reading this section of the Rails documentation for views Layouts and Rendering
Additionally, it sounds like you may be new to rails so I would take a look at the conventions that rails offers. Rails, as you may or may not know, is an opinionated framework which means certain things need to go in certain locations in order for it to work. Here is another resource on just the view part of Rail's MVC framework Action View Overview. Hope this helps.
---Updated-----
If you really want to render a partial from a helper file, there are a few ways to do so but the best one to use is outlined below:
In app/helpers
module MyHelper
def custom_render
concat(render(:partial => 'cube_icon'))
end
end
Here are some links from stackoverflow to help you out.
Rendering a partial from helper #1
Rendering from a partial from herlper #2
Using concat rails

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 & Refinery CMS: Determine the Controller class name from view?

I'm working with an existing Refinery CMS app for a client that has many controllers in many different places. If you are n00b to Refinery CMS, you can nest entire rails apps INSIDE the vender folder and they act like plugins. Its complex how it works and even worse a lot of the models/controllers are embedded in the refinery gem so a controller might exist but theres not file for it.
I wanted to extend a controller by following this example:
http://refinerycms.com/edge-guides/extending-controllers-and-models-with-decorators
which I did but my code was not firing. I did actually fix this so my problem is solved but in the future it would be useful to know what controller called this view I have. The view is tucked away in the gem HOWEVER a partial that it references was already overridden so I could throw something like:
<%= raise self.class.to_yaml %>
The problem with this I get the following error:
can't dump anonymous class: #<Class:0x000000061f5850>
Which isn't very helpful.
My question is this: How can I output the class name of the controller that calls any given view/partial ?
Thanks!
You can use params[:controller]
And params[:action] for current action

Resources