Sending a DELETE request from Sinatra - ruby

I am trying to develop a RESTful Sinatra application. Now, I know how to respond to a delete request with something like
delete '/user/:id' do |id|
#do something in the model
end
What I am interested in is how do I get to execute that method. I can't have link that does a DELETE instead of a GET, can I?
The only solution I found so far is sending a DELETE request via jQuery: How to send a PUT/DELETE request in jQuery?
I tried looking into different RESTful Sinatra projects on github but my Ruby knowledge is probably to limited to get how they are doing it.

Put following line in your code.
use Rack::MethodOverride
It will help you interpret post methods with parameter "_method" with value "delete" as put.
Then you can write
delete '/user/:id' do |id|

I thinks it's like the Rails way. You need define a params '_method' with 'delete' value and add it on your form.
When you POST you form with this particular params, you do a DELETE request in sinatra.
Like :
<form action="/search" method="post">
<div style="margin:0;padding:0">
<input name="_method" type="hidden" value="delete" />
</div>
</form>
It's the same with PUT method

Another way is to use Curl:
curl -X DELETE http://host/user/1

%form{:action => "/note/delete/#{#note.id}", :method => "post"}
%input{:type => 'submit', :name=> "_method", :value => 'delete', :class => 'button'}
You can also trigger the delete route with a button like so

see also Call Sinatra delete route with jQuery for how to do this with jQuery and JSON at the front end and Sinatra on the back end.

Related

How to PATCH or DELETE from a form without javascript

I am writing a simple crud app on Phoenix. How does one submit PATCH and DELETE requests from a form without using javascript?
Ah I figured it out, same as rails:
<form method="POST">
<input name="_method" type="hidden" value="patch" />
...
Handled in Plug.MethodOverride: https://github.com/elixir-lang/plug/blob/master/lib/plug/method_override.ex
<form> elements only support sending GET and POST requests. The workaround that Rails uses is to read the request method from the _method request parameter, overriding the actual request method (the GET or POST method).
Phoenix does exactly the same through Plug, the Rack-like framework that Phoenix is built on. Long story short, Plug provides middlewares and one of the middlewares it provides is Plug.MethodOverride, which does exactly what we discussed. Doing so in a middleware, the Phoenix app barely knows that the original request was not a GET/POST.
You can see Plug.MethodOverride used in Phoenix's source code.
As the others have mentioned, Phoenix handles this in the routing via the MethodOverride plug.
In order to change this in the template with the form_for helper, use the :method parameter:
<%= form_for #changeset, path(#conn, :update), [multipart: true, method: "patch"], fn f -> %>
This will add the hidden input to the HTML, as noted by #greggreg:
<input name="_method" type="hidden" value="patch" />

Backbone JS and Ruby on Rails CRUD issue

This looks like an issue to me. Normal way Backbone works is using same URL and GET, POST, PUT and DELETE. But obviously:
1) All but POST methods need an ID either in URL or in request body
2) DELETE request can not contain body or some servers ignore body
So how can you make let's say a Ruby on Rails server app successfully work with Backbone without need to hack Backbone such as model.destroy() needs to have ID in the URL? And, as our RoR developer is telling me, normal way to do routes for PUT is to also have an ID in the URL?
There are 5 routes you need to implement to make use of backbone's default sync behavior. For a resource user, they are:
GET /user/ // Get a list of users
GET /user/:id // Get a single users by id
POST /user/ // Create a new user
PUT /user/:id // Update an existing user by id
DELETE /user/:id // Delete an existing user by id
I'm not very familiar with Ruby on Rails, but glancing at their documentation you could fulfill this specification with something like:
match "/user/" => "users#all", :via => :get
match "/user/:user_id" => "users#one", :via => :get
match "/user/" => "users#create", :via => :post
match "/user/:user_id" => "users#update", :via => :put
match "/user/:user_id" => "users#delete", :via => :delete
You should not have to hack Backbone for it to work with RoR. Backbone is smart enough to know (to a certain extent) what URL and what method it should use.
For example, for the initial fetch of a model, if you set url to '/tasks', it will do a GET request to '/tasks/id'. When you change that model, and call model.save, it will do a PUT request to '/tasks/id'. When you call model.destroy, it will send a DELETE request (with an empty body)
The one thing you do have to consider is the CSRF token. I suggest you include backbone-rails in your Gemfile. It includes some JavaScripts to help with Rails/Backbone integration.

Rails -- submitting a form_for to a non-RESTful method

So I know how to submit a form to a server via form_for to a model's create and update methods, but if a model has some method foobar, how do I submit a form specifically to the foobar method? I tried specifying :method => "foobar" but that didn't seem to do the trick. How can I submit a form_for that goes to a non-RESTful method?
EDIT:
I don't want said method to be accessible to anyone by typing the address in their address bar. eg. www.site_name.com/controller_name/foobar should not be valid - the foobar method should solely be used for processing the submitted form.
You can use the :url optional parameter to form_for to specify the url you want to post to.
form_for(#model, :url => foobar_model_path) do |f|
...
end

Is this a valid Sinatra route handler?

Is this a valid route handler?
post '/lists/:id/delete' do
#delete list
...
end
I can't get it to fire.
Yes, it is. But it will only fire when you do a HTTP POST request, e.g. submit a form using the post method:
<form action="/lists/17/delete" method="post">
...
</form>
If you enter the same URL in you browser, however, a HTTP GET request will be fired. If this is what you want, you should replace post with get in your route.
A great place to start: http://www.sinatrarb.com/intro.

Codeigniter: Pass form variable into URI

Not sure if this can be done but it seems my main issue is because i have a default route to a method called "index." I want to be able to list all users tagged with a specific keyword. In addition, users can search for other users based on these keywords.
i.e.
www.domain.com/tags/apples
www.domain.com/tags/oranges
www.domain.com/tags/blueberry
It works fine if I go to the URL manually. I'm having issues getting it to work with a form field.
Snippet of the form_open:
<?=form_open('tags/');?>
<p>Search for Tag: <input type="text" name="tag" /></p>
<p><input type="submit" value="Search" /></p>
Here's a snippet of my controller:
function index() {
$data['result'] = $this->tags_model->searchByTag($this->uri->segment(2));
$this->load->view('tags_view', $data);
}
Here's a snippet of my router:
$route['tags'] = "tags/index";
$route['tags/(:any)'] = "tags/index/$1";
Now, I can easily fix all this if I have a method called search, but I don't want the URL to show up as www.domain.com/tags/search/orange.
When you create your form you set it to use POST variables instead of GET, that way they don't go through the url, that's codeigniter's default method for forms.
So your form_open code will generate the following code:
<form method="post" action="tags/" />
If you want them to got through url though, call the form opener this way instead:
form_open('tags/', array('method' => 'get'));
The same applies to any other attributes you want to specify for the form, just follow the same pattern attribute_name => attribute_value inside the array.
More info on the user guide
The problem here is that your form will be submitting all it's data to "/tags", with nothing trailing it, as POST data doesn't come in as part of the URL. Even if it was a GET request however, I don't think that CodeIgniter will take anything out of the querystring and use it as part of the routing segments.
I think what you should do is have a small Javascript function that automatically updates the form action parameter to be tags/<select option value> whenever the select value is changed. This way it will submit to the right place. In order to handle non-javascript enabled browsers, you could have a default action called tags/search that would simply analyze your form data and put out a 301 redirect to the proper tags/<location> once you'd figured it out.
It seems like a bit of overkill here however, as you could really point the form at tags/index and not worry about it. I'm not sure search engines index form submission locations, and even if they did, they certainly wouldn't index a form that submits to dynamic URIs in the way that you want it to. You could still link to the search result pages using tags/apples, etc, but the form could work quite normally just by going to tags/index.
I ended up redirecting the URL and passed the keyword into the URI.
i.e. domain.com/tags/view/

Resources