Pawel J. Wal
Pawel J. Wal

Reputation: 1185

remove the need for main_app prefix in path helpers in main app

I've got an isolated, mountable rails engine which mounts itself in a namespace that is already declared in the host application. Here's how it looks more or less:

routes.rb in the plugin:

# my_engine/config/routes.rb 
MyEngine::Engine.routes.draw do
  namespace :admin do
    resources :some_resource
  end
end

And the appropriate snippet from the host app's routes.rb:

# config/routes.rb
mount MyEngine::Engine, at: "/"

namespace :admin do
  resources :host_resource
end

I'm mounting the engine at / since there are some routes in the engine that should be available there, and the admin namespaces overlap is intentional.

These routes generates several path helpers, including my_engine.admin_some_resources_path and what I would expect to be admin_host_resources_path. The latter is what doesn't work.

Assume there's a controller in the main application that is declared so:

# app/controllers/admin/application_controller.rb
class Admin::ApplicationController < ApplicationController
  layout 'admin'
end

And the engine's AdminController is declared in such a way as to inherit from it:

# my_engine/app/controlles/admin/application_controller.rb
class MyEngine::Admin::ApplicationController < Admin::ApplicationController
end

So far, all that works as expected. Now, assume that in the main application's admin layout, referenced from the Admin::ApplicationController, I try to use something essentially like:

# app/views/layouts/admin.html.erb
<%= link_to "Host resources", admin_host_resources_path %>
<%= link_to "Plugin's resources", my_engine.admin_some_resources_path %>

This works fine when I load a path like '/admin/host_resources' - it renders the admin layout from the main application no problem. When opening the path '/admin/some_resources' - which goes into the plugin - it stops working complaining that admin_host_resources_path was not found. It starts working when I change it to this:

# app/views/layouts/admin.html.erb
<%= link_to "Host resources", main_app.admin_host_resources_path %>
<%= link_to "Plugin's resources", my_engine.admin_some_resources_path %>

Which is not what I want, nor is it what I expect. Also I've seen enough rails plugins to know that this shouldn't be necessary, but after reading the Engines documentation I'm still at a loss. I've also scoured Stack, but to no avail. My question is: how to include the main_app helpers so that the prefix main_app is no longer needed?

It's worth noting that the views for host_resource are in the main application itself, while the views for some_resource are in the engine. This shouldn't matter though, since the view which doesn't work is the admin layout in the main application.

Also excuse the heavily anonymised code, but this is required of me.

Upvotes: 4

Views: 2775

Answers (2)

MarkWPiper
MarkWPiper

Reputation: 933

Add this in your controller:

helper Rails.application.routes.url_helpers

Upvotes: 6

Pawel J. Wal
Pawel J. Wal

Reputation: 1185

I found an answer to my question here: http://candland.net/2012/04/17/rails-routes-used-in-an-isolated-engine/

What I needed to do was to specify a helper like this in the engine:

module MyEngine
  module ApplicationHelper
    def method_missing method, *args, &block
      if method.to_s.end_with?('_path') or method.to_s.end_with?('_url')
        if main_app.respond_to?(method)
          main_app.send(method, *args)
        else
          super
        end
      else
        super
      end
    end

    def respond_to?(method)
      if method.to_s.end_with?('_path') or method.to_s.end_with?('_url')
        if main_app.respond_to?(method)
          true
        else
          super
        end
      else
        super
      end
    end
  end
end

Then make sure it is loaded when the engine is loaded by adding this to my engine class:

initializer 'my_engine.action_controller' do |app|
  ActiveSupport.on_load :action_controller do
    helper MyEngine::ApplicationHelper
  end
end

Thanks to this the paths that can be served by the engine are served by the engine, and everything else is sent to the main_app helper.

Upvotes: 3

Related Questions