I am currently creating a Rails engine. So far the engine has a few new routes and a few new controllers. The routes work fine, as I was able to mount them adding the following to config/routes.rb:
mount MyEngine::Engine => '/'
The problem comes with the controllers. When trying to access the actions I have defined, I get:
uninitialized constant MyEngine::ApplicationController
I am a bit surprised, as the documentation suggests that everything inside app/ is autoloaded from the engine. I have even tried to explicitly load the controllers in engine.rb, to no avail:
module MyEngine
class Engine < ::Rails::Engine
isolate_namespace MyEngine
Dir["#{config.root}/app/controllers/**/"].each do |path|
config.eager_load_paths << path
end
end
end
I'm confused. Aren't the contents of app/controllers/ supposed to be autoloaded by the application from the engine?
It looks like your engine's controller is trying to reference an ApplicationController inside your engine, when you might have wanted to inherit from the main application's ApplicationController (correct me if I'm wrong).
I don't know how the controller code in your engine looks like (feel free the share if I am not on the right track), but I assume it looks something like this
module MyEngine
class MyController < ApplicationController
end
end
In this scenario, it requires you to also have defined MyEngine::ApplicationController somewhere, and based on the error message, I assume this controller does not exist.
To solve this, you could either define the engine specific ApplicationController, or you can specify that you want to inherit from the root controller, like this:
module MyEngine
class MyController < ::ApplicationController
end
end
If you later want to be even more flexible in your engine, it is often left to an initializer configuration option to set which controller to inherit from (see example from Devise).
module MyEngine
class MyController < MyEngine.parent_controller.constantize
end
end
Related
I have a module ApplicationHelper in app/helpers/application_helper.rb
defined like this
module ApplicationHelper
def some_method(arg)
end
end
and i have my view file is here
app/views/v1/admin/messages/show.json.jbuilder
So i am trying to access the
some_method()
in view file but it doesn't reflect!
Is this due to namespacing? or what i am not able to understand.
It would be Great if someone explains the concept.
Thanks in advance!
it says undefined method error
what could be the reason?
You didn't include your controller code, but we'll assume it ultimately inherits from ActionController::API (as it should it if it is an API controller). If so, that is the root of it rather than namespacing, etc. Per the ActionController documentation:
An API Controller is different from a normal controller in the sense
that by default it doesn't include a number of features that are
usually required by browser access only: layouts and templates
rendering, flash, assets, and so on. This makes the entire controller
stack thinner, suitable for API applications. It doesn't mean you
won't have such features if you need them: they're all available for
you to include in your application, they're just not part of the
default API controller stack.
One of the side effects of the thinner API controller is that they don't automatically include helpers like a standard Rails controller. You can easily add that back in, though.
messages_controller.rb
class Api::V1::MessagesController < ActionController::API
include ActionController::Helpers
helper ApplicationHelper
def show
# whatever
end
end
app/helpers/application_helper.rb
module MessagesHelper
def some_method(arg)
# whatever
end
end
app/views/messages/show.json.jbuilder
json.bogus do
thing1: some_method('banana')
end
If you have lots of API controllers, you can of course stick it in a base controller class they all inherit from like so:
class Api::V1::ApiController < ActionController::API
include ActionController::Helpers
helper ApplicationHelper
end
I have 2 classes
MyController which is under app/controllers/api
MyManager which is under libs/managers
I am trying to use this manager class from my controller and I'm getting the following error.
Uninitialized constant API::MyController::MyManager
How do I reference and use MyManager class from MyController class?
Controller
class API::MyController < API::BaseController
before_action :setup
def something
#myManager.doSomething
end
def setup
# Exception is thrown here
#myManager = MyManager.new
end
end
Manager
class MyManager
def doSomething
puts('something')
end
end
Make sure you autoloaded the lib/managers directory:
# in config/application.rb
config.autoload_paths += %W(#{config.root}/lib/managers)
If MyManager is a Class (not a module) then you can just call MyManager.new without any problem.
Also just a note. In Rails 4 there are a couple of 'concerns' directories added under app/controllers and app/models (app/controllers/concerns and app/models/concerns). Any files under these directories will be autoloaded. By standards, only model related concerns (be it modules or classes) will be placed under app/models/concerns (same applies for controller related concerns).
This has to do with the way rails loads modules, take a look at this
I think what you want is to make sure your autoloading the lib directory and then call ::MyManager.method since its trying to get the module from the Controller context.
::Module indicates the absolute module.
I have found a few articles addressing the issue of helpers within an engine not being accessible to the consuming (parent) application. To make sure we are all on the same page, let's say we have this:
module MyEngine
module ImportantHelper
def some_important_helper
...do something important...
end
end
end
If you look at the rails engine documentation in the "Isolated engine's helpers" (L293), it says:
# Sometimes you may want to isolate engine, but use helpers that are defined for it.
# If you want to share just a few specific helpers you can add them to application's
# helpers in ApplicationController:
#
# class ApplicationController < ActionController::Base
# helper MyEngine::SharedEngineHelper
# end
#
# If you want to include all of the engine's helpers, you can use #helpers method on an engine's
# instance:
#
# class ApplicationController < ActionController::Base
# helper MyEngine::Engine.helpers
# end
So if I ask anybody consuming my engine to add this to their application_controller.rb, then they will get access to all my important helper methods:
class ApplicationController < ActionController::Base
helper MyEngine::ImportantHelper
end
This is what I want and it works, but that's kind of a pain, especially if, as is my use case, everything the engine exposes can/should be used anywhere in the consuming app. So I dug around a bit more and found a solution that suggested I do the following:
module MyEngine
class Engine < Rails::Engine
isolate_namespace MyEngine
config.to_prepare do
ApplicationController.helper(ImportantHelper)
end
end
end
Now this is exactly what I want: to add all my ImportantHelper method to the parent app's application helper. However, it doesn't work. Can anybody help me figure out why this more-better solution does not work?
I am running ruby 1.8.7 with rails 3.1.3. Let me know if I missed any important info germane to the issue, and thanks in advance.
You can create an initializer to accomplish this like so:
module MyEngine
class Engine < Rails::Engine
initializer 'my_engine.action_controller' do |app|
ActiveSupport.on_load :action_controller do
helper MyEngine::ImportantHelper
end
end
end
end
I have written two blog posts about creating engines from scratch, and following them everything should work as expected (without additional configurations needed). Maybe you are still interested in:
Rails 3.1 Engines: Part I – The engine
Rails 3.1 Engines: Part II – The gem
Rails 3.1 Engines: Part III – The environment
Update: It's three articles in the meantime, and there's still some info to come. Please give me feedback.
If you want to keep the code in the engine, instead of every implementing application, use this:
module MyEngine
class Engine < Rails::Engine
isolate_namespace MyEngine
config.to_prepare do
MyEngine::ApplicationController.helper Rails.application.helpers
end
end
end
module YourEngine
module Helpers
def a_helper
end
...
end
end
ActionController::Base.send(:helper, YourEngine::Helpers)
Include this code in engine.rb is also be very helpful
config.before_initialize do
ActiveSupport.on_load :action_controller do
helper MyEngine::Engine.helpers
end
end
Basically your engine would look like
module MyEngine
class Engine < Rails::Engine
isolate_namespace MyEngine
# Here comes the code quoted above
end
end
I am working on a rails engine and I have a problem with the helpers.
Apparently this is a known "problem" but there's not a lot of solutions out there. The problem is that I have an AuthenticationHelper which I want to access globally - but it's not working.
I've read that you could add a few lines to your init.rb but it does not seem to have any effect.
Any idea what the best way to make an application available in an engine?
EDIT: Fixed it- Just put the code (from the link) in the engine.rb instead.
Put this code in engine.rb:
config.to_prepare do
ApplicationController.helper(MyEngineHelper)
end
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
Adding this just in case:
I had the same problem using the Administrate gem with Rails 7. I wanted to access my main app helper modules.
Simply adding helper all_helpers_from_path 'app/helpers' in Admin::ApplicationController solved this. You can find the official documentation here.
My file now looks like this:
module Admin
class ApplicationController < Administrate::ApplicationController
before_action :authenticate_admin_user!
helper all_helpers_from_path "app/helpers"
end
end
I found the answer here.
I am upgrading my Rails plugin to be an engine that works with the latest 3.0RC1 release and I'm having a bit of trouble figuring out the best (and most correct) way to extend ActionController. I've seen this post by DHH and this question here on SO, but my question is more about how to properly call code within the ActionController.
For instance, I need to call the following within my engine's controller:
class ApplicationController < ActionController::Base
helper :all
before_filter :require_one_user
after_filter :store_location
private
def require_one_user
# Code goes here
end
def store_location
# Code goes here
end
end
I know how to properly include my two private functions, but I can't find a way to get it to properly call helper, before_filter and after_filter.
I would greatly appreciate some links or a way to make this work. I have tried naming my controller something other than ApplicationController and having the real ApplicationController extend it, but that doesn't seem to work either. I'm really up for any solution that makes the life of the engine user as easy as possible. Ideally, they wouldn't have to extend my class, but they'd have all of the functionality built into their own ApplicationController.
You may also want to look into the initializers inside your engine subclass, so you don't have to include view helpers inside your controller class. And this will give you control over the load order of these modules.
Here is what I have been using:
module MyEngine
class Engine < Rails::Engine
initializer 'my_engine.helper' do |app|
ActionView::Base.send :include, MyEngineHelper
end
initializer 'my_engine.controller' do |app|
ActiveSupport.on_load(:action_controller) do
include MyEngineActionControllerExtension
end
end
end
end
Also, another option for the action controller extension is using a mixin module. This will let you use the before_filter, after_filter, etc..
module MyEngineActionControllerExtension
def self.included(base)
base.send(:include, InstanceMethods)
base.before_filter :my_method_1
base.after_filter :my_method_2
end
module InstanceMethods
#...........
end
end
One other thing... if you create the default rails directories at the top level of your gem, you don't have to worry about requiring the helpers or controllers. Your engine subclass has access to them. So I add my application controller and application helper extensions here:
/myengine/app/helpers/myengine_application_helper_extension.rb
/myengine/app/controllers/my_engine_action_controller_extension.rb
I like this setup because it looks similar to the application_controller and application_helper in your rails app. Again, this is just personal preference, but I try to keep anything that is directly rails related, such as controllers, helpers and models inside /my_engine/app and anything that is related to the plugin in general inside /my_engine/lib
Check out this tutorial by Jose Valim for more info on initializers:
https://gist.github.com/e139fa787aa882c0aa9c (engine_name is deprecated now, but most of this doc seems up-to-date)
So, I finally figured out the solution and I hope it helps someone else.
You need to create a file in your lib directory because you are actually going to extend the class. I did myplugin/lib/extensions/action_controller_base.rb.
Then, inside of your myplugin/lib/myplugin.rb file, do the following:
require 'extensions/action_controller_base.rb'
Inside of myplugin/lib/extensions/action_controller_base.rb put the following:
require 'action_controller' # Make sure ActionController::Base is defined
ActionController::Base.class_eval {
private
def my_method_1
# Code Goes Here
end
def my_method_2
# Code Goes Here
end
}
ActionController::Base.instance_eval {
helper_method :my_method_1, :my_method_2
before_filter :my_method_1
after_filter :my_method_2
}
If you need to have view helpers, create them in the myplugin/lib/helpers directory (or anything inside of lib, the name "helpers" doesn't matter) also and add the following to the bottom of myplugin/lib/extensions/action_controller_base.rb:
require 'helpers/helper_file_1'
require 'helpers/helper_file_2'
ActionView::Base.send :include, MyHelper1
ActionView::Base.send :include, MyHelper2