This post is part of a series of reviews on the book Agile Web Development With Rails. Check out the Introduction post for a full table of contents along with some initial notes.

In this task we’ll take a peek at how to genereate XML feed from our application. We’ll start by generating a link that will tell us who bought a certain product, so it would be something link http://mystore.com/info/who_bought/product_id
Notice that’s the opposite direction of our application structure, you can’t access a line item from a product, but you can acess a product from a line item, so how do we tell Rails to do it both ways? By adding one line of code to the product model:

class Product < ActiveRecord::Base
  has_many:orders, :through=>:line_items
  has_many:line_items
#rest of the code

Now it knows that a product has orders, but it has to go through the line items database table first to get to those orders. We’re all set to generate the info controller with the who_bought action now (look back at the link mentioned). To the command propmt we go:

ruby script/generate controller info who_bought

With all files created, time to edit that who_bought action (and override the authorize method):

class InfoController < ApplicationController

  def who_bought
    @product=Product.find(params[:id])
    @orders=@product.orders
    respond_to do |format|
      format.xml{ render :layout => false }
    end
  end

  protected

  def authorize
  end
end

Who_bought is basically returning the orders from a given product ID. Notice it will format XML requests with no layout, so it will just be pure XML. Now we’ll need to implement a template that will return XML data to XML requests, and we can do that by creating an XML builder. Create a who_bought.xml.builder file in the info view directory and code this in:

xml.order_list(:for_product => @product.title) do
  for o in @orders
    xml.order do
	    xml.name(o.name)
	    xml.email(o.email)
    end
  end
end

This builder will render whenever a XML request is made to the who_bought action, so, as a view file, it can use all the instance variables from the info controller. As you can see, that’s just pure Ruby code that’s telling the XML object to do a few things:
– Create an orderlist tag with a for_product attribute filled in with the product title data.
– Create an order tag (with no data in) with 2 other tags embedded in it: name (with the order name data in it) and email (with the order email data in it).

To add an attribute all you do is add a hash collection to the tag you want, to add data you just pass it as a parameter and to nest other tags within a tag you just put them in a block inside the outer tag. To check this format out, just access the method and pass an ID with a .xml extension in the end, such as: http://localhost:3000/info/who_bought/1.xml

It’d be nice to render HTML case somebody accesses the page normally, with a GET request, so let’s create a simple view file that will supply mailing links to all the people who ordered a specific product (e-mail marketing is one of the reasons you should add XML feed to your applications, and that’s why we’re mentioning mail_to here). Edit your who_bought.html.erb file with this:

<h3>People Who Bought <%= @product.title %></h3>
<ul>
  <% for order in @orders -%>
  <li>
    <%= mail_to order.email, order.name %>
  </li>
  <% end -%>
</ul>

Since now we have some HTML to render, we need to tell the controller that he should render HTML case that’s what the HTTP request wants:

def who_bought
    @product=Product.find(params[:id])
    @orders=@product.orders
    respond_to do |format|
      format.html
      format.xml{ render :layout => false }
    end
  end

We have 2 ways of generating XML, one is through the builder which we already did, and another one is through the to_xml method that every model object has, which is easier to implement but not easy to maintain, edit the who_bought method again so that it will generate XML automatically:

def who_bought
    @product=Product.find(params[:id])
    @orders=@product.orders
    respond_to do |format|
      format.html
      format.xml{ render :xml=> @product.to_xml(:include => :orders) }
    end
  end

Now all the product information will be dumped and rendered when a XML request comes. As said, this is hard to maintain because you can’t really set the XML the way you want, for that we use the builder template, but it’s always nice to know an easier way to do it cause you don’t mind just dumping everything.

Before finishing off our application, let’s generate its documentation. Rails turns the hassle of documenting everything into one single line of code in the command propmt, but before you do that, edit the README_FOR_APP file in the ‘doc’ directory with some description about this application, because it will show on the first page of the generated documentation. Now, to the code:

rake doc:app

And we’re done coding the application. But don’t think it’s over, on the next task and last task we’ll be doing one of the most important things in application development: testing. See you then.