Dominique McDonnell
Dominique McDonnell

Reputation: 2520

Difference between path helper and [action, controller] in button_to

I wanted to add another action to a controller. So I defined the action in my controller, added a button to activate it and added it to routes.rb.

orders_controller.rb

...
def shipped
...
end
...

routes.rb

resources :orders do
  put :shipped, on: :member
end
...

view/orders/edit.html.rb

...
<%= button_to 'Ship', order_shipped_path, method: :put %>
...

However when I tried to view /orders/1/edit I got this error:

No route matches {:action=>"shipped", :controller=>"orders"}

rake routes gave me the following output:

 shipped_order PUT    /orders/:id/shipped(.:format)      orders#shipped
        orders GET    /orders(.:format)                  orders#index
               POST   /orders(.:format)                  orders#create
     new_order GET    /orders/new(.:format)              orders#new
    edit_order GET    /orders/:id/edit(.:format)         orders#edit
         order GET    /orders/:id(.:format)              orders#show
               PUT    /orders/:id(.:format)              orders#update
               DELETE /orders/:id(.:format)              orders#destroy
...

After searching and trying to find info about path helpers and routes I found the following alternate syntax, which worked:

<%= button_to 'Ship', [:shipped, @order], method: :put %>

So the question is, why did the alternate syntax work? What's going on behind the scenes? As an aside, is this the correct way of adding actions to routes?

If it is relevant, I'm using Ruby on Rails 3.2

Thanks

Upvotes: 1

Views: 985

Answers (2)

zeantsoi
zeantsoi

Reputation: 26193

By nesting the PUT shipped route within your resource route, you are effectively declaring that there is a shipped action on the Order controller. Actions are performed on models, and to that extent, the Rails naming convention for link_to helpers is action_controller (e.g., new_post_path).

Running rake routes yields the following:

# rake routes
shipped_order PUT    /orders/:id/shipped(.:format)      orders#shipped

To invoke the route, you can utilize the syntax that you're using:

<%= button_to 'Ship', [:shipped, @order], method: :put %>

This bracketed syntax understands the :shipped symbol to be the action, and orders_controller to be the controller. It therefore composes the same route that would be created using the path helper:

<%= button_to 'Ship', order_shipped_path(@order), method: :put %>

Upvotes: 2

engineerDave
engineerDave

Reputation: 3935

In the first one you're giving the button_to the route_path but not the resource specific path. The correct way would be to do

order_shipped_path(@order)

The route needs to resolve the :id portion and the helper can only do that when the specific object is passed to it.

shipped_order PUT    /orders/:id/shipped(.:format)      orders#shipped

In your second case you're telling it both the route, :shipped, and the resource, @order.

Upvotes: 2

Related Questions