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

advertisement

REST on Rails

November 02, 2005

Imagine a news website with simple URLs for its news items and the categories they belong to: http://example.com/item/15 and http://example.com/category/rails. Suppose that it has been built using Ruby on Rails. In the article, we'll discuss how to REST-enable this site, giving read-write HTTP access to machine-readable representations of its resources. By the end of the article, we'll be able to add these features to our site in just a couple of lines of code.

If you're not yet familiar with the Rails framework, I suggest you introduce yourself to it via an article like "What Is Ruby on Rails" before reading on. You won't regret it. For more background on the REST architectural style, Joe Gregorio's series of XML.com articles "The Restful Web" are very useful.

(rest_resource.rb contains all the code excerpted in this article.)

Mapping REST Concepts to Rails Patterns

Following the Model/View/Controller pattern, our site's resources will be represented by Model classes, in this case, an Item and a Category class. In REST terminology, these are resources, URL-addressable entities that you can interact with over HTTP. Views are a way to create REST's representations, useful serializations of the models that clients can interpret. We decide which representation of which resource to use in dispatch, a role played by the Controller.

HTTP's standard PUT/GET/POST/DELETE verbs translate directly to the Create/Read/Update/Delete features built into every Rails ActiveRecord model. This means no changes or additions for existing model classes to be REST-enabled -- as is usual in the MVC framework, models don't have to know or care what they're being used for.

We'll provide a view that does an automatic mapping from any Rails model to a default XML representation. We'll build it with a template using ActiveRecord introspection to discover the schema and record values at runtime. The XML format will look something like this:

<nameofmodelclass>
  <field1>field 1's value</field1>
  <field2>field 2's value</field2>
  <onetomanyfield1
href='http://example.comm/url/of/relatedrecord1' />
  <onetomanyfield1
href='http://example.comm/url/of/relatedrecord2' />
</nameofmodelclass>

When a client POSTs such an XML document to the URL of an existing resource, we'll parse it and update the values of the record in the database. Clients will also be able to create new resources by POSTing XML to a create URL and being redirected to the newly created resource. This logic will be implemented in the Controller.

Dispatch with ActionController

To handle incoming requests to create, read, update or delete resources, we need to inspect the incoming request before we can take action. Rails handles most of the hard work in decoding a URL and calling the right controller method, but it doesn't differentiate between different HTTP verbs. We'll have to make this choice in our controller method. ActionController provides a bunch of convenient Boolean methods for this using different HTTP verbs, so the skeleton of our code looks like this:

if request.post?
  if params[:id]
    # it's an update
  else
    # no ID supplied, so create new resource
  end
end
if request.get?
  # serve up a representation
end
if request.delete?
  # delete the resource
end

You may like to compare this dispatch mechanism to the Python code in Joe Gregorio's Dispatching in a REST Protocol Application.

Using REXML and Builder

To implement the XML handling for this application, we're going to need a way of consuming XML, and a way of producing it. Fortunately every Rails installation ships with REXML and Builder.

Builder is the standard Rails mechanism for producing XML. If you're familiar with .rhtml files used for HTML templating then making the requisite .rxml files won't be a great leap, and will integrate with your application's controllers in just the same way. Here's a simple example of producing some XML:

xml.instruct! :xml, :version=>"1.0",
:encoding=>"UTF-8"
xml.example('version' => '2.0') do
  xml.foo("This is the title")
end

The template produces this output:

<?xml version="1.0"
encoding="UTF-8"?>
<example>
  <foo>This is the title</foo>
</example>

Ruby ships with REXML, which incorporates both a parser and an XPath implementation. You can parse an XML document like this:

doc = REXML::Document.new(resp.body)

and extract data with XPath like so:

REXML::XPath.each(doc,"/example/foo") { |xml|
  puts xml.text
}

Pages: 1, 2, 3

Next Pagearrow