During our Rails 3 upgrade we ran into a non-obvious routing issue. Pre-upgrade we had a person route defined as follows:
match '/person/email' => 'person/emails#index'
map.resources :person do |person|
person.resources :emails
end
Unfortunately we have to include the 'match' rule for backwards compatibility for older clients. This worked great until we upgraded to Rails 3. When we ran
rake routes
we got the following output:
person_emails GET /person/emails(.:format) {:action=>"index", :controller=>"person/emails"}
POST /person/emails(.:format) {:action=>"create", :controller=>"person/emails"}
new_person_email GET /person/emails/new(.:format) {:action=>"new", :controller=>"person/emails"}
edit_person_email GET /person/emails/:id/edit(.:format) {:action=>"edit", :controller=>"person/emails"}
person_email GET /person/emails/:id(.:format) {:action=>"show", :controller=>"person/emails"}
PUT /person/emails/:id(.:format) {:action=>"update", :controller=>"person/emails"}
DELETE /person/emails/:id(.:format) {:action=>"destroy", :controller=>"person/emails"}
/person/email(.:format) {:action=>"index", :controller=>"person/emails"}
Everything looks like it's supposed to. Notice that the last line in the 'rake routes' output is the 'match' rule. From within out app we were using the
person_email_path( @email )
helper and would produce the URL
/person/emails/34dga3-fg7899-aa645c
. Everything was working perfectly until we upgraded to Rails 3. During the upgrade we avoided making drastic changes to routes.rb but we had to adapted it to Rails' new way of defining resources, but other than that our routes stayed the same. Our Rails 3 routes.rb file is below:
match '/person/email' => 'person/emails#index'
namespace :person do
resources :emails
end
Now unexpectedly, the output of the
person_email_path( @email )
helper produced the following
/person/emails.34dga3-fg7899-aa645c
. Additionally, after running
rake routes
, we observed the following output:
person_email /person/email(.:format) {:action=>"index", :controller=>"person/emails"}
person_emails GET /person/emails(.:format) {:action=>"index", :controller=>"person/emails"}
POST /person/emails(.:format) {:action=>"create", :controller=>"person/emails"}
new_person_email GET /person/emails/new(.:format) {:action=>"new", :controller=>"person/emails"}
edit_person_email GET /person/emails/:id/edit(.:format) {:action=>"edit", :controller=>"person/emails"}
GET /person/emails/:id(.:format) {:action=>"show", :controller=>"person/emails"}
PUT /person/emails/:id(.:format) {:action=>"update", :controller=>"person/emails"}
DELETE /person/emails/:id(.:format) {:action=>"destroy", :controller=>"person/emails"}
If you compare the two
rake routes
output closely you'll notice that after upgrading to Rails 3 the
match '/person/email' => 'person/emails#index'
rule hijacked the
person_email_path
helper. Previously this helper was used to generate the URL to the
#show
action, now after the upgrade it's being used to match the rule supporting backwards compatibility, which leads clients to the
#index
action.
Fortunately the fix for this was really simple, the hard put was tracking it down. All we had to do was move the rule supporting backwards compatibility below the resource definitions, unfortunately we have a lot of legacy clients and weren't able to remove rule. Below is the fixed routes.rb:
namespace :person do
resources :emails
end
match '/person/email' => 'person/emails#index'
I don't know if this is a defect in Rails, or if Rails is just trying to be cute. I was ( and still am ) under the impression that the path and URL helpers were only applicable to resources. In any case, I hope this post helps someone.
No comments:
Post a Comment