Alessandro Desantis
Alessandro Desantis

Reputation: 14343

Accessing helpers from the parent app in an isolated Rails engine

I'm writing a configurable Rails engine. I have an authentication_helper configuration option to define which helper should be called in a before_action in all controllers needing authentication.

The problem is that I don't have access to the parent app's helpers from the engine's controllers. My understanding is that this happens because the engine is isolated.

I have considered using a block instead of a method name, but I'm not sure if that would work, or if I would be able to cleanly access the authorization logic from outside my controllers.

Active Admin, which I have used in the past, has a similar configuration option. I have noticed that their engine is not isolated, so perhaps I'm overrating the importance of engine isolation?

Is there an elegant way to have the benefits of engine isolation while also allowing this kind of customization? Or should I just forego isolation altogether?

EDIT #1

Brad Werth pointed my in the right direction, as this works with a regular controller inheriting from ApplicationController::Base:

module MyBigFancyEngine
  class Engine < Rails::Engine

    isolate_namespace MyBigFancyEngine  

    config.to_prepare do
      # Make the implementing application's helpers available to the engine.
      # This is required for the overriding of engine views and helpers to work correctly.
      MyBigFancyEngine::ApplicationController.helper Rails.application.helpers
    end

  end
end

However, my engine's ApplicationController inherits from RocketPant::Base, which does not provide a helper method. I've tried to use a simple include (which works fine for regular controllers), but that doesn't work either (the controller can't find the helper).

Any ideas?

Upvotes: 7

Views: 2779

Answers (5)

bobao77
bobao77

Reputation: 21

You probably moved on by now, but if anyone else needs access to the "parent app" application helpers. You could always just include it explicitly in the application controller in your engine. like so:

include Rails.application.helpers

Upvotes: 0

smallbutton
smallbutton

Reputation: 3437

The RailsAdmin Engine is also isolated, but they have the same configuration options as you would like to implement. They have configurable before_filters for both authentication and authorization. Have a look at this.

As far as I can tell, they just subclass the parent controller like this::ApplicationController or instead you can configure one (ref).

For your controller you could just create your own EngineController, that inherits from RocketPant::Base and maybe just create a method there that calls the configured authentication method directly via send on the parent controller.

As the RocketPant::Base Class does not inherit from ApplicationController::Base I guess you have to find some custom way around this and can't go the normal ways for Rails Engines. Maybe you could also try to file an issue to the rocket_pant repo, to add the helper method. As far as I read they soon want to inherit from ApplicationController::Base anyway in 2.0 (ref).

Upvotes: 2

Brad Werth
Brad Werth

Reputation: 17647

You can expose the implementing application's helpers available to the engine by including the following code in your engine.rb file:

engine.rb

module MyBigFancyEngine
  class Engine < Rails::Engine

    isolate_namespace MyBigFancyEngine  

    config.to_prepare do
      # Make the implementing application's helpers available to the engine.
      # This is required for the overriding of engine views and helpers to work correctly.
      MyBigFancyEngine::ApplicationController.helper Rails.application.helpers
    end

  end
end

Upvotes: 6

Sohair Ahmad
Sohair Ahmad

Reputation: 457

I found this discussion particularly insightful. There are also some interesting ideas in the Rails Engine API under Isolated engine helpers.

The Rails Engine API docs helped me figure out a good solution for url_helpers

Upvotes: 0

Axel Tetzlaff
Axel Tetzlaff

Reputation: 1364

I think you impulse on keeping the isolation is good - because a solution that relies on a method 'just being there' is a pain to debug if s.th. goes wrong in the app using your gem (i.e. typo in method name).

Further if your gem evolves, an some day it won't need the method, in an explicit way it's very convient to give a meainingful error:

You could provide a config method to be called in an initializer:

YourGem.configure do |config|
  config.add_callback { MyApp.doIt() }
end

Upvotes: 0

Related Questions