I've been pleasantly surprised by how easy it's been to integrate our Rails application with other disparate systems—systems that produce or consume XML in a pre-existing format. Lately we've extended the reach of the application to include consuming the results of a RESTful web service and producing XML to another system. So I thought I'd show two quick examples of how your Rails application can take XML over the wire and dish it out, too.

Consuming XML

Consider an external system that provides basic user information given a unique id for that user. The request is a simple REST-style GET that's easy to tickle with curl:

curl https://somewhere.com/identify?guid=N8S7TU8YU8W8XTVS

In response, we get something that looks like:

<response code="success">
  <user>
    <guid>N8S7TU8YU8W8XTVS</guid>
    <user_name>fred@flintstones.com</user_name>
    <first_name>Fred</first_name>
    <last_name>Flinstone</last_name>
  </user>
  <other_stuff>
  </other_stuff>
</response>

To slurp in that XML over HTTP inside of our Rails application, we use Ruby's built-in net/http library and REXML toolkit.

endpoint = Net::HTTP.new("somewhere.com", 443)
endpoint.use_ssl = true
response = endpoint.post("/identify", "guid=#{guid}")
    
document = REXML::Document.new(response.body)
@user_name = REXML::XPath.first(document, "/response/user/user_name").to_s
@first_name = REXML::XPath.first(document, "/response/user/first_name").to_s
@last_name = REXML::XPath.first(document, "/response/user/last_name").to_s

Nothing Rails-specific going on here: We simply post the user identifier over HTTP and hack through the contents of the response body with XPath, pulling out relevant information as we go.

Producing XML

Now consider an external system that wants to query our Rails application for the contents of a user's shopping cart, and it wants the response in a pre-existing XML format. Here's an example of what it expects:

<response code="success">
  <items>
    <item>
      <sku>123-456-789</sku>
      <description>The Complete Works of Shakespeare</description>
      <unit_price currency="usd">19.95</unit_price>
      <quantity>1</quantity>
    </item>
    <item>
      <sku>789-456-123</sku>
      <description>Historic Supreme Court Decisions</description>
      <unit_price currency="usd">29.95</unit_price>
      <quantity>2</quantity>
    </item>
  </items>
</response>

First we need a Rails controller with an action to serve as the endpoint. It sounds like a lot, but it's actually just a webservice_controller.rb file, for example, in the app/controllers directory. The controller file contains the following code:

class WebserviceController < ApplicationController

  def items
    @items = CartItem.find_all_by_user_guid(params[:id])
    @code = "success" # do the right thing here
    render :layout => false
  end

end

The items action queries the database for all cart items owned by the specified user and squirrels away the results in the @items instance variable. Requesting the action should result in an XML response being generated that contains those items and conforms to format of the example above.

It turns out REXML works dandy for consuming XML, but it's a bit heavy-handed for producing XML from within a Rails application. Better to use Builder templates—one of the two types of templates supported by Rails—to construct XML responses. A builder template lives in a .rxml file, so to match the controller action we created an items.rxml file in the app/views/webservice directory. The Builder template uses Ruby code to generate the XML that gets rendered by the controller's action.

xml.response(:code => @code) do 
  xml.items do
    @items.each do |item| 
      xml.item do
        xml.sku(item.product.sku)
        xml.description(item.product.description)
        xml.unit_price(item.product.list_price, :currency => "usd")
        xml.quantity(1)
      end
    end
  end 
end

Notice there are no angle brackets here. Builder does that dirty work for you. Each method name is converted into an XML tag of the same name. The first parameter to a method is used as the content of the tag, and all remaining parameters are a hash of attributes for the tag. Instance variables set in the action—@items and @code—are available in the template. For example, we loop through all of the items in the @items collection prepared in the controller's item action. (Imagine how easy it would be to generate an RSS feed of the cart's contents.)

The external system then simply issues an HTTP GET request to our Rails application.

http://somewhere.com/webservice/items/N8S7TU8YU8W8XTVS

Moving On

That's it! When it comes to RESTful web services, you can quickly glue systems together with Rails. As well, Rails supports SOAP and XML-RPC protocols. For more on that, I refer you to the excellent web services chapter of Agile Web Development with Rails.