Gavin Miller
Gavin Miller

Reputation: 43825

request.subdomain is not being set in Rails 3 during conditional routing

I'm trying to setup Subdomains in Rails 3 per Ryan Bates screencast on subdomains. However it's not working for me. I have the following setup:

# routes.rb
constraints(Subdomain) do
  get 'devices' => 'devices#all'
end

# lib/subdomain.rb
class Subdomain
  def self.matches?(request)
    # binding.pry
    request.subdomain.present? && request.subdomain == "admin"
  end
end

Loading the url admin.localhost:3000/devices should route me to devices#all, however I get a routing error Routing Error No route matches [GET] "/devices" Clearly my routing isn't working.

I setup a pry debug session where the comment is above, and it gets hit, so my constraint is working, but I get the following output:

[1] pry(Subdomain)> request.subdomain
=> ""
[2] pry(Subdomain)> request.subdomains
=> []
[3] pry(Subdomain)> request.host
=> "admin.localhost"

So rails isn't picking up the admin portion of the url and placing it into the subdomain variable. While I could easily just use the host value to filter to the admin routes, I'd like to keep things clean and correct.

How come rails isn't setting the subdomain value?


Edit

Both jdoe and coreyward are correct with their answers. What was throwing me off was that I was also using pow and xip.io to access the site and getting the same error. Which was odd because http://admin.app_name.192.168.1.68.xip.io/devices has a tld > 1. What was happening was that the subdomain for xip was admin.app_name.192.168.1.68, which also fails given the matches logic and doesn't route.

Upvotes: 6

Views: 5172

Answers (2)

coreyward
coreyward

Reputation: 80041

Because there isn't a subdomain as far as Rails considers it.

Rails expects you to provide a Top-Level Domain (TLD) length for TLDs with more than 1 dot. For example, if you had bbc.co.uk it would pickup "bbc" as the subdomain unless you specified the TLD length is 2, in which case the subdomain would be blank:

request.subdomain(2) #=> ""

This is also why you're seeing subdomains return an empty array: the last two segments are being discarded automatically. You could specify 0 as the TLD length to get back "admin", but that code will break in production with a real domain name, so I don't recommend it.

I recommend using a domain like admin.yourapp.dev that is routed back to your computer via localhost to make your app properly detect a subdomain. Just edit /etc/hosts to do it simply.

If you're curious, here is the Rails source for the subdomain method.

Upvotes: 12

jdoe
jdoe

Reputation: 15771

Try using special address: http://admin.lvh.me:3000/devices

Or set:

config.action_dispatch.tld_length = 0

in your development.rb and restart your app.

Upvotes: 7

Related Questions