Help me out here. I came across what I think is a bug in Rails core. It’s a really small issue, and an even smaller patch. Seriously, it’s only two lines of code, and three new tests — as you can see in the Lighthouse Ticket.
Oh, you want a description? The description I wrote in the ticket wasn’t enough? Fine, I’ll expand a bit here. First, let me state that this issue was only uncovered due to our semi-unorthodox deployment strategy. I say semi-unorthodox because I’ve never read of anyone else using a similar style, but I don’t see an alternative.
Here at work we build apps for two sets of users: the public and our business team. These sets of users see totally different applications; in fact, they aren’t even running on the same boxes. Think of our internal tool as a CMS and Customer Service tool. Instead of writing two applications and making them share models, we have one larger application but can control how it reacts by changing the RAILS_ENV. Our basic configuration contains three environments: development, production and admin (this is a simplification: we also have multiple test environments, but they are unimportant for the sake of this discussion).
To limit what can take place on each environment we have segregated our routes.rb like you see below.
# back end services
if %w(admin development test).include?(RAILS_ENV)
path_prefix = (RAILS_ENV == "development") ? "admin" : "")
map.namespace(:admin, :path_prefix => path_prefix) do |admin|
admin.connect "/applicants/:id", :controller => "applicants", :action => "show"
end
end
# front end site
if %w(production development test).include?(RAILS_ENV)
map.root :controller => "applicants", :action => "new"
end
Let’s go over this routing real quick. You’ll immediately notice two blocks, the first being for “back end services” (i.e.: internal business users), and the second for the “front end site” (i.e.: the public). If you look closely you’ll see the only difference between the conditionals for each block is that the first includes the “admin” environment while the second uses the “production” environment.
The issue I ran into stems from line #3. When path_prefix is blank, RouteBuilder generates improper URLs, therefore affecting everything inside the admin namespace.
- When path_prefix is set, the admin URLs look like: /admin/applicants/1
- When path_prefix is blank they look like: //applicants/1
That extra slash is a bit of a problem with the way we deploy our “admin” environment. We use some Apache fu to map http://admin/project to the root of the application, meaning our internal users need to go to http://admin/project//applicants/1. This URL is clearly incorrect and should never be generated.
Without the path_prefix we are severely limited in our ability to run the full project in development mode. With the path_prefix set in “development” mode we can view the public portion of the site at http://localhost:3000/applicants/1 and the private part at http://localhost:3000/admin/applicants/1. It should be noted that these two routes go to two completely different controllers, one being in the global namespace, and the other existing inside an Admin module.
Now I know if you payed attention you’re probably thinking: “Andrew, you can get around this with a simple refactor, maybe try this…”
path_prefix = (RAILS_ENV == "development") ? "admin" : nil)
Yes, you’re right, that would work: having the path_prefix set to nil is a simple solution. But, a bug is a bug, and adding an extra slash to a URL isn’t an appropriate thing to do. So please, if you made it this far and you don’t think I’m a raving loon then +1 my patch.