REST and Rails – no reason to be scared
I’ve been meaning to try simply_restul for a while now, and today’s announcement on rails-core pushed me over the edge. It’s no longer a plugin, it lost it’s name and changed a few things, but it’s pretty much the same. I’m going to do a quick tutorial on setting up a RESTful controller. It’s not hard — I was just scared of it!
rails simple
This creates a new rails project named “simple”. Without a -d flag, it will use mysql by default
cd rails
mysqladmin -u root create simple_development
mysqladmin -u root create simple_test
create our databases – if this were a longer tutorial, we’d test it as we went.
rake rails:freeze:edge
Make your project use the latest version of rails “Edge”. You have to do this if you want to follow along. In the future, this step will be unnecessary
script/generate model Person
Our example object – a Person
[source:ruby]
db/migrate/001_create_people.rb:
class CreatePeople < ActiveRecord::Migration
def self.up
create_table :people do |t|
t.column "name", :string
t.column "bio", :text
end
end
def self.down
drop_table :people
end
end
[/source]
rake db:migrate
We’re creating our table now using a migration. If everything went well, you’ll now have a table in your database – “people”, with two columns, name and bio.
Add the following line to your config/routes.rb
map.resources :people
(this is a change from simply_restful, plural now) Here is where the magic happens. This one line of code will give you a bunch of stuff. All the mappings between REST and your controller will now just work.
script/generate controller people index new create show edit update destroy
Create our controller with all the REST / rails actions.
app/controllers/people_controller.rb:
# maps to /people – named route of person_url
# HTTP verb: GET
def index
@people = Person.find(:all)
respond_to do |format|
format.html
format.xml { render :xml => @people.to_xml }
end
end
# maps to /people/new – named route of new_person_url
def new
@person = Person.new
end
# gets called when for POSTs to /people
def create
@person = Person.new(params[:person])
@person.save!
respond_to do |format|
format.html do
flash[:notice] = “Person was successfuly created”
redirect_to person_url(@person)
end
format.js
end
end
# maps to /people/:id – named route of person_url(person_object)
# HTTP verb: GET
def show
respond_to do |format|
format.html
format.xml { render :xml => @person.to_xml }
end
end
# maps to /people/:id;edit – named route of edit_person_url(person_object)
def edit
end
# gets called for PUT to /people/:id
def update
@person.attributes = params[:person]
@person.save!
respond_to do |format|
format.html do
flash[:notice] = “Person updated”
redirect_to person_url(@person)
end
format.js
end
end
# maps to /person/:id;destroy
# HTTP verb: DELETE
def destroy
@person.destroy
respond_to do |format|
format.html do
flash[:notice] = “Person destroyed”
redirect_to people_url
end
format.xml { render :nothing => true }
end
end
protected
def find_person
@person = Person.find(params[:id])
end
end
Here’s a lot of code. This is the person controller and all the actions that you want to do on a person. They map to HTTP verbs very nicely. You should type the code (the generator made a lot if it for you, you just need to fill in the methods)
You’ll notice the funny “respond_to do |format|” stuff. This is one of the other slick features: you can send the client the format they want. So if someone were to request /person/1.xml – they clearly want xml output – this handles that (and so much more).
That was all fine and good. We could write a very nice test suite for all the code to now and still be lost on the views. Let’s go through them quickly.
app/views/people/index.rhtml
app/views/people/new.rhtml
New person
< % form_for :person, @person, :url => person_url, :method => :post do |f| %>
< %= render :partial => “form”, :object => f %>
< % end %>
app/views/people/show.rhtml
< %= h @person.name %>
< %= h @person.bio %>
< %= link_to "Edit", edit_person_url(@person) %>
app/views/people/edit.rhtml
Edit person: < %= h @person.name >
< % form_for :person, @person, :url => person_url(@person), :html => { :method => ‘put’ } do |f| >
< %= render :partial => ‘form’, :object => f >
< % end >
app/views/people/_form.rhtml
< %= form.text_field :name >
< %= form.text_area :bio >
< %= submit_tag %>
This concludes the whirlwind tour of the new RESTful Rails controllers.
I’ll be changing this post a lot based on feedback, so check back.