[Devise] redundant use of method: :delete while using destroy_user_session_path

While using devise I have to use %li= link_to “Sign up”, destroy_user_session_path, method: :delete now that destroy_user_session_path should automatically default to delete method because that’s how it is defined in routes. Why is it generate links to get method for default for given path?

Before I answer your question, something looks a little off. destroy_user_session_path would normally be used to Log Off, not for Sign Up.

However, to answer your question, there are two independent actions here. The first is the use of the link_to method to generate a html anchor tag. In your case, this would look something like:

Sign Up

This gets passed to the browser as part of the html for the page.

Now, let’s assume the user clicks on this link. This is generates a completely separate action from the one above.

The browser now processes the anchor link. However, in HTML, anchor links (the <a … >) only support GET method requests and no matter what you put in there, the request generated will be a GET. Rails has written javascript to intervene, detect the “data-method” and generate a request with the appropriate method (in this case, delete). If javascript is disabled or there’s an error, it will get sent as a GET request.

Therefore, the following information gets passed to the routing table:

URL: /users/sign_out

METHOD: Delete

It looks for a route matching both of those parameters and routes accordingly. Please note the routing table does not define methods for a request, it uses the method passed in a request in combination with the URL to determine the appropriate route for the request (or reject it if no rule is defined). You might want to read (or re-read) the following:

My question is that why link_to method does not default to DELETE method when passed link is destroy_user_session_path as we know from routes what it should use. I don’t have problem passing method: :delete but it feels redundant.

Also my bad. “Sign Up” should be “Sign out”.

I think that would turn link_to from a very simple help which is just generating some markup for you into a much more complicated ones that interrogates routes in order find out what methods are acceptable.

Fred

I agree with Fred and adding overhead to view helpers is generally not good. There is also another problem with doing that. The example you chose happens to have only one method mapped to the URI pattern. This frequently isn’t the case. For example, suppose you have the following in your routes.db file:

resources :products

That’s going to generate four routes for product_path, all having the url ‘/products/:id(.:format)’. product_path/method:GET maps to one action. product_path/method:PATCH maps to a different action, and so on (the other two are PUT and DELETE).

If you do as you suggest and have the following statement:

<%= link_to “my example”, product_path(1) %>

There would be no way the program could assign a method from the routes.db file since it would have four to choose from.

mike

You are missing what the purpose of routes.rb is. It is to tell the system what to do when a request is received (how to route it in fact). It is nothing to do with generating the code in the views.

Colin

My question is that why link_to method does not default to DELETE method when passed link is destroy_user_session_path as we know from routes what it should use. I don't have problem passing method: :delete but it feels redundant.

You are missing what the purpose of routes.rb is. It is to tell the system what to do when a request is received (how to route it in fact). It is nothing to do with generating the code in the views.

True, the routes file doesn't generate the views directly, but doesn't the routes file generate or seed the url helpers? When I type in

<%= widgets_path %>

in a view, that's coming (somewhat) directly from the line

  resources :widgets

in the routes.rb, right?

Walter

I don't believe so, no, though someone may correct me. Easy to find out. Have a look at the html generated by widgets_path in the view, delete resources :widgets in routes.rb, restart the server and view the page again. Compare the html, which I believe will show no difference. If you click the link you should get a route not found error.

Colin

My question is that why link_to method does not default to DELETE method when passed link is destroy_user_session_path as we know from routes what it should use. I don't have problem passing method: :delete but it feels redundant.

You are missing what the purpose of routes.rb is. It is to tell the system what to do when a request is received (how to route it in fact). It is nothing to do with generating the code in the views.

True, the routes file doesn't generate the views directly, but doesn't the routes file generate or seed the url helpers? When I type in

<%= widgets_path %>

in a view, that's coming (somewhat) directly from the line

resources :widgets

in the routes.rb, right?

I don't believe so, no, though someone may correct me. Easy to find out. Have a look at the html generated by widgets_path in the view, delete resources :widgets in routes.rb, restart the server and view the page again. Compare the html, which I believe will show no difference. If you click the link you should get a route not found error.

Wow, tested this just now by removing the index method from routes with except: :index, and sure enough, the helper still works. This directly contradicts what I heard from Obie Fernandez a number of years ago at the Philly ETE conference. Probably used to be true, and didn't survive the many changes to Rails over the years. I tried removing the index method from my widgets_controller too, just to see if that was the source, and I can't make this helper give up. Very odd.

Walter

I think there might be a bug here. Otherwise, I’m stumped. Dynamic URI’s are created using the routes.rb file and made available for any ruby code (views, controllers, etc.). I did a little testing as recommended above. I created a base application using a REST class Products. I then created a page with a link to products_path (as a GET) which should invoke the index method. The link looks as follows:

<%= link_to “testing product link”, products_path %>

In the first test case, I had the following in my routes file:

resources :products

root ‘pages#home’

Note, the root entry is just to define a default. The home page is where I had my link. This works as you would expect.

Next, I changed this to:

resources :products, except: [:index]

root ‘pages#home’

In this case, when I run rake routes, the route for products_path, method:GET is gone. Also, if I type ‘localhost:3000/products’, I get a routing error. However, the home page still comes up and the link still translates to the following:

This is unexpected.

Next, I eliminated the resources route and defined the following route:

get ‘/getproducts’ => ‘pages#about’, as: :products

pages#about is just a static page i built for this example. I did this because the only way the link_to function could get this right would be to read the routes file. It did. The link produced was:

Finally, I deleted all routes except the root ‘pages#home’. Now, the home page gives me an error on the link_to function because of an invalid route.

Therefore, the link_to function does use dynamic paths that are generated from the routes file, although this is not a specific part of view rendering, it’s available anywhere you have ruby code, such as controllers. However, in the one case where REST routes are defined, it’s ignoring the except parameter (or, at least, that’s what it appears to be doing).

I see you are correct, those paths *are* generated from the routes. I was completely wrong.

Colin