Using helpers from mounted engine in parent Rails app - ruby-on-rails

I mounted engine to the application, and I want to use one of the engine's helper all over the parent application.
In my Dummy app I put this in Dummy's application_controller.rb:
class ApplicationController < ActionController::Base
include Sourcebuster::CookieSettersHelper
before_filter :set_sourcebuster_data
helper_method :extract_sourcebuster_data
private
def set_sourcebuster_data
set_sourcebuster_cookies
end
end
And it was ok.
But when I mounted it to the real app, helper from engine just don't work.
How to load it, so it can do the stuff all over the parent app?

Got it.
I tried to set_sourcebuster_data inside namespaced engine (http://lvh.me:3000/sourcebuster).
before_filter in application's application_controller.rb don't work in this case.
Added it to engine's application_controller.rb, now it's doing the stuff all over the app)

Related

Render a view in Rails 5 API

I generated an API-only rails app with Rails 5 via rails new <application-name> --api. I've decided I want to include a view for testing some things and am having issues getting a view to load.
I created a users/index.html.erb file with some text and my controller is now simply def index; end but there is nothing appearing when I hit the /users URL. I also tried commenting out the # config.api_only = true in config/application.rb but that didn't affect anything. Any suggestions on how to proceed?
You don't need to uncomment config.api_only = true for this purpose, just inherit your controller from ActionController::Base, or do it in your ApplicationController (default for common rails generation).
Code:
For this controller only YourController < ActionController::Base
For all apllication ApplicationController < ActionController::Base
this is from the ActionController::Metal docs https://apidock.com/rails/ActionController/Metal
it says:
ActionController::Metal by default provides no utilities for rendering >views, partials, or other responses aside from explicitly calling of >response_body=, content_type=, and status=. To add the render helpers >you’re used to having in a normal controller, you can do the following:
class HelloController < ActionController::Metal
include AbstractController::Rendering
include ActionView::Layouts
append_view_path "#{Rails.root}/app/views"
def index
render "hello/index"
end
end
So I've tried it myself and adding just by adding the two modules actually work just fine when using it for ActionController::API

Rails mountable engines helper methods not found when routing from main app

I have a module as a mountable engine mounted to a main app
through
mount MyEngine::Engine, :at => '/myengine'
I have everything namespaced in the negine and the engine has it's own views in engine/app/views/myengine/
Everything works fine when I run rails server then try to access
localhost:3000/myengine
first then go to the root of the main app and come back to the engine through a link in the index view of the main app
However when I start the server, go to localhost:3000 and from there click on the link to the engine module it tries to fetch the views correctly however the methods contained in the engine's helpers raises an error upon call for them being undefined.
I am on rails 4
I used eager_load to load the mountable engine in it's initialized in the main app and everything seems to be working now.
Myengine::Engine.eager_load!
I just ran into this issue as well. I had defined an ApplicationController inside my engine and was seeing NoMethodErrors when I tried to use helper methods inside one of my engine's controllers.
The Problem
The code looked something like this:
in my_engine/app/controllers/my_engine/application_controller.rb
module MyEngine
class ApplicationController < ActionController::Base
helper ApplicationHelper
end
end
in my_engine/app/controllers/my_engine/projects_controller.rb
module MyEngine
class ProjectsController < ApplicationController
def new
# some action code here
end
end
end
in my_engine/app/helpers/my_engine/application_helper.rb
module MyEngine
module ApplicationHelper
def translations_include_tag
javascript_include_tag "translations-#{I18n.locale}.js"
end
end
end
If I navigated to a route in the host rails app, then clicked a link that navigated to my_engine/projects/new, I would get a NoMethodError saying translations_include_tag didn't exist.
The Explanation
I think this was happening because the application had two classes with the same name: ApplicationController in MyEngine and ApplicationController in the host app. When the first route outside the engine is visited, Rails autoloads the ApplicationController from the host app. What that means is Rails associates the main app's ApplicationController with the name "ApplicationController". When you navigate to my_engine/projects/new, Rails lazy loads ProjectsController which also inherits from ApplicationController. Rails/ruby thinks you're referring to the same ApplicationController it has already loaded, so in effect ProjectsController ends up inheriting from the host app's ApplicationController instead of the one in the engine. Since the translations_include_tag method isn't defined in the host app's ApplicationController, ruby raises a NoMethodError when the view tries to call it.
The Solution
I was able to fix the problem by explicitly inheriting from MyEngine's ApplicationController:
in my_engine/app/controllers/my_engine/projects_controller.rb
module MyEngine
# changed from just plain 'ol ApplicationController
class ProjectsController < ::MyEngine::ApplicationController
def new
# some action code here
end
end
end
After fixing the inheritance issue, the NoMethodError went away.
The accepted answer says to use eager loading, which will also fix the problem because it forces the engine's ApplicationController to load first. Since the engine's ApplicationController is namespaced inside MyEngine, it's full name is MyEngine::ApplicationController, which doesn't conflict with the main app's ApplicationController because the constant names are different.
You need to include the helpers explicitely in your lib/engine.rb file:
initializer 'your-gem-name.setup_helpers' do |app|
app.config.to_prepare do
ActionController::Base.send :include, HelperModuleName
end
end
end

Best practice to make model accessible throughout app

I'm in the process of creating my own simple blog application and I want to include a 'latest posts' section on the sidebar, so my posts model needs to be accessible by the entire app. I'm looking for the best way of doing so.
I'm thinking a before_filter in the application controller followed up a private method to call the scope I have:
class ApplicationController < ActionController::Base
before_filter :latest_news
private
def latest_news
#latest = News.latest.limit(5)
end
end
Is this the best way?
Instead of a before_filter, I'd recommend using a lazy-load approach that does basically the same thing.
class ApplicationController < ActionController::Base
helper_method :latest_news
def latest_news
#latest_news ||= News.latest.limit(5)
end
end
This way you can call latest_news from any controller or view (which is what the helper_method macro does for you) and then it'll load it if it's not loaded already the first time it's called and any subsequent calls will be cached. This is a pretty common pattern for getting things like the current user record, etc.

Where to put Ruby helper methods for Rails controllers?

I have some Ruby methods certain (or all) controllers need. I tried putting them in /app/helpers/application_helper.rb. I've used that for methods to be used in views. But controllers don't see those methods. Is there another place I should put them or do I need to access those helper methods differently?
Using latest stable Rails.
You should define the method inside ApplicationController.
For Rails 4 onwards, concerns are the way to go. There was a decent article which can still be viewed via the Wayback Machine.
In essence, if you look in your controllers folder you should see a concerns sub-folder. Create a module in there along these lines
module EventsHelper
def do_something
end
end
Then, in the controller just include it
class BadgeController < ApplicationController
include EventsHelper
...
end
you should define methods inside application controller, if you have few methods then you can do as follow
class ApplicationController < ActionController::Base
helper_method :first_method
helper_method :second_method
def first_method
... #your code
end
def second_method
... #your code
end
end
You can also include helper files as follow
class YourController < ApplicationController
include OneHelper
include TwoHelper
end
You can call any helper methods from a controller using the view_context, e.g.
view_context.my_helper_method
Ryan Bigg response is good.
Other possible solution is add helpers to your controller:
class YourController < ApplicationController
include OneHelper
include TwoHelper
end
Best Regards!
Including helpers in controller will end-up exposing helper methods as actions!
# With new rails (>= 5)
helpers.my_helper_method
# For console
helper.my_helper_method

Rails: Where to define a helper method that will be available to several controllers?

I would like to define a helper method, my_method, that will be available inside BuyersController methods (like index, create, e.t.c.).
I tried to define it in app/helpers/application_helper.rb but it didn't work:
undefined method `my_method' for #<BuyersController:0x26df468>
It should be in some shared place because I want to use it in other controllers also. This is why I tried app/helpers/application_helper.rb.
What is the right place to define it ?
It should be in app/controllers/application_controller.rb
The app/helpers/application_helper.rb is for shared view helpers.
You should include the application helper module in your application controller so that its methods will be available everywhere (all controllers and views) during a request.
class ApplicationController < ActionController::Base
helper ApplicationHelper
…
end
See the API docs for the helper method
Starting from Rails 3 you could also call view_context.my_method inside your controller
Expanding on the accepted answer, if you did want to share a controller method with views, helper_method seems well suited for this. Declaring a helper_method in the controller makes it accessible to the views.
class ApplicationController < ActionController::Base
helper_method :current_user, :logged_in?
def current_user
#current_user ||= User.find_by(id: session[:user])
end
def logged_in?
current_user != nil
end
end
Before rails 4, helpers with the same controller names will be only available in their controllers and views. Starting from rails 4, all helpers are available for all controllers (if you included them) and views. With that said, all helpers will be shared across your views.
Note: if you don't want this behavior for specific controllers, you can use clear_helpers, it will only include the helper with the same controller name.
For more information about using helpers see this

Resources