Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 571
Gavin Miller Avatar asked Jun 15 '12 18:06

Gavin Miller


People also ask

How to define routes for multiple subdomains in rails?

To define routes for multiple subdomains, we just have to add multiple constraints blocks in our routes.rb file. Rails routing provides request constraints and segment constraints. Segment constraints add rules on the request path whereas request constraints add conditions on the incoming request.

Can a rails app support multiple subdomains?

In today's post, we'll learn how to build a Rails app that can support multiple subdomains. Let's assume that we have a gaming website funkygames.co and we want to support multiple subdomains such as app.funkygames.co, api.funkygames.co, and dev.funkygames.co with a single Rails application.

Why some routes can't be placed inside the subdomain constraints?

Certain routes can't be placed inside the subdomain constraints. A typical example is healthcheck or ping endpoints. If we are using a load balancer in front of our Rails app, the load balancer needs to periodically check if the app is up or not.

Does constraints based subdomain routing work with third-party APIs?

Though the constraints based subdomain routing works in most cases, it can be a pain in certain situations. When we are working with third-party APIs and building integrations, the local development TLDs such as .local or .dev are not allowed.


2 Answers

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.

like image 180
coreyward Avatar answered Oct 14 '22 00:10

coreyward


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.

like image 29
jdoe Avatar answered Oct 14 '22 01:10

jdoe