16

I wondered if there were any plugins or methods which allow me to convert resource routes which allow me to place the controller name as a subdomain.

Examples:

map.resources :users
map.resource :account
map.resources :blog
...

example.com/users/mark
example.com/account
example.com/blog/subject
example.com/blog/subject/edit
...

#becomes

users.example.com/mark
account.example.com
blog.example.com/subject
blog.example.com/subject/edit
...

I realise I can do this with named routes but wondered if there were some way to keep my currently succinct routes.rb file.

mark
  • 10,316
  • 6
  • 37
  • 58
  • 2
    I've added a 100 rep bounty to this - I'm encountering a similar dilemma, so a viable answer would be great. – Jeriko Sep 20 '10 at 12:00
  • Which version of Rails? 2.x or 3? – tommasop Sep 24 '10 at 09:51
  • In my case, 2.3, though jeriko deserves more attention on this now. – mark Sep 24 '10 at 10:49
  • Mark what are your thoughts on the new answers? I need to award bounty in 22 hours but neither really get to the bottom of it - Rack middleware to rewrite incoming urls might be the easiest, but there isn't much detail given - your opinion? – Jeriko Sep 27 '10 at 08:21
  • Hi Jeriko. I haven't tried Igor's commented solution having been distracted by other stuff but think even if it didn't work, named routes and the plugin would be the best answer; this is purely cosmetic after all. – mark Sep 27 '10 at 09:27

5 Answers5

5

I think that subdomain-fu plugin is exacly what you need. With it you will be able to generate routes like

map.resources :universities,
    :controller => 'education_universities',
    :only => [:index, :show],
    :collection => {
        :all    => :get,
        :search => :post
    },
    :conditions => {:subdomain => 'education'}

This will generate the following:

education.<your_site>.<your_domain>/universities GET
education.<your_site>.<your_domain>/universities/:id GET
education.<your_site>.<your_domain>/universities/all GET
education.<your_site>.<your_domain>/universities/search POST
Igor Alexandrov
  • 236
  • 1
  • 5
  • Hi Igor and thanks for your reply. I have looked at subdomainfu but the problem is it effectively adds a namespace as the subdomain name, rather than using the controller name which is what I was hoping for. In your example I would wish to drop education and move universities (the controller name) to be the subdomain. – mark Sep 07 '10 at 12:19
  • Hello Mark, now I have the same issue and that is what I've done. I created namespace Education, with parameters :name_prefix => '' and :path_prefix => ''. In this namespace you can do whatever you want, I have several additional namespaces inside it. But for each route inside it, you should add :conditions => {:subdomain => 'education'}. I don't know, how to write better about it, but I think that it works well. If you want, contact me, and I will show you. – Igor Alexandrov Sep 09 '10 at 07:24
3

The best way to do it is to write a simple rack middleware library that rewrites the request headers so that your rails app gets the url you expect but from the user's point of view the url doesn't change. This way you don't have to make any changes to your rails app (or the routes file)

For example the rack lib would rewrite: users.example.com => example.com/users

This gem should do exactly that for you: http://github.com/jtrupiano/rack-rewrite

UPDATED WITH CODE EXAMPLE

Note: this is quickly written, totally untested, but should set you on the right path. Also, I haven't checked out the rack-rewrite gem, which might make this even simpler

# your rack middleware lib.  stick this in you lib dir
class RewriteSubdomainToPath

  def initialize(app)
    @app = app
  end

  def call(env)
    original_host = env['SERVER_NAME']
    subdomain = get_subdomain(original_host)
    if subdomain
      new_host = get_domain(original_host)
      env['PATH_INFO'] = [subdomain, env['PATH_INFO']].join('/')
      env['HTTP_X_FORWARDED_HOST'] = [original_host, new_host].join(', ')
      logger.info("Reroute: mapped #{original_host} => #{new_host}") if defined?(Rails.logger)
    end

    @app.call(env)

  end

  def get_subdomain
    # code to find a subdomain.  simple regex is probably find, but you might need to handle 
    # different TLD lengths for example .co.uk
    # google this, there are lots of examples

  end

  def get_domain
    # get the domain without the subdomain. same comments as above
  end
end

# then in an initializer
Rails.application.config.middleware.use RewriteSubdomainToPath
Nader
  • 5,493
  • 4
  • 33
  • 31
  • This makes the most sense to me, since the emphasis is on being restful. If you can elaborate a bit more, maybe with a code example, I'll give you the bounty. Would this method have any potential problems with cookies? – Jeriko Sep 27 '10 at 09:58
  • Updated with a code example. Cookies should be fine as all requests will be coming to the same base domain as far as your rails app is concerned. Unless you are trying to maintain different sets of cookies for each user. – Nader Sep 27 '10 at 22:04
  • Damn - it seems the bounty expired before I could award it to you. Not sure if it's completely lost or if there's anything I can do to recover it, will find out.. – Jeriko Sep 28 '10 at 10:10
3

This is possible without using plugins.

Given the directory structure app/controllers/portal/customers_controller.rb And I want to be able to call URL helpers prefixed with portal, i.e new_portal_customer_url. And the URL will only be accessible via http://portal.domain.com/customers. Then... use this:

constraints :subdomain => 'portal' do
  scope :module => 'portal', :as => 'portal', :subdomain => 'portal' do
    resources :customers
  end
end
ileitch
  • 532
  • 4
  • 12
2

As ileitch mentioned you can do this without extra gems it's really simple actually.

I have a standard fresh rails app with a fresh user scaffold and a dashboard controller for my admin so I just go:

constraints :subdomain => 'admin' do
    scope :subdomain => 'admin' do
        resources :users

        root :to => "dashboard#index" 
    end
end

So this goes from this:

  • site.com/users

to this :

  • admin.site.com/users

you can include another root :to => "{controller}#{action}" outside of that constraint and scope for site.com which could be say a pages controller. That would get you this:

constraints :subdomain => 'admin' do
    scope :subdomain => 'admin' do
        resources :users

        root :to => "dashboard#index" 
    end
end

root :to => "pages#index"

This will then resolve:

  • site.com
  • admin.site.com
  • admin.site.com/users
David Geere
  • 404
  • 5
  • 10
0

Ryan Bates covers this in his Railscast, Subdomains.

Igbanam
  • 5,904
  • 5
  • 44
  • 68
  • 1
    Sorry Yasky, the Railscast doesn't really address restful subdomains specifically, and it ends up being a bit vague. Not my -1, though. – Jeriko Sep 27 '10 at 09:55