Rails REST 101 meets Rails 2.0 20
But You Said You Were Done
Last time, we concluded our REST 101 series.
Or so we thought.
We developed those articles when 1.2 was a mature, stable release, and when it had also become clear that 2.0 would not significantly change what had already been achieved in 1.2.
However, Rails 2.0 did fine-tune the way we develop RESTful applications in Rails, prompting us to add one more article and bring the series up to date.
REST Refresher
Let’s review what we covered in the first five articles:
- When deciding what controllers you need, think in terms of resources.
- Don’t think twice about what actions your controllers should have. Use the Golden Seven for each resource: index, new, edit, create, show, update, delete.
- A resource maps to Rails controller, not necessarily to an underlying model.
- Deliver your resources in a way that’s appropriate to the user’s experience. Some users want HTML, some want RSS, some want audio, some have big screens, some have tiny cell phone screens, some want to use your application like a web service and expect XML to be returned.
- The client application will indicate their preference with the “Accept” header in the http request. Use
respond_toin your controller to automatically map the Accept header to your response.
Once you’ve identified a resource that you want to implement, you just need to implement it RESTfully in Rails.
The Hard Way
I can’t believe I’m saying that anything in Rails is actually hard, but if there is a “hard” way to write RESTful code, this would be it (“by hand” is more precise, I suppose). Here’s the recipe:
1. If your resource needs a supporting model, use script/generate model <resource-name> (or ruby script\generate model on Windows). You can now specify any columns you already know your model needs right on the command line, which will give you a nice head start in your migration file.
Don’t forget to rake db:migrate before you start running your tests (you thought I was going to say “running your app”, didn’t you?)
script/generate model Airport city:string identifier:string
2. Generate a nice empty controller with script/generate controller <resource-name> and fill it with the standard seven actions.
script/generate controller Airports
Using textmate? You can steal our Textmate snippet to quickly write all the boilerplate code for you.
3. Create views only for the actions that need them, typically these are:
index.html.erbnew.html.erbedit.html.erbupdate.html.erbshow.html.erb
In Rails 2.0, the middle part of the template filename maps to the HTTP mime type (more or less), so you might also have files named index.iphone.erb or show.xml.builder.
Also, be sure to learn the cool new form_for syntax in Rails 2.0 that automatically generates the right target url based on whether you give it a new object (which will target the create action) or one that’s already been saved to the database (which will target the update action).
In fact for a nice overview of all that’s new in Rails 2.0, we recommend the PeepCode Rails 2.0 PDF by Ryan Daigle (disclaimer: PeepCode supports this blog.) Ryan’s blog is also an excellent way to keep up on all the new stuff in Rails.
4. Add a map.resources line to your routing file. There are lots of options here, but the simplest looks like:
map.resources :airports
This will generate a package of handy named routes, map incoming action/verb pairs to the right controller action, and automatically ensure that actions like create, update and delete only respond to POST verbs only.
But you knew all this already – it’s been around since Rails 1.2.
5. Always, always, always try to use named routes whenever you use link_to and any other helper method that expects a hash of url options. The syntax of named paths has changed somewhat in 2.0 to make it easier to construct links for the most typical scenarios. Check the Rails API docs and use the new rake routes task as a cheat sheet whenever you need it.
Help Rails Help You
Ok, now for what’s new in Rails 2.0 that can make this process much easier.
Rails 2.0 has two built-in generators to make your RESTful life easier.
1. Generating A Resource Without Views
If you have a model underpinning your resource, you can get 80% of the way done by using the totally awesome resource generator (again, available since Rails 1.2, but somehow few people know about it). It’s syntax is identical to the model generator, but it will also create a RESTful controller with the Golden Seven actions and add a map.resources line for you.
script/generate resource Airport city:string identifier:string
So what was the other 20%? The views are not generated for you.
2. Generating Everything With The Scaffold Generator
You can inch up to 95% of the way for the simplest situations by using the new scaffold generator.
Ok, time out.
We’ve always said that the scaffold generator was bad. Very bad.
So now we’re recommending it?
Not if you’re using 1.2.x or earlier. In that case, do not use the scaffold generator. If you’re using 1.2, you can use the scaffold_resource generator instead.
But in Rails 2.0, the scaffold generator is no longer evil, it is downright righteous. It will create the model, controller, routes, and view scaffolding that demonstrates how to use the new form_for syntax. Very cool stuff. Again, it uses the model generator syntax, so use the singular form of your resource and specify any columns you already know up front:
script/generate scaffold Airport city:string identifier:string
You’ll end up rewriting the views to truly fit your application, but that’s the whole idea. It’s scaffolding, not the real thing, silly.
If you’ve used the resource generator and now want the scaffolded views as well, you’re in luck: Brian Hogan has released a gem to do just that for you.
(Oh, and I know we had you at “easier.”)
Ok, NOW we’re done
We hope this wraps up our REST series. At least for now.
Questions? Comments? Applause? Complaints? Bring it on in the comments.



As always, a rockin' tutorial and nice usage of 'righteous'.
There is an even EASIER way to do Restful Rails. I've demonstrated James Golick's resource_controller plugin in a screencast at my blog (http://www.akitaonrails.com/2008/1/25/easy-restful-rails-screencast). As you said, in Restful programming in Rails we have the "golden seven". But the way of using them today is not DRY at all. So I think this plugin - or any similar technique, for that matter - is a very good way to make it DRY as it should be.
Nice addition in Rails, I haven't dipped my toe in since 1.2 so this and a few other blogs is about how I stay current these days. I think I'll steal these idea and see about folding it into SubSonic's scaffolding generator to get the same bits spit out for the new ASP.NET MVC framework.
Can you explain the "golden seven" in more detail? What is the difference between update and edit, and create and new?
@mark: Thanks for that!
@Shawn: I think ASP.NET MVC looks promising. I also think Sapphire's plugins will make Rails happen in a Visual Studio environment one day soon. So lots of good things are happening in the Microsoft landscape with regards to Ruby and Rails.
@Ed: Sure. The edit and new actions will render the appropriate HTML forms. The <form> tag for the new action should target (that is, post to) the create action in your controller. The edit form should target the update action. Both create and update are responsible for inserting or updating data in the database, and then redirecting somewhere logical (usually to the show or index actions, so you can see the results). See part 4 for more info, and feel free to follow up here again.
I am curious. This works great in Rails, but have you journeyed into Merb yet ? I am sitting here hacking at code right now to test this out as well.
Using RESTful support in Rail 2.0 how is it possible to create a new table row from an exiting row? This would be like opening an edit form, making changes, then creating a new row from the edited data. Now the user need not re-type the attributes that do not change.
Great update to the tutorial series! Good to have extra documentation and explanation on blogs like this to help follow the new changes in rails.
how would you REST simple functions such as change password or upload picture for a user?
@rb: Changing a password is often seen as part of the "edit" action for the logged-in user, where they can change whatever they want about their account or profile with your site. The form in the edit action targets the "update" action. Uploading a picture might be handled in the same way, or see Mike Clark's example (http://clarkware.com/cgi/blosxom/2007/02/24) of using a separate restful controller to handle picture uploads.
SKELETON NOT SCAFFOLD
Scaffold should be called Skeleton on which you build rather than try to remove later.
Excellent tutorials. Clear, concise and very generous. Keep em up. I'll be back.
Hi,
Thanks for the great post. I have one issue though. When I run script/generate resource Airport city:string identifier:string should this create the methods in the controller for me, because for some reason it creates the controller, and the map.resources, but does not created the controller methods.
Thanks again!
@euro: I tend to agree. In Rails 1.x scaffolding was definitely something you would want to throw away. But in 2.x you'll keep the routes, model, and controller code (just please toss the view code). So skeleton might have been a better name.
@Ryan: Sorry about that, that one paragraph is plain wrong :-( I think resource should create the controller code and I thought it was working that way when I wrote the article, but I haven't had a chance to update the text yet. You have to write the controller code yourself. If you're using textmate, try our Textmate Snippet.
Great articles. I get the concept when resources represent single entities (like articles, images, recipes, etc.) but I'm not sure how to handle many-to-many relationships. For example: a person can have many books, and a book can be owned by many people. If I want to remove a book from a person's book collection, what is the URL? /people/1/books/3 + DELETE? If so, then does flipping the URL around still follow REST principles: /books/3/people/1? If I should encapsulate the concept of "owning a book" into a "virtual" resource like /book_collections/1, what does index return? A hash of people-to-lists of books? How would I remove a book from someone's collection? I need the collection ID and the book ID. Thanks you for any insight you can provide.
Well, I suppose I've mostly answered my question, though my brain is not 100% there yet. I found a short discussion in the local forum: http://forum.softiesonrails.com/forums/3/topics/152.
Jeff, thanks for the writeup. I’ve used this and it works great, and I finally get RESTful conventions.
Quick question – when it creates my edit/index/new/... files, it hardcodes the display of the fields in the files. So, to use your Airport example, everything would contain and display the fields “city” and “identifier”. If I wanted to add in new fields – “state” and “country”, for example – to my model and have them be added to the corresponding RESTful files, is there a quick way to do that?
Thanks in advance for the help.
@lbast: There’s no built-in way to add fields to your scaffolded views, you’ll have to add them yourself after you create your database migration. For an intro to forms in the same style as this series, check out our Forms in Rails series: http://www.softiesonrails.com/search?q=forms+in+rails. Hope that helps. I’m glad REST is starting to sink in!
Thanks for the great tutorials, this definitely improved my RoR skills!
Since some of the objects in space and time are analytic, it must not be supposed that the thing in itself is a body of demonstrated doctrine, and some of it must be known a priori; with the sole exception of the employment of the Antinomies, the Ideal of practical reason is just as necessary as our ideas.
Pure reason, for example, is a body of demonstrated doctrine, and some of it must be known a priori; in all theoretical sciences, our faculties occupy part of the sphere of the thing in itself concerning the existence of the phenomena in general.