routing by subdomain (was: Re: [Rails] Unable to get the domain in my routes.rb to map customroute tables per domain)

D Andrew Reynhout reynhout at quesera.com
Fri Jan 13 22:25:38 GMT 2006


Re: problems with Rails routing by hostname, e.g.:

  http://www.verybigsite.com/ -->
    :controller => 'sites', :action => 'list'
  http://sanfrancisco.verybigsite.com/ -->
    :controller => 'sites', :action => 'show', :id => 'sanfrancisco'
  http://boston.verybigsite.com/ -->
    :controller => 'sites', :action => 'show', :id => 'boston'

I really wanted to make this work in routing, not in URL rewriting,
before_filters, etc.  Here's my understanding of the problem, and
my simple but kludgy solution (suggestions welcomed!).

(running current versions of lighttpd, fastcgi, and rails)

First of all, lighttpd doesn't seem to pass the useful bits of the
HTTP request header in environment variables.  Or perhaps FastCGI
doesn't forward them along to Rails.  This might be a configuration
problem.

Anyway, Rails builds the route map at startup, and (in development
mode ONLY) before each request.  The CGI request is fully parsed
before the route is followed, but since routes are not normally
reloaded for each request, there would be no value in passing the
request data for routing decision making...so it isn't.

So we have to do two things:
  - Store the request data somewhere persistent (or pass it to
    the routing code)
  - Reload the routes for each request

Depending on your circumstances, this might cause an unacceptable 
performance hit.  My routes are lightweight, so I decided to give
it a spin.  Very limited testing suggests that reloading routes
is not a big deal at all.

Here's what I did...

A couple of small changes to dispatcher.rb (my RubyGems install
puts it in /usr/local/lib/ruby/gems/1.8/gems/rails-1.0.0/lib):


--- dispatcher.rb-orig	Fri Jan 13 16:37:42 2006
+++ dispatcher.rb	Fri Jan 13 16:38:29 2006
@@ -34,6 +34,7 @@
     def dispatch(cgi = nil, session_options = ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, output = $stdout)
       if cgi ||= new_cgi(output)
         request, response = ActionController::CgiRequest.new(cgi, session_options), ActionController::CgiResponse.new(cgi)
+        ENV['REQUEST_HOST']=request.host ## set env variable
         prepare_application
         ActionController::Routing::Routes.recognize!(request).process(request, response).out(output)
       end
@@ -66,7 +67,7 @@
       end
 
       def prepare_application
-        ActionController::Routing::Routes.reload if Dependencies.load?
+        ActionController::Routing::Routes.reload ## force reload
         prepare_breakpoint
         Controllers.const_load!(:ApplicationController, "application") unless Controllers.const_defined?(:ApplicationController)
       end

And then in config/routes.rb:

hostname=ENV['REQUEST_HOST']
if hostname and hostname != 'bigsite.com' and hostname != 'www.bigsite.com'
  cityname = hostname.split('.').first
  map.connect '', :controller => 'sites', :action => 'show', :id => cityname
else
  map.connect '', :controller => 'sites', :action => 'list'
end

...it works.  Better ideas welcomed!

Andrew



On Sat, Dec 31, 2005 at 05:22:04PM -0800, Nathaniel S. H. Brown wrote:
> I have two domains setup on my rails app:
> 
> example.COM and example.NET
> 
> I want to be able to have a case in the routes, that if the URL in the
> location bar is example.NET to use this route:
> 
> if hostname.match(/NET/)
>   map.connect '', :controller => 'special', :action => 'case'
> else
>   map.connect '', :controller => 'default', :action => 'index'
> End
> 
> That way I can have a new root URL for example.NET, but use the same
> application.
> 


More information about the Rails mailing list