Agile Web Development with Rails, Edition 4

Agile Web Development with Rails, Edition 4

13.1 Iteration H1: Email Notifications 12.3 Iteration G3: Pagination

12.4 Playtime

Add the xml format to the controller

edit app/controllers/products_controller.rb
  def who_bought
    @product = Product.find(params[:id])
    respond_to do |format|
      format.xml { render :xml => @product }
      format.atom
    end
  end

Fetch the XML, see that there are no orders there

curl --silent --user dave:secret http://localhost:3000/products/2/who_bought.atom
<?xml version="1.0" encoding="UTF-8"?>
<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom">
  <id>tag:localhost,2005:/products/2/who_bought</id>
  <link type="text/html" href="http://localhost:3000" rel="alternate"/>
  <link type="application/atom+xml" href="http://localhost:3000/products/2/who_bought.atom" rel="self"/>
  <title>Who bought CoffeeScript</title>
  <updated>2012-06-29T16:33:20Z</updated>
  <entry>
    <id>tag:localhost,2005:Order/1</id>
    <published>2012-06-29T16:33:20Z</published>
    <updated>2012-06-29T16:33:20Z</updated>
    <link type="text/html" href="http://localhost:3000/orders/1" rel="alternate"/>
    <title>Order 1</title>
    <summary type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
        <p>Shipped to 123 Main St</p>
        <table>
          <tr>
            <th>Product</th>
            <th>Quantity</th>
            <th>Total Price</th>
          </tr>
          <tr>
            <td>CoffeeScript</td>
            <td>1</td>
            <td>$36.00</td>
          </tr>
          <tr>
            <th colspan="2">total</th>
            <th>$36.00</th>
          </tr>
        </table>
        <p>Paid by Check</p>
      </div>
    </summary>
    <author>
      <name>Dave Thomas</name>
      <email>customer@example.com</email>
    </author>
  </entry>
</feed>

Include "orders" in the response

edit app/controllers/products_controller.rb
  def who_bought
    @product = Product.find(params[:id])
    respond_to do |format|
      format.xml { render :xml => @product.to_xml(:include => :orders) }
      format.atom
    end
  end

Fetch the xml, see that the orders are included

curl --silent --user dave:secret http://localhost:3000/products/2/who_bought.xml
<?xml version="1.0" encoding="UTF-8"?>
<product>
  <price type="decimal">36.0</price>
  <created-at type="datetime">2012-06-29T16:29:59Z</created-at>
  <image-url>/images/cs.jpg</image-url>
  <title>CoffeeScript</title>
  <updated-at type="datetime">2012-06-29T16:29:59Z</updated-at>
  <id type="integer">2</id>
  <description>&lt;p&gt;
        CoffeeScript is JavaScript done right. It provides all of JavaScript's
	functionality wrapped in a cleaner, more succinct syntax. In the first
	book on this exciting new language, CoffeeScript guru Trevor Burnham
	shows you how to hold onto all the power and flexibility of JavaScript
	while writing clearer, cleaner, and safer code.
      &lt;/p&gt;</description>
  <orders type="array">
    <order>
      <address>123 Main St</address>
      <name>Dave Thomas</name>
      <created-at type="datetime">2012-06-29T16:33:20Z</created-at>
      <updated-at type="datetime">2012-06-29T16:33:20Z</updated-at>
      <pay-type>Check</pay-type>
      <id type="integer">1</id>
      <email>customer@example.com</email>
    </order>
  </orders>
</product>

Define an HTML view

edit app/views/products/who_bought.html.erb
<h3>People Who Bought <%= @product.title %></h3>
 
<ul>
  <% for order in @product.orders %>
    <li>
      <%= mail_to order.email, order.name %>
    </li>
  <% end %>
</ul>

Add the html format to the controller

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

See the (raw) HTML

curl --silent --user dave:secret http://localhost:3000/products/2/who_bought
<!-- START:head -->
<!DOCTYPE html>
<html>
<head>
  <title>Pragprog Books Online Store</title>
  <link href="/stylesheets/depot.css?1340987593" media="screen" rel="stylesheet" type="text/css" />
  <script src="/javascripts/depot.js" type="text/javascript"></script>
  <meta name="csrf-param" content="authenticity_token"/>
<meta name="csrf-token" content="rPQ&#47;&#47;3JXVl1dJ2UNG6Dkge5PRGpTpHki3xkxQ7vNvsg="/>
</head>
<!-- END:head -->
<body class="products">
  <div id="banner">
    <img alt="Logo" src="/images/logo.png?1340987396" />
    Pragmatic Bookshelf
  </div>
  <div id="columns">
    <div id="side">
<!-- START_HIGHLIGHT -->
      <!-- START:hidden_div -->
<!-- START_HIGHLIGHT -->
<!-- END_HIGHLIGHT -->
    <!-- END:hidden_div -->
 
<!-- END_HIGHLIGHT -->
      <ul>
        <li><a href="http://www....">Home</a></li>
        <li><a href="http://www..../faq">Questions</a></li>
        <li><a href="http://www..../news">News</a></li>
        <li><a href="http://www..../contact">Contact</a></li>
      </ul>
    </div>
    <div id="main">
      <h3>People Who Bought CoffeeScript</h3>
 
<ul>
    <li>
      <a href="mailto:customer@example.com">Dave Thomas</a>
    </li>
</ul>
 
    </div>
  </div>
</body>
</html>

Anything that XML can do, JSON can too...

edit app/controllers/products_controller.rb
  def who_bought
    @product = Product.find(params[:id])
    respond_to do |format|
      format.html
      format.xml { render :xml => @product.to_xml(:include => :orders) }
      format.atom
      format.json { render :json => @product.to_json(:include => :orders) }
    end
  end

Fetch the data in JSON format

curl --silent --user dave:secret http://localhost:3000/products/2/who_bought.json
{"product":{"price":"36.0","created_at":"2012-06-29T16:29:59Z","image_url":"/images/cs.jpg","title":"CoffeeScript","updated_at":"2012-06-29T16:29:59Z","orders":[{"name":"Dave Thomas","address":"123 Main St","created_at":"2012-06-29T16:33:20Z","updated_at":"2012-06-29T16:33:20Z","pay_type":"Check","id":1,"email":"customer@example.com"}],"id":2,"description":"<p>\n        CoffeeScript is JavaScript done right. It provides all of JavaScript's\n\tfunctionality wrapped in a cleaner, more succinct syntax. In the first\n\tbook on this exciting new language, CoffeeScript guru Trevor Burnham\n\tshows you how to hold onto all the power and flexibility of JavaScript\n\twhile writing clearer, cleaner, and safer code.\n      </p>"}}

Customize the xml

edit app/views/products/who_bought.xml.builder
xml.order_list(:for_product => @product.title) do
  for o in @product.orders
    xml.order do
      xml.name(o.name)
      xml.email(o.email)
    end
  end
end

Change the rendering to use templates

edit app/controllers/products_controller.rb
  def who_bought
    @product = Product.find(params[:id])
    respond_to do |format|
      format.html
      format.xml
      format.atom
      format.json { render :json => @product.to_json(:include => :orders) }
    end
  end

Fetch the (much streamlined) XML

curl --silent --user dave:secret http://localhost:3000/products/2/who_bought.xml
<order_list for_product="CoffeeScript">
  <order>
    <name>Dave Thomas</name>
    <email>customer@example.com</email>
  </order>
</order_list>

Verify that the tests still pass

rake test
Loaded suite /home/rubys/.rvm/gems/ruby-1.8.7-p352/gems/rake-0.9.2.2/lib/rake/rake_test_loader
Started
.........
Finished in 0.390543 seconds.
 
9 tests, 30 assertions, 0 failures, 0 errors
Loaded suite /home/rubys/.rvm/gems/ruby-1.8.7-p352/gems/rake-0.9.2.2/lib/rake/rake_test_loader
Started
.................................
Finished in 1.478945 seconds.
 
33 tests, 55 assertions, 0 failures, 0 errors

Commit

git commit -a -m "Orders"
[master 5f981cc] Orders
 7 files changed, 82 insertions(+), 28 deletions(-)
git tag iteration-g

13.1 Iteration H1: Email Notifications 12.3 Iteration G3: Pagination