jbutkowski
4/18/2013 - 2:32 AM

gistfile1.textile

h4. Implement Routing for Subdomains

Rails 3.0 introduced support for routing constrained by subdomains.

A subdomain can be specified explicitly, like this:

<pre>
match '/' => 'home#index', :constraints => { :subdomain => 'www' } 
</pre>

Or a set of subdomains can be matched using a regular expression:

<pre>
match '/' => 'profiles#show', :constraints => { :subdomain => /.+/ } 
</pre>

Finally, for greatest flexibility, router constraints can also take objects, allowing custom code.

Create a class like this:

*lib/subdomain.rb*

<pre>
class Subdomain
  def self.matches?(request)
    case request.subdomain
    when 'www', '', nil
      false
    else
      true
    end
  end
end
</pre>

This class allows use of a route when a subdomain is present in the request object. If the subdomain is "www," the class will respond as if a subdomain is not present.

Make sure the class is autoloaded when the application starts. You can @require 'subdomain'@ at the top of the *config/routes.rb* file. Or you can modify the file *config/application.rb* (recommended):

<pre>
# config.autoload_paths += %W(#{config.root}/extras)
config.autoload_paths += %W(#{config.root}/lib)
</pre>

Use this class when you create routes in the file *config/routes.rb*:

<pre>
devise_for :users
resources :users, :only => :show
constraints(Subdomain) do
  match '/' => 'profiles#show'
end
root :to => "home#index"
</pre>

A match from a "/" URL (such as http://myname.myapp.com) will route to the @show@ action of the @Profiles@ controller only when a subdomain is present. If a subdomain is not present (or is "www"), a route with less priority will be applied (in this case, a route to the @index@ action of the @Home@ controller).

Be sure to comment out (or remove) the route that was added by the Rails generator when we created the controller:

<pre>
#get "profiles/show"
</pre>

h2. Home Page

The rails3-mongoid-devise example app provides a home page that lists all registered users. We'll modify the home page to add a link to each user's profile page, using a URL with a custom subdomain.

*app/views/home/index.html.haml*

<pre>
%h4 Home
- @users.each do |user|
  %br/ 
  User: #{link_to user.name, user}
  Profile: #{link_to root_url(:subdomain => user.name), root_url(:subdomain => user.name)}
</pre>

h4. URL Helpers With Subdomains

Applications that do not use subdomains use routing helpers to generate links that either include the site's hostname (for example, @users_url@ generates @http://mysite.com/users@) or links that only contain a relative path (for example, @users_path@ generates @/users@). To provide navigation between sites hosted on the subdomains and the main site, you must use URL helpers ("users_url") not path helpers ("users_path") because path helpers do not include a hostname. Rails 3.1 provides a way to include a subdomain as part of the hostname when generating links.

You can specify a hostname when creating a link, with the syntax:

@root_url(:subdomain => @subdomain)@

If you need a link to the main site (a URL without a subdomain), you can force the URL helper to drop the subdomain:

@root_url(:host => request.domain)@

bq. Is there a better way to do this? Open an "issue":http://github.com/RailsApps/rails3-subdomains/issues if you have a suggestion.

h2. Test the Application With Subdomains

If you launch the application, it will be running at "http://localhost:3000/":http://localhost:3000/ or "http://0.0.0.0:3000/":http://0.0.0.0:3000/. However, unless you've made some configuration changes to your computer, you won't be able to resolve an address that uses a subdomain, such as "http://foo.localhost:3000/":http://foo.localhost:3000/. 

h4. Some Options

There are several complex solutions to this problem. You could set up your own domain name server on your localhost and create an A entry to catch all subdomains. You could modify your */etc/hosts* file (but it won't accommodate dynamically created subdomains). You can create a "proxy auto-config (PAC)":http://en.wikipedia.org/wiki/Proxy_auto-config file and set it up as the proxy in your web browser preferences. 

h4. Use lvh.me

There's a far simpler solution that does not require reconfiguring your computer or web browser preferences. The developer Levi Cook registered a domain, "lvh.me":http://lvh.me:3000/ (short for: local virtual host me), that resolves to the localhost IP address 127.0.0.1 and supports wildcards (accommodating dynamically created subdomains).