Skip to content

Instantly share code, notes, and snippets.

@lazybios
Forked from bsodmike/gist:1369419
Created October 11, 2021 12:36
Show Gist options
  • Save lazybios/9b3c5dc54f38dca2de73af1a4e0b4b54 to your computer and use it in GitHub Desktop.
Save lazybios/9b3c5dc54f38dca2de73af1a4e0b4b54 to your computer and use it in GitHub Desktop.
Subdomain Routing with Rails 3.1

Implement Routing for Subdomains

Rails 3.0 introduced support for routing constrained by subdomains.

A subdomain can be specified explicitly, like this:

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

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

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

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

Create a class like this:

lib/subdomain.rb

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

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):

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

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

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

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:

#get "profiles/show"

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

%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)}

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)

Is there a better way to do this? Open an issue if you have a suggestion.

Test the Application With Subdomains

If you launch the application, it will be running at http://localhost:3000/ or 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/.

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 file and set it up as the proxy in your web browser preferences.

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 (short for: local virtual host me), that resolves to the localhost IP address 127.0.0.1 and supports wildcards (accommodating dynamically created subdomains).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment