(first posted: Dec 11, 2007)
(1650 Reads)
keywords: routes subdomains
Permalink
Url For Subdomains
Using subdomains to find objects in actions is as easy as nested resources. Whereas a nested resource might expect a path like "/foo/:foo_name/bars/:id" so in the BarsController you add a before_filter
class BarsController < ApplicationController
before_filter :find_foo
def find_foo
@foo = Foo.find_by_name(params[:foo_name]) if params[:foo_name]
end
...
But instead if you want to use a url like "http://foodoo.mysite.com/bars/:id", you can rewrite find like so:
def find_foo
@foo = Foo.find_by_name( self.request.subdomains[0] )
end
Cool.
In the latter case, you don't need to specify a nested resource in routes.rb at all. That is, routes.rb doesn't need to know anything about the subdomain, you just map.resources :bar , giving you named routes like bar_url(@bar) (=> http://www.mysite.com/bars/123) and bar_path(@bar) (=> /bars/123).
Now, how do you force the named routes to set the subdomain? Taken further, lets say some links in your app will use the foo.mysite.com host, and others should use www.mysite.com. Handle this by overriding the default_url_options to set the :host option. For example, in controllers/application.rb
def default_url_options( options={} )subdomain = (@foo.nil? or options[:controller] == 'pages') ? 'www' : @foo.name
{ :host => "#{subdomain}.#{request.domain}" }end
Now bar_url(@bar) will generate "http://fooname.mysite.com/bars/123", provided you'd defined an instance variable @foo beforehand. But pages_url(@page) will generate "http://www.mysite.com/pages/456"
Note bar_path (vs bar_url) is used for relative urls, and :host is ignored. So if you know a link can be relative to the host of the current page, use _path, but if you know it is different or unsure, use the _url version to include the host name in the generated url.
Caveats
After using this technique for a day or two I've run into some problems. There appear to be cases where default_url_options is not used at all, so my subdomain was not getting set.
Usually it's fine calling foo_url from a view template or a controller; it did not work when calling foo_url from a helper method. Digging further I see there are two versions of url_for in rails:
In actionpack/lib/action_controller/url_rewriter.rb url_for does
options = self.class.default_url_options.merge(options)
In actionpack/lib/action_view/helpers/url_helper.rb does NOT call default_url_options
Thus depending where you call bar_url, you may or may not get the subdomain replaced. The risks of trying to play under the hood of a speeding locomotive...
Solution
But, I also realized I usually need my links to be relative to the current subdomain, and only some very specific cases when it's changing. So, it'd be ok to be explicit in these cases rather than the general url_for approach. Now, I just do this:
in application.rb
def www_url( path='' )
subdomain_url( 'www', path )
end
def subdomain_url( subdomain, path='' )
"http://#{subdomain}.#{request.domain}#{request.port_string}#{path}"
end
To force a link to be the www subdomain,
<%= link_to "About this site", www_url( home_path )) %>
To link to a specific subdomain,
<%= link_to "Your account", subdomain_url(@account.name, foo_path)) %>
Word.




Url For Subdomains
Posted by: Deepak Jois on April 20, 2008 12:00 PM#