Functional Form

Avoiding side-effects since 2004.

Wednesday, May 23, 2007

REST: From theory to practice

REST. What is it, and how can it be used to design better web applications?

A presentation at RailsConf did me a great service by first pointing out all the things REST is not. It isn't CRUD. It isn't pretty URLs. It is neither a protocol nor an architecture, but it can play a role in your implementation of all of the above. REST itself though, is less concrete than all of that. It is a theoretical framework, a way of thinking about designing distributed software systems. For me, the first step in absorbing its principles is to forget about the database and focus on the fundamentals. This article will start there, then drill down to show how these ideas can help organize the development of your Rails applications.

REST encourages a focus on resources. A resource is anything that can be named, and your system can have as many resources and corresponding names as you want. Conversely, there is a limited set of operations defined on those resources. Unlike objects in object-oriented programming languages, which support very diverse, rich interfaces, resources in a RESTful system are relatively uniform. So how can a sophisticated API be developed if REST requires a fixed and limited number of operations that resources can support? The answer: add more resources!

Lets play with an example. Say I'm writing a web application that has a collection of shapes that can be moved to new locations. In an object oriented program, I might have a move method defined on each shape, but in this RESTful API, I have assigned consistent semantics to a limited set of operations supported by the HTTP protocol. I can show (GET), create (POST), update (PUT), and destroy (DELETE) my resources. Nowhere in the HTTP specification is the a MOVESHAPE method defined. A naiive approach to remedying this limitation is to shoehorn this operation into the protocol by abusing URLs.

http://againstgrain.com/shapes/move/1

I call this abuse because URL stands for uniform resource locator, and it's very hard to see this imperative-style command encoding as much of a resource. This is an API decision that fights the nature of the protocol it uses. How can the API go with the flow?

There are many potential solutions. Let me outline two, the first very simple, the second more sophisticated.

The first solution is to recognize that a move operation is just a change to the location of an object. If we expose this location as a resource and allow it to be updated, we'll implement movement in a natural way without contorting URLs to name things that aren't actually resources:

So we combine one of the four standard operations:
update, represented by an HTTP PUT

With a resource:
http://withgrain.com/shapes/1/location

Updating the location of the shape will naturally equate to moving it.

But what if we want the movement to be relative to the objects current position, so that the client can say that they want a shape to move 5 pixels up and 10 pixels to the right without needing to know the objects current position or do any computation? To solve that problem, we apply a technique I learned doing computational semantics: reification.

Reification means that we give solid form or objecthood to something formerly fleeting or ephemeral. Anything can be reified. The fact that I am named Nathan Sobo can be thought of as my NathanSoboness, which is an (albeit abstract) conceptual object. Here we'll apply the technique in a more conservative fashion, and say that shapes are associated with a history of movements. This movement history is a collection, which is itself a resource.

http://withgrain.com/shapes/1/relative_movements

Now say we want to move the shape. We combine the above resource locator with one of our standard operations, create, implemented as an HTTP POST. By posting a new movement to a shapes history, we cause the shape to move.

Now we're working with HTTP rather than around it.

So how does this transfer to the design of Rails applications? Embracing resource oriented application development means you'll be writing more controllers with fewer, more consistent methods. Lets work through a potential implementation of the shape API in Rails. It will all start in the routes file, with map.resources...

Lets say we want to expose both the relative and absolute means of moving a shape. First we'll start with a shapes resource.

map.resources :shapes

This represents the collection of all shapes in our system. It assumes the existince of a corresponding ShapesController and will set up a series of routes and url-generating methods to help reference the actions therein. Note the controller and its standard complement of methods below.

class ShapesController < ActionController::Base
def index
end

def show
end

def create
end

def edit
end

def update
end

def destroy
end
end

But with map.resources, the actions on the controller do not play a critical role in the url. They merely name the operations to which a given pairing of HTTP request method and URL will map. GETting /shapes will execute index. POSTing to /shapes will execute create. GETting /shapes/:id will execute show. PUTting to /shapes/:id will execute update, and DELETEing /shapes/:id will execute destroy. So even though there are 5 actions, there are really only two url patterns, one referencing the resource that is the collection of all shapes, and another referencing resources that are members of that collection. We can reference these urls with automatically defined methods:

shapes_url
shape_url(@square) or shape_url(@square.id)

By pairing these with the correct HTTP method, we can access every operation we need.

Now lets add relative movement:

map.resources :shapes do |shape|
shape.resources :relative_movements, :name_prefix => 'shape_'
end

This again assumes the existence of a RelativeMovementController with all of the standard methods defined on it. Except the resources supported by this controller are nested within shape resources, so the URL patterns look like this:

/shapes/1/relative_movements
/shapes/1/relative_movements/4

Because of the :name_prefix we supplied (which will no longer be needed at some release of Rails in the future), we can refer to these URLs with helper methods that look like this:

shape_relative_movements_url(@triangle) to get /shapes/1/relative_movements
shape_relative_movement_url(@triangle, 4) to get /shapes/1/relative_movements/4

All of the same rules about HTTP method choice allow access to the RelativeMovementController's actions.

Now a cool twist: Singleton resources. Lets add the nested position resource to shapes.

map.resources :shapes do |shape|
shape.resources :relative_movements, :name_prefix => 'shape_'
shape.resource :position, :name_prefix => 'shape_'
end

And a corresponding controller, this time with a different complement of methods:

class PositionController < ActionController::Base
def show
end

def edit
end

def update
end
end

Because position is a singleton resource nested inside of shape, this controller is designed to deal with a single resource rather than a collection of them, so there is no need for an index action. The HTTP verb / URL combination mappings are also different. So PUTting to /shapes/1/position will invoke the update action.

None of these changes are Earth-shattering, but the simple act of focusing on resources is a force that will organize your application. Rather than growing a hodgepodge of actions on ever fattening controllers, you'll instead create a greater number of controllers that are more circumscribed in their responsibilities.

What does this say about your model? Not much. I used to think that it was important to have a controller for every model object, and I no longer do. Controllers are responsible for supporting the exposure of resources to a remote API. This collection of resources is, in a sense, your remote client's model. Whether your resources map precisely onto your underlying data model is your business. For example, you might expose resources that have no direct correspondence in the model layer. Or you might have model objects that you don't choose to expose as resources.

But regardless, REST finally provides an organizing principle for the controller layer. Even if you don't plan on exposing a RESTful API as a service, thinking in terms of resources will help you build more consistent applications and help you make fewer decisions.

I realize that this article has by no means covered every aspect of REST, but I hope it fills a gap that I felt as I was learning all of this.

Tuesday, May 22, 2007

Treetop 0.1.0 - My First Alpha

I've just released the first gem of my first free software project. It's a packrat parser for Ruby. Right now it's licensed under the GPL but I'm probably going to change it to the MIT license so other Ruby people can make use of it in their projects more readily.

Friday, April 27, 2007

Critical Mass

I rode with my first Critical Mass tonight and it was awe inspiring. To the pissed off motorists who were inconvenienced by my protest, I remind them of how their decision to drive a car around my city inconveniences me every day, along with seriously damaging the planet. For me the ride is a statement against the degradation caused by a car-dominated culture and the celebration of an alternative. It is in part a manifestation of an undercurrent of conflict between motorists and those whose quality of life they diminish. On this night, it is a conflict they cannot ignore as they zoom through intersections built to speed them along through a fractured social space. A landscape designed for the needs of their machines is the norm. But for once, during this ride, it is the river of rushing metal and exhaust that is quelled, in deference to another priority.

Saturday, September 16, 2006

With duck typing, you only find Geese at runtime.

Friday, September 15, 2006

Inspired

There's a chance that I'll be writing a whole lot of Ruby in the near future. On Rails, as it were.

Kinda funny, since I just started to really like Java. Reading Howard Lewis Ship's blog has been especially inspiring. After reading back a few posts, I like the way he writes, thinks, and his personality. And he highlights what I like about Java. It's mature, as are so many of its developers. Gavin King is another hero of mine, especially after reading Hibernate in Action, which was so articulate and intellectual in its tone. There's a lot about Java that isn't fun, slick, or cool. But there's one word that seems fitting: robust. There's so much quality code out there, so many quality frameworks. Take a look at JiBX, or Hibernate, or the multiple inversion of control containers like Spring and HiveMind. Jakarta, CodeHaus. This software delivers real functionality, quietly. There's so much I haven't gotten good at yet... Aspect Oriented Programming, annotations, really shopping around for an IoC container I like, byte-code manipulation. Hell, I haven't even really pushed Hibernate to the limit.

The Rubinistas... they seem a bit excitable. I haven't had enough exposure to really say, but a lot of the libraries seem to be kind of crappy. There seems to be this ethic of corner cutting or something. Of waving the hands over a demo and exclaiming "look how simple". This is a good post by Tim Bray on the subject. Funny quote: "So, am I a boring old factory factory factory fart wanting to inflict unnecessary pain and failing to recognize the obvious 80/20 point here? Or will the pleasant buzz you get today from mapping markup into code maybe give you a nasty hangover tomorrow? Is it Truth vs. Beauty? Is it Life vs. Art? Is it Alien vs. Predator? Oh, the anguish." I'm sick of the 80-20 rule because in my experience, I need 100 percent. Convention over configuration... I don't think I buy into it. You can't pee into a Mr. Coffee and expect Taster's Choice. I want concise as much as the next guy, but not if I have to sacrifice correct.

I guess the thing that bugs me about the touters of Ruby I've encountered is that they are so impressed with themselves. I don't exactly know how to explain it. Like Ruby was the first language to invent closures or something. And I guess when Ruby is your second programming language and your first was PHP, or when the Java you've written is for some corporation that bought into software-by-commitee standards like EJBs, Ruby and its train tracks seem like the greatest thing in the long history of Ever.

With that off my chest, if Ruby it's going to be then Ruby it will be. Maybe in a year I'll be drinking the red Kool-Aid, too. One thing's for sure, I can't wait to find the first cool way to use Module#define_method.

Sunday, August 06, 2006

Missing the point completely yet again.

The traditional media's analysis of the CT senate primary as an instance of "liberal blogs dragging the party to the left" seems to ignore the fact that current polls reflect a majority opposition to the war. If blogs are dragging the Democratic Party anywhere, they're dragging it to be more in line with the sentiments of the majority of the country. What the Washington establishment (both politicians and journalists) need to realize is that Internet activism is a fundamentally new phenomenon. The people whose voices are now being heard online are nothing like elitist left-wing interest groups of the past. New technology is bringing about a new paradigm that still confuses politicians and pundits. Ordinary individuals are more empowered than ever before to share their ideas and communicate with one another without intermediaries. This power to communicate has never existed, and the old guard can't see a revolution taking place right in front of their noses because they still can't wrap their minds around the implications of that power. They try to interpret new realities within old frameworks, and naturally fail to produce relevant insight. It is not the 1970s. History is not repeating itself. Until pundits begin to address Internet-based power as a fundamentally new force on the social and political landscape, they will continue to misinterpret events.

Thursday, May 25, 2006

An Inconvenient Truth

I just saw An Inconvenient Truth, and I must say, it was eye-opening in the extreme. More like life opening. Like a giant screaming mouth: "HELLOOOO." We need to wake up. Television and the mainstream media must be cast to the sidelines because they are exerting too much control. How did I not know it was this bad? It's really amazing. The best way to do this is to exercise our freedom in every way we can. We need to multiply freedom, taking every opportunity to release it into the cultural atmosphere. Write free as in freedom software. Write and distribute free as in freedom music. Lock nothing down. At this point, we're bailing water out of a sinking ship. And the sea level is rising.