XML.com: XML From the Inside Out
oreilly.comSafari Bookshelf.Conferences.

advertisement

REST on Rails
by Matt Biddulph | Pages: 1, 2, 3

In Action

Let's see a command-line session with our REST-enabled web app. We'll use the REST hacker's number one web services client: /usr/bin/curl.

First create a new Item by POSTing some XML to the app:

$ curl -i -X POST -d
"<item><title>Article
title</title><bodytext>no
body</bodytext></item>"
http://localhost:3000/blog/item    


HTTP/1.1 201 Created
Date: Sun, 09 Oct 2005 13:12:54 GMT
Location: http://localhost:3000/blog/item/1137975

The server parsed our XML, created an Item model instance, initialised it with our data and stored it in the database. It indicated success with the HTTP code 201 Created, and told us where to find the new resource with the Location header.

Now request a representation of the resource from its newly-minted URL:

$ curl http://localhost:3000/blog/item/1137975
<item id="1137975">
  <title>Article title</title>
  <bodytext>no body</bodytext>
</item>

The XML comes back as expected. Make a change to the resource by posting data to its URL:

$ curl -i -X POST -d
"<item><title>New
title</title></item>"
http://localhost:3000/blog/item/1137975

HTTP/1.1 200 OK
Date: Sun, 09 Oct 2005 13:14:29 GMT
Content-Type: text/xml

<item id="1137975">
  <title>New title</title>
  <bodytext>no body</bodytext>
</item>

Note that the title has changed but the bodytext field remains untouched as we didn't specify it in our input.

Finally, delete the resource:

$ curl -i -X
DELETE http://localhost:3000/blog/item/1137975

HTTP/1.1 204 Deleted
Date: Sun, 09 Oct 2005 13:14:29 GMT
Content-Type: text/xml

And verify that it's gone:

$ curl -i http://localhost:3000/blog/item/1137975

HTTP/1.1 404 Not Found
Date: Sun, 09 Oct 2005 13:17:29 GMT

Let's Make it a One-Liner

Now we have our code written, wouldn't it be nice if we could apply it to any model in any rails project? Let's package up the code so that it can be pulled in with one line of controller code, just like Rails builtins such as belongs_to, validates_uniqueness_of and layout.

To do this, we exploit Ruby's dynamic nature by providing a mixin class that transparently adds features to an existing class. There's a fairly simple code pattern for doing this:

module ExtraMethods
    def self.append_features(base)
       
super
       
base.extend(ClassMethods)
    end
    module ClassMethods
        def
additional_method(message)
         
module_eval <<-"end_eval",__FILE__,__LINE__
         
  def example
         
    puts "I've been added to give you this message:
'#{message}'"
         
  end
          end_eval
        end
    end
end

We can now use this in any class with a call to include:

class SomeClass
    include ExtraMethods
    additional_method "your dynamic method insertion
worked"
end
>> SomeClass.new().example()
I've been added to give you this message: 'your dynamic method
insertion worked

You can see this Ruby pattern in use in the code that accompanies this article. With the final code present in the lib directory of the project, a REST-enabled controller looks like this:

class NewsController << ApplicationController
    rest_resource :item
    rest_resource :category
end

Conclusion

Rails is a strong foundation for the REST style. Its approach to database modeling and URL routing leads to good URLs representing resources. These are easy to enable for XML reading and writing using simple dispatch mechanisms. Ruby's dynamic language features make it possible to package up convenience code into an easy-to-use one-liner.

In any application where the client can make changes to your database, you have to think carefully about granting access rights. The code presented here could be enhanced to use a Rails-based authentication system, or alternatively a web server such as Apache can be used to front an application and grant fine-grained rights dependent on user and HTTP verb.

It's instructive to compare the style of interaction in this article with the emerging Atom API standard, and to think about how this kind of API lends itself well to embedding in all sorts of clients, from command utilities to OSX Dashboard widgets, to Web 2.0 mashups.



1 to 7 of 7
  1. Isn't this making it too hard?
    2007-11-07 12:17:57 SteveMidgley
  2. PUT vs. POST
    2006-02-18 16:09:11 StefanTilkov
  3. Another way to do it
    2005-12-30 22:39:28 BenStiglitz
  4. Getting request verb..
    2005-12-28 10:50:29 cjbrown102
  5. The "simple" design pattern for dynamic class extension
    2005-12-19 15:41:02 richbodo
  6. case out the http verbs
    2005-11-09 13:37:17 bitsweat
  7. Nice article but question on code examples
    2005-11-08 20:33:09 vcosby
1 to 7 of 7