Helpers in Rails engine - ruby-on-rails

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.

Related

Controllers not being loaded when mounting a Rails Engine

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

Using Concern Nested in Module

We have a concern we want to use as a mixin for our User classes. This concern is found in our separate rails engine that we use for multiple products.
Everything in this engine, we keep in the same module, which we will call MyEngine.
module MyEngine
module EngineUser
extend ActiveSupport::Concern
end
end
And we are tring to include it like any other concern in our Rails Application:
class User < ActiveRecord::Base
include MyEngine::EngineUser
# ...
end
This causes an error where it says: (formatted some for readability)
/Users/foo/.rvm/gems/ruby-2.1.5/gems/activesupport-4.2.4/lib/active_support/dependencies.rb:495:in `load_missing_constant':
Unable to autoload constant EngineUser,
expected
/Users/foo/Perforce/engine_folder/app/models/concerns/engine_user.rb
to define it
Which..... is the right file... <.<
If I remove the module MyEngine around the class, and form the include in User, it works just fine.
I know rails does so autoloading behind the scenes, but why isn't this working? It knows that file has the class... if I move it to engine/app/concerns it says it cant find it there. So frustrating.
This should solve your problem. I had the same issue recently..
Take a look at the additional module, Concerns, I've added.
module MyEngine
module Concerns
module EngineUser
extend ActiveSupport::Concern
end
end
end
# lib/my_engine/engine.rb
module MyEngine
class Engine < ::Rails::Engine
config.autoload_paths += %W(#{MyEngine::Engine.root}/app/models/my_engine/concerns/engine_user.rb)
isolate_namespace MyEngine
end
end
# app/models/user.rb
class User < ActiveRecord::Base
include MyEngine::Concerns::EngineUser
# ...
end
Here is the RailsGuide post that led me to the answer.
So the answer from Justin Licata is probably the "correct" answer, but the solution that I ended up going with was as follows:
in engine_folder/lib/my_engine.rb, which is what is included as part of loading the engine, I just used the line:
Dir[File.dirname(__FILE__) + "/my_engine/concerns/**/*.rb"].each { |file| require file[0..-4]}
It certainly has some smell to it, I admit, but it lets us follow our own conventions and structure, and allows us to add folder hierarchy as needed without worrying about rails autoloading issues.
Thanks for the answers!

Rails 3.1: Better way to expose an engine's helper within the client app

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

Can't access model from inside ActionController method added by a Rails engine

I am developing a Rails engine to be packaged as a gem. In my engine's main module file, I have:
module Auditor
require 'engine' if defined?(Rails) && Rails::VERSION::MAJOR == 3
require 'application_controller'
end
module ActionController
module Auditor
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def is_audited
include ActionController::Auditor::InstanceMethods
before_filter :audit_request
end
end
module InstanceMethods
def audit_request
a = AuditorLog.new
a.save!
end
end
end
end
ActionController::Base.send(:include, ActionController::Auditor)
where AuditorLog is a model also provided by the engine. (My intent is to have "is_audited" added to the controllers in an application using this engine which will cause audit logging of the details of the request.)
The problem I have is that when this code gets called from an application where the engine is being used, the AuditorLog model isn't accessible. It looks like Ruby thinks it should be a class in ActionController:
NameError (uninitialized constant
ActionController::Auditor::InstanceMethods::AuditorLog)
rather than a model from my engine.
Can anyone point me in the right direction? This is my first time creating an engine and attempting to package it as a gem; I've searched for examples of this and haven't had much luck. My approach to adding this capability to the ActionController class was based on what mobile_fu does, so please let me know if I'm going about this all wrong.
Use ::AuditorLog to access the ActiveRecord class (unless you have it in a module or namespace, in which case you'll need to include the module name).

Rails 3.0 Engine - Execute code in ActionController

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

Resources