<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Rails on the Run &#187; xml builder</title>
	<atom:link href="http://railsontherun.com/tag/xml-builder/feed/" rel="self" type="application/rss+xml" />
	<link>http://railsontherun.com</link>
	<description>Rails experiments by Matt Aimonetti</description>
	<lastBuildDate>Tue, 23 Feb 2010 07:28:00 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<xhtml:meta xmlns:xhtml="http://www.w3.org/1999/xhtml" name="robots" content="noindex" />
		<item>
		<title>How to test a XML builder view</title>
		<link>http://railsontherun.com/2007/10/31/how-to-test-a-xml-builder-view/</link>
		<comments>http://railsontherun.com/2007/10/31/how-to-test-a-xml-builder-view/#comments</comments>
		<pubDate>Wed, 31 Oct 2007 08:32:00 +0000</pubDate>
		<dc:creator>Matt Aimonetti</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[BDD]]></category>
		<category><![CDATA[hpricot]]></category>
		<category><![CDATA[parser]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[RSpec]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[TDD]]></category>
		<category><![CDATA[test]]></category>
		<category><![CDATA[view]]></category>
		<category><![CDATA[xml builder]]></category>

		<guid isPermaLink="false">http://railsontherun.com/2007/10/31/how-to-test-a-xml-builder-view</guid>
		<description><![CDATA[As a good Rubyist, I do TDD and even BDD. 
Since I&#8217;ve started using RSpec I&#8217;ve started writing tests against my views. RSpec makes things really easy and I&#8217;ve been enjoying testing my views.
I&#8217;m not the only one having fun, check this great post from Mr Planet Argon aka Robby Russel
Recently I was working on [...]]]></description>
			<content:encoded><![CDATA[<p>As a good Rubyist, I do <a href="http://en.wikipedia.org/wiki/Test-driven_development">TDD</a> and even <a href="http://en.wikipedia.org/wiki/Behavior_driven_development">BDD</a>. </p>
<p>Since I&#8217;ve started using <a href="http://rspec.rubyforge.org/">RSpec</a> I&#8217;ve started writing tests against my views. RSpec makes things really easy and I&#8217;ve been enjoying testing my views.</p>
<p>I&#8217;m not the only one having fun, check <a href="http://www.robbyonrails.com/articles/2007/08/02/spec-your-views">this great post</a> from Mr <a href="http://www.planetargon.com/">Planet Argon</a> aka <a href="http://www.robbyonrails.com">Robby Russel</a></p>
<p>Recently I was working on implementing some <a href="http://railsontherun.com/2007/10/4/sexy-charts-in-less-than-5-minutes">Sexy Charts</a> and I was using a XML builder to create an XML view of for a controller. Since I wanted to be a good Rails Ninja and obey the BDD rules, I figured I needed to test my XML view. Making sure that the nodes and the attributes were properly created. Turned out that is wasn&#8217;t too hard, there was many options but none were very well documented so I decided to write this quick tutorial.</p>
<h2>UPDATE 31 Oct 2007: After a comment from <a href="http://joshknowles.com">Josh Knowles</a>, I updated the tests to test with have_tags (built in RSpec) and hpricot.</h2>
<h2>Hpricot</h2>
<p><a href="http://code.whytheluckystiff.net/hpricot/">hpricot</a> is a awesome HTML parser perfect for <a href="http://en.wikipedia.org/wiki/Screen_scraping">screen scraping</a>. But wait, there&#8217;s more to this awesome library, <a href="http://code.whytheluckystiff.net/hpricot/wiki/HpricotXML">hpricot can also parse XML</a>.</p>
<p>If you watched the excellent <a href="http://peepcode.com/products">RSpec peepcasts</a> you probably noticed that <a href="http://topfunky.com/">topfunky</a> aka <a href="http://geoffreygrosenbach.com/">Geoffrey Grosenbach</a> uses hpricot to test a remote API.</p>
<p>In our case, we&#8217;ll use hpricot to test that our generated XML follows our expectations.</p>
<h2>XML Builder + RSpec</h2>
<p>Let&#8217;s write a quick test to make sure our controller uses a XML builder view:</p>
<table class="CodeRay">
<tr>
<td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }">
<pre>1<tt>
</tt>2<tt>
</tt>3<tt>
</tt>4<tt>
</tt><strong>5</strong><tt>
</tt>6<tt>
</tt>7<tt>
</tt>8<tt>
</tt>9<tt>
</tt><strong>10</strong><tt>
</tt>11<tt>
</tt>12<tt>
</tt>13<tt>
</tt>14<tt>
</tt><strong>15</strong><tt>
</tt>16<tt>
</tt>17<tt>
</tt></pre>
</td>
<td class="code">
<pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">  describe <span class="co">AveragesController</span>, <span class="s"><span class="dl">&quot;</span><span class="k">handling GET /averages.xml</span><span class="dl">&quot;</span></span> <span class="r">do</span><tt>
</tt><tt>
</tt>    before <span class="r">do</span><tt>
</tt>      <span class="co">Average</span>.stub!(<span class="sy">:find</span>).and_return(<span class="iv">@average</span>)<tt>
</tt>    <span class="r">end</span><tt>
</tt>  <tt>
</tt>    <span class="r">def</span> <span class="fu">do_get</span><tt>
</tt>      <span class="iv">@request</span>.env[<span class="s"><span class="dl">&quot;</span><span class="k">HTTP_ACCEPT</span><span class="dl">&quot;</span></span>] = <span class="s"><span class="dl">&quot;</span><span class="k">application/xml</span><span class="dl">&quot;</span></span><tt>
</tt>      get <span class="sy">:index</span><tt>
</tt>    <span class="r">end</span><tt>
</tt>  <tt>
</tt>    it <span class="s"><span class="dl">&quot;</span><span class="k">should render the action using the XML builder</span><span class="dl">&quot;</span></span> <span class="r">do</span><tt>
</tt>      do_get<tt>
</tt>      response.should render_template(<span class="s"><span class="dl">'</span><span class="k">averages/index.xml.builder</span><span class="dl">'</span></span>)<tt>
</tt>    <span class="r">end</span><tt>
</tt><tt>
</tt>  <span class="r">end</span></pre>
</td>
</tr>
</table>
<p>To make this example pass, we need to modify our rspec generated controller.</p>
<table class="CodeRay">
<tr>
<td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }">
<pre>1<tt>
</tt>2<tt>
</tt>3<tt>
</tt>4<tt>
</tt><strong>5</strong><tt>
</tt>6<tt>
</tt>7<tt>
</tt>8<tt>
</tt></pre>
</td>
<td class="code">
<pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">  <span class="r">def</span> <span class="fu">index</span><tt>
</tt>    <span class="iv">@averages</span> = <span class="co">Average</span>.find(<span class="sy">:all</span>)<tt>
</tt>  <tt>
</tt>    respond_to <span class="r">do</span> |format|<tt>
</tt>      format.html <span class="c"># index.html.erb</span><tt>
</tt>      format.xml  { render <span class="sy">:action</span> =&gt; <span class="s"><span class="dl">&quot;</span><span class="k">index.xml.builder</span><span class="dl">&quot;</span></span>, <span class="sy">:layout</span> =&gt; <span class="pc">false</span> }<tt>
</tt>    <span class="r">end</span><tt>
</tt>  <span class="r">end</span></pre>
</td>
</tr>
</table>
<p>(Please note that I&#8217;m using Rails 2.0 and that&#8217;s why I&#8217;m not using a .rxml view)</p>
<p>Here is what our XML file should end up looking like:</p>
<table class="CodeRay">
<tr>
<td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }">
<pre>1<tt>
</tt>2<tt>
</tt>3<tt>
</tt>4<tt>
</tt><strong>5</strong><tt>
</tt>6<tt>
</tt>7<tt>
</tt>8<tt>
</tt>9<tt>
</tt><strong>10</strong><tt>
</tt>11<tt>
</tt>12<tt>
</tt>13<tt>
</tt>14<tt>
</tt><strong>15</strong><tt>
</tt>16<tt>
</tt>17<tt>
</tt>18<tt>
</tt>19<tt>
</tt><strong>20</strong><tt>
</tt>21<tt>
</tt>22<tt>
</tt>23<tt>
</tt>24<tt>
</tt><strong>25</strong><tt>
</tt>26<tt>
</tt>27<tt>
</tt>28<tt>
</tt>29<tt>
</tt><strong>30</strong><tt>
</tt>31<tt>
</tt>32<tt>
</tt>33<tt>
</tt>34<tt>
</tt><strong>35</strong><tt>
</tt>36<tt>
</tt>37<tt>
</tt>38<tt>
</tt>39<tt>
</tt><strong>40</strong><tt>
</tt>41<tt>
</tt>42<tt>
</tt>43<tt>
</tt>44<tt>
</tt><strong>45</strong><tt>
</tt>46<tt>
</tt>47<tt>
</tt>48<tt>
</tt>49<tt>
</tt><strong>50</strong><tt>
</tt>51<tt>
</tt>52<tt>
</tt>53<tt>
</tt>54<tt>
</tt></pre>
</td>
<td class="code">
<pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">  &lt;<span class="i">?x</span>ml version=<span class="s"><span class="dl">&quot;</span><span class="k">1.0</span><span class="dl">&quot;</span></span> encoding=<span class="s"><span class="dl">&quot;</span><span class="k">UTF-8</span><span class="dl">&quot;</span></span>?&gt;<tt>
</tt>  &lt;chart&gt;<tt>
</tt>    &lt;series&gt;<tt>
</tt>      &lt;value xid=<span class="s"><span class="dl">&quot;</span><span class="k">0</span><span class="dl">&quot;</span></span>&gt;<span class="co">January</span>&lt;<span class="rx"><span class="dl">/</span><span class="k">value&gt;<tt>
</tt>      &lt;value xid=&quot;1&quot;&gt;February&lt;</span><span class="dl">/</span></span>value&gt;<tt>
</tt>      &lt;value xid=<span class="s"><span class="dl">&quot;</span><span class="k">2</span><span class="dl">&quot;</span></span>&gt;<span class="co">March</span>&lt;<span class="rx"><span class="dl">/</span><span class="k">value&gt;<tt>
</tt>      &lt;value xid=&quot;3&quot;&gt;April&lt;</span><span class="dl">/</span></span>value&gt;<tt>
</tt>      &lt;value xid=<span class="s"><span class="dl">&quot;</span><span class="k">4</span><span class="dl">&quot;</span></span>&gt;<span class="co">May</span>&lt;<span class="rx"><span class="dl">/</span><span class="k">value&gt;<tt>
</tt><tt>
</tt>      &lt;value xid=&quot;5&quot;&gt;June&lt;</span><span class="dl">/</span></span>value&gt;<tt>
</tt>      &lt;value xid=<span class="s"><span class="dl">&quot;</span><span class="k">6</span><span class="dl">&quot;</span></span>&gt;<span class="co">July</span>&lt;<span class="rx"><span class="dl">/</span><span class="k">value&gt;<tt>
</tt>      &lt;value xid=&quot;7&quot;&gt;August&lt;</span><span class="dl">/</span></span>value&gt;<tt>
</tt>      &lt;value xid=<span class="s"><span class="dl">&quot;</span><span class="k">8</span><span class="dl">&quot;</span></span>&gt;<span class="co">September</span>&lt;<span class="rx"><span class="dl">/</span><span class="k">value&gt;<tt>
</tt>      &lt;value xid=&quot;9&quot;&gt;October&lt;</span><span class="dl">/</span></span>value&gt;<tt>
</tt>      &lt;value xid=<span class="s"><span class="dl">&quot;</span><span class="k">10</span><span class="dl">&quot;</span></span>&gt;<span class="co">November</span>&lt;<span class="rx"><span class="dl">/</span><span class="k">value&gt;<tt>
</tt><tt>
</tt>      &lt;value xid=&quot;11&quot;&gt;December&lt;</span><span class="dl">/</span></span>value&gt;<tt>
</tt>    &lt;<span class="rx"><span class="dl">/</span><span class="k">series&gt;<tt>
</tt>    &lt;graphs&gt;<tt>
</tt>      &lt;graph fill_alpha=&quot;50&quot; color=&quot;#FF0000&quot; fill_color=&quot;#CC0000&quot; title=&quot;high&quot;&gt;<tt>
</tt>        &lt;value xid=&quot;0&quot;&gt;65.1&lt;</span><span class="dl">/</span></span>value&gt;<tt>
</tt>        &lt;value xid=<span class="s"><span class="dl">&quot;</span><span class="k">1</span><span class="dl">&quot;</span></span>&gt;<span class="fl">65.7</span>&lt;<span class="rx"><span class="dl">/</span><span class="k">value&gt;<tt>
</tt>        &lt;value xid=&quot;2&quot;&gt;64.9&lt;</span><span class="dl">/</span></span>value&gt;<tt>
</tt><tt>
</tt>        &lt;value xid=<span class="s"><span class="dl">&quot;</span><span class="k">3</span><span class="dl">&quot;</span></span>&gt;<span class="fl">66.7</span>&lt;<span class="rx"><span class="dl">/</span><span class="k">value&gt;<tt>
</tt>        &lt;value xid=&quot;4&quot;&gt;67.1&lt;</span><span class="dl">/</span></span>value&gt;<tt>
</tt>        &lt;value xid=<span class="s"><span class="dl">&quot;</span><span class="k">5</span><span class="dl">&quot;</span></span>&gt;<span class="fl">69.3</span>&lt;<span class="rx"><span class="dl">/</span><span class="k">value&gt;<tt>
</tt>        &lt;value xid=&quot;6&quot;&gt;73.0&lt;</span><span class="dl">/</span></span>value&gt;<tt>
</tt>        &lt;value xid=<span class="s"><span class="dl">&quot;</span><span class="k">7</span><span class="dl">&quot;</span></span>&gt;<span class="fl">74.8</span>&lt;<span class="rx"><span class="dl">/</span><span class="k">value&gt;<tt>
</tt>        &lt;value xid=&quot;8&quot;&gt;75.4&lt;</span><span class="dl">/</span></span>value&gt;<tt>
</tt><tt>
</tt>        &lt;value xid=<span class="s"><span class="dl">&quot;</span><span class="k">9</span><span class="dl">&quot;</span></span>&gt;<span class="fl">73.4</span>&lt;<span class="rx"><span class="dl">/</span><span class="k">value&gt;<tt>
</tt>        &lt;value xid=&quot;10&quot;&gt;68.9&lt;</span><span class="dl">/</span></span>value&gt;<tt>
</tt>        &lt;value xid=<span class="s"><span class="dl">&quot;</span><span class="k">11</span><span class="dl">&quot;</span></span>&gt;<span class="fl">65.3</span>&lt;<span class="rx"><span class="dl">/</span><span class="k">value&gt;<tt>
</tt>      &lt;</span><span class="dl">/</span></span>graph&gt;<tt>
</tt>      &lt;graph fill_alpha=<span class="s"><span class="dl">&quot;</span><span class="k">50</span><span class="dl">&quot;</span></span> color=<span class="s"><span class="dl">&quot;</span><span class="k">#0000CC</span><span class="dl">&quot;</span></span> fill_color=<span class="s"><span class="dl">&quot;</span><span class="k">#0000CC</span><span class="dl">&quot;</span></span> title=<span class="s"><span class="dl">&quot;</span><span class="k">low</span><span class="dl">&quot;</span></span>&gt;<tt>
</tt>        &lt;value xid=<span class="s"><span class="dl">&quot;</span><span class="k">0</span><span class="dl">&quot;</span></span>&gt;<span class="fl">48.9</span>&lt;<span class="rx"><span class="dl">/</span><span class="k">value&gt;<tt>
</tt>        &lt;value xid=&quot;1&quot;&gt;50.7&lt;</span><span class="dl">/</span></span>value&gt;<tt>
</tt><tt>
</tt>        &lt;value xid=<span class="s"><span class="dl">&quot;</span><span class="k">2</span><span class="dl">&quot;</span></span>&gt;<span class="fl">52.9</span>&lt;<span class="rx"><span class="dl">/</span><span class="k">value&gt;<tt>
</tt>        &lt;value xid=&quot;3&quot;&gt;55.6&lt;</span><span class="dl">/</span></span>value&gt;<tt>
</tt>        &lt;value xid=<span class="s"><span class="dl">&quot;</span><span class="k">4</span><span class="dl">&quot;</span></span>&gt;<span class="fl">59.2</span>&lt;<span class="rx"><span class="dl">/</span><span class="k">value&gt;<tt>
</tt>        &lt;value xid=&quot;5&quot;&gt;61.9&lt;</span><span class="dl">/</span></span>value&gt;<tt>
</tt>        &lt;value xid=<span class="s"><span class="dl">&quot;</span><span class="k">6</span><span class="dl">&quot;</span></span>&gt;<span class="fl">65.7</span>&lt;<span class="rx"><span class="dl">/</span><span class="k">value&gt;<tt>
</tt>        &lt;value xid=&quot;7&quot;&gt;67.3&lt;</span><span class="dl">/</span></span>value&gt;<tt>
</tt><tt>
</tt>        &lt;value xid=<span class="s"><span class="dl">&quot;</span><span class="k">8</span><span class="dl">&quot;</span></span>&gt;<span class="fl">65.7</span>&lt;<span class="rx"><span class="dl">/</span><span class="k">value&gt;<tt>
</tt>        &lt;value xid=&quot;9&quot;&gt;61.0&lt;</span><span class="dl">/</span></span>value&gt;<tt>
</tt>        &lt;value xid=<span class="s"><span class="dl">&quot;</span><span class="k">10</span><span class="dl">&quot;</span></span>&gt;<span class="fl">54.0</span>&lt;<span class="rx"><span class="dl">/</span><span class="k">value&gt;<tt>
</tt>        &lt;value xid=&quot;11&quot;&gt;48.7&lt;</span><span class="dl">/</span></span>value&gt;<tt>
</tt>      &lt;<span class="rx"><span class="dl">/</span><span class="k">graph&gt;<tt>
</tt>    &lt;</span><span class="dl">/</span></span>graphs&gt;<tt>
</tt>  &lt;<span class="rx"><span class="dl">/</span><span class="k">chart&gt;<tt>
</tt>  </span></span></pre>
</td>
</tr>
</table>
<p>Let&#8217;s write some tests to make sure our view is ok:</p>
<p>index.xml.builder_spec.rb</p>
<table class="CodeRay">
<tr>
<td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }">
<pre>1<tt>
</tt>2<tt>
</tt>3<tt>
</tt>4<tt>
</tt><strong>5</strong><tt>
</tt>6<tt>
</tt>7<tt>
</tt>8<tt>
</tt>9<tt>
</tt><strong>10</strong><tt>
</tt>11<tt>
</tt>12<tt>
</tt>13<tt>
</tt>14<tt>
</tt><strong>15</strong><tt>
</tt>16<tt>
</tt>17<tt>
</tt>18<tt>
</tt>19<tt>
</tt><strong>20</strong><tt>
</tt>21<tt>
</tt>22<tt>
</tt>23<tt>
</tt>24<tt>
</tt><strong>25</strong><tt>
</tt>26<tt>
</tt>27<tt>
</tt>28<tt>
</tt>29<tt>
</tt><strong>30</strong><tt>
</tt>31<tt>
</tt>32<tt>
</tt>33<tt>
</tt>34<tt>
</tt><strong>35</strong><tt>
</tt>36<tt>
</tt>37<tt>
</tt>38<tt>
</tt>39<tt>
</tt><strong>40</strong><tt>
</tt>41<tt>
</tt>42<tt>
</tt>43<tt>
</tt>44<tt>
</tt><strong>45</strong><tt>
</tt>46<tt>
</tt>47<tt>
</tt>48<tt>
</tt>49<tt>
</tt><strong>50</strong><tt>
</tt>51<tt>
</tt>52<tt>
</tt>53<tt>
</tt>54<tt>
</tt><strong>55</strong><tt>
</tt>56<tt>
</tt>57<tt>
</tt>58<tt>
</tt>59<tt>
</tt><strong>60</strong><tt>
</tt>61<tt>
</tt>62<tt>
</tt>63<tt>
</tt>64<tt>
</tt><strong>65</strong><tt>
</tt>66<tt>
</tt>67<tt>
</tt>68<tt>
</tt>69<tt>
</tt><strong>70</strong><tt>
</tt>71<tt>
</tt>72<tt>
</tt>73<tt>
</tt>74<tt>
</tt><strong>75</strong><tt>
</tt>76<tt>
</tt>77<tt>
</tt>78<tt>
</tt>79<tt>
</tt><strong>80</strong><tt>
</tt>81<tt>
</tt></pre>
</td>
<td class="code">
<pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">require <span class="co">File</span>.dirname(<span class="pc">__FILE__</span>) + <span class="s"><span class="dl">'</span><span class="k">/../../spec_helper</span><span class="dl">'</span></span><tt>
</tt>require <span class="s"><span class="dl">'</span><span class="k">hpricot</span><span class="dl">'</span></span><tt>
</tt><tt>
</tt>describe <span class="s"><span class="dl">&quot;</span><span class="k">/averages/index.xml.builder</span><span class="dl">&quot;</span></span> <span class="r">do</span><tt>
</tt>  include <span class="co">AveragesHelper</span><tt>
</tt>  <tt>
</tt>  before <span class="r">do</span><tt>
</tt>    average_1 = mock_model(<span class="co">Average</span>)<tt>
</tt>    average_1.stub!(<span class="sy">:month</span>).and_return(<span class="s"><span class="dl">&quot;</span><span class="k">January</span><span class="dl">&quot;</span></span>)<tt>
</tt>    average_1.stub!(<span class="sy">:high</span>).and_return(<span class="s"><span class="dl">&quot;</span><span class="k">74.5</span><span class="dl">&quot;</span></span>)<tt>
</tt>    average_1.stub!(<span class="sy">:low</span>).and_return(<span class="s"><span class="dl">&quot;</span><span class="k">61.5</span><span class="dl">&quot;</span></span>)<tt>
</tt>    average_2 = mock_model(<span class="co">Average</span>)<tt>
</tt>    average_2.stub!(<span class="sy">:month</span>).and_return(<span class="s"><span class="dl">&quot;</span><span class="k">February</span><span class="dl">&quot;</span></span>)<tt>
</tt>    average_2.stub!(<span class="sy">:high</span>).and_return(<span class="s"><span class="dl">&quot;</span><span class="k">82.5</span><span class="dl">&quot;</span></span>)<tt>
</tt>    average_2.stub!(<span class="sy">:low</span>).and_return(<span class="s"><span class="dl">&quot;</span><span class="k">71.5</span><span class="dl">&quot;</span></span>)<tt>
</tt><tt>
</tt>    assigns[<span class="sy">:averages</span>] = [average_1, average_2]<tt>
</tt>  <span class="r">end</span><tt>
</tt><tt>
</tt>  it <span class="s"><span class="dl">&quot;</span><span class="k">should render the months in the series</span><span class="dl">&quot;</span></span> <span class="r">do</span><tt>
</tt>    render <span class="s"><span class="dl">&quot;</span><span class="k">/averages/index.xml.builder</span><span class="dl">&quot;</span></span><tt>
</tt>    response.should have_tag(<span class="s"><span class="dl">&quot;</span><span class="k">value</span><span class="dl">&quot;</span></span>, <span class="s"><span class="dl">'</span><span class="k">January</span><span class="dl">'</span></span>)<tt>
</tt>    response.should have_tag(<span class="s"><span class="dl">&quot;</span><span class="k">value</span><span class="dl">&quot;</span></span>, <span class="s"><span class="dl">'</span><span class="k">February</span><span class="dl">'</span></span>)<tt>
</tt>    <span class="c"># Same thing but with Hpricot</span><tt>
</tt>    doc = <span class="co">Hpricot</span>.XML(response.body.to_s)<tt>
</tt>    (doc/<span class="sy">:value</span>).first.inner_html.should == <span class="s"><span class="dl">'</span><span class="k">January</span><span class="dl">'</span></span><tt>
</tt>    (doc/<span class="sy">:value</span>)[<span class="i">1</span>].inner_html.should == <span class="s"><span class="dl">'</span><span class="k">February</span><span class="dl">'</span></span><tt>
</tt>  <span class="r">end</span><tt>
</tt>  <tt>
</tt>  it <span class="s"><span class="dl">&quot;</span><span class="k">should set the xid attributes for the series</span><span class="dl">&quot;</span></span> <span class="r">do</span><tt>
</tt>    render <span class="s"><span class="dl">&quot;</span><span class="k">/averages/index.xml.builder</span><span class="dl">&quot;</span></span><tt>
</tt>    response.should have_tag(<span class="s"><span class="dl">&quot;</span><span class="k">value[xid=0]:first-child</span><span class="dl">&quot;</span></span>)<tt>
</tt>    response.should have_tag(<span class="s"><span class="dl">&quot;</span><span class="k">value[xid=1]:last-child</span><span class="dl">&quot;</span></span>)<tt>
</tt>    <span class="c"># Same thing but with Hpricot</span><tt>
</tt>    doc = <span class="co">Hpricot</span>.XML(response.body.to_s)<tt>
</tt>    (doc/<span class="sy">:value</span>).first[<span class="s"><span class="dl">&quot;</span><span class="k">xid</span><span class="dl">&quot;</span></span>].should == <span class="s"><span class="dl">'</span><span class="k">0</span><span class="dl">'</span></span><tt>
</tt>    (doc/<span class="sy">:value</span>).last[<span class="s"><span class="dl">&quot;</span><span class="k">xid</span><span class="dl">&quot;</span></span>].should == <span class="s"><span class="dl">'</span><span class="k">1</span><span class="dl">'</span></span><tt>
</tt>  <span class="r">end</span><tt>
</tt>  <tt>
</tt>  it <span class="s"><span class="dl">&quot;</span><span class="k">should have 2 graphs and they should have a title</span><span class="dl">&quot;</span></span> <span class="r">do</span><tt>
</tt>    render <span class="s"><span class="dl">&quot;</span><span class="k">/averages/index.xml.builder</span><span class="dl">&quot;</span></span><tt>
</tt>    response.should have_tag(<span class="s"><span class="dl">&quot;</span><span class="k">graph[title=high]:first-child</span><span class="dl">&quot;</span></span>)<tt>
</tt>    response.should have_tag(<span class="s"><span class="dl">&quot;</span><span class="k">graph[title=low]:last-child</span><span class="dl">&quot;</span></span>)<tt>
</tt>    <span class="c"># Same thing but with Hpricot</span><tt>
</tt>    doc = <span class="co">Hpricot</span>.XML(response.body.to_s)<tt>
</tt>    (doc/<span class="sy">:graph</span>).size.should == <span class="i">2</span><tt>
</tt>    (doc/<span class="sy">:graph</span>).first[<span class="s"><span class="dl">&quot;</span><span class="k">title</span><span class="dl">&quot;</span></span>].should == <span class="s"><span class="dl">'</span><span class="k">high</span><span class="dl">'</span></span><tt>
</tt>    (doc/<span class="sy">:graph</span>).last[<span class="s"><span class="dl">&quot;</span><span class="k">title</span><span class="dl">&quot;</span></span>].should == <span class="s"><span class="dl">'</span><span class="k">low</span><span class="dl">'</span></span><tt>
</tt>  <span class="r">end</span><tt>
</tt>  <tt>
</tt>  it <span class="s"><span class="dl">&quot;</span><span class="k">should have a color set by graph</span><span class="dl">&quot;</span></span> <span class="r">do</span><tt>
</tt>    render <span class="s"><span class="dl">&quot;</span><span class="k">/averages/index.xml.builder</span><span class="dl">&quot;</span></span><tt>
</tt>    response.should have_tag(<span class="s"><span class="dl">&quot;</span><span class="k">graph[color]:first-child</span><span class="dl">&quot;</span></span>)<tt>
</tt>    response.should have_tag(<span class="s"><span class="dl">&quot;</span><span class="k">graph[color]:last-child</span><span class="dl">&quot;</span></span>)<tt>
</tt>    response.should have_tag(<span class="s"><span class="dl">&quot;</span><span class="k">graph[fill_color]:last-child</span><span class="dl">&quot;</span></span>)<tt>
</tt>    response.should have_tag(<span class="s"><span class="dl">&quot;</span><span class="k">graph[fill_alpha]:last-child</span><span class="dl">&quot;</span></span>)<tt>
</tt>    <span class="c"># Same thing but with Hpricot</span><tt>
</tt>    doc = <span class="co">Hpricot</span>.XML(response.body.to_s)<tt>
</tt>    (doc/<span class="sy">:graph</span>).first[<span class="s"><span class="dl">&quot;</span><span class="k">color</span><span class="dl">&quot;</span></span>].should_not be_nil<tt>
</tt>    (doc/<span class="sy">:graph</span>).last[<span class="s"><span class="dl">&quot;</span><span class="k">color</span><span class="dl">&quot;</span></span>].should_not be_nil<tt>
</tt>    (doc/<span class="sy">:graph</span>).last[<span class="s"><span class="dl">&quot;</span><span class="k">fill_color</span><span class="dl">&quot;</span></span>].should_not be_nil<tt>
</tt>    (doc/<span class="sy">:graph</span>).last[<span class="s"><span class="dl">&quot;</span><span class="k">fill_alpha</span><span class="dl">&quot;</span></span>].should_not be_nil<tt>
</tt>  <span class="r">end</span><tt>
</tt>  <tt>
</tt>  it <span class="s"><span class="dl">&quot;</span><span class="k">should have an xid for each graph value</span><span class="dl">&quot;</span></span> <span class="r">do</span><tt>
</tt>    render <span class="s"><span class="dl">&quot;</span><span class="k">/averages/index.xml.builder</span><span class="dl">&quot;</span></span><tt>
</tt>    response.should have_tag(<span class="s"><span class="dl">&quot;</span><span class="k">graph &gt; value[xid=0]:first-child</span><span class="dl">&quot;</span></span>)<tt>
</tt>    <span class="c"># Same thing but with Hpricot</span><tt>
</tt>    doc = <span class="co">Hpricot</span>.XML(response.body.to_s)<tt>
</tt>    (doc/<span class="sy">:graph</span>/<span class="sy">:value</span>).first[<span class="s"><span class="dl">&quot;</span><span class="k">xid</span><span class="dl">&quot;</span></span>].should == <span class="s"><span class="dl">&quot;</span><span class="k">0</span><span class="dl">&quot;</span></span><tt>
</tt>  <span class="r">end</span><tt>
</tt>  <tt>
</tt>  it <span class="s"><span class="dl">&quot;</span><span class="k">should have the high average as values of the first graph</span><span class="dl">&quot;</span></span> <span class="r">do</span><tt>
</tt>    render <span class="s"><span class="dl">&quot;</span><span class="k">/averages/index.xml.builder</span><span class="dl">&quot;</span></span><tt>
</tt>    response.should have_tag(<span class="s"><span class="dl">&quot;</span><span class="k">graph &gt; value:first-child</span><span class="dl">&quot;</span></span>, <span class="s"><span class="dl">&quot;</span><span class="k">74.5</span><span class="dl">&quot;</span></span>)<tt>
</tt>    <span class="c"># Same thing but with Hpricot</span><tt>
</tt>    doc = <span class="co">Hpricot</span>.XML(response.body.to_s)<tt>
</tt>    (doc/<span class="sy">:graph</span>/<span class="sy">:value</span>).first.inner_html.should == <span class="s"><span class="dl">&quot;</span><span class="k">74.5</span><span class="dl">&quot;</span></span><tt>
</tt>  <span class="r">end</span><tt>
</tt>  <tt>
</tt><span class="r">end</span></pre>
</td>
</tr>
</table>
<p>The first thing you must do (after installing the hpricot gem) is to require hpricot in your test:</p>
<table class="CodeRay">
<tr>
<td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }">
<pre><tt>
</tt></pre>
</td>
<td class="code">
<pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">  require <span class="s"><span class="dl">'</span><span class="k">hpricot</span><span class="dl">'</span></span></pre>
</td>
</tr>
</table>
<p>Now that hpricot is created we can use it to parse the response and check against our expectations.</p>
<p>(we create mock objects to pass to the view so we know exactly what to expect and we separate Model/Controller/Views tests)</p>
<p>To check against our response we have to use hpricot parser syntax. It might look at bit funny at first, but believe me it&#8217;s really easy once you get it.</p>
<p>But first, let&#8217;s parse the view:</p>
<table class="CodeRay">
<tr>
<td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }">
<pre>1<tt>
</tt>2<tt>
</tt>3<tt>
</tt>4<tt>
</tt></pre>
</td>
<td class="code">
<pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"><span class="c"># Render the mocked up data using the xml view</span><tt>
</tt>render <span class="s"><span class="dl">&quot;</span><span class="k">/averages/index.xml.builder</span><span class="dl">&quot;</span></span><tt>
</tt><span class="c"># Load and parse the view response body:</span><tt>
</tt>doc = <span class="co">Hpricot</span>.XML(response.body.to_s)  </pre>
</td>
</tr>
</table>
<p>Let&#8217;s look at the first test:</p>
<table class="CodeRay">
<tr>
<td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }">
<pre>1<tt>
</tt>2<tt>
</tt>3<tt>
</tt>4<tt>
</tt><strong>5</strong><tt>
</tt>6<tt>
</tt></pre>
</td>
<td class="code">
<pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">  it <span class="s"><span class="dl">&quot;</span><span class="k">should render the months in the series</span><span class="dl">&quot;</span></span> <span class="r">do</span><tt>
</tt>    render <span class="s"><span class="dl">&quot;</span><span class="k">/averages/index.xml.builder</span><span class="dl">&quot;</span></span><tt>
</tt>    doc = <span class="co">Hpricot</span>.XML(response.body.to_s)<tt>
</tt>    (doc/<span class="sy">:value</span>).first.inner_html.should == <span class="s"><span class="dl">'</span><span class="k">January</span><span class="dl">'</span></span><tt>
</tt>    (doc/<span class="sy">:value</span>)[<span class="i">1</span>].inner_html.should == <span class="s"><span class="dl">'</span><span class="k">February</span><span class="dl">'</span></span><tt>
</tt>  <span class="r">end</span></pre>
</td>
</tr>
</table>
<p>(doc/:value) returns all the value nodes, we take the first one and extract its content. We expect that it would match the name of the month for the first average.</p>
<p>Let&#8217;s now look at another test:</p>
<table class="CodeRay">
<tr>
<td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }">
<pre>1<tt>
</tt>2<tt>
</tt>3<tt>
</tt>4<tt>
</tt><strong>5</strong><tt>
</tt>6<tt>
</tt>7<tt>
</tt></pre>
</td>
<td class="code">
<pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">  it <span class="s"><span class="dl">&quot;</span><span class="k">should have 2 graphs and they should have a title</span><span class="dl">&quot;</span></span> <span class="r">do</span><tt>
</tt>    render <span class="s"><span class="dl">&quot;</span><span class="k">/averages/index.xml.builder</span><span class="dl">&quot;</span></span><tt>
</tt>    doc = <span class="co">Hpricot</span>.XML(response.body.to_s)<tt>
</tt>    (doc/<span class="sy">:graph</span>).size.should == <span class="i">2</span><tt>
</tt>    (doc/<span class="sy">:graph</span>).first[<span class="s"><span class="dl">&quot;</span><span class="k">title</span><span class="dl">&quot;</span></span>].should == <span class="s"><span class="dl">'</span><span class="k">high</span><span class="dl">'</span></span><tt>
</tt>    (doc/<span class="sy">:graph</span>).last[<span class="s"><span class="dl">&quot;</span><span class="k">title</span><span class="dl">&quot;</span></span>].should == <span class="s"><span class="dl">'</span><span class="k">low</span><span class="dl">'</span></span><tt>
</tt>  <span class="r">end</span></pre>
</td>
</tr>
</table>
<p>The thing to look at here is the fact that we are checking on the node&#8217;s attribute &#8220;title&#8221;. Really simple syntax and clean test, isn&#8217;t it?</p>
<p>Finally let&#8217;s look at the last example:</p>
<table class="CodeRay">
<tr>
<td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }">
<pre>1<tt>
</tt>2<tt>
</tt>3<tt>
</tt>4<tt>
</tt><strong>5</strong><tt>
</tt></pre>
</td>
<td class="code">
<pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">  it <span class="s"><span class="dl">&quot;</span><span class="k">should have the high average as values of the first graph</span><span class="dl">&quot;</span></span> <span class="r">do</span><tt>
</tt>    render <span class="s"><span class="dl">&quot;</span><span class="k">/averages/index.xml.builder</span><span class="dl">&quot;</span></span><tt>
</tt>    doc = <span class="co">Hpricot</span>.XML(response.body.to_s)<tt>
</tt>    (doc/<span class="sy">:graph</span>/<span class="sy">:value</span>).first.inner_html.should == <span class="s"><span class="dl">&quot;</span><span class="k">74.5</span><span class="dl">&quot;</span></span><tt>
</tt>  <span class="r">end</span></pre>
</td>
</tr>
</table>
<p>We are checking that the content of the first value node nested inside a graph node is equal to 74.5 which is the high average for the first month. </p>
<p>In practice, you probably won&#8217;t write all these tests at once, but anyway, let&#8217;s look at our XML builder which will make all these tests pass:</p>
<table class="CodeRay">
<tr>
<td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }">
<pre>1<tt>
</tt>2<tt>
</tt>3<tt>
</tt>4<tt>
</tt><strong>5</strong><tt>
</tt>6<tt>
</tt>7<tt>
</tt>8<tt>
</tt>9<tt>
</tt><strong>10</strong><tt>
</tt>11<tt>
</tt>12<tt>
</tt>13<tt>
</tt>14<tt>
</tt><strong>15</strong><tt>
</tt>16<tt>
</tt>17<tt>
</tt>18<tt>
</tt>19<tt>
</tt><strong>20</strong><tt>
</tt>21<tt>
</tt>22<tt>
</tt>23<tt>
</tt></pre>
</td>
<td class="code">
<pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">xml.instruct! <span class="sy"> <img src='http://railsontherun.com/wp-includes/images/smilies/icon_mad.gif' alt=':x' class='wp-smiley' /> ml</span>, <span class="sy">:version</span>=&gt;<span class="s"><span class="dl">&quot;</span><span class="k">1.0</span><span class="dl">&quot;</span></span>, <span class="sy">:encoding</span>=&gt;<span class="s"><span class="dl">&quot;</span><span class="k">UTF-8</span><span class="dl">&quot;</span></span><tt>
</tt>xml.chart <span class="r">do</span><tt>
</tt>  xml.series <span class="r">do</span>    <tt>
</tt>    <span class="iv">@averages</span>.each_with_index <span class="r">do</span> |average, index|<tt>
</tt>      xml.value average.month, <span class="sy"> <img src='http://railsontherun.com/wp-includes/images/smilies/icon_mad.gif' alt=':x' class='wp-smiley' /> id</span> =&gt; index<tt>
</tt>    <span class="r">end</span><tt>
</tt>  <span class="r">end</span><tt>
</tt>  <tt>
</tt>  xml.graphs <span class="r">do</span><tt>
</tt>    xml.graph <span class="sy">:title</span> =&gt; <span class="s"><span class="dl">'</span><span class="k">high</span><span class="dl">'</span></span>, <span class="sy">:color</span> =&gt; <span class="s"><span class="dl">&quot;</span><span class="k">#FF0000</span><span class="dl">&quot;</span></span>, <span class="sy">:fill_alpha</span> =&gt; <span class="s"><span class="dl">&quot;</span><span class="k">50</span><span class="dl">&quot;</span></span>, <span class="sy">:fill_color</span> =&gt; <span class="s"><span class="dl">&quot;</span><span class="k">#CC0000</span><span class="dl">&quot;</span></span> <span class="r">do</span><tt>
</tt>      <span class="iv">@averages</span>.each_with_index <span class="r">do</span> |average, index|<tt>
</tt>        xml.value average.high, <span class="sy"> <img src='http://railsontherun.com/wp-includes/images/smilies/icon_mad.gif' alt=':x' class='wp-smiley' /> id</span> =&gt; index<tt>
</tt>      <span class="r">end</span><tt>
</tt>    <span class="r">end</span><tt>
</tt>    <tt>
</tt>    xml.graph <span class="sy">:title</span> =&gt; <span class="s"><span class="dl">'</span><span class="k">low</span><span class="dl">'</span></span>, <span class="sy">:color</span> =&gt; <span class="s"><span class="dl">&quot;</span><span class="k">#0000CC</span><span class="dl">&quot;</span></span>, <span class="sy">:fill_alpha</span> =&gt; <span class="s"><span class="dl">&quot;</span><span class="k">50</span><span class="dl">&quot;</span></span>, <span class="sy">:fill_color</span> =&gt; <span class="s"><span class="dl">&quot;</span><span class="k">#0000CC</span><span class="dl">&quot;</span></span> <span class="r">do</span><tt>
</tt>      <span class="iv">@averages</span>.each_with_index <span class="r">do</span> |average, index|<tt>
</tt>        xml.value average.low, <span class="sy"> <img src='http://railsontherun.com/wp-includes/images/smilies/icon_mad.gif' alt=':x' class='wp-smiley' /> id</span> =&gt; index<tt>
</tt>      <span class="r">end</span><tt>
</tt>    <span class="r">end</span><tt>
</tt>  <span class="r">end</span><tt>
</tt>  <tt>
</tt><span class="r">end</span></pre>
</td>
</tr>
</table>
<p>Hpricot is a <em>really</em> nice tool which can make your BDD life much easier. And even if you don&#8217;t do BDD/TDD yet, it&#8217;s a great way to verify that any XML data you receive/generate is valid.</p>
<h2>Happy testing</h2>
]]></content:encoded>
			<wfw:commentRss>http://railsontherun.com/2007/10/31/how-to-test-a-xml-builder-view/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Sexy charts in less than 5 minutes</title>
		<link>http://railsontherun.com/2007/10/04/sexy-charts-in-less-than-5-minutes/</link>
		<comments>http://railsontherun.com/2007/10/04/sexy-charts-in-less-than-5-minutes/#comments</comments>
		<pubDate>Thu, 04 Oct 2007 03:06:00 +0000</pubDate>
		<dc:creator>Matt Aimonetti</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[5 minutes]]></category>
		<category><![CDATA[amcharts]]></category>
		<category><![CDATA[charts]]></category>
		<category><![CDATA[graphs]]></category>
		<category><![CDATA[gruff]]></category>
		<category><![CDATA[jfreechart]]></category>
		<category><![CDATA[rails 2.0]]></category>
		<category><![CDATA[swfobject]]></category>
		<category><![CDATA[tutorial]]></category>
		<category><![CDATA[xml]]></category>
		<category><![CDATA[xml builder]]></category>

		<guid isPermaLink="false">http://railsontherun.com/2007/11/05/sexy-charts-in-less-than-5-minutes</guid>
		<description><![CDATA[NOV 04 Update: demo app now available there. Sexy charts BDD style presentation at the SDRuby group to be posted soon on video podcast
Last time, in our &#8216;do it in less than 5 minutes&#8217; series, we saw how to add quickly and simply add Ajax pagination.
This time we&#8217;ll see how to add some sexy/fancy charts [...]]]></description>
			<content:encoded><![CDATA[<h2>NOV 04 Update: demo app now available <a href="http://railsontherun.com/assets/sexy_charts.zip">there</a>. Sexy charts BDD style presentation at the <a href="http://sdruby.com">SDRuby group</a> to be posted soon on <a href="http://podcast.sdruby.com/">video podcast</a></h2>
<p><a href="http://railsontherun.com/2007/9/27/ajax-pagination-in-less-than-5-minutes">Last time</a>, in our <em>&#8216;do it in less than 5 minutes&#8217;</em> series, we saw how to add <a href="http://railsontherun.com/2007/9/27/ajax-pagination-in-less-than-5-minutes">quickly and simply add Ajax pagination</a>.</p>
<p>This time we&#8217;ll see how to add some sexy/fancy charts to your rails app.</p>
<p>The goal is to end up with something like:</p>
<p><img src="http://farm2.static.flickr.com/1322/1480241002_e67637a659_o.png" alt="chart"/></p>
<p><img src="http://farm2.static.flickr.com/1414/1479377969_805d23a55d_o.png" alt="charts2"/></p>
<h2>Various options</h2>
<p>You might have heard or even tried solution such as <a href="http://nubyonrails.com/pages/gruff">Gruff</a> or <a href="http://www.jfree.org/jfreechart/">JFreeChart</a>.</p>
<p>While these solutions are great, they are certainly a pain in the butt. Gruff requires RMagick (avoid RMagick as much as can) and creates static files (a real pain when your graphs change all the time) JFreeChart on the other hand requires Java, Java skills and I hate <a href="http://wiki.rubyonrails.org/rails/pages/HowtoGenerateJFreeCharts">the way</a> you create graphs:</p>
<table class="CodeRay">
<tr>
<td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }">
<pre>1<tt>
</tt>2<tt>
</tt>3<tt>
</tt>4<tt>
</tt><strong>5</strong><tt>
</tt></pre>
</td>
<td class="code">
<pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">  <span class="r">def</span> <span class="fu">CreateChart</span><tt>
</tt>         pipe = <span class="co">IO</span>.popen <span class="s"><span class="dl">&quot;</span><span class="k">java -cp C:</span><span class="ch">\\</span><span class="k">InstantRails</span><span class="ch">\\</span><span class="k">rails_apps</span><span class="ch">\\</span><span class="k">project</span><span class="ch">\\</span><span class="k">jfree</span><span class="ch">\\</span><span class="k">src;C:</span><span class="ch">\\</span><span class="k">InstantRails</span><span class="ch">\\</span><span class="k">rails_apps</span><span class="ch">\\</span><span class="k">project</span><span class="ch">\\</span><span class="k">jfree</span><span class="ch">\\</span><span class="k">lib</span><span class="ch">\\</span><span class="k">jcommon-1.0.0-rc1.jar;C:</span><span class="ch">\\</span><span class="k">InstantRails</span><span class="ch">\\</span><span class="k">rails_apps</span><span class="ch">\\</span><span class="k">project</span><span class="ch">\\</span><span class="k">jfree</span><span class="ch">\\</span><span class="k">lib</span><span class="ch">\\</span><span class="k">jfreechart-1.0.0-rc1.jar; CreateChart</span><span class="dl">&quot;</span></span> <tt>
</tt>         pipe.close<tt>
</tt>         redirect_to <span class="s"><span class="dl">&quot;</span><span class="k">/graph/report</span><span class="dl">&quot;</span></span> <tt>
</tt>      <span class="r">end</span></pre>
</td>
</tr>
</table>
<p>Anyway, none of these solutions would let us create our charts in less than 5 minutes so let&#8217;s cut the story short. The best solution IMHO is to use Flash. But <em>wait</em>, you don&#8217;t need to know ActionScript or to own a license of Flash or Flex, we have libraries available for us to use without any Flash knowledge <img src='http://railsontherun.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p><a href="http://www.maani.us/xml_charts/index.php?menu=Gallery">XML/SWF</a> is cool Flash library which should fulfill our needs, you can even find a <a href="http://ziya.liquidrail.com/">rails plugin</a> to make things easier.</p>
<h2>amCharts</h2>
<p>But, to be honest I&#8217;d like to have something a bit &#8220;cleaner/sexy/fancy&#8221; and easier to setup.  So we&#8217;re going to use <a href="http://www.amcharts.com/">amCharts</a>  Don&#8217;t get me wrong, XML/SWF is a great library and you can make your graphs look nice (but you have to pay for support).<br />
Since we are running out of time let&#8217;s see how to implement a nice graph using *my* favorite library.</p>
<p><img src="http://www.amcharts.com/images/logo.gif" alt="amcharts"/></p>
<p>[DISCLAIMER: amCharts is <em>NOT open source</em> and <em>NOT free</em>. But, it's <em>cheap</em> (85 euros per site) especially when you think of how much time you will save. AND there is a <em>FREE version</em>. The Free version is the same as the full version but with a link back to amcharts.com]</p>
<h2>Setup</h2>
<p>Let&#8217;s go ahead and download one of the package: <a href="http://www.amcharts.com/column/download/">http://www.amcharts.com/column/download/</a> for instance.</p>
<p>Unpack the files and put them in their own folder in your public folder.<br />
Make sure you have the .swf file (amcolumn.swf for instance), a XML settings file and the fonts folder.<br />
(You might want to also create an empty amcharts_key.txt in the same folder since the plugin tries to load the key and you don&#8217;t want to pollute your logs.)</p>
<h2>Usage</h2>
<p>Now you need to understand how amCharts works. </p>
<p>After being loaded, amCharts expects a datastream. The datastream is then parsed and displayed as a chart.<br />
You can modify the aspect of any chart by changing its settings.<br />
Settings are set at runtime and/or in a setting file.</p>
<p>Great! I won&#8217;t cover the settings file. It&#8217;s a well documented XML file you just copied in your public folder. (or check the documentation)</p>
<p>What we want to focus on, is the <em>datastream</em>. Basically we just need to create a XML file that can be parsed by amCharts.</p>
<p>Let&#8217;s imagine that we have a reports_controller.rb file  We want to display the population of the cities in California.</p>
<p>let&#8217;s add a new action to render our XML file:</p>
<table class="CodeRay">
<tr>
<td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }">
<pre>1<tt>
</tt>2<tt>
</tt>3<tt>
</tt>4<tt>
</tt><strong>5</strong><tt>
</tt>6<tt>
</tt>7<tt>
</tt>8<tt>
</tt></pre>
</td>
<td class="code">
<pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">  <span class="r">def</span> <span class="fu">population</span><tt>
</tt>    <span class="iv">@cities</span> = <span class="co">City</span>.find(<span class="sy">:all</span>)<tt>
</tt>    <span class="iv">@population_data_link</span> = formatted_population_reports_url(<span class="sy"> <img src='http://railsontherun.com/wp-includes/images/smilies/icon_mad.gif' alt=':x' class='wp-smiley' /> ml</span>)<tt>
</tt>    respond_to <span class="r">do</span> |format|<tt>
</tt>      format.html<tt>
</tt>      format.xml  { render <span class="sy">:action</span> =&gt; <span class="s"><span class="dl">&quot;</span><span class="k">population.xml.builder</span><span class="dl">&quot;</span></span>, <span class="sy">:layout</span> =&gt; <span class="pc">false</span> }<tt>
</tt>    <span class="r">end</span><tt>
</tt>  <span class="r">end</span></pre>
</td>
</tr>
</table>
<p>(notice that I&#8217;m using rails 2.0 and that&#8217;s why my XML template is not RXML)</p>
<p>As you can see, we have 2 values: @cities and @population<em>data</em>link</p>
<p>@cities contains all the City records, including their population etc..</p>
<p>@population<em>data</em>link contains the url to retrieve the datastream.</p>
<p>If you wonder how I got this url? I&#8217;m simply using a named route defined in my routes.rb:</p>
<table class="CodeRay">
<tr>
<td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }">
<pre><tt>
</tt></pre>
</td>
<td class="code">
<pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">  map.resources <span class="sy">:reports</span>, <span class="sy">:collection</span> =&gt; {<span class="sy">:population</span> =&gt; <span class="sy">:get</span>}</pre>
</td>
</tr>
</table>
<p>(note that you don&#8217;t need to create a restful route for that, a simple named route would have worked too)</p>
<h2>Flash detection</h2>
<p>Since we are going to use Flash, we want to make sure that people have the Flash plugin installed on their browser. For that we will use <a href="http://blog.deconcept.com/swfobject/">swfobject</a>. Simply make sure to add swfobject.js (available in any amChart package) to your public/javascript folder. Then make sure you linked the javascript in your header:</p>
<table class="CodeRay">
<tr>
<td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }">
<pre><tt>
</tt></pre>
</td>
<td class="code">
<pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">  &lt;%= javascript_include_tag <span class="s"><span class="dl">'</span><span class="k">swfobject</span><span class="dl">'</span></span> <span class="s"><span class="dl">%&gt;</span></span></pre>
</td>
</tr>
</table>
<p>We now need to create our 2 views: <em>population.html.erb</em> and <em>population.xml.builder</em></p>
<h2>population.html.erb</h2>
<p>Basically, this view only loads amCharts and provides it with the details of the datastream:</p>
<table class="CodeRay">
<tr>
<td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }">
<pre>1<tt>
</tt>2<tt>
</tt>3<tt>
</tt>4<tt>
</tt><strong>5</strong><tt>
</tt>6<tt>
</tt>7<tt>
</tt>8<tt>
</tt>9<tt>
</tt><strong>10</strong><tt>
</tt>11<tt>
</tt>12<tt>
</tt>13<tt>
</tt>14<tt>
</tt><strong>15</strong><tt>
</tt>16<tt>
</tt></pre>
</td>
<td class="code">
<pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">  &lt;div id=<span class="s"><span class="dl">&quot;</span><span class="k">population_chart</span><span class="dl">&quot;</span></span> <span class="r">class</span>=<span class="s"><span class="dl">'</span><span class="k">chart</span><span class="dl">'</span></span>&gt;<tt>
</tt>    &lt;strong&gt;<span class="co">Text</span> displayed <span class="r">when</span> the user doesn<span class="s"><span class="dl">'</span><span class="k">t have Flash. You might want to display a simple table with the population, search engines and visitor without flash would love that.&lt;/strong&gt;<tt>
</tt>    &lt;p&gt; To see this page properly, you need to upgrade your Flash Player, please visit the Adobe web site&lt;/p&gt;<tt>
</tt>  &lt;/div&gt;<tt>
</tt><tt>
</tt>  &lt;script type=&quot;text/javascript&quot;&gt;<tt>
</tt>    // &lt;![CDATA[    <tt>
</tt>    var so = new SWFObject(&quot;/amcolumn/amcolumn.swf&quot;, &quot;population_chart&quot;, &quot;800&quot;, &quot;380&quot;, &quot;8&quot;, &quot;#000000&quot;);<tt>
</tt>    so.addVariable(&quot;path&quot;, &quot;/amcolumn/&quot;);<tt>
</tt>    so.addVariable(&quot;settings_file&quot;, escape(&quot;/amcolumn/column_settings.xml&quot;));<tt>
</tt>    so.addVariable(&quot;data_file&quot;, escape(&quot;&lt;%= @population_data_link %&gt;&quot;));<tt>
</tt>    so.addVariable(&quot;additional_chart_settings&quot;, &quot;&lt;settings&gt;&lt;labels&gt;&lt;label&gt;&lt;x&gt;250&lt;/x&gt;&lt;y&gt;25&lt;/y&gt;&lt;text_size&gt;18&lt;/text_size&gt;&lt;text&gt;&lt;![CDATA[&lt;b&gt;California Population as of &lt;%= Time.now.to_s(:db) %&gt;&lt;/b&gt;]]&gt;&lt;/text&gt;&lt;/label&gt;&lt;/labels&gt;&lt;/settings&gt;&quot;);<tt>
</tt>    so.addVariable(&quot;preloader_color&quot;, &quot;#000000&quot;);<tt>
</tt>    so.write(&quot;population_chart&quot;);<tt>
</tt>    // ]]&gt;<tt>
</tt>  &lt;/script&gt;</span></span></pre>
</td>
</tr>
</table>
<p>As you can see, we have a div called population_chart. This div is replaced at load time by the Flash object if the visitor has Flash setup locally. Think about providing some data in case the user doesn&#8217;t have Flash.</p>
<p>The rest is simple Javascript. I unpacked the amchart column lib in mypublic/amcolumn folder and that&#8217;s why I setup the path as &#8220;amcolumn&#8221;</p>
<table class="CodeRay">
<tr>
<td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }">
<pre><tt>
</tt></pre>
</td>
<td class="code">
<pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">  so.addVariable(<span class="s"><span class="dl">&quot;</span><span class="k">path</span><span class="dl">&quot;</span></span>, <span class="s"><span class="dl">&quot;</span><span class="k">/amcolumn/</span><span class="dl">&quot;</span></span>);</pre>
</td>
</tr>
</table>
<p>My settings file is called column_settings.xml :</p>
<table class="CodeRay">
<tr>
<td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }">
<pre><tt>
</tt></pre>
</td>
<td class="code">
<pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">  so.addVariable(<span class="s"><span class="dl">&quot;</span><span class="k">settings_file</span><span class="dl">&quot;</span></span>, escape(<span class="s"><span class="dl">&quot;</span><span class="k">/amcolumn/column_settings.xml</span><span class="dl">&quot;</span></span>));</pre>
</td>
</tr>
</table>
<p>and the most important part:</p>
<table class="CodeRay">
<tr>
<td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }">
<pre><tt>
</tt></pre>
</td>
<td class="code">
<pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">  so.addVariable(<span class="s"><span class="dl">&quot;</span><span class="k">data_file</span><span class="dl">&quot;</span></span>, escape(<span class="s"><span class="dl">&quot;</span><span class="k">&lt;%= @population_data_link %&gt;</span><span class="dl">&quot;</span></span>));</pre>
</td>
</tr>
</table>
<p>Finally, I added some dynamic settings just to show you how easy it is:</p>
<table class="CodeRay">
<tr>
<td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }">
<pre>1<tt>
</tt>2<tt>
</tt></pre>
</td>
<td class="code">
<pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">  so.addVariable(<span class="s"><span class="dl">&quot;</span><span class="k">additional_chart_settings</span><span class="dl">&quot;</span></span>,<tt>
</tt>  <span class="s"><span class="dl">&quot;</span><span class="k">&lt;settings&gt;&lt;labels&gt;&lt;label&gt;&lt;x&gt;250&lt;/x&gt;&lt;y&gt;25&lt;/y&gt;&lt;text_size&gt;18&lt;/text_size&gt;&lt;text&gt;&lt;![CDATA[&lt;b&gt;California Population as of &lt;%= Time.now.to_s(:db) %&gt;&lt;/b&gt;]]&gt;&lt;/text&gt;&lt;/label&gt;&lt;/labels&gt;&lt;/settings&gt;</span><span class="dl">&quot;</span></span>);</pre>
</td>
</tr>
</table>
<p>Ok, let&#8217;s now create our XML view:</p>
<h2>population.xml.builder</h2>
<table class="CodeRay">
<tr>
<td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }">
<pre>1<tt>
</tt>2<tt>
</tt>3<tt>
</tt>4<tt>
</tt><strong>5</strong><tt>
</tt>6<tt>
</tt>7<tt>
</tt>8<tt>
</tt>9<tt>
</tt><strong>10</strong><tt>
</tt>11<tt>
</tt>12<tt>
</tt>13<tt>
</tt>14<tt>
</tt><strong>15</strong><tt>
</tt>16<tt>
</tt>17<tt>
</tt>18<tt>
</tt>19<tt>
</tt><strong>20</strong><tt>
</tt>21<tt>
</tt>22<tt>
</tt>23<tt>
</tt>24<tt>
</tt><strong>25</strong><tt>
</tt>26<tt>
</tt></pre>
</td>
<td class="code">
<pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">  xml.instruct! <span class="sy"> <img src='http://railsontherun.com/wp-includes/images/smilies/icon_mad.gif' alt=':x' class='wp-smiley' /> ml</span>, <span class="sy">:version</span>=&gt;<span class="s"><span class="dl">&quot;</span><span class="k">1.0</span><span class="dl">&quot;</span></span>, <span class="sy">:encoding</span>=&gt;<span class="s"><span class="dl">&quot;</span><span class="k">UTF-8</span><span class="dl">&quot;</span></span><tt>
</tt>  xml.chart <span class="r">do</span><tt>
</tt>    <span class="c"># xml.message &quot;You can broadcast any message to chart from data XML file&quot;, :bg_color =&gt; &quot;#FFFFFF&quot;, :text_color =&gt; &quot;#000000&quot;</span><tt>
</tt>    xml.series <span class="r">do</span>    <tt>
</tt>      <span class="iv">@cities</span>.each_with_index <span class="r">do</span> |city, index|<tt>
</tt>        xml.value city.name, <span class="sy"> <img src='http://railsontherun.com/wp-includes/images/smilies/icon_mad.gif' alt=':x' class='wp-smiley' /> id</span> =&gt; index<tt>
</tt>      <span class="r">end</span><tt>
</tt>    <span class="r">end</span><tt>
</tt><tt>
</tt>    xml.graphs <span class="r">do</span><tt>
</tt>     <span class="c">#the gid is used in the settings file to set different settings just for this graph</span><tt>
</tt>      xml.graph <span class="sy">:gid</span> =&gt; <span class="s"><span class="dl">'</span><span class="k">population</span><span class="dl">'</span></span> <span class="r">do</span><tt>
</tt>        <span class="iv">@cities</span>.each_with_index <span class="r">do</span> |city, index|<tt>
</tt>          population = city.population<tt>
</tt>          <span class="r">case</span> population<tt>
</tt>            <span class="c"># When the population is &gt; 1 million, show the bar in red/pink</span><tt>
</tt>            <span class="r">when</span> &gt; <span class="i">100000</span><tt>
</tt>              xml.value value, <span class="sy"> <img src='http://railsontherun.com/wp-includes/images/smilies/icon_mad.gif' alt=':x' class='wp-smiley' /> id</span> =&gt; index, <span class="sy">:color</span> =&gt; <span class="s"><span class="dl">&quot;</span><span class="k">#ff43a8</span><span class="dl">&quot;</span></span>, <span class="sy">:gradient_fill_colors</span> =&gt; <span class="s"><span class="dl">&quot;</span><span class="k">#960040,#ff43a8</span><span class="dl">&quot;</span></span>, <span class="sy">:description</span> =&gt; level<tt>
</tt>            <span class="r">else</span><tt>
</tt>              xml.value value, <span class="sy"> <img src='http://railsontherun.com/wp-includes/images/smilies/icon_mad.gif' alt=':x' class='wp-smiley' /> id</span> =&gt; index, <span class="sy">:color</span> =&gt; <span class="s"><span class="dl">&quot;</span><span class="k">#00C3C6</span><span class="dl">&quot;</span></span>, <span class="sy">:gradient_fill_colors</span> =&gt; <span class="s"><span class="dl">&quot;</span><span class="k">#009c9d,#00C3C6</span><span class="dl">&quot;</span></span>, <span class="sy">:description</span> =&gt; level<tt>
</tt>            <span class="r">end</span><tt>
</tt>        <span class="r">end</span><tt>
</tt>      <span class="r">end</span><tt>
</tt>    <span class="r">end</span><tt>
</tt><tt>
</tt>  <span class="r">end</span></pre>
</td>
</tr>
</table>
<p>Nothing fancy, we first created a series with all the city names:</p>
<table class="CodeRay">
<tr>
<td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }">
<pre>1<tt>
</tt>2<tt>
</tt>3<tt>
</tt>4<tt>
</tt><strong>5</strong><tt>
</tt></pre>
</td>
<td class="code">
<pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">  xml.series <span class="r">do</span>    <tt>
</tt>    <span class="iv">@cities</span>.each_with_index <span class="r">do</span> |city, index|<tt>
</tt>      xml.value city.name, <span class="sy"> <img src='http://railsontherun.com/wp-includes/images/smilies/icon_mad.gif' alt=':x' class='wp-smiley' /> id</span> =&gt; index<tt>
</tt>    <span class="r">end</span><tt>
</tt>  <span class="r">end</span></pre>
</td>
</tr>
</table>
<p>Then we created another node with the values for each city.<br />
Since it would be cool to display some bars in a different color, we used a case-switch statement:</p>
<table class="CodeRay">
<tr>
<td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }">
<pre>1<tt>
</tt>2<tt>
</tt>3<tt>
</tt>4<tt>
</tt><strong>5</strong><tt>
</tt>6<tt>
</tt>7<tt>
</tt>8<tt>
</tt>9<tt>
</tt><strong>10</strong><tt>
</tt>11<tt>
</tt>12<tt>
</tt></pre>
</td>
<td class="code">
<pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">    xml.graph <span class="sy">:gid</span> =&gt; <span class="s"><span class="dl">'</span><span class="k">population</span><span class="dl">'</span></span> <span class="r">do</span><tt>
</tt>      <span class="iv">@cities</span>.each_with_index <span class="r">do</span> |city, index|<tt>
</tt>        population = city.population<tt>
</tt>        <span class="r">case</span> population<tt>
</tt>          <span class="c"># When the population is &gt; 1 million, show the bar in red/pink</span><tt>
</tt>          <span class="r">when</span> &gt; <span class="i">100000</span><tt>
</tt>            xml.value value, <span class="sy"> <img src='http://railsontherun.com/wp-includes/images/smilies/icon_mad.gif' alt=':x' class='wp-smiley' /> id</span> =&gt; index, <span class="sy">:color</span> =&gt; <span class="s"><span class="dl">&quot;</span><span class="k">#ff43a8</span><span class="dl">&quot;</span></span>, <span class="sy">:gradient_fill_colors</span> =&gt; <span class="s"><span class="dl">&quot;</span><span class="k">#960040,#ff43a8</span><span class="dl">&quot;</span></span>, <span class="sy">:description</span> =&gt; level<tt>
</tt>          <span class="r">else</span><tt>
</tt>            xml.value value, <span class="sy"> <img src='http://railsontherun.com/wp-includes/images/smilies/icon_mad.gif' alt=':x' class='wp-smiley' /> id</span> =&gt; index, <span class="sy">:color</span> =&gt; <span class="s"><span class="dl">&quot;</span><span class="k">#00C3C6</span><span class="dl">&quot;</span></span>, <span class="sy">:gradient_fill_colors</span> =&gt; <span class="s"><span class="dl">&quot;</span><span class="k">#009c9d,#00C3C6</span><span class="dl">&quot;</span></span>, <span class="sy">:description</span> =&gt; level<tt>
</tt>          <span class="r">end</span><tt>
</tt>      <span class="r">end</span><tt>
</tt>    <span class="r">end</span></pre>
</td>
</tr>
</table>
<p>Depending on what you want to display, you might need to have different colors or a different tooltip text, or load an animation or image&#8230;  and as you can see, it&#8217;s <em>REALLY</em> easy.</p>
<p>Got to http://yoursite.com/reports/population to enjoy your new fancy graph.</p>
<h2>That&#8217;s it, you are done! </h2>
<p>Time to tweak your settings file to make your graph look <em>awesome</em>.<br />
Since you now have a lot of free time, you can start re-factoring your code and make sure you have a good test coverage.</p>
<p>Good luck!</p>
]]></content:encoded>
			<wfw:commentRss>http://railsontherun.com/2007/10/04/sexy-charts-in-less-than-5-minutes/feed/</wfw:commentRss>
		<slash:comments>35</slash:comments>
		</item>
	</channel>
</rss>

