How do you perform delete and put operations restfully in rails? I have read the documentation and thought I was doing everything properly, but I can't seem to get it to work.
For example, if I wanted to delete an employee I would create a controller called "EmployeesController" and create a destroy method to perform the delete.
Then I went into the routes.rb file and entered map.resources :employees, which gives you access to the URL helper functions.
In whatever I want to call the Ajax operation from, I should just have a line like:
<%= link_to_remote "Delete", employee_path(#employee), :method => :delete %>
When I click on the link, it is still is sending a POST operation, so it does nothing.
What am I missing or doing wrong?
Try
:url => employee_url(#employee)
IIRC, *_path is a named route generated by the :resource directive which includes the method, thus overwriting your :method => :delete
From my code:
<%= link_to_remote "Delete", :url => post_url(post), :method => :delete %>
Just to add a few extra details: Using :url => employee_url(#employee) helped (from the accepted answer). The other part that was messing me up was the fact that I was expecting an HTTP delete request, but I kept getting POST requests with a parameter "_method" (automatically added by rails) which was set to delete.
So it was calling the proper destroy action, which I proved by adding a couple of debug statements to the controller. Yes, my delete code was wrong in the controller, so it wasn't really deleting when I thought it was.
If your problem is not having AJAX request you have to add proper javascript tags
Related
I need make system call from method of Ruby of Rails , but I want it to stay on the same page.
Right now for some reason it does not execute , but shows :
Routing Error
No route matches [POST] "/devices/22918"
Try running rake routes for more information on available routes.
This is the button:
<%= link_to image_tag("/images/glossy_green_button.png"), device , :method => :turnon, :confirm => "Are you sure?" %>
This is method:
def turnon
#device = Device.find(params[:id])
result = `/perl/toggle.pl #device.infodot on`
end
please let me know what I am doing wrong,
thank you
D
You're simply not using the method correctly. You're using it to target the action of the controller you want to execute (note that I explicitly said action and not method for clarity).
Available actions in a controller are defined by your routes.rb file.You should eventually read or re read that
In your case, let's say you have a resource device (I guess this is what you have), you'll first create a new action in your routes.rb file
resources :devices do
put :turnon, on: :member
end
You can read doc about this syntax here but basically I'm making the action turnon available via the HTTP PUT method on each devices, meaning that it will be accessible through the URL /devices/1/turnon or via the url_helper : turnon_device_path (or turnon_device_url)
I assume that your turnon action will modify existing things, not creating new things, that's why I'm using the PUT verb
Then the link will look something like :
<%= link_to image_tag("/images/glossy_green_button.png"), turnon_device_path(device) , :method => :put, :confirm => "Are you sure?" %>
see that the method is the HTTP method corresponding to the new route I created.
I also assume that you put the turnon method in the DevicesController.
Finally as you want to do that in ajax, you can have a look a the remote: true option
I have a form to update an attribute of a model - I don't want it to go to the standard update action I want it to go to a different action.
<% for car in #cars %>
<%= form_for car, :url => { :action => "custom_action/#{car.id}" } do |f| -%>
This is giving the following error -
No route matches {:action=>"custom_action/1", :controller=>"cars"}
However if I visit the url - custom_action/1 - I don't get the routing error.
Any idea why I can't do this?
In addition to what Frederick Cheung said about GET vs POST vs PUT, I think your code might be wrong in general.
To do exactly what you want, try:
form_for car, :url => {:action => "custom_action", :id => car, :controller => "cars"} do |f|
I don't think this is a good idea, and will probably cause you pain. I suggest taking a look at the Ruby on Rails Routing Guide, to understand how to do this properly. (Routing is a topic where I always have to consult the manual.)
With correct routes your code should look something like this:
form_for car, custom_action_car_path(car) do |f|
Which will be easier to change in the future if/when you refactor your app.
If you go to the url by typing it into the url bar, you're making a GET request, whereas forms emit POST requests by default, and in this case would probably emit a PUT request (since you're acting on an existing record).
It sounds like your route is only routing GET requests. Given that you say your custom action does update the record, it sounds like you should update your routes to accept PUT requests instead.
I know this is a trivial question. But I have search all over google but cannot find a simple answer to this question.
Basically I have a line that says <%= link_to 'Run it', :method => 'doIt' %> in the view, then in the corresponding controller, I have the doIt method as follows:
def doIt
puts "Just do it"
end
I just want to check that if i click on Run it, it will output the string "Just do it". I ran this on localhost and there is no errors, but I can't find the output "Just do it" anywhere. It is not displayed in the rails console or rails server log. I just want to know where does puts output the string to , where to find it ?
Round 2: So this is what I tried ....
Added this line in the index.html.erb (which is the root)
<%= link_to 'Run it', :method => 'do_it' %>
and in the url, it is just basically http://localhost:3000/ (since i route controller#index as root)
The display is just an underlined 'Run it' that links to 'do_it' method in the controller.
In the controller, i include this method
def do_it
logger.debug "Just do it"
end
when i click on 'Run it', the url change to http://localhost:3000/gollum_starters?method=do_it and in the development.log, the following is written into it:
Started GET "/gollum_starters?method=do_it" for 127.0.0.1 at 2011-08-25 15:27:49 -0700
Processing by GollumStartersController#index as HTML
Parameters: {"method"=>"do_it"}
[1m[35mGollumStarter Load (0.3ms)[0m SELECT "gollum_starters".* FROM "gollum_starters"
Rendered gollum_starters/index.html.erb within layouts/application (3.6ms)
Completed 200 OK in 16ms (Views: 7.7ms | ActiveRecord: 0.3ms)
Additionally, i tried all the logger.error/info/fatal/etc ... and Rails.logger.error/info/fatal/etc, all did not print out the line "Just do it" in the development log
#Paul: I did not touch the environment folder or file, i assume by default when a new rails app is created, it is in development ?
#Maz: Yes you are right, I am just trying to test if the do_it method is getting called. To do that, I just want to print something out in the controller. Can't think of any way simpler that just print a string out, but this problem is making me miserable. I am just using textmate, no IDE.
Round 3:
#Paul thx alot, but i encountered error
My routes files is now:
resources :gollum_starters
root :to => "gollum_starters#index"
match 'gollum_starters/do_it' => 'gollum_starters#do_it', :as => 'do_it'
My index.html.erb is now:
<%= link_to "Do it", do_it_path %>
My gollum_starters_controller.rb
def do_it
logger.debug 'Just do it'
end
I am getting this error:
Couldn't find GollumStarter with ID=do_it
the error is in here, 2nd line:
def show
#gollum_starter = GollumStarter.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #gollum_starter }
end
end
I wonder why does it route to show ? When i click do_it, it actually goes to localhost:3000/gollum_starters/do_it which is correct, but apparently the error points to the show method ?
Round 4:
#Paul, i shifted resources :gollum_starters down:
root :to => "gollum_starters#index"
match 'gollum_starters/do_it' => 'gollum_starters#do_it', :as => 'do_it'
resources :gollum_starters
but got this error (omg i wanna kill myself),
Template is missing
Missing template gollum_starters/do_it with {:handlers=>[:erb, :rjs,
:builder, :rhtml, :rxml], :formats=>[:html], :locale=>[:en, :en]} in
view paths "~/project_name/app/views"
:/
---------- Answer to Round 4 ------------
Basically as the error explains, there is no template(i.e a webpage) to show hence error thrown. The solution is to add a redirect_to , in this case I redirect to root_url.
def do_it
logger.debug 'Just do it'
redirect_to(root_url)
end
Everything works now, "Just do it" finally outputs to development.log and the rails server console.
Thank you Maz and Paul and Andrew for helping me out. Learn a lot.
That link_to does not do what you think it does the value for :method is referring to the HTTP verbs.
Taken from the docs for ActionView::Helpers::UrlHelper
:method - Symbol of HTTP verb. Supported verbs are :post, :get, :delete and :put. By default it will be :post.
You would need to define a route in your routes.rb file that uses your method
# The order of routes is important as the first matched will be used
# therefore the match needs to be above 'resources :controller'
match 'controller/do_it' => 'controller#do_it', :as => 'do_it'
resources :gollum_starters # <--- This needs to be below the match or this will catch first
The controller/do_it is the route to be matched
The controller#do_it is the controller followed by the action to be used (separated by #)
The value for :as creates the path do_it_path that can be used in your link_to
Your link_to may look something like
<%= link_to "Do it", do_it_path %>
And to complete the lifecycle of a request you will need to add a view to be rendered
app/views/gollum_startes/do_it.html.erb # <-- Add file
Summary
Doing all of this creates a bit of a mess just to print something out to the logs, but it should help you understand the whole lifecycle a bit better now. Plus this answers serves as a document to help you rewind this mess.
You are not understanding what "method" means in the context of a link.
The "method" here refers to the request method, which means the kind of request you are asking the browser to make. From the perspective of a RESTful application like Rails there are four relevant request types: GET, POST, PUT, and DELETE. These request types affect how the controller responds to a request.
GET => INDEX or SHOW
POST => CREATE
PUT => UPDATE
DELETE => DESTROY
There are two other "standard" rails actions, NEW and EDIT. These are GET requests to present an interface to the user. NEW gives you a form to POST (CREATE) a new object, and EDIT gives you a form to PUT (UPDATE) and existing one.
See the rails guide for more on how HTTP Verbs relate to CRUD operations.
The important, basic thing to understand is that links, by default, are GET requests, and forms, by default, are POST requests.
So, when your link looks like this:
<%= link_to 'Run it', :method => 'do_it' %>
...it is bogus. There is no such HTTP method as "do_it", so you're not triggering anything. Because there is no such method, Rails actually passes this on as a parameter of the URL. Hence if you click that you should see your url bar now says ?method=do_it at the end.
There are several problems with what you're trying to do. First of all, the link_to helper expects at least two arguments: 1, the text for the link, and 2 the HREF for the link. So, you really need to use:
link_to 'Run it', url
Second, you need to know what URL to pass to get your controller action.
Please be familiar with the following rails convention: When referring to a controller action you can abbreviate it using the form: controller_name#controller_action. e.g. pages#show or articles#index.
Assuming your controller is called ExamplesController, you can manually trigger the seven standard controller actions as follows:
link_to 'examples#index', '/examples'
link_to 'examples#show', '/examples/123' # 123 is the id of a specific example
link_to 'examples#new', '/examples/new'
link_to 'examples#create', '/examples', :method => :post
link_to 'examples#edit', '/examples/123/edit'
link_to 'examples#update', '/examples/123', :method => :put
link_to 'examples#destroy', '/examples/123', :method => :delete
Note that in the above, INDEX, SHOW, NEW, and EDIT are all GET requests. You could specify :method => :get but that is unnecessary
To abstract this away and take care of assigning the ID when required Rails provides path helpers.
So, to repeat the above using the path helpers you could use:
link_to 'examples#index', examples_path
link_to 'examples#show', example_path( #example )
link_to 'examples#new', new_example_path
link_to 'examples#create', examples_path, :method => :post
link_to 'examples#edit', edit_example_path( #example )
link_to 'examples#update', example_path( #example ), :method => :put
link_to 'examples#destroy', example_path( #example ), :method => :delete
Now, you get these path helpers from the router, and they are defined in your routes.rb file. Within that file if you define:
resources :examples
...then you will get all the path_helpers above.
If you are using a normal RESTful controller and you want to add a custom action, then you need to make one decision: does the action operate on the entire set of objects handled by that controller (like index) or just a single specific one (like show). The reason this is important is this tells the router whether the new action you're defining needs to receive a record ID as part of the request.
If you want to act on the entire collection of objects, you define:
resources :examples do
collection do
get 'do_it'
end
end
If you want to act on just a single member of the collection you define:
resources :examples do
member do
get 'do_it'
end
end
Where I wrote 'get' in the examples above you can use any of the four verbs -- GET is normally what you do if you want to show a page, and POST is normally what I'd use if you're submitting a form. You can also write this shorthand like so:
resources :examples do
get 'do_it', :on => :collection
post 'something', :on => :member
end
For more on defining custom controller actions, see the rails guide.
Now that you've defined a route, you should run rake routes in terminal to see the name of the new path helper. Let's assume you added do_it as a collection method, your path helper would be: do_it_examples_path.
Now then, back to your link, if you put:
<%= link_to 'Do it.', do_it_examples_path %>
... then you would trigger the do_it action. When the action is triggered your puts should normally render to the server log (assuming you're running rails s in a terminal window you should see it right after started GET on examples#do_it...).
Now, in the browser you would get a missing template error as a GET request is going to expect to render a view, but that's a subject for another question. Basically, now you should understand what the controller actions are, how you get to them. If you want to learn more about what to do with your controller action, see the guide :)
I hope you understand what's going on now. Feel free to ask questions.
You want to use the Rails logging mechanism:
http://guides.rubyonrails.org/debugging_rails_applications.html#sending-messages
This means that even if you don't launch the server using rails s the output will still go to the right place.
I am using rails3 and I have a user model. This model has a status column. I am showing admin following table
Mary approve reject
John approve reject
Both approve and reject are links. Since clicking on approve will approve user's record it should not be a get request. It should be a post request. I was thinking to achieve a post request I should make clicking on approve or reject an ajax call.
In the ajax call I would make post call instead of get.
Is this a good strategy? Anyone has any better suggestion.
Thanks
Just pass :method => 'post' to your link_to call:
<%= link_to 'approve', approve_user_path(user), :method => 'post' %>
http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to
You're right, it shouldn't be a get request. Actually, I think it's neither a post request, because you already have the record and want to change it.
You could just pass :method => :put to link_to. Rails will make a JS handler and when the link is clicked, it will create an invisible form with action=PUT and submit it.
BUT, AJAX is a nice thing too and it's just as hard as setting the method: :remote => true
HTTP POST is used for create, and HTTP PUT is used for update. As such, you should be using doing a PUT (add :method => 'put' to your link_to) since you are updating the user record. Here's more details: http://guides.rubyonrails.org/routing.html#crud-verbs-and-actions
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 :)