link making GET (rather than DELETE) request to wrong controller - ruby-on-rails

I have a link in views/questions/show.html.erb that lets users delete tags.
<%=link_to "x",
:remote => true,
:url => remove_question_tag_path(#question, tag),
:method => :delete,
:html => { :id => "delete-#{tag.name.parameterize}"} %>
<% end %>
The remove_question_tag_path route is created by nesting the tags resource inside the questions resource.
resources :questions do
resources :answers do
member { post :vote }
end
resources :tags do
member do
delete :remove
end
end
end
Rake routes shows that this route exists as I try to use it in the url
remove_question_tag DELETE /questions/:question_id/tags/:id/remove(.:format) tags#remove
However, when I click on the link, it's making a get request to the show action of the Questions controller, rather than the remove action of the Tags controller, as rake routes indicates is the destination for the route.
Started GET "/questions/25?html%5Bid%5D=delete-outdoors&method=delete&url=%2Fquestions%2F25%2Ftags%2F2%2Fremove" for 127.0.0.1 at 2013-03-26 19:01:00 -0700
Can you explain what I might be doing wrong?

Try this:
<%= link_to "x", remove_question_tag_path(#question, tag), :remote => true, :method => :delete, :html => { :id => "delete-#{tag.name.parameterize}"} %>
Explanation: you do not specify url for link so link_to makes a hash of all given arguments except "x" and treats them as url options. Therefore, :method option is just added to GET parameters instead of generating DELETE request.

Related

Custom action not firing using link_to with remote=true

Very new to ruby on rails and trying to get my first ajax call working. It is making the ajax call, but it always calls the #index action and seems to ignore the URL parameter. Here's the code:
class UserController < ApplicationController
def flag
logger.debug "in flag user"
respond_to do |format|
format.js { render :layout=>false }
end
end
end
In my routes.db:
resources :users do
member do
post 'flag'
get 'flag'
end
end
And then in my view I create the link like this:
<%= link_to "Flag User", :url => flag_user_path(user.id), :method => :get, :remote => true %>
the HTML source is:
Flag User
rake routes produces this:
flag_user POST /users/:id/flag(.:format) users#flag
GET /users/:id/flag(.:format) users#flag
Whenever I click on the link, the user#index method always gets executed. How do I get the user#flag method to execute?
The issue was the :url symbol. I changed:
<%= link_to "Flag User", :url => flag_user_path(user.id), :method => :get, :remote => true %>
to:
<%= link_to "Flag User", flag_user_path(user.id), :method => :get, :remote => true %>
and everything works was expected. I was incorrectly using it like link_to_remote which requires the :url symbol. Thanks everyone for their input.
The problem is in how you are defining your routes. It needs to be like this,
resources :users do
post 'flag'
get 'flag', on: :member
end
Check the rails guides for routing to get more idea.
If you run rake routes, I think you'll see that the route created with name "flag_user_path" expects "post" not "get"

Rails: Custom nested controller actions

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 %>
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
Adding more RESTful actions
Nested resources
in your route file:
match 'request/accept/:id' => 'requests#accept', :as => :accept
and in view
link_to "Accept", accept_path(request)

No route matches in Rails 3.0.4

Been staring at this problem for a while now. Here's the error I'm getting when I try to view the page.
No route matches {:action=>"confirm", :controller=>"locations"}
This is what I have in the view.
<%= form_for(#location, :url => { :action => :confirm }) do |f| %>
<% end %>
And I think my routes file is set up correctly.
Finder::Application.routes.draw do
resources :locations do
member do
post :confirm
end
end
root :to => 'locations/index'
end
Any ideas?
Updated:
Ran rake routes and get what I think is correct.
confirm_location POST /locations/:id/confirm(.:format) {:action=>"confirm", :controller=>"locations"}
You can debug your routes easily in the future by running $ rake routes and looking at the output. ;)
I think what is happening is that your post :confirm isn't registering the route you're expecting. In the guides, match and it's brethren accept a string as a URL segment like so:
resources :locations do
member do
post 'confirm'
end
end
Note that "confirm" is now a string instead of a symbol.
If this doesn't help, run $ rake routes and tack the output onto your question.
Update
After seeing your rake output, I think that you just need to specify the POST method on your form_for:
<%= form_for(#location, :url => { :action => :confirm }, :method => :post) do |f| %>
<% end %>
You can also make this more readable using that helper method that Rails defines:
<%= form_for(#location, :url => confirm_location_path(#location), :method => :post) do |f| %>
<% end %>
Did you define the confirm action in your LocationsController?
Try adding a :method => :post to your form_for
<%= form_for(#location, :url => { :action => :confirm }, :method => :post) do |f| %>
<% end %>
Make sure that form_for doesn't sneak in a hidden field with _method=put if you have declared the route as accepting only post in your routes file.

How do properly delete this?

Something is seriously not adding up here.. My page just refreshes, nothing happens, it never touches any of my debuggers hanging out on all my methods except for index.
my html:
<%- for image in #images %>
<%= image.attachment_file_name %>
<%-# link_to_delete image, :url => destroy_image_admin_wysiwyg_path(image.id) %>
<%= link_to 'delete', { :url => destroy_image_image_path(image.id) },
#:confirm => 'Are you sure?',
:post => true
%>
<br />
<% end %>
my controller
def destroy_image
debugger
#img = Image.find(params[:id])
#img.destroy
respond_to do |format|
format.html { redirect_to admin_image_rotator_path }
end
end
My routes:
map.resources :images, :member => { :destroy_image => :post }
My disgusting hack that works that I will replace as soon as I find something better
I moved the action over to a simpler controller I built myself.
Changed my routes to :
admin.resources :wysiwygs, :member => { :destroy_image => :post }
Changed my html :
<%= link_to 'delete', :controller => "wysiwygs", :action => "destroy_image" %>
But when I clicked on the link..it brought up.. the show action ?? fffffffffuuuuuuu
I retaliated by just moving my action to the show action, and passing a hidden field in my html..
<%= link_to 'delete', :controller => "wysiwygs", :action => "destroy_image", :hidden_field => {:value => image.id} %>
def show
# this was previously in destroy_image
#img = Image.find(params[:hidden_field][:value])
#img.destroy
respond_to do |format|
format.html { redirect_to admin_image_rotator_path }
end
end
It seems you're going down the wrong path here. If a before_filter is blocking your action, figure out why. Use skip_before_filter :filter_name if the filter is not needed.
Don't use show actions or HTTP GET for deletes. Even if it works, it will confuse things down the road. Use a DELETE verb:
map.resources :images, :member => { :destroy_image => :delete }
pass it in the link helper:
<%= link_to "delete", destroy_image_image_path(image), :method => :delete %>
And use ImagesController#destroy_image to perform the action. Better yet, consider using the standard RESTful ImagesController#destroy which map.resources gives you for free.
Not sure what was wrong in the first place, but in your second, working solution, i think you should write your link_to as follows:
link_to 'delete', :controller => "wysiwygs", :action => "destroy_image", :id => image.id
That at least would send you to the correct action.
Depending on your routes, you will have to make this a method => :post or not.
Check your rake routes output, it will show you what are the possible routes, and also what names they got, which in turn you can use as a method (add _path or _url at the end). Then it should be even easier to write something like:
link_to 'delete', wysiwygs_destroy_image_path(image)
Good luck!
You're doing a POST but your resource says that :destroy_image is only available via GET. Try changing your route to:
map.resources :images, :member => { :destroy_image => :post }
Also, take a look at your link_to. The second parameter takes a URL, not a hash that has a :url key. As mentioned elsewhere, depending on your Rails version you may need :method => :post instead of :post => true. In Rails 2.3.8, you would want this line instead:
<%= link_to 'delete', destroy_image_image_path(image), :method => :post %>

Calling a controller's action without a named route in Rails 3

In Rails 3, is there a way to link to a controller's action using ajax without having a named route?
I tried <%= link_to 'Reload', '#', url_for(:action => :reload, :id => #user.id), :remote => true, :method => 'post' %>
but it returns with the error No route matches {:controller=>"users", :id=>2, :action=>"reload"}
My main concern is that I don't want the action to be called by someone typing in a route in the address bar. Is there another way to go about this?
Thanks!
Tim
if your User resource is in your routes.rb file then you need to add a route to the 7 restful actions.
resources :user, :member => {:reload => :get}
That will give you the route to work with
<%= link_to "Reload", user_reload_path(current_user)%>
and that should work for reloading your user object
rake routes
that will show you all your routes you can work with

Resources