Rails: Custom nested controller actions
Asked Answered
F

2

5

I want to setup a custom nested controller actions but I can't figure out the routing.

I keep getting the following error

No route matches [GET] "/assets"

routes.rb

resources :companies do
  resources :requests do
    match :accept
  end
end

index.html.rb

<% @requests.each do |request| %>
  <ul class="users">
    <li>
    <%= full_name(request.profile) %> 
    <%= request.status %> 
    <%= link_to "Accept",
            :controller => "requests", :action => "accept",
            :id => request.id %>
    </li>
  </ul>
<% end %>
Franctireur answered 30/6, 2012 at 9:32 Comment(3)
As you get closer to a working solution, please leave the original question intact to help other people who find this page in future. You've now edited the question so much that the answers look irrelevant.Neisse
Sorry about that, I have rolled it back to the original questionFranctireur
No problem, it's just always worth thinking about two things on SO: Getting help with your immediate problem and helping people who find this question on Google later.Neisse
N
26

There are a couple of problems: routing to the accept action and building a URL to a nested resource.

Defining custom actions

You can add custom actions to your RESTful resources using this syntax:

resources :requests do
  get 'accept', :on => :member
end

This will give you a route that looks like this:

requests/:id/accept

And you can generate paths in your views using:

accept_request_path(a_request)

The :on => :member part indicates that you're routing to a new action on each individual request, rather than the collection of all requests. If you used :on => :collection the route would be requests/accept

Nesting resources

When you nest resources:

resources :companies do
  resources :requests do
    get 'accept', :on => :member
  end
end

You get routes that look like this, note that because the requests is nested inside companies the route includes both a company_id and an id:

companies/:company_id/requests/:id/accept

And helpers like this:

accept_company_request_path(a_company, a_request)

You could do this long-hand, as you're currently trying to do, with something like:

<%= link_to "Accept",
        :controller => "requests", :action => "accept",
        :id => request.id, :company_id => request.company.id %>

But it's easier to use the helpers:

<%= link_to "Accept", accept_company_request_path(request.company, request) %>

Appropriate verbs

Accept sounds a lot like something that updates your database in some way, and if that's the case you should consider using a PUT request rather than a GET request.

The HTTP/1.1 spec says that the convention has been established that the GET and HEAD methods SHOULD NOT have the significance of taking an action other than retrieval (RFC2616, section 9) which has the real-world implication that non-human web clients — search engine indexers, browser extensions, etc. — are allowed to follow links (which make GET requests) but not allowed to submit forms that make other types of requests.

If you do switch to using a PUT request then the button_to helper will come in handy. As with link_to you can pass the controller, action, method, and all the parameters required by the route to button_to:

<%= button_to 'Accept',
      {:controller => :requests, :action => :accept,
       :company_id => request.company, :id => request},
      :method => :put %>

Or you can use the helpers to generate the path which is much easier:

<%= button_to 'Accept',
      accept_company_request_path(request.company, request),
      :method => :put %>

More documentation

Neisse answered 30/6, 2012 at 10:34 Comment(10)
Thanks, That is one of the most detailed and clear answer I have come across to dateFranctireur
Will using PUT just require changing the code for route from GET to PUT?Franctireur
You'll need to change the route, and also change the view to use a <form> instead of a link (<a>). The button_to helper I linked to in the answer will help generate the form (and a bit of CSS can make it look like a link, if that's what you want)Neisse
I have made the changes that you have suggested, but am still encountering a similar issue. what does the :member symbol represent.Franctireur
I've further clarified the question and updated it with new error and codeFranctireur
I've updated the answer to clarify what :on => :member means, you should also follow the links in the answer for a more complete explanation in the routing docs.Neisse
More updates: This time about using button_to. I think you would benefit from reading the whole of the routing guide; it explains many of the things that have come up in this question.Neisse
let us continue this discussion in chatNeisse
Thanks that did the trick, good advice I'm on to it right nowFranctireur
@Neisse thank you for your answer: what if you wanted to do all that you've suggested above AND customise the route helper method's name: i tried using the "as:" keyword, but it doesn't relinquish full control. any advice?Decrepitude
V
2

in your route file:

match 'request/accept/:id' => 'requests#accept', :as => :accept

and in view

link_to "Accept", accept_path(request) 
Vaasta answered 30/6, 2012 at 9:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.