27

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.

Brad Werth
  • 17,411
  • 10
  • 63
  • 88
ynkr
  • 25,946
  • 4
  • 32
  • 30

5 Answers5

44

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
JDutil
  • 3,622
  • 25
  • 32
  • You can actually simplify even more to: helper MyEngine::ImportantHelper without the send. I've updated my post. – JDutil Apr 06 '12 at 16:46
  • Has this changed? It seems that *all* my helpers are loaded automatically without the code above! Here's my engine: https://github.com/sientia-jmu/iq_menu – Joshua Muheim Sep 20 '12 at 07:43
  • It really seems like all helpers are included automatically to every controller, at least when having a `--full` engine. – Joshua Muheim Sep 27 '12 at 13:33
  • It may have changed since I originally posted this, but at the time it was required. – JDutil Sep 28 '12 at 04:48
  • We tried the solution in our rails 3.2.8 engine and got the error: ActionView::Helpers::ControllerHelper#session delegated to controller.session, but controller is nil: #[:en]... – user938363 Nov 28 '12 at 19:36
  • It has changed. In the latest versions of Rails, helpers are all automatically loaded. http://api.rubyonrails.org/classes/Rails/Engine.html – danielpcox Jan 25 '13 at 19:02
  • here with rails 4 even engine's helpers were not working inside the engine. Using the code above did the trick :) – Marcus Mansur Aug 20 '13 at 19:54
  • 1
    I think they are not loaded if the engine is isolated, removing `isolate_namespace MyEngine` means they get loaded, but of course has other side effects. – Kris Sep 04 '13 at 09:21
  • yeah dude, it worked for me in rails 4 with a mountable engine. – jspooner Sep 17 '13 at 20:55
  • Worked on rails5. – Waclock Dec 16 '16 at 14:56
6

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:

Update: It's three articles in the meantime, and there's still some info to come. Please give me feedback.

Joshua Muheim
  • 12,617
  • 9
  • 76
  • 152
6

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
Brad Werth
  • 17,411
  • 10
  • 63
  • 88
1

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
rplaurindo
  • 1,277
  • 14
  • 23
1
module YourEngine
  module Helpers
    def a_helper
    end

    ...
  end
end

ActionController::Base.send(:helper, YourEngine::Helpers)
Tower He
  • 41
  • 2