Monday, 28 October 2013

Using an Object's Slug as the Subdomain in Rails 3

Recently I had the problem of allowing each user in my system to have their own subdomain. There are various blog posts about subdomains in Rails, but none seem to really handle this requirement.

The way I did it was to leave my routes file intact, and use Rack middleware to rewrite urls to match the routes (this also has the advantage of not breaking existing urls).

I used the rack-rewrite gem ( to rewrite the urls as follows

 config.middleware.insert_before(Rack::Lock, Rack::Rewrite) do  
    rewrite /.*/, { |path, rack_env|  
      slug = rack_env['SERVER_NAME'].split(".")[0]  
     :if => {|rack_env|   
      rack_env["HTTP_ACCEPT"] =~ /(text\/html|application\/json)/ && !(rack_env['SERVER_NAME'] =~ /www\./i)  

Then I had to make sure all of the urls point to the correct domain and path. For this, I overwrote the url_for method as follows

 def with_subdomain(subdomain)  
   subdomain = (subdomain || "")  
   subdomain += "." unless subdomain.empty?  
   [subdomain, request.domain, request.port_string].join  
  def url_for(options = nil)  
   if options.is_a?(User)   
    return "http://#{with_subdomain(options.slug)}"  
   if options.kind_of?(Hash)  
    options[:only_path] = false  
    options[:port] = nil  
    if options[:_positional_args] && user = options[:_positional_args].find {|pa| pa.is_a?(User)}  
     options[:host] = with_subdomain(user.slug)  
     return super(options).gsub(/\/users\/\d+/, '')  
    options[:host] = with_subdomain("www")  

No comments:

Post a Comment