Putting, Modifying and the REST
This article is part of the Sinatra Story series.
Until now, the texts had to be hard coded into the fixture. That of course is just ridiculous for a webapp. The whole point of an application like this is that it is interactive, so that I can publish my own texts, modify my old ones and delete the garbage.
As you might know, the HTTP has four main verbs:
- GET: A content is shown, no modification should occur.
- POST: Modify a content by sending the server information.
- PUT: Create new content on the server by URL
- DELETE: What do you think?
There are some others, but these four help you design your interface. For example, our application manages texts, so the following requests should be possible:
GET /textshould display a list of all texts.PUT /textshould publish a new text.GET /text/:idshould display the text with the given id.POST /text/:idshould modify the text.DELETE /text/:id...What do you think?
Using only meaningful URLs and HTTP methods and not cookies, etc, to navigate the user through the site is known as REST. Not only does it give an simple interface, it also helps performance, etc. Putting a cool new name on it like "REST" on it hides the fact, that this was how the makers of HTTP imagined the web all along.
Sinatra is RESTful by default, but of course, you have to take care of the
URLs yourself. The last time, we defined the two GET methods, so lets add
publishing a new text first:
post "/text" do
text = Text.new(params[:title], params[:text])
text.save
end
Pretty easy, huh? The params will come from a form, but that's not our
problem now.
Next, we can post changes:
post "/text/:id" do
text = Text.by_id(params[:id].to_i)
return 404 if text.nil?
text.title, text.text = params[:title], params[:text]
end
This mixes the two sources for params.
delete "/text/:id" do
text = Text.by_id(params[:id].to_i)
return 404 if text.nil?
text.delete
end
Ok, now we can face the problem, that we can't actually access these methods, since we don't have a form for the editing part yet.
First of all, we notice that it would be stupid to write the same form twice -
once for the editing and once for the creating. We'd prefer to just show the
same form. This problem can be solved with partials: basically, we insert a
HAML call into the HAML template. Define a HAML file form.haml, and use it
from both new.haml and edit.haml.
# form.haml
%form(action = actionurl method = "POST")
%label
Title:
%input(type='text' name="title" value="#{title}")
%label
Text:
%input(type='textarea' name="text" value="#{text}")
%input(type='submit')
# edit.haml
%h1
Edit
%q= @text.title
= haml :form, :locals => {:title => @text.title, :text => @text.text, :actionurl => "/text/#{@text.id}"}
# new.haml
%h1 New Txt
= haml :form, :locals => {:title => "", :text => "", :actionurl => "/text"}
We need to add URLs for the form of course, for example
get "/text/new" do
haml :new
end
get "text/:id/edit" do
@text = Text.by_id(params[:id].to_i)
haml :edit
end
- Instead of adding
:locals, you can also define instance variables. This is often preferable.
There is only putting the respective links into the views:
# index.haml
%ul
- for text in texts
%li
%a( href="/text/#{text.id}" )= text.title
%a( href="/text/new" ) new text
# show.haml
%h1
= text.title
%div
:markdown
#{text.text}
%ul
%li
%a(href="/text/#{text.id}") edit
%li
%form(action="/text/#{text.id}" method="POST")
%input(name ="_method" type="hidden" value="delete")
%input(type="submit" value="delete")
You might be wondering, what the heck the line
%input(name ="_method" type="hidden" value="delete")
is about. Fun fact: Browsers can't use PUT or DELETE. It's embarrassing
but true. The fix is the following: We sent a POST, which tells the server
that it actually should have been a DELETE method. Sinatra understands this and
translates.
Please don't let GET requests change anything on the server, or a google
spider might delete all your posts.
You should now know
- What REST is and how RESTful application typically build up their URLs.
- How to use partials.
- How to use the
PUTandDELETEmethods, even though browsers don't support it.
blog comments powered by Disqus