The Depot Application
12.1 Generating the XML Feed
11.4 Iteration F4: Adding a Sidebar, More Administration
12.1 Generating the XML Feed
edit app/models/product.rb
class Product < ActiveRecord::Base
has_many :orders, :through => :line_items
has_many :line_items
# ...
rails generate controller info who_bought
/home/rubys/.rvm/ruby-1.9.1-p376/lib/ruby/site_ruby/1.9.1/rubygems.rb:14: warning: already initialized constant VERSION
/home/rubys/.rvm/ruby-1.9.1-p376/lib/ruby/site_ruby/1.9.1/rubygems.rb:14: warning: already initialized constant RubyGemsVersion
/home/rubys/.rvm/ruby-1.9.1-p376/lib/ruby/site_ruby/1.9.1/rubygems.rb:194: warning: already initialized constant MUTEX
/home/rubys/.rvm/ruby-1.9.1-p376/lib/ruby/site_ruby/1.9.1/rubygems.rb:196: warning: already initialized constant RubyGemsPackageVersion
/home/rubys/.rvm/ruby-1.9.1-p376/lib/ruby/site_ruby/1.9.1/rubygems.rb:202: warning: already initialized constant WIN_PATTERNS
/home/rubys/.rvm/ruby-1.9.1-p376/lib/ruby/site_ruby/1.9.1/rubygems.rb:1079: warning: already initialized constant MARSHAL_SPEC_DIR
/home/rubys/.rvm/ruby-1.9.1-p376/lib/ruby/site_ruby/1.9.1/rubygems.rb:1084: warning: already initialized constant YAML_SPEC_DIR
/home/rubys/.rvm/ruby-1.9.1-p376/lib/ruby/site_ruby/1.9.1/rubygems/version.rb:72: warning: already initialized constant VERSION_PATTERN
/home/rubys/.rvm/ruby-1.9.1-p376/lib/ruby/site_ruby/1.9.1/rubygems/requirement.rb:20: warning: already initialized constant OPS
/home/rubys/.rvm/ruby-1.9.1-p376/lib/ruby/site_ruby/1.9.1/rubygems/requirement.rb:30: warning: already initialized constant OP_RE
/home/rubys/.rvm/ruby-1.9.1-p376/lib/ruby/site_ruby/1.9.1/rubygems/version.rb:246: warning: already initialized constant Requirement
/home/rubys/.rvm/ruby-1.9.1-p376/lib/ruby/site_ruby/1.9.1/rubygems/dependency.rb:18: warning: already initialized constant TYPES
/home/rubys/.rvm/ruby-1.9.1-p376/lib/ruby/site_ruby/1.9.1/rubygems/platform.rb:171: warning: already initialized constant RUBY
/home/rubys/.rvm/ruby-1.9.1-p376/lib/ruby/site_ruby/1.9.1/rubygems/platform.rb:177: warning: already initialized constant CURRENT
/home/rubys/.rvm/ruby-1.9.1-p376/lib/ruby/site_ruby/1.9.1/rubygems/specification.rb:39: warning: already initialized constant NONEXISTENT_SPECIFICATION_VERSION
/home/rubys/.rvm/ruby-1.9.1-p376/lib/ruby/site_ruby/1.9.1/rubygems/specification.rb:50: warning: already initialized constant CURRENT_SPECIFICATION_VERSION
/home/rubys/.rvm/ruby-1.9.1-p376/lib/ruby/site_ruby/1.9.1/rubygems/specification.rb:56: warning: already initialized constant SPECIFICATION_VERSION_HISTORY
/home/rubys/.rvm/ruby-1.9.1-p376/lib/ruby/site_ruby/1.9.1/rubygems/specification.rb:72: warning: already initialized constant MARSHAL_FIELDS
/home/rubys/.rvm/ruby-1.9.1-p376/lib/ruby/site_ruby/1.9.1/rubygems/specification.rb:75: warning: already initialized constant TODAY
/home/rubys/.rvm/ruby-1.9.1-p376/lib/ruby/site_ruby/1.9.1/rubygems/source_index.rb:593: warning: already initialized constant Cache
/home/rubys/.rvm/ruby-1.9.1-p376/lib/ruby/site_ruby/1.9.1/rubygems/config_file.rb:14: warning: already initialized constant DEFAULT_BACKTRACE
/home/rubys/.rvm/ruby-1.9.1-p376/lib/ruby/site_ruby/1.9.1/rubygems/config_file.rb:15: warning: already initialized constant DEFAULT_BENCHMARK
/home/rubys/.rvm/ruby-1.9.1-p376/lib/ruby/site_ruby/1.9.1/rubygems/config_file.rb:16: warning: already initialized constant DEFAULT_BULK_THRESHOLD
/home/rubys/.rvm/ruby-1.9.1-p376/lib/ruby/site_ruby/1.9.1/rubygems/config_file.rb:17: warning: already initialized constant DEFAULT_VERBOSITY
/home/rubys/.rvm/ruby-1.9.1-p376/lib/ruby/site_ruby/1.9.1/rubygems/config_file.rb:18: warning: already initialized constant DEFAULT_UPDATE_SOURCES
/home/rubys/.rvm/ruby-1.9.1-p376/lib/ruby/site_ruby/1.9.1/rubygems/config_file.rb:24: warning: already initialized constant OPERATING_SYSTEM_DEFAULTS
/home/rubys/.rvm/ruby-1.9.1-p376/lib/ruby/site_ruby/1.9.1/rubygems/config_file.rb:30: warning: already initialized constant PLATFORM_DEFAULTS
/home/rubys/.rvm/ruby-1.9.1-p376/lib/ruby/site_ruby/1.9.1/rubygems/config_file.rb:53: warning: already initialized constant SYSTEM_WIDE_CONFIG_FILE
create app/controllers/info_controller.rb
invoke erb
create app/views/info
create app/views/info/who_bought.html.erb
invoke test_unit
create test/functional/info_controller_test.rb
invoke helper
create app/helpers/info_helper.rb
invoke test_unit
create test/unit/helpers/info_helper_test.rb
edit app/controllers/info_controller.rb
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
edit app/views/info/who_bought.xml.builder
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
curl --silent http://localhost:3000/info/who_bought/3
<order_list for_product="Pragmatic Version Control">
<order>
<name>Dave Thomas</name>
<email>customer@example.com</email>
</order>
</order_list>
curl --silent http://localhost:3000/info/who_bought/3
<order_list for_product="Pragmatic Version Control">
<order>
<name>Dave Thomas</name>
<email>customer@example.com</email>
</order>
</order_list>
sqlite3> select * from products
id = 2
title = Pragmatic Project Automation
description = <p>
<em>Pragmatic Project Automation</em> shows you how to improve the
consistency and repeatability of your project's procedures using
automation to reduce risk and errors.
</p>
<p>
Simply put, we're going to put this thing called a computer to work
for you doing the mundane (but important) project stuff. That means
you'll have more time and energy to do the really
exciting---and difficult---stuff, like writing quality code.
</p>
image_url = /images/auto.jpg
created_at = 2010-02-11 19:11:58.571133
updated_at = 2010-02-11 19:11:58.571133
price = 29.95
id = 3
title = Pragmatic Version Control
description = <p>
This book is a recipe-based approach to using Subversion that will
get you up and running quickly---and correctly. All projects need
version control: it's a foundational piece of any project's
infrastructure. Yet half of all project teams in the U.S. don't use
any version control at all. Many others don't use it well, and end
up experiencing time-consuming problems.
</p>
image_url = /images/svn.jpg
created_at = 2010-02-11 19:11:58.583948
updated_at = 2010-02-11 19:11:58.583948
price = 28.5
id = 4
title = Pragmatic Unit Testing (C#)
description = <p>
Pragmatic programmers use feedback to drive their development and
personal processes. The most valuable feedback you can get while
coding comes from unit testing.
</p>
<p>
Without good tests in place, coding can become a frustrating game of
"whack-a-mole." That's the carnival game where the player strikes at a
mechanical mole; it retreats and another mole pops up on the opposite side
of the field. The moles pop up and down so fast that you end up flailing
your mallet helplessly as the moles continue to pop up where you least
expect them.
</p>
image_url = /images/utc.jpg
created_at = 2010-02-11 19:11:58.586169
updated_at = 2010-02-11 19:11:58.586169
price = 27.75
sqlite3> select * from line_items
id = 1
product_id = 3
order_id = 1
quantity = 1
total_price = 28.5
created_at = 2010-02-11 19:12:56.698654
updated_at = 2010-02-11 19:12:56.698654
curl --silent http://localhost:3000/info/who_bought/3
<order_list for_product="Pragmatic Version Control">
<order>
<name>Dave Thomas</name>
<email>customer@example.com</email>
</order>
</order_list>
edit app/views/info/who_bought.html.erb
<h3>People Who Bought <%= @product.title %></h3>
<ul>
<% for order in @orders -%>
<li>
<%= mail_to order.email, order.name %>
</li>
<% end -%>
</ul>
edit app/controllers/info_controller.rb
class InfoController < ApplicationController
def who_bought
@product = Product.find(params[:id])
@orders = @product.orders
respond_to do |format|
format.html
format.xml { render :layout => false }
end
end
protected
def authorize
end
end
curl --silent -H "Accept: text/html" http://localhost:3000/info/who_bought/3
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!-- START:jit -->
<html>
<head>
<title>Pragprog Books Online Store</title>
<!-- START:stylesheet -->
<link href="/stylesheets/depot.css?1265915574" media="all" rel="stylesheet" type="text/css" />
<!-- END:stylesheet -->
<script src="/javascripts/prototype.js?1265915464" type="text/javascript"></script>
<script src="/javascripts/effects.js?1265915464" type="text/javascript"></script>
<script src="/javascripts/dragdrop.js?1265915464" type="text/javascript"></script>
<script src="/javascripts/controls.js?1265915464" type="text/javascript"></script>
<script src="/javascripts/rails.js?1265915464" type="text/javascript"></script>
<script src="/javascripts/application.js?1265915464" type="text/javascript"></script>
</head>
<!-- END:jit -->
<body id="store">
<div id="banner">
<img alt="Logo" src="/images/logo.png?1265915520" />
Pragmatic Bookshelf
</div>
<div id="columns">
<div id="side">
<!-- START:hidden_div -->
<!-- START_HIGHLIGHT -->
<!-- END_HIGHLIGHT -->
<!-- END:hidden_div -->
<a href="http://www....">Home</a><br />
<a href="http://www..../faq">Questions</a><br />
<a href="http://www..../news">News</a><br />
<a href="http://www..../contact">Contact</a><br />
<!-- START_HIGHLIGHT -->
<!-- END_HIGHLIGHT -->
</div>
<div id="main">
<!-- START:flash -->
<!-- END:flash -->
<h3>People Who Bought Pragmatic Version Control</h3>
<ul>
<li>
<a href="mailto:customer@example.com">Dave Thomas</a>
</li>
</ul>
</div>
</div>
</body>
</html>
curl --silent -H "Accept: application/xml" http://localhost:3000/info/who_bought/3
<order_list for_product="Pragmatic Version Control">
<order>
<name>Dave Thomas</name>
<email>customer@example.com</email>
</order>
</order_list>
cp app/controllers/info_controller.rb app/controllers/info_controller.save
edit app/controllers/info_controller.rb
class InfoController < ApplicationController
def who_bought
@product = Product.find(params[:id])
@orders = @product.orders
respond_to do |format|
format.html
format.xml { render :layout => false ,
:xml => @product.to_xml(:include => :orders) }
end
end
protected
def authorize
end
end
curl --silent http://localhost:3000/info/who_bought/3.xml
<?xml version="1.0" encoding="UTF-8"?>
<product>
<created-at type="datetime">2010-02-11T19:11:58Z</created-at>
<description><p>
This book is a recipe-based approach to using Subversion that will
get you up and running quickly---and correctly. All projects need
version control: it's a foundational piece of any project's
infrastructure. Yet half of all project teams in the U.S. don't use
any version control at all. Many others don't use it well, and end
up experiencing time-consuming problems.
</p></description>
<id type="integer">3</id>
<image-url>/images/svn.jpg</image-url>
<price type="decimal">28.5</price>
<title>Pragmatic Version Control</title>
<updated-at type="datetime">2010-02-11T19:11:58Z</updated-at>
<orders type="array">
<order>
<address>123 Main St</address>
<created-at type="datetime">2010-02-11T19:12:56Z</created-at>
<email>customer@example.com</email>
<id type="integer">1</id>
<name>Dave Thomas</name>
<pay-type>check</pay-type>
<updated-at type="datetime">2010-02-11T19:12:56Z</updated-at>
</order>
</orders>
</product>
edit app/views/info/who_bought.atom.builder
atom_feed do |feed|
feed.title "Who bought #{@product.title}"
feed.updated @orders.first.created_at
for order in @orders
feed.entry(order) do |entry|
entry.title "Order #{order.id}"
entry.summary :type => 'xhtml' do |xhtml|
xhtml.p "Shipped to #{order.address}"
xhtml.table do
xhtml.tr do
xhtml.th 'Product'
xhtml.th 'Quantity'
xhtml.th 'Total Price'
end
for item in order.line_items
xhtml.tr do
xhtml.td item.product.title
xhtml.td item.quantity
xhtml.td number_to_currency item.total_price
end
end
xhtml.tr do
xhtml.th 'total', :colspan => 2
xhtml.th number_to_currency \
order.line_items.map(&:total_price).sum
end
end
xhtml.p "Paid by #{order.pay_type}"
end
entry.author do |author|
entry.name order.name
entry.email order.email
end
end
end
end
edit app/controllers/info_controller.rb
class InfoController < ApplicationController
def who_bought
@product = Product.find(params[:id])
@orders = @product.orders
respond_to do |format|
format.html
format.atom { render :layout => false }
format.xml { render :layout => false ,
:xml => @product.to_xml(:include => :orders) }
end
end
protected
def authorize
end
end
curl --silent http://localhost:3000/info/who_bought/3.atom
<?xml version="1.0" encoding="UTF-8"?>
<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom">
<id>tag:localhost,2005:/info/who_bought/3</id>
<link rel="alternate" type="text/html" href="http://localhost:3000"/>
<link rel="self" type="application/atom+xml" href="http://localhost:3000/info/who_bought/3.atom"/>
<title>Who bought Pragmatic Version Control</title>
<updated>2010-02-11T19:12:56Z</updated>
<entry>
<id>tag:localhost,2005:Order/1</id>
<published>2010-02-11T19:12:56Z</published>
<updated>2010-02-11T19:12:56Z</updated>
<link rel="alternate" type="text/html" href="http://localhost:3000/orders/1"/>
<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>Pragmatic Version Control</td>
<td>1</td>
<td>$28.50</td>
</tr>
<tr>
<th colspan="2">total</th>
<th>$28.50</th>
</tr>
</table>
<p>Paid by check</p>
</div>
</summary>
<author>
<name>Dave Thomas</name>
<email>customer@example.com</email>
</author>
</entry>
</feed>
edit app/controllers/info_controller.rb
class InfoController < ApplicationController
def who_bought
@product = Product.find(params[:id])
@orders = @product.orders
respond_to do |format|
format.html
format.atom { render :layout => false }
format.xml { render :layout => false ,
:xml => @product.to_xml(:include => :orders) }
format.json { render :layout => false ,
:json => @product.to_json(:include => :orders) }
end
end
protected
def authorize
end
end
curl --silent -H "Accept: application/json" http://localhost:3000/info/who_bought/3
{"created_at":"2010-02-11T19:11:58Z","description":"<p>\n This book is a recipe-based approach to using Subversion that will \n get you up and running quickly---and correctly. All projects need\n version control: it's a foundational piece of any project's \n infrastructure. Yet half of all project teams in the U.S. don't use\n any version control at all. Many others don't use it well, and end \n up experiencing time-consuming problems.\n </p>","id":3,"image_url":"/images/svn.jpg","price":28.5,"title":"Pragmatic Version Control","updated_at":"2010-02-11T19:11:58Z","orders":[{"address":"123 Main St","created_at":"2010-02-11T19:12:56Z","email":"customer@example.com","id":1,"name":"Dave Thomas","pay_type":"check","updated_at":"2010-02-11T19:12:56Z"}]}
mv app/controllers/info_controller.save app/controllers/info_controller.rb
rake doc:app
rm -r doc/app
Generating HTML...
(in /home/rubys/git/awdwr/work-191/depot)
doc/README_FOR_APP:
app/helpers/orders_helper.rb: M
app/helpers/admin_helper.rb: M
app/helpers/users_helper.rb: M
app/helpers/store_helper.rb: Mm
app/helpers/application_helper.rb: M
app/helpers/products_helper.rb: M
app/helpers/info_helper.rb: M
app/helpers/line_items_helper.rb: M
app/controllers/store_controller.rb: Cmmmmmmmm
app/controllers/application_controller.rb: Cm
app/controllers/users_controller.rb: Cmmmmmmm
app/controllers/products_controller.rb: Cmmmmmmm
app/controllers/info_controller.rb: Cmm
app/controllers/orders_controller.rb: Cmmmmmmm
app/controllers/line_items_controller.rb: Cmmmmmmm
app/controllers/admin_controller.rb: Cmmm
app/models/cart.rb: Cmmmm
app/models/user.rb: Cmmmmmmm
app/models/line_item.rb: Cm
app/models/cart_item.rb: Cmmmm
app/models/product.rb: Cmm
app/models/order.rb: Cm
Files: 23
Classes: 14
Modules: 8
Methods: 62
Elapsed: 0.8s
rake stats
(in /home/rubys/git/awdwr/work-191/depot)
+----------------------+-------+-------+---------+---------+-----+-------+
| Name | Lines | LOC | Classes | Methods | M/C | LOC/M |
+----------------------+-------+-------+---------+---------+-----+-------+
| Controllers | 503 | 337 | 8 | 42 | 5 | 6 |
| Helpers | 22 | 22 | 0 | 1 | 0 | 20 |
| Models | 231 | 130 | 6 | 19 | 3 | 4 |
| Libraries | 0 | 0 | 0 | 0 | 0 | 0 |
| Integration tests | 0 | 0 | 0 | 0 | 0 | 0 |
| Functional tests | 204 | 162 | 7 | 0 | 0 | 0 |
| Unit tests | 60 | 45 | 11 | 0 | 0 | 0 |
+----------------------+-------+-------+---------+---------+-----+-------+
| Total | 1020 | 696 | 32 | 62 | 1 | 9 |
+----------------------+-------+-------+---------+---------+-----+-------+
Code LOC: 489 Test LOC: 207 Code to Test Ratio: 1:0.4