Can I use :method => :delete with url_for()? - ruby-on-rails

I'm using Devise for authentication and creating an item in a dropdown for Sign Out. This is how I've built it:
<span data-icon="" /> Sign Out
I'm using an icon font to stick a nice little image in to the left of the text. With Devise, it uses the DELETE verb for the destroy_user_session_path route. Most people would use a button_to or link_to but I don't think this will work for me. Using link_to is going to stick in the closing tag automatically, which won't allow me to put my span tag in. Is there a way for me to build the above link and use the DELETE verb?
For now I'm using the solution in this thread. Specifically changing config.sign_out_via = :delete to config.sign_out_via = :get in devise.rb.

Just add data-method="delete" to your link tag. This is what link_to does when you call it with :method => :delete. JQuery does the rest of the work.
<span data-icon="" /> Sign Out

Related

How to create a href tag with link_to and action 'create'

I want to create a href tag like href=contacts/create. In my contacts_controller, I have a create GET action. I know this is against rails convention. I still need to create the above link using options = {controller=> 'contacts', action=>'create'}. It works for any other arbitrary action name
You can the hardcoded path option:
<%= link_to "Create", "contacts/create" %>
or the Rails generated path option:
<%= link_to "Create", { controller: "contacts", action: "create" } %>
This is not just against Rails' convention, but against sounds HTTP usage. This often causes serious problems that you can't predict in advance. Web crawling is just one of them, where something like the Google bot accidentally creates a new contact in your database, simply by crawling the page. Or script kiddies who find you have a create link, and send 100,000 clicks to it in quick succession.
Numerous other issues happen like this, including, at one well-known time, Google Chrome pre-fetching GET urls from the page to "speed up the user experience"; this was felt far and wide by sites that had used this technique. It's not an idle warning or a style issue: this can have a disastrous impact on your site.
First off this is really bad idea since GET requests should be idempotent. You're not just flouting convention - you're setting yourself and your users up for a really bad time since for example pressing the back and forward buttons will cause resources to be created - over and over. And there is guaranteed a better way to solve whatever you are trying to do such as:
# a "discrete form"
<%= button_to "Create contact", contacts_path, method: :post %>
# or use the rails ujs
<%= link_to "Create contact", contacts_path, method: :post %>
If you ABSOLUTELY have do this:
Rails.application.routes.draw do
get "contacts/create"
end
You can now do:
<%= link_to "Create", { controller: 'contacts', action: 'create' } %>
Congratulations, you broke the internets.
Like you mentioned, this is against rails convention, but if absolutely necessary, you can do this from your controller:
options = {controller=> 'contacts', action=>'create'}
view_context.link_to url_for(options)
If you need the href to only be the path, you can do:
options = {controller=> 'contacts', action=>'create'}
view_context.link_to url_for(options.merge(only_path: true))

Ruby on Rails Convert Button To Link

I have this button in a view:
<%= button_to 'Add Review', {controller: 'reviews', action: 'new', id: booking.showing.film.id } %>
Which then sends the user to the new review path and uses the film's id to enable the new review form to know what film is being reviewed. This is done through the routes:
post 'reviews/new/:id', to: 'reviews#new'
But the button does not look very nice so I want a link_to. I have tried this:
<%= link_to 'Add Review', {controller: 'reviews', action: 'new', id: booking.showing.film.id } %>
But through my errors routing:
match '*a', to: 'errors#routing', via: [:get, :post]
It will send the user back to the index page and display the message: No such path as 'reviews/new/6'
How can I make a link that does exactly the same as the button?
Your route requires a POST request, which the button makes, but link_to makes an <a> element, which will make a GET request. You can't make an <a> submit a POST. I suggest you use the button and restyle it with CSS to look like a link.
You can use the :method option to set http verb (e.g. :post) in link_to (see http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to).
However, you should consider changing your route since the reviews#new action renders the new review form. Use the POST request when creating or updating data.

RESTful link_to :method => destroy in Rails 3, with fallback for users with javascript disabled

Is there a simple RESTful way to have a fallback url for the link_to with :method => :delete that will work for folks who have javascript disabled?
For example, suppose I have a list of comments. To add a link to delete a comment, I might put:
<%= link_to "Delete", comment_path(comment.id), :method => :delete %>
This would produce a link with the data-method="delete" that my ujs driver (jquery_ujs in my case) will turn into a DELETE request. However, when javascript is turned off, the link is followed as a GET request (as advertised in the docs), and my controller gets rightfully confused.
Is there a good and simple way to resolve this problem? There's Railscast 77, which shows one solution, but this seems to require an awful lot of extra code and throws out the quite elegant :method => :delete solution. I'd be OK with it producing a link to a confirm-delete page when javascript is disabled.
The only solution I can think of is to use button_to on the page and UJS to replace it with the corresponding link_to. This somehow doesn't seem quite right to me, but maybe it's OK? Any other ideas?
Personally i don't like the idea of mapping the :delete to a :get, it's not RESTFul...The solution i use is the same than the one you have been thinking about, ie a button_to instead of the link_to, and if needed i can use CSS to style the button so that it looks like a link.

Rails 3 edit_path method not working

sorry if this is a dumb Q, this is my first Rails3 project...
For some reason, this <%= link_to 'edit', edit_geofence_path(geofence) %>
renders as edit (my geofence's id is 2).
And <%= link_to 'delete', {:action=>'destroy', :id=>geofence}, :confirm=>"You sure?", :method=> :delete %>
renders as delete,
which might be fine, but clicking the link generates this in the logs Started GET "/geofence?id=2". So, not DELETE, just GET.
My routes.rb file is just resource :geofence.
On a related note, for some reason the default action for a geofence is "show". So /geofence/ DOES NOT call the index method, it calls the show method. I think that also must be wrong.
I'm done cursing at this app for now, I'm going to take a day to cool off and hopefully get this SIMPLE SCAFFOLD working tomorrow night... Help me, stackoverflow! You're my only hope!
<%= link_to 'delete', {:action=>'destroy', :id=>geofence}, :confirm=>"You sure?", :method=> :delete %>
should be:
<%= link_to 'delete', {:action=>'destroy', :id=>geofence}, :confirm=>"You sure?", :method=> :delete, :remote => true %>
Without :remote => true, the click isn't handled by javascript.
And in your routes.rb file, you should have that defined as:
resources :geofence
Setting it as resource implies that there is only one, and is causing a lot of your weird behavior.
As a side note, to complete ctide answer I would suggest you to use the plural form of your controllers name as a convention. It will sound more natural to put:
resources :geofences
inside your routes.rb file.
Here is a previous StackOverflow question, about using the plural form as a convention for controllers.
When you use resource :geofence in your routes file you are telling your application that there is only one geofence resource, and that it is not a collection. You will get show, update, create, new, but not index - and the id value will not be used because there is only one resource. (The show action here will have the path /geofence
If you use resources :geofences (notice the pluralization) then you've defined a collection of resources, /geofences will now give you the index action and your url helpers will work correctly with the show action rendering /geofences/3.
Hope this helps you understand why the plural form is necessary for this sort of resource :)

ruby on rails - link_to() problem

I made this link in order to destroy a comment :
  <%= link_to 'Destroy Comment', [comment.post, comment],
:confirm => 'Are you sure?', :method => :delete %>
this suppose to send to the destroy action in the comments_controller.
the problem is that it searches for the 'show' action, Instead of the 'destroy' action :
Unknown action
The action 'show' could not be found for CommentsController
Do you think you know why it does that?
Thanks,
Oded
edit: problem solved I used 'button_to'
Rails 3:
When you use JQuery, make sure you have the right rails.js file (https://github.com/rails/jquery-ujs). When you use Prototype, the correct rails.js file is already installed. Also, make sure the following is added in your layout head:
<%= csrf_meta_tag %>
And also make sure that both the JS framework and the rails.js file is being loaded.
<%= javascript_include_tag "jquery", "rails" %>
# or
<%= javascript_include_tag "prototype", "rails" %>
Just a side-note - You can also point to the Googleapis link: http://scriptsrc.net/.
When you use :method => :delete inside a link, the following HTML will be created:
Click me!
As you see, the HTML5 data- attribute is being used. The rails.js file automaitcally puts click events on links with these attributes. When data-method="delete" is set, the request will be done with the DELETE HTTP method. So clicking it will destroy the comment. Also, setting :confirm will create a data-confirm attribute which does what you would expect.
Rails 2:
When you use Prototype, the :method => :delete thing will work automatically. Just make sure you include the right Javascript files:
<%= javascript_include_tag :defaults %>
When using JQuery you should install the 'jrails' plugin (https://github.com/aaronchi/jrails). It allows you to use the same Prototype helpers for JQuery. The plugin uses an old version of JQuery, so make sure you update that one.
I don't know for sure if the :method attribute uses Prototype in Rails 2 or just regular Javascript. So it could be that you don't even need Prototype or JQuery for the :method attribute in Rails 2.
As I said in the comment: I never use button_to for DELETE links. You can just as easily get it working with link_to. And as far as I know it's the helper most people use when creating these kind of links. Hope it helps. :)
ERROR: ActionController::RoutingError (No route matches [GET] "/javascripts/jquery.js")
Solution, download: http://code.jquery.com/jquery-1.6.3.js
ERROR: AbstractController::ActionNotFound (The action 'show' could not be found for CommentsController)
Solution, download: https://github.com/rails/jquery-ujs/raw/master/src/rails.js
In rails 3.1.0 save the above js files to app/public/javascripts/
Rename or remove your existing js files.
I've just solved this problem in my own App (rails 3). I followed the steps for rails 3 and, the most important issue, installed the correct rails.js file in my public/javascripts folder. It didn't work until I've installed rails.js.
The one i chose is this:
https://raw.github.com/rails/jquery-ujs/master/src/rails.js
I just came across this same issue with Rails 3. I'm using jQuery with the updated rails.js file. What fixed it for me was something simple - use :method => :delete, not :method => :destroy.
=link_to( 'delete account', user_admin_path(current_user.id), :confirm => "Deleting your account is irreversible!! Are you sure you wish to continue?", :method => :delete )
And in the header I have:
= javascript_include_tag "https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js", "jquery.colorbox-min", "jquery.validate.min", "rails"
Works like a charm :)
Make sure you reference //= require jquery and //= require jquery_ujs (in that order) in your application.js file, in \app\assets\javascripts.

Resources