1

I hoped route constraints would allow me to have admin.example.com/widgets/ and example.com/admin/widgets/ be effectively the same, with the URL helper admin_widgets_path pointing to the correct one based on the current subdomain. However, the route helper seems to only point to one or the other regardless of constraints.

Am I doing something wrong? Is this a bug? Is there a better way to solve this problem?

A rails app with an example of the problem has been published here, but the relevant details are below.

My config/routes.rb

class Subdomains
  # any domain that starts with 'admin.'
  def self.admin?
    -> (request) { request.host =~ /^admin\./ }
  end

  # any other domain
  def self.primary?
    -> (request) { !admin?.(request) }
  end
end

Rails.application.routes.draw do
  constraints(Subdomains.primary?) do
    namespace :admin do
      resources :widgets
    end
  end

  constraints(Subdomains.admin?) do
    scope module: "admin", as: :admin do
      resources :widgets
    end
  end

  root to: "admin/widgets#index"
end

My app/views/admin/widgets/index.html.erb

<%= link_to "Widgets", admin_widgets_url %>

In this configuration, admin_widgets_url always returns /admin/widgets/ which isn't a valid route on admin.example.com so clicking the link results in a routing error on that subdomain. If the admin subdomain constraint block is put first in the routes, then the URL helper always returns /widgets/, breaking the link on a non-admin domain.

The routes otherwise work, but not sure how to get the URL helpers to point to the correct one.

Carl Zulauf
  • 39,378
  • 2
  • 34
  • 47

1 Answers1

1

Your expectations are wrong - Rails reads the entire routes definition as part of the setup phase. This is also when the route helpers are created. The constraint is not actually evaluated until later when the request is matched.

Rails splits this into distinct phases to allow forking.

Instead you may need to override the route helpers.

Community
  • 1
  • 1
max
  • 96,212
  • 14
  • 104
  • 165
  • I don't have a more concrete solution since what you are trying to accomplish may not be possible since route helpers are used in contexts like models that are not request aware. A better solution may be to stick to a single canonical set of routes. – max Apr 06 '17 at 01:56
  • Thank you for clarifying that route helpers won't do what I want here. The best I came up with is to give the conflicting routes different names and then have custom URL helpers that return the correct route based on the current subdomain. This works and is probably as good as it's going to get. – Carl Zulauf Apr 06 '17 at 18:36