Today I came across some strange (and very inconvenient) Ruby on Rails behavior that even persistent combing of the net did not yield a satisfying answer to. Note: I translated the method and route names to be easier to read in English, and hope I did not introduce any inconsistencies.
Situation
Environment
Ruby on Rails 4.2.0 executing under Ruby 2.0 (also tested under Ruby 2.2.0)
Relevant Code
consider a controller with these actions, among others:
class AssignmentsController < ApplicationController
def update
...
end
def takeover_confirmation
...
end
end
routes.rb
Since I use a lot of manually defined routes, I did not use resources in routes.rb. The routes in question are defined as follows:
...
post 'assignments/:id' => 'assignments#update', as: 'assignment'
post 'assignments/takeover_confirmation' => 'assignments#takeover_confirmation'
...
The relevant output of rake routes
:
assignment POST /assignments/:id(.:format) assignments#update
assignments_takeover_confirmation POST /assignments/takeover_confirmation(.:format) assignments#takeover_confirmation
Problem
When I do a POST to the assignments_takeover_confirmation_path
, rails routes it to the update
method instead. Server log:
Started POST "/assignments/takeover_confirmation" for ::1 at ...
Processing by AssignmentsController#update as HTML
Mitigation
If I put the update
route definition after the takeover_confirmation
one, it works as intended (didn't check a POST to update
though).
Furthermore, after writing all this I found out I used the wrong request type for the update
method in routes.rb (POST instead of PATCH). Doing this in routes.rb does indeed solve my problem:
patch 'assignments/:id' => 'assignments#update', as: 'assignment'
However, even when defining it as POST, Rails should not direct a POST request to the existing path "/assignments/takeover_confirmation" to a completely different action, should it? I fear the next time I use two POST routes for the same controller it will do the same thing again.
It seems I have a severe misconception of Rails routing, but cannot lay my finger on it...
Edit: Solution
As katafrakt explained, the above request to /assignments/takeover_confirmation
matched the route assignments/:id
because Rails interpreted the "takeover_confirmation" part as string and used it for the :id parameter. Thus, this is perfectly expected behavior.
Working Example
For the sake of completeness, here is a working (if minimalistic) route-definition that does as it should, inspired by Chris's comment:
resources :assignments do
collection do
post 'takeover_confirmation'
end
end
In this example, only my manually created route is explicitly defined. The routes for update, show, etc. (that I defined manually at first) are now implicitly defined by resources: :assignments
.
Corresponding excerpt from rake routes
:
...
takeover_confirmation_assignments POST /assignments/takeover_confirmation(.:format) assignments#takeover_confirmation
...
assignment GET /assignments/:id(.:format) assignments#show
PATCH /assignments/:id(.:format) assignments#update
PUT /assignments/:id(.:format) assignments#update
DELETE /assignments/:id(.:format) assignments#destroy
....
Thanks for the help!