Dave
Dave

Reputation: 533

setting an app's root to a route in an engine

I'd like to set my application's root to an action of an engine's controller. I thought this in my config/routes.rb would get it done:

root action: :show, controller: :pages, module: :MyEngine

But I'm getting a uninitialized constant PagesController error. Are the routes being set before the engine's controllers are being auto loaded?

Upvotes: 2

Views: 1093

Answers (2)

Dave
Dave

Reputation: 533

I explicitly declared the controller I was using without the name spacing in config/routes.rb.

PagesController = MyEngine::PagesController

root action: :show, controller: :pages

That did it. I'm wondering if I found a bug with the module option?

Upvotes: 0

Chris Hall
Chris Hall

Reputation: 905

The engine's controllers won't be loaded until after it's mounted, I believe. I couldn't find a way to route to an engine the way you want, but I did find these ways:

1) Mount the engine at root (probably not strictly what you wanted)

mount MyEngine => '/', as: :myengine

2) Redirect root to the engine

# For redirecting to the engine's mount
root to: redirect { Rails.application.routes.url_helpers.myengine_path }

# For a specific engine path
root to: redirect { MyEngine.routes.url_helpers.some_path }

If you don't mind hard-coding your engine path, you can just:

root to: '/myengine/url'

There probably exists a better way of handling these helpers in the routes, but this was the most direct way I could find. I did stumble across this answer on a slightly related question that mentions creating a helper specifically for this kind of thing. Adapting it for use with an engine might give something like:

class UrlRedirectHelper

  # This makes the case where you just want to use the main app's routes less verbose
  def self.method_missing(method, *args, **kwargs)
    new.public_send(method, *args, **kwargs)
  end

  # This is some entirely unnecessary sugar, I just like the way it reads
  def self.for(engine)
    new(engine)
  end

  def initialize(engine = Rails.application)
    @engine = engine
  end

  # This is called by `redirect` when it's preparing to redirect the user
  def call(_params, _request)
    url_helpers.public_send(@method, *@args, **@kwargs)
  end

  def method_missing(method, *args, **kwargs)
    super unless url_helpers.respond_to? method

    @method = method
    @args = args
    @kwargs = kwargs

    self
  end

  private

  # you could also `delegate` this, but it keeps the API cleaner to make it private
  def url_helpers
    @engine.routes.url_helpers
  end
end

Which is then used like so:

get :somepath, to: redirect(UrlRedirectHelper.some_other_path)

# or, for an engine
get :somepath, to: redirect(UrlRedirectHelper.for(MyEngine).some_other_path)

Upvotes: 4

Related Questions