For those of you familiar with Rails, DHH has recently merged the simply_restful plugin with the Rails core. Only recently have I actually begun to truly understand the power and flexibility of REST design.
What it boils down to is that virtually every operation your website can perform should be modeled as a CRUD action, on a specific resource identified by a URI. That probably doesn’t mean much right now, so I’ll give an example (from Scott Raymond) that helped me understand what this was all about.
Say you have a fairly standard web application. In this case, it’s the IconBuffet shopping cart. You likely have several controllers and actions similar to the following:
- about: home, index, license, privacy, tos, feed
- account: signup, login, authenticate, set_subscription, retrieve_password, index, logout, update, cart, checkout, update_states
- delivery: index, signup, deliver, download, receive
- products: category, product, add_to_cart, remove_from_cart, download
- admin/categories: index, create, update, delete
- admin/deliveries: index, new, create, edit, update, update_sidebar, push, download_file, delete_file, delete_small_preview, delete_large_preview, delete
- admin/people: index, search, set_administrator, new, create, edit, update, delete, push, gift, ungift
- admin/products: dashboard, index, new, create, edit, update, update_sidebar, download_file, delete_file, delete_sample, delete_small_preview,delete_large_preview, delete
- admin/site: index, update, update_sidebar, delete_feature
- admin/specifications: new, create, edit, update, delete
This is layed out as if it were an RPC — calling procedures on remote objects. If you want to log a user in, you call Account::Login. To see products by category, you call, Products::Category, and to add something to your cart, Products::Add_To_Cart. In these kinds of systems, the emphasis isn’t really on the noun; it’s all about the verb. What you can do with an object is what counts. As it turns out, this can be an incredbily onerous style of software development. As you add more and more verbs, the overall complexity of your software increases significantly. What can we do to combat this?
By focusing on the nouns. Instead of an RPC-like call structure, we can consider everything to be a derivative of a CRUD operation on some first-class object. To clarify, look at the transformation of the controllers/actions from the previous example.
- about: home, index, license, privacy, tos
- cart_items: index, create, destroy
- categories: index, create, show, update, destroy
- deliverables: index, new, create, show, edit, update, destroy
- deliveries: index, new, create, show, update
- orders: index, create
- people: index, new, create, show, edit, update, destroy, retrieve_password
- products: index, new, create, show, edit, update, destroy
- purchases: update
- pushes: index, create, show
- sessions: new, create, destroy
- settings: index, update
- specifications: index, new, create, show, edit, update, destroy
So you don’t have to count, I’ll summarize for you. The previous example used ten controllers and seventy-six actions. It’s a reasonably complex (real) web application. Moving to a CRUD style of left them with thirteen controllers and fifty-eight actions. They reduced the number of fundamental operations on their data by nearly twenty, and the entire ugly (and probably redundant) admin/ tree was able to be removed. Plus, look at the action names; almost all of them are completely uniform, using some subset of the seven standard Rails operations. The complexity of their code dropped by a clearly tangible amount, all without abandoning a single feature.
So, organizing our controllers and actions in terms of objects and a few basic actions can (maybe) be a good thing. What does this have to do with anything? Enter REST.
REST drops us down a layer of abstraction. If you think about the HTTP stack, we only really deal with two methods: GET and POST. When you want to retrieve a page, you send a GET. If you want to do something potentially dangerous (for instance, delete, update, insert, or edit data), it comes wrapped in a POST. Simple enough. But HTTP actually provides us with all four CRUD operations, POST is for Create, GET is for Read, and the two redheaded stepchildren: PUT for Update and DELETE for Destroy. Now, the actions you want to perform don’t necessarily need to be part of the URI, especially if you’re using the standard Rails actions. URIs can be actual resource identifiers, rather than the mishmash of actions and objects they point to right now.
For example, once you’ve enabled REST routing for a controller in Rails, the various actions map as follows.
- GET /posts
- :controller => ‘posts’, :action => ‘index’
- POST /posts
- :controller => ‘posts’, :action => ‘create’, :params => { # form data }
- GET /posts/1
- :controller => ‘posts’, :action => ’show’, :id => ‘1′
- GET /posts/1;edit
- :controller => ‘posts’, :action => ‘edit’, :id => ‘1′
- PUT /posts/1
- :controller => ‘posts’, :action => ‘update’, :params => { :id => 1, # form data }
- DELETE /posts/1
- :controller => ‘posts’, :action => ‘destroy’, :id => ‘1′
- GET /posts/new
- :controller => ‘posts’, :action => ‘new’
This isn’t too different from the default mapping (:controller/:action/:id), except that the action is removed in the cases where the HTTP method accurately describes the action we’re trying to perform. There are a few interesting things to note, though. Every resource has its own unique location. When you don’t specify a specific item, you get the collection of them. And sending an HTTP method to a resource identifier causes exactly what you would expect to happen.
There’s more. In his keynote speech (which I strongly encourage you to watch), DHH talks about some of the new features Rails will be able to incorporate with this convention of resource structure. I’ll cover more about this in my next post.
Post a Comment
You must be logged in to post a comment.