Route definitions matter

Note: this post was migrated from my old Tumblr-backed blog

Since this past friday, I’ve been dedicating the majority of my time to getting our RSpec tests to pass on our API orders controller. Being that I was tasked with adding some features to this endpoint, I wanted to make sure the existing tests were passing before writing new ones. This would give me a good baseline to begin work and ensure that my code functions as required.

Now, this particular component of our system has gone through a lot of refactoring in the last 6 months and the tests haven’t been kept up, so there were a bunch of failures that were just the result of out of date tests and were very easy to get working again. Then came this doozy.

First some background on the system:

When creating the order in the system, we have 2 endpoints that actually point to the same create action in our orders controller. This is defined in our routes.rb file as follows:

resources :orders, :only => [:index, :show, :create, :update] do
  post :another_action, :on => :collection

  # the problem action is right here:
  post :action_in_question, :on => :collection, :action => :create

  member do
    post :a
    post :b
    post :c
  end
end

With this, one is able to post to /orders or /orders/action_in_question and both will hit our create action, but behave slightly differently.

We do this with code like the following:

if request.path.include?('action_in_question')
  # special treatment
end

The problem arose when we would call post :create, post_params on the orders controller. The URI exposed to the controller always included action_in_question.

When running rake routes I could see that the POST /orders/action_in_question route was always listed before the POST /orders action as well, so I focused my energy on solving this.

The solution was to ensure that this action_in_question action would be lower down in the list so it wouldn’t be automatically generated by RSpect. This was accomplished by changing the above route to look like:

resources :orders, :only => [:index, :show, :create, :update] do
  post :another_action, :on => :collection

  # the problem action is right here:
  post :action_in_question, :on => :collection, :action => :create

  member do
    post :a
    post :b
    post :c
  end
end

# re-open this nested route and declare action_in_question here
resources :orders, :only => [:index, :show, :create, :update] do
  post :action_in_question, :on => :collection, :action => :create
end

That successfully solved our issue and the tests now generate the correct URI.