Accessing application helpers from RailsEngine - ruby-on-rails

I have Rails application with mounted Engine.
#{main_app}/config/routes.rb
mount CommentIt::Engine => "/talk", :as => 'comment_it'
And want to open engine views within main application layout.
#{main_app}/app/views/layouts/application.html.erb
<html>
<body>
<%= link_to "users", users_path %>
<%= yield %>
</body>
</html>
When accessing engine views(0.0.0.0:3000/talk) I got error 'undefined method `users_path' for #<#:0x007f9dbf0f7800>'
users_path works fine in main application views.
How I get route helpers from main application, when accessing Engine pages?

I'm not yet sure how (un)wise this might be, but have just found the following to work for a mountable/isolated engine I'm working on:
# in app/helpers/my_engine/application_helper.rb
module MyEngine
module ApplicationHelper
def method_missing(method, *args, &block)
main_app.send(method, *args, &block)
rescue NoMethodError
super
end
end
end
Thoughts?

As of Rails 3.2 the only way to do this is convert your engine into a 'full' engine and not a mountable engine. In mountable engines, the engine has no knowledge of the host application and its path/url helpers are not accessible by default.
This answer explains what needs to be done, which worked for me.
The alternative is to traverse the host application's files and include the proper view/application helpers into your engine. It works, but for me it was too much of a hassle. Simply converting to a full engine did the trick.

If you want to access engine helpers from the main application, you can use the name that you created for the app when you mounted it.
In config/routes.rb in your main application:
MyApplication::Application.routes.draw do
mount MyEngine::Engine => "/some_engine", :as => "some_engine"
get "/path_i_want_to_reference_from_main_app" => "some_controller#some_action"
end
Inside main application controllers/views:
some_engine.path_i_want_to_reference_from_main_app_path
So in your case you would use:
<%= link_to "users", comment_it.users_path %>
If you want to access main application helpers from an engine, try main_app.users_path
http://edgeapi.rubyonrails.org/classes/Rails/Engine.html

To access main app helpers (ApplicationHelper) from engine's views I tried include this:
app/helpers/your_engine/application_helper.rb
module YourEngine
module ApplicationHelper
include ActionView::Helpers::ApplicationHelper
end
end
It works, but once, when I restarted dev server, it throws me uninitialized constant ActionView::Helpers::ApplicationHelper, but I can't reproduce this exception.
EDIT
Removed this include and made this one:
lib/my_engine/engine.rb (it's inside engine)
module MyEngine
class Engine < ::Rails::Engine
isolate_namespace MyEngine
config.to_prepare do
ApplicationController.helper(ActionView::Helpers::ApplicationHelper)
end
end
end

In my case i had MyCustomEngineController inside my gem, which inherited ApplicationController, like so:
module MyEngine
class MyCustomEngineController < ApplicationController
#...
i changed this inheritance a little bit:
module MyEngine
class MyCustomEngineController < ::ApplicationController
#...
(notice :: before ApplicationController)
and now all my app's helpers are available for my engine's views

Related

ApplicationHelper not loaded in Rails 4 engine

Long time reader of SO here. I'm working on a Rails Engine. The big picture problem is that I get a NoMethodError on a helper method living in my Engine's ApplicationHelper. This is for work so I'm going to call the engine Blorge.
I have my helper method that is causing issues anywhere it is called. The Helper method is returning a NoMethodError. I thought maybe I needed to manually add helper Blorge::ApplicationHelper to Blorge::ApplicationController but the issue is still happening.
Am I missing something fundamental about Engines here?
Here is some actual code to give you a better idea of what I'm looking at.
index_header partial
app/views/blorge/shared/_index_header.html.erb
# require_locals is the helper method in question here
<% require_locals ['title'], local_assigns %>
<% title = title.pluralize %>
<section class="main_content-header">
<div class="main_content-header-wrapper">
<%= content_tag :h1, title %>
<div class="main_content-header-save">
<%= link_to "New #{title.singularize}", #new_path, class: "add-button" %>
</div>
</div>
</section>
Pages#home view
app/views/blorge/pages/home.html.erb
<%= render 'blorge/shared/index_header', title: "Welcome, #{current_user.full_name}" %>
...
Engine Application Helper
app/helpers/blorge/application_helper.rb
module Blorge
module ApplicationHelper
def require_locals(local_array, local_assigns)
local_array.each do |loc|
raise "#{loc} is a required local, please define it when you render this partial" unless local_assigns[loc.to_sym].present?
end
end
end
end
Engine Pages Controller
app/controller/blorge/pages_controller.rb
module Blorge
class PagesController < ApplicationController
def home
end
end
end
Engine Application Controller
app/controllers/blorge/application_controller.rb
class Blorge::ApplicationController < ActionController::Base
helper Blorge::ApplicationHelper
...
end
If I restart the server and reload the page, it will usually work just fine, and once it works, the issue doesn't come back for a couple days. After reading Helpers in Rails engine and Rails Engines: Helpers only are reloaded when restarting the server it sounds like I need to include the helper in my application controller with the to_prepare method in my engine.rb file. I am going to try this next but I most want to know if I'm missing something very basic here, If i do just have to add it to engine.rb, can someone explain why?
This might have been too much information, but I'd rather give more than not enough. Thanks in advance.
Edit
the fix seems to have been adding the helpers to application controller within engine.rb. I suspected this would be the fix, but I still have no clue why this is. Does anyone know why I should have to do this?
The Solution
config.to_prepare do
ApplicationController.helper(MyEngineHelper)
end

how to access helper when building ActionView manually and not from a Rails Controller

I have a class (not a controller) that generates external javascript that we then store in redis and am using Rails 3.2.19. One of the views calls a helper that is located in application_helper but it is now erroring out saying that it can't find the helper.
I would think that calling ActionView::Base.new would bring in the helpers from application_helper.rb but it doesn't appear to by default. However, on production on heroku, this does work as anticipated. I guess the question is what is the path for the views created via ActionView::Base.new?
I have:
class MenuGenerator
def self.js_external(id)
#menu=Menu.find(id)
str=ActionView::Base.new(
Rails.configuration.paths["app/views"]).render(
:partial => 'api/get_menu_3',
:locals => { :menu => #menu, :format => 'div' })
key='menus/' + id.to_s + '/js-external'
$redis.set(key, str)
return str
end
and have tried including my application_helper via
def self.js_external(id)
include ApplicationHelper
#menu=Menu.find(id)
or
class MenuGenerator
include ApplicationHelper
but neither works. What woudl I need to add to make this helper accessible by my view?

Include ActionController::Base outside of views directory

I can have a view file that contains only this:
root/app/views/layouts/application.html.erb
<%= link_to root_url %>
And of course it works. This is because
ActionController
is included in the view file somehow.
How does this work? Inside each view we don't write 'include ActionController' so how is it magically included?
Let's say I'm using an angular template:
root/app/assets/templates/angularview.html.erb
<%= link_to root_url %>
Everything works perfectly apart from the fact that the link_to isn't included in this view:
undefined method `link_to' for #<#<Class:0x000000020417b0>:0x0000000468f2c8>
How should I include ActionController into a file stored at root/app/assets/templates/angularview.html.erb?
What should I edit in my project to automatically make all files inside root/app/assets/templates include ActionController? Is this possible? I want them to behave like 'normal' views, and magically include everything a normal view includes.
It is not working as you have your angularview template in assets directory(as you have mentioned in your question: root/app/assets/templates/angularview.html.erb). You need to create it inside your application's app/views/layouts/ directory.
See these answers for more information:
https://stackoverflow.com/a/6951986/645886
https://stackoverflow.com/a/19849989/645886
UPDATE: However, if you must do that then you can create an initializer and put this code:
Rails.application.assets.context_class.class_eval do
include ActionView::Helpers
include MyAppHelper
include Rails.application.routes.url_helpers
end
Source: https://stackoverflow.com/a/14284279/645886

Are application helper methods available to all views?

Rails 4.1
Ruby 2.0
Windows 8.1
In my helpers/application_helper.rb, I have:
def agents_and_ids_generator
agents = Agent.all.order(:last)
if agents
agents_and_ids = [['','']]
agents.each do |l|
name = "#{l.first} #{l.last}"
agents_and_ids << [name,l.id]
end
return agents_and_ids
end
end
In my views/agents/form.html.erb, I have the following:
<%= f.select :agent_id, options_for_select(agents_and_ids_generator) %>
In my controllers/agents_controller.rb, I have the following:
include ApplicationHelper
But when I go to this view, I get the following error message:
undefined local variable or method `agents_and_ids_generator' for #<#:0x00000006fc9148>
If I move the agents_and_ids_generator method to the helpers/agents_helper.rb, it works fine.
I thought that by putting methods in the application helper and including the application in a controller, then these methods are available to the views. Am I incorrect in that assumption?
Answer:
Making sure that application helper is not included in controllers, and added the following simplification:
<%= f.collection_select :agent_id, Agent.all.order(:last), :id, :name_with_initial, prompt: true %>
#app/models/agent.rb
Class Agent < ActiveRecord::Base
def name_with_initial
"#{self.first} #{self.last}"
end
end
Helpers
The bottom line answer is the application_helper is available in all your views.
Rails actually uses helpers all over the place - in everything from the likes of form_for to other built-in Rails methods.
As Rails is basically just a series of classes & modules, the helpers are loaded when your views are rendered, allowing you to call them whenever you need. Controllers are processed much earlier in the stack, and thus you have to explicitly include the helpers you need in them
Important - you don't need to include the ApplicationHelper in your ApplicationController. It should just work
Your Issue
There may be several potentialities causing the problem; I have two ideas for you:
Is your AgentsController inheriting from ApplicationController?
Perhaps your inclusion of ApplicationHelper is causing an issue
Its strange that your AgentsHelper works, and ApplicationHelper does not. One way to explain this would be that Rails will load a helper depending on the controller which is being operated, meaning if you don't inherit from ApplicationController, the ApplicationHelper won't be called.
You'll need to test with this:
#app/controllers/application_controller.rb
Class AgentsController < ApplicationController
...
end
Next, you need to get rid of the include ApplicationHelper in your controller. This only makes the helper available for that class (the controller), and will not have any bearing on your view
Having said this, it may be causing a problem with your view loading the ApplicationHelper - meaning you should definitely test removing it from your ApplicationController
Method
Finally, your method could be simplified massively, using collection_select:
<%= f.collection_select :agent_id, Agent.all.order(:last), :id, :name_with_initial, prompt: true %>
#app/models/agent.rb
Class Agent < ActiveRecord::Base
def name_with_initial
"#{l.first} #{l.last}"
end
end
Rails 5 update. I ran into a similar issue with views not picking up a method from application_helper.rb. This post helped me. The files that are provided in the helpers directory of a new rails app are for those views only. Methods in the application_helper.rb will not be available automatically to all views. To create a helper method that is available to all views, create a new helper file in the helper directory such as clean_emails_helper.rb and add your custom method here like this:
Module CleanEmailsHelper
def clean_email(email)
*do some stuff to email*
return email
end
end
Then you can call <%= clean_email(email) %> from any view in your app.

Ruby on Rails - using helpers in html.erb

I'm probably missing some things.
Say for example I have a helper function in app/helpers/foo_controller.rb and the code is as follows:
def sample_helper(count)
#implementaton...
end
and I want to use this helper in a webpage generated by rails and the code is as follows:
<%= sample_helper(user.id) %>
and if I try to run the webpage it will throw me an error saying that the method is not defined.
Thanks in advance!
You don't quite have the naming conventions right.
Name your helper file app/helpers/foo_helper.rb and in it you should have this:
module FooHelper
def sample_helper(count)
"#{count} items" # or whatever
end
end
And now, from any view rendered by FooController you should be able to use the sample_helper method.
Also, you should know that if you use the rails generators this structure is setup for you. All you need to do is add methods to the files that get generated. That way you don't need to guess the naming conventions.
For example, this command will make a controller file, controller test files, a helper file, and and an index view file, all ready for you to customize.
rails g controller foo index
Is your helper should be in a file called app/helpers/foo_helper.rb that contains a a module of the same name as the helper (camelized) Like:
module FooHelper
def sample_helper(cont)
# implementation
end
end
That's the way Rail auto loads helpers.

Resources