<?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>everburning &#187; Programming</title>
	<atom:link href="http://everburning.com/news/category/programming/feed/" rel="self" type="application/rss+xml" />
	<link>http://everburning.com</link>
	<description>picking at the fringes of reality</description>
	<lastBuildDate>Wed, 04 Jan 2012 18:42:26 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Stage left: Enter Goliath</title>
		<link>http://everburning.com/news/stage-left-enter-goliath/</link>
		<comments>http://everburning.com/news/stage-left-enter-goliath/#comments</comments>
		<pubDate>Tue, 08 Mar 2011 19:27:59 +0000</pubDate>
		<dc:creator>dj2</dc:creator>
				<category><![CDATA[Article]]></category>
		<category><![CDATA[Goliath]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://everburning.com/?p=752</guid>
		<description><![CDATA[<p><img class="alignleft size-thumbnail wp-image-760" title="_MG_9957" src="http://everburning.com/wp-content/uploads/2011/03/MG_9957-150x150.jpg" alt="" width="150" height="150" />Over at <a href="http://labs.postrank.com">PostRank Labs</a> we&#8217;ve released the current version of our API server framework to the wild. Allow me to introduce <a href="http://goliath.io">Goliath</a>. Goliath is built on the back of the work at <a href="http://postrank.com">PostRank</a>, <a href="http://code.macournoyer.com/thin/">Thin</a>, <a href="http://www.sinatrarb.com/">Sinatra</a>, <a href="https://github.com/copiousfreetime/http-parser.rb">http-parser.rb</a> and various other projects.</p>
<p>We&#8217;ve been using a version of Goliath internally for over a year serving a sustained 500 requests/sec and shuttling around gigabytes of data.</p>
<p>How did we get here you&#8217;re asking? Our API servers from the beginning have been built on the awesome  <a href="http://rubyeventmachine.org">EventMachine</a> library. The last several iterations have used a threaded model. A request comes in, a thread is grabbed from the pool, request is processed, thread goes back to the pool. This was cool and worked quite well for us for several years. The problem we ran into, we were getting spaghetti code. A lot of our APIs call other APIs internally. Handling the callbacks, error backs and various other delayed operations was getting difficult to maintain.</p>
<p>Then along wandered <a href="http://ruby-lang.org">Ruby 1.9</a> with these cool little things called <a href="http://www.ruby-doc.org/core-1.9/classes/Fiber.html">Fibers</a>. Fibers allow us to build synchronous looking code backed by asynchronous calls. To achieve this we&#8217;re using the <a href="http://github.com/igrigorik/em-synchrony">EM-Synchrony</a> library. Tying this back into Goliath, as each request comes in, a new Fiber is created, the request is processed within that Fiber and, when completed, the Fiber goes away.</p>
<p>Once we had the foundation in place we went a step further and built a few extra little goodies into Goliath including <a href="https://github.com/postrank-labs/goliath/blob/master/examples/async_upload.rb">streaming requests</a> and <a href="https://github.com/postrank-labs/goliath/blob/master/examples/stream.rb">responses</a>. This lets us do some cool things like hook AMQP exchanges up to a Goliath API to pipe content through a streaming response.</p>
<p>Using http-parser.rb allows us to open up the option of alternate Ruby VMs. At the time of writing, Goliath has been tested with MRI, JRuby and Rubinius. MRI is currently the fastest VM for Goliath but there is lots of work going on in the alternate VMs to make them faster.</p>
<p>Ok, ok, enough of the hype, lets see some code already.</p>
<p>Well, first we need to get Goliath installed. In general this is pretty simple, you can use your system Ruby, RVM or some other system and all you need to do is:</p>
<div class="dean_ch" style="white-space: wrap;">gem install goliath</div>
<p>What we&#8217;re going to build here is an HTTP proxy that stores information about each request/response into <a href="http://www.mongodb.org/">MongoDB</a>. (The full code listing can be found in <a href="https://gist.github.com/7c2fe52d73fb290690cf">this gist</a>.)</p>
<p>Before we dig into the code, a little on how Goliath works. By default, Goliath will execute your API when you run the API file. In order to do this, the API file has to have a snake cased name based on the class name. So, our <code>HttpLog</code> API will live in a file called <code>http_log.rb</code>. When you need to add configuration to your API you&#8217;ll do this by creating a <code>config</code> directory in the same directory as your file. You then create a file in the <code>config</code> directory with the same name as your API. In our case, we&#8217;ll create a <code>config/http_log.rb</code> file to hold our configuration.</p>
<p>Goliath has been built to be <a href="http://rack.rubyforge.org/">Rack</a>-aware. This means we can make use of Rack middlewares, as long as they&#8217;re async safe or have been wrapped by <a href="https://github.com/rkh/async-rack">async-rack</a>.</p>
<p>We&#8217;re going to build some specs along with our API. Create <code>spec/http_log_spec.rb</code> and we can get this party started.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw3">require</span> <span class="st0">&#8216;rubygems&#8217;</span><br />
<span class="kw3">require</span> <span class="st0">&#8216;goliath/test_helper&#8217;</span><br />
<span class="kw3">require</span> <span class="kw4">File</span>.<span class="me1">expand_path</span><span class="br0">&#40;</span><span class="kw4">File</span>.<span class="me1">join</span><span class="br0">&#40;</span><span class="kw4">File</span>.<span class="me1">dirname</span><span class="br0">&#40;</span><span class="kw2">__FILE__</span><span class="br0">&#41;</span>, <span class="st0">&#8216;..&#8217;</span>, <span class="st0">&#8216;http_log&#8217;</span><span class="br0">&#41;</span><span class="br0">&#41;</span></p>
<p>describe HttpLog <span class="kw1">do</span><br />
&nbsp; <span class="kw1">include</span> <span class="re2">Goliath::TestHelper</span></p>
<p>&nbsp; let<span class="br0">&#40;</span><span class="re3">:err</span><span class="br0">&#41;</span> <span class="br0">&#123;</span> <span class="kw3">Proc</span>.<span class="me1">new</span> <span class="br0">&#123;</span> |c| <span class="kw3">fail</span> <span class="st0">&quot;HTTP Request failed #{c.response}&quot;</span> <span class="br0">&#125;</span> <span class="br0">&#125;</span></p>
<p>&nbsp; it <span class="st0">&#8216;responds to requests&#8217;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; with_api<span class="br0">&#40;</span>HttpLog<span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; &nbsp; get_request<span class="br0">&#40;</span><span class="br0">&#123;</span><span class="br0">&#125;</span>, err<span class="br0">&#41;</span> <span class="kw1">do</span> |c|<br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span>.<span class="me1">status</span>.<span class="me1">should</span> == <span class="nu0">200</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>With Goliath we&#8217;ve provided a simple <code>goliath/test_helper</code> file that makes the testing of APIs a little easier. Once you&#8217;ve required the library, you just need to <code>include Goliath::TestHelper</code> and you can start using the helper methods. These include the <code>with_api</code> and <code>get_request</code> methods that are being used above. The <code>with_api</code> call will start an instance of our API running on port 9000 and anything done within its block will executing inside an EM reactor. This method will not return until <code>stop</code> is executed.</p>
<p>We&#8217;re, essentially, creating an integration test suite for our API. Everything except for the API launching will be tested by the test suite. The configuration files will be parsed, all the middlewares will be loaded and executed. Everything that your normal application would do except for the launching.</p>
<p>Calling <code>get_request</code> will send a GET to our API. The first parameter is any query parameters to send to the API. In our case, we send nothing. The second is an error handler to add. This is fired if we are unable to communicate with the API. For this example, we&#8217;re just using the RSpec <code>fail</code> method to fail the spec in the error handler.</p>
<p>The block passed to <code>get_request</code> will be executed and provided the request object when the request is complete. This allows us to look at the response headers and response body. In this case, we&#8217;re just verifying that server responds with a 200 code. When you&#8217;re using <code>get_request</code> or <code>post_request</code> a default error and callback handler will be installed that calls <code>stop</code> for you.</p>
<p>With that, running the spec should fail. Let&#8217;s create a simple API to make it pass. The following code in <code>http_log.rb</code> will give us a passing spec.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="co1">#!/usr/bin/env ruby</span></p>
<p><span class="kw3">require</span> <span class="st0">&#8216;rubygems&#8217;</span><br />
<span class="kw3">require</span> <span class="st0">&#8216;goliath&#8217;</span></p>
<p><span class="kw1">class</span> HttpLog &lt; <span class="re2">Goliath::API</span><br />
&nbsp; <span class="kw1">def</span> response<span class="br0">&#40;</span>env<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span><span class="nu0">200</span>, <span class="br0">&#123;</span><span class="br0">&#125;</span>, <span class="st0">&quot;Hello&quot;</span><span class="br0">&#93;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>You can execute the API with <code>./http_log.rb -sv</code> which will launch the API on a default port of 9000.</p>
<p>The <code>-sv</code> flags tell the API to log to STDOUT (s) and use verbose logging (v). You can run <code>./http_log.rb -h</code> to see a list of all the default options provided by Goliath. You can add <a href="https://github.com/postrank-labs/goliath/blob/master/examples/conf_test.rb">your own options</a> as well if you need special argument handling for your API.</p>
<p>With the API running we can query the API using curl.</p>
<div class="dean_ch" style="white-space: wrap;">dj2@titania ~ $ curl localhost:<span class="nu0">9000</span><br />
Hello</div>
<p>Running the spec test again our test case should also pass at this point.</p>
<p>To make our lives easier, we&#8217;re going to use the <code>Rack::Reloader</code> middleware to handle reloading our Rack middleware on each request.</p>
<p>This is done by adding <code>use ::Rack::Reloader, 0</code> into our API. Since we only want reloading when we&#8217;re doing development we&#8217;re going to make the <code>use</code> statement conditional on the Goliath environment. By default Goliath executes in a development environment. We can confirm this by calling <code>Goliath.dev?</code> which will return <code>true</code> if the current environment is development.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">class</span> HttpLog &lt; <span class="re2">Goliath::API</span><br />
&nbsp; use ::<span class="re2">Rack::Reloader</span>, <span class="nu0">0</span> <span class="kw1">if</span> Goliath.<span class="me1">dev</span>?</p>
<p>&nbsp; <span class="kw1">def</span> response<span class="br0">&#40;</span>env<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span><span class="nu0">200</span>, <span class="br0">&#123;</span><span class="br0">&#125;</span>, <span class="st0">&quot;Hello&quot;</span><span class="br0">&#93;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>You&#8217;ll notice that we didn&#8217;t create another file to place the middleware. With Goliath, the middleware is built into the API class itself. We&#8217;ve found this is a lot easier to deal with when you keep everything together in the same file. (You can also add <a href="https://github.com/postrank-labs/goliath/wiki/Plugins">plugins</a> in the same fashion by using the <code>plugin</code> keyword.)</p>
<p>The next step is to start forwarding to our backend server. To do this, we&#8217;ll specify in our configuration file the forwarder URL. Create <code>config/http_log.rb</code> and add the following:</p>
<div class="dean_ch" style="white-space: wrap;">config<span class="br0">&#91;</span><span class="st0">&#8216;forwarder&#8217;</span><span class="br0">&#93;</span> = <span class="st0">&#8216;http://localhost:8080&#8242;</span></div>
<p>The <code>config</code> hash allows us to store configuration data that will become available through the <code>env</code> parameter to the <code>response</code> method. In this case, we&#8217;ve specified we want our forwarder to send requests to <code>http://localhost:8080</code>.</p>
<p>In order to test this, I&#8217;m going to create a Goliath API server inside our spec files. I&#8217;ll run the server on port 8080 and check for our server response in the response from our API. Add the following to the top of the spec file.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">class</span> Responder &lt; <span class="re2">Goliath::API</span><br />
&nbsp; <span class="kw1">def</span> response<span class="br0">&#40;</span>env<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span><span class="nu0">200</span>, <span class="br0">&#123;</span><span class="st0">&quot;Special&quot;</span> =&gt; <span class="st0">&quot;Header&quot;</span><span class="br0">&#125;</span>, <span class="st0">&quot;Hello World&quot;</span><span class="br0">&#93;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>And the following spec:</p>
<div class="dean_ch" style="white-space: wrap;">it <span class="st0">&#8216;forwards to our API server&#8217;</span> <span class="kw1">do</span><br />
&nbsp; with_api<span class="br0">&#40;</span>HttpLog<span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; server<span class="br0">&#40;</span>Responder, <span class="nu0">8080</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; get_request<span class="br0">&#40;</span><span class="br0">&#123;</span><span class="br0">&#125;</span>, err<span class="br0">&#41;</span> <span class="kw1">do</span> |c|<br />
&nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span>.<span class="me1">status</span>.<span class="me1">should</span> == <span class="nu0">200</span><br />
&nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span><span class="br0">&#91;</span><span class="st0">&#8216;Special&#8217;</span><span class="br0">&#93;</span>.<span class="me1">should</span> == <span class="st0">&#8216;Header&#8217;</span><br />
&nbsp; &nbsp; &nbsp; c.<span class="me1">response</span>.<span class="me1">should</span> == <span class="st0">&#8216;Hello from Responder&#8217;</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span></div>
<p>This is similar to our previous spec except I&#8217;ve added a <code>server(Responder, 8080)</code> call to launch an instance of our <code>Responder</code> API on port 8080. Running this test should fail since we aren&#8217;t sending or receiving any data to the proxied API.</p>
<p>As a first pass, we&#8217;ll send the request and proxy the responses back.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> response<span class="br0">&#40;</span>env<span class="br0">&#41;</span><br />
&nbsp; req = <span class="re2">EM::HttpRequest</span>.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&quot;#{env.forwarder}&quot;</span><span class="br0">&#41;</span>.<span class="me1">get</span></p>
<p>&nbsp; <span class="br0">&#91;</span>req.<span class="me1">response_header</span>.<span class="me1">status</span>, req.<span class="me1">response_header</span>, req.<span class="me1">response</span><span class="br0">&#93;</span><br />
<span class="kw1">end</span></div>
<p>So, that&#8217;s cool. (Make sure you add the <code>server(Responder, 8080)</code> to the first example after doing this or it will end up failing.) There is one, non-apparent problem. It turns out the headers get transformed by <code>EM-HTTP-Request</code>. When we go to log this stuff we&#8217;ll want the non-transformed headers so we need to transform them back into the normal HTTP header format. Let&#8217;s start with a couple tests:</p>
<div class="dean_ch" style="white-space: wrap;">context <span class="st0">&#8216;HTTP header handling&#8217;</span> <span class="kw1">do</span><br />
&nbsp; it <span class="st0">&#8216;transforms back properly&#8217;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; hl = HttpLog.<span class="me1">new</span><br />
&nbsp; &nbsp; hl.<span class="me1">to_http_header</span><span class="br0">&#40;</span><span class="st0">&quot;SPECIAL&quot;</span><span class="br0">&#41;</span>.<span class="me1">should</span> == <span class="st0">&#8216;Special&#8217;</span><br />
&nbsp; &nbsp; hl.<span class="me1">to_http_header</span><span class="br0">&#40;</span><span class="st0">&quot;CONTENT_TYPE&quot;</span><span class="br0">&#41;</span>.<span class="me1">should</span> == <span class="st0">&#8216;Content-Type&#8217;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>Simple enough, we need to fix the casing and change <code>_</code> into <code>-</code>. Let&#8217;s add that to our HttpLog class.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> to_http_header<span class="br0">&#40;</span>k<span class="br0">&#41;</span><br />
&nbsp; k.<span class="me1">downcase</span>.<span class="kw3">split</span><span class="br0">&#40;</span><span class="st0">&#8216;_&#8217;</span><span class="br0">&#41;</span>.<span class="me1">collect</span> <span class="br0">&#123;</span> |e| e.<span class="me1">capitalize</span> <span class="br0">&#125;</span>.<span class="me1">join</span><span class="br0">&#40;</span><span class="st0">&#8216;-&#8217;</span><span class="br0">&#41;</span><br />
<span class="kw1">end</span></div>
<p>That should make our transform tests pass and we can build the change into the <code>response</code> method by changing it to look like:</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> response<span class="br0">&#40;</span>env<span class="br0">&#41;</span><br />
&nbsp; req = <span class="re2">EM::HttpRequest</span>.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&quot;#{env.forwarder}&quot;</span><span class="br0">&#41;</span>.<span class="me1">get</span></p>
<p>&nbsp; response_headers = <span class="br0">&#123;</span><span class="br0">&#125;</span><br />
&nbsp; req.<span class="me1">response_header</span>.<span class="me1">each_pair</span> <span class="kw1">do</span> |k, v|<br />
&nbsp; &nbsp; response_headers<span class="br0">&#91;</span>to_http_header<span class="br0">&#40;</span>k<span class="br0">&#41;</span><span class="br0">&#93;</span> = v<br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; <span class="br0">&#91;</span>req.<span class="me1">response_header</span>.<span class="me1">status</span>, response_headers, req.<span class="me1">response</span><span class="br0">&#93;</span><br />
<span class="kw1">end</span></div>
<p>Cool, so now we&#8217;re forwarding all requests to the proxied server. There is a bit of missing information that we need, that being the headers, query parameters, request path and different types of requests from just GET.</p>
<p>Lets start with the query parameters. To test this I&#8217;m going to augment our <code>Responder</code> class to return the request parameters as part of the headers.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">class</span> Responder &lt; <span class="re2">Goliath::API</span><br />
&nbsp; use <span class="re2">Goliath::Rack::Params</span></p>
<p>&nbsp; <span class="kw1">def</span> response<span class="br0">&#40;</span>env<span class="br0">&#41;</span><br />
&nbsp; &nbsp; query_params = env.<span class="me1">params</span>.<span class="me1">collect</span> <span class="br0">&#123;</span> |param| param.<span class="me1">join</span><span class="br0">&#40;</span><span class="st0">&quot;: &quot;</span><span class="br0">&#41;</span> <span class="br0">&#125;</span><br />
&nbsp; &nbsp; headers = <span class="br0">&#123;</span><span class="st0">&quot;Special&quot;</span> =&gt; <span class="st0">&quot;Header&quot;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="st0">&quot;Params&quot;</span> =&gt; query_params.<span class="me1">join</span><span class="br0">&#40;</span><span class="st0">&quot;|&quot;</span><span class="br0">&#41;</span><span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span><span class="nu0">200</span>, headers, <span class="st0">&quot;Hello from Responder&quot;</span><span class="br0">&#93;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>We&#8217;re using another middleware here, <code>Goliath::Rack::Params</code><code>, which will parse the query and body parameters and put them into the </code><code>params</code> hash of the environment. Using that we can send the parameters back as a header in the response.</p>
<p>The test for query parameters is pretty simple, pass them in, make sure they come back:</p>
<div class="dean_ch" style="white-space: wrap;">context <span class="st0">&#8216;query parameters&#8217;</span> <span class="kw1">do</span><br />
&nbsp; it <span class="st0">&#8216;forwards the query parameters&#8217;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; with_api<span class="br0">&#40;</span>HttpLog<span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; &nbsp; server<span class="br0">&#40;</span>Responder, <span class="nu0">8080</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; &nbsp; get_request<span class="br0">&#40;</span><span class="br0">&#123;</span>:query =&gt; <span class="br0">&#123;</span>:first =&gt; <span class="re3">:foo</span>, <span class="re3">:second</span> =&gt; <span class="re3">:bar</span>, <span class="re3">:third</span> =&gt; <span class="re3">:baz</span><span class="br0">&#125;</span><span class="br0">&#125;</span>, err<span class="br0">&#41;</span> <span class="kw1">do</span> |c|<br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span>.<span class="me1">status</span>.<span class="me1">should</span> == <span class="nu0">200</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span><span class="br0">&#91;</span><span class="st0">&quot;PARAMS&quot;</span><span class="br0">&#93;</span>.<span class="me1">should</span> == <span class="st0">&quot;first: foo|second: bar|third: baz&quot;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>To make this pass, we just need to <code>use Goliath::Rack::Params</code> and change the <code>EM::HttpRequest#get</code> request to provide the params. This is done by using: <code>req = EM::HttpRequest.new("#{env.forwarder}").get({:query =&gt; env.params})</code>.</p>
<p>Next up, let&#8217;s handle the request path. We&#8217;ll start with the spec.</p>
<div class="dean_ch" style="white-space: wrap;">context <span class="st0">&#8216;request path&#8217;</span> <span class="kw1">do</span><br />
&nbsp; it <span class="st0">&#8216;forwards the request path&#8217;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; with_api<span class="br0">&#40;</span>HttpLog<span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; &nbsp; server<span class="br0">&#40;</span>Responder, <span class="nu0">8080</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; &nbsp; get_request<span class="br0">&#40;</span><span class="br0">&#123;</span>:path =&gt; <span class="st0">&#8216;/my/request/path&#8217;</span><span class="br0">&#125;</span>, err<span class="br0">&#41;</span> <span class="kw1">do</span> |c|<br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span>.<span class="me1">status</span>.<span class="me1">should</span> == <span class="nu0">200</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span><span class="br0">&#91;</span><span class="st0">&#8216;PATH&#8217;</span><span class="br0">&#93;</span>.<span class="me1">should</span> == <span class="st0">&#8216;/my/request/path&#8217;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>Our <code>Responder</code> needs to return the path in the headers. This is done by adding <code>"Path" =&gt; env[Goliath::Request::REQUEST_PATH]</code> into the headers return hash. Making this spec pass is as simple as using the same <code>env[Goliath::Request::REQUEST_PATH]</code> data in our forwarder request.</p>
<div class="dean_ch" style="white-space: wrap;">req = <span class="re2">EM::HttpRequest</span>.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&quot;#{env.forwarder}#{env[Goliath::Request::REQUEST_PATH]}&quot;</span><span class="br0">&#41;</span>.<span class="me1">get</span><span class="br0">&#40;</span>params<span class="br0">&#41;</span></div>
<p>With the request path out of the way, let&#8217;s look into the headers. Goliath has built in facilities to do streaming requests. As part of that, we can hook into the <code>on_headers</code> method to receive a callback with the headers when they&#8217;re available. We can then store them into the environment and return them in a similar fashion to the query parameters.</p>
<p>We&#8217;re going to use the same <code>on_headers</code> callback in the <code>Responder</code> class to give us access to the headers. Then, the same as with query params, we&#8217;ll return them in a header for the response.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">class</span> Responder &lt; <span class="re2">Goliath::API</span><br />
&nbsp; use <span class="re2">Goliath::Rack::Params</span></p>
<p>&nbsp; <span class="kw1">def</span> on_headers<span class="br0">&#40;</span>env, headers<span class="br0">&#41;</span><br />
&nbsp; &nbsp; env<span class="br0">&#91;</span><span class="st0">&#8216;client-headers&#8217;</span><span class="br0">&#93;</span> = headers<br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; <span class="kw1">def</span> response<span class="br0">&#40;</span>env<span class="br0">&#41;</span><br />
&nbsp; &nbsp; query_params = env.<span class="me1">params</span>.<span class="me1">collect</span> <span class="br0">&#123;</span> |param| param.<span class="me1">join</span><span class="br0">&#40;</span><span class="st0">&quot;: &quot;</span><span class="br0">&#41;</span> <span class="br0">&#125;</span><br />
&nbsp; &nbsp; query_headers = env<span class="br0">&#91;</span><span class="st0">&#8216;client-headers&#8217;</span><span class="br0">&#93;</span>.<span class="me1">collect</span> <span class="br0">&#123;</span> |param| param.<span class="me1">join</span><span class="br0">&#40;</span><span class="st0">&quot;: &quot;</span><span class="br0">&#41;</span> <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; headers = <span class="br0">&#123;</span><span class="st0">&quot;Special&quot;</span> =&gt; <span class="st0">&quot;Header&quot;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="st0">&quot;Params&quot;</span> =&gt; query_params.<span class="me1">join</span><span class="br0">&#40;</span><span class="st0">&quot;|&quot;</span><span class="br0">&#41;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="st0">&quot;Path&quot;</span> =&gt; env<span class="br0">&#91;</span><span class="re2">Goliath::Request::REQUEST_PATH</span><span class="br0">&#93;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="st0">&quot;Headers&quot;</span> =&gt; query_headers.<span class="me1">join</span><span class="br0">&#40;</span><span class="st0">&quot;|&quot;</span><span class="br0">&#41;</span><span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span><span class="nu0">200</span>, headers, <span class="st0">&quot;Hello from Responder&quot;</span><span class="br0">&#93;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>With a spec test similar to the query example as well.</p>
<div class="dean_ch" style="white-space: wrap;">context <span class="st0">&#8216;headers&#8217;</span> <span class="kw1">do</span><br />
&nbsp; it <span class="st0">&#8216;forwards the headers&#8217;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; with_api<span class="br0">&#40;</span>HttpLog<span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; &nbsp; server<span class="br0">&#40;</span>Responder, <span class="nu0">8080</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; &nbsp; get_request<span class="br0">&#40;</span><span class="br0">&#123;</span>:head =&gt; <span class="br0">&#123;</span>:first =&gt; <span class="re3">:foo</span>, <span class="re3">:second</span> =&gt; <span class="re3">:bar</span><span class="br0">&#125;</span><span class="br0">&#125;</span>, err<span class="br0">&#41;</span> <span class="kw1">do</span> |c|<br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span>.<span class="me1">status</span>.<span class="me1">should</span> == <span class="nu0">200</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span><span class="br0">&#91;</span><span class="st0">&quot;HEADERS&quot;</span><span class="br0">&#93;</span>.<span class="me1">should</span> =~ /First: foo\|Second: bar/<br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>Let&#8217;s make the tests pass:</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> on_headers<span class="br0">&#40;</span>env, headers<span class="br0">&#41;</span><br />
&nbsp; env.<span class="me1">logger</span>.<span class="me1">info</span> <span class="st0">&#8216;proxying new request: &#8216;</span> + headers.<span class="me1">inspect</span><br />
&nbsp; env<span class="br0">&#91;</span><span class="st0">&#8216;client-headers&#8217;</span><span class="br0">&#93;</span> = headers<br />
<span class="kw1">end</span></div>
<p>You&#8217;ll notice I added a call to <code>env.logger.info</code>. Goliath comes with built in logging capabilities. The logger is based on <code>Log4r</code> and is accessed through the environment. The Goliath environment is a subclass of <code>Hash</code> and can be accessed as such to store and retrieve information.</p>
<p>Once we&#8217;ve got the headers stored, adding them to our request is a simple process.</p>
<div class="dean_ch" style="white-space: wrap;">params = <span class="br0">&#123;</span>:head =&gt; env<span class="br0">&#91;</span><span class="st0">&#8216;client-headers&#8217;</span><span class="br0">&#93;</span>, <span class="re3">:query</span> =&gt; env.<span class="me1">params</span><span class="br0">&#125;</span><br />
req = <span class="re2">EM::HttpRequest</span>.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&quot;#{env.forwarder}#{env[Goliath::Request::REQUEST_PATH]}&quot;</span><span class="br0">&#41;</span>.<span class="me1">get</span><span class="br0">&#40;</span>params<span class="br0">&#41;</span></div>
<p>One step left and our proxy is complete. Let&#8217;s send the right request method through to the proxied server (we&#8217;re just going to do GET and POST in this example). First step, add <code>"Method" =&gt; env[Goliath::Request::REQUEST_METHOD]</code> to the headers returned from our <code>Responder</code> API.</p>
<p>We can then add the specs:</p>
<div class="dean_ch" style="white-space: wrap;">context <span class="st0">&#8216;request method&#8217;</span> <span class="kw1">do</span><br />
&nbsp; it <span class="st0">&#8216;forwards GET requests&#8217;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; with_api<span class="br0">&#40;</span>HttpLog<span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; &nbsp; server<span class="br0">&#40;</span>Responder, <span class="nu0">8080</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; &nbsp; get_request<span class="br0">&#40;</span><span class="br0">&#123;</span><span class="br0">&#125;</span>, err<span class="br0">&#41;</span> <span class="kw1">do</span> |c|<br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span>.<span class="me1">status</span>.<span class="me1">should</span> == <span class="nu0">200</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span><span class="br0">&#91;</span><span class="st0">&quot;METHOD&quot;</span><span class="br0">&#93;</span>.<span class="me1">should</span> == <span class="st0">&quot;GET&quot;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; it <span class="st0">&#8216;forwards POST requests&#8217;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; with_api<span class="br0">&#40;</span>HttpLog<span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; &nbsp; server<span class="br0">&#40;</span>Responder, <span class="nu0">8080</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; &nbsp; post_request<span class="br0">&#40;</span><span class="br0">&#123;</span><span class="br0">&#125;</span>, err<span class="br0">&#41;</span> <span class="kw1">do</span> |c|<br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span>.<span class="me1">status</span>.<span class="me1">should</span> == <span class="nu0">200</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span><span class="br0">&#91;</span><span class="st0">&quot;METHOD&quot;</span><span class="br0">&#93;</span>.<span class="me1">should</span> == <span class="st0">&quot;POST&quot;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>To make this pass we&#8217;ll use the <code>Goliath::Request::REQUEST_METHOD</code> to determine the right type of request to make. You&#8217;ll notice I&#8217;m putting the response into a <code>resp</code> variable, so the references to <code>req</code> in the rest of the method need to be update as well.</p>
<div class="dean_ch" style="white-space: wrap;">req = <span class="re2">EM::HttpRequest</span>.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&quot;#{forwarder}#{env[Goliath::Request::REQUEST_PATH]}&quot;</span><span class="br0">&#41;</span><br />
resp = <span class="kw1">case</span><span class="br0">&#40;</span>env<span class="br0">&#91;</span><span class="re2">Goliath::Request::REQUEST_METHOD</span><span class="br0">&#93;</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">when</span> <span class="st0">&#8216;GET&#8217;</span> &nbsp;<span class="kw1">then</span> req.<span class="me1">get</span><span class="br0">&#40;</span>params<span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">when</span> <span class="st0">&#8216;POST&#8217;</span> <span class="kw1">then</span> req.<span class="me1">post</span><span class="br0">&#40;</span>params.<span class="me1">merge</span><span class="br0">&#40;</span><span class="re3">:body</span> =&gt; env<span class="br0">&#91;</span><span class="re2">Goliath::Request::RACK_INPUT</span><span class="br0">&#93;</span>.<span class="me1">read</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">else</span> <span class="kw3">p</span> <span class="st0">&quot;UNKNOWN METHOD #{env[Goliath::Request::REQUEST_METHOD]}&quot;</span><br />
<span class="kw1">end</span></div>
<p>The only significant change from the previous version, in the POST request, we need to send the body data through to the forwarded server. The body is stored in a <code>StringIO</code> object which we can <code>read</code>. The object is stored in the environment under the <code>Goliath::Request::RACK_INPUT</code> key.</p>
<p>With that done, we should now have a functioning HTTP proxy.</p>
<p>Let&#8217;s take a look at how we can hook this up to <em>MongoDB</em> to give us the <em>Log</em> part of our the API name.</p>
<p>In order to talk to MongoDB we&#8217;re going to use the <em>em-mongo</em> gem. Since we&#8217;re working in an asynchronous environment we may have several requests being processed at the same time, so we&#8217;re going to wrap our MongoDB connection into a connection pool. There is connection pool logic built into <em>em-synchrony</em> for us to utilize.</p>
<p>I&#8217;m also, for the sake of this tutorial, going to say we don&#8217;t want to create a connection to a real Mongo instance when we&#8217;re running our spec tests. So, I&#8217;m going to restrict the connection creation to only happen in development mode.</p>
<p>Make sense? Ok, add the following to your <em>config/http_log.rb file.</em></p>
<div class="dean_ch" style="white-space: wrap;">environment<span class="br0">&#40;</span><span class="re3">:development</span><span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; config<span class="br0">&#91;</span><span class="st0">&#8216;mongo&#8217;</span><span class="br0">&#93;</span> = <span class="re2">EventMachine::Synchrony::ConnectionPool</span>.<span class="me1">new</span><span class="br0">&#40;</span>size: <span class="nu0">20</span><span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; conn = <span class="re2">EM::Mongo::Connection</span>.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&#8216;localhost&#8217;</span>, <span class="nu0">27017</span>, <span class="nu0">1</span>, <span class="br0">&#123;</span>:reconnect_in =&gt; <span class="nu0">1</span><span class="br0">&#125;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; conn.<span class="me1">db</span><span class="br0">&#40;</span><span class="st0">&#8216;http_log&#8217;</span><span class="br0">&#41;</span>.<span class="me1">collection</span><span class="br0">&#40;</span><span class="st0">&#8216;aggregators&#8217;</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>The <code>environment(:development)</code> will cause this code block to only execute if we&#8217;re in development mode. We could have also passed <code>:test</code> or <code>:production</code> depending on which mode we want. Along with a single option, you can also provide an array. So, we could have said <code>environment([:test, :development])</code> to execute in both test and development mode but not in production mode.</p>
<p>I&#8217;m going to leave it as an exercise to you, my reader, to figure out the synchrony and mongo code we&#8217;re using in the connection. Let&#8217;s just leave it at, we will now have access to a MongoDB collection object in <code>config['mongo']</code> which we can use in our application.</p>
<p>Just before we return the status, headers and body we&#8217;re going to record the request into Mongo. We&#8217;ll do that by adding:</p>
<div class="dean_ch" style="white-space: wrap;">record<span class="br0">&#40;</span>process_time, resp, env<span class="br0">&#91;</span><span class="st0">&#8216;client-headers&#8217;</span><span class="br0">&#93;</span>, response_headers<span class="br0">&#41;</span></div>
<p>The implementation of <code>record</code> uses the various environment variables we&#8217;ve seen above to do it&#8217;s work:</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> record<span class="br0">&#40;</span>resp, client_headers, response_headers<span class="br0">&#41;</span><br />
&nbsp; e = env<br />
&nbsp; EM.<span class="me1">next_tick</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; doc = <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; request: <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; http_method: e<span class="br0">&#91;</span><span class="re2">Goliath::Request::REQUEST_METHOD</span><span class="br0">&#93;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; path: e<span class="br0">&#91;</span><span class="re2">Goliath::Request::REQUEST_PATH</span><span class="br0">&#93;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; headers: client_headers,<br />
&nbsp; &nbsp; &nbsp; &nbsp; params: e.<span class="me1">params</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span>,<br />
&nbsp; &nbsp; &nbsp; response: <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; status: resp.<span class="me1">response_header</span>.<span class="me1">status</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; length: resp.<span class="me1">response</span>.<span class="me1">length</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; headers: response_headers,<br />
&nbsp; &nbsp; &nbsp; &nbsp; body: resp.<span class="me1">response</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span>,<br />
&nbsp; &nbsp; &nbsp; date: <span class="kw4">Time</span>.<span class="me1">now</span>.<span class="me1">to_i</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">if</span> e<span class="br0">&#91;</span><span class="re2">Goliath::Request::RACK_INPUT</span><span class="br0">&#93;</span><br />
&nbsp; &nbsp; &nbsp; doc<span class="br0">&#91;</span><span class="re3">:request</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="re3">:body</span><span class="br0">&#93;</span> = e<span class="br0">&#91;</span><span class="re2">Goliath::Request::RACK_INPUT</span><span class="br0">&#93;</span>.<span class="me1">read</span><br />
&nbsp; &nbsp; <span class="kw1">end</span></p>
<p>&nbsp; &nbsp; e.<span class="me1">mongo</span>.<span class="me1">insert</span><span class="br0">&#40;</span>doc<span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>There are a couple of things to note, first, I&#8217;m doing this work in an <code>EM.next_tick</code> block so that I don&#8217;t block the response from returning to the client. Because I&#8217;m doing this in the next tick I&#8217;m going to lose access to the <em>env</em> so I tuck it away into a <code>e</code> variable which gets bound up with the <code>next_tick</code> block.</p>
<p>Finally, the last thing we do is call <code>e.mongo.insert(doc)</code> which will access the <code>config['mongo']</code> object we created earlier and call the <code>insert</code> method.</p>
<p>With that code added, all of our tests fail. We&#8217;re going to need to create a <em>mongo</em> key in the environment that the tests can use.</p>
<p>To do that, I&#8217;ve created a <code>mock_mongo</code> method:</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> mock_mongo<br />
&nbsp; <span class="re1">@api_server</span>.<span class="me1">config</span><span class="br0">&#91;</span><span class="st0">&#8216;mongo&#8217;</span><span class="br0">&#93;</span> = mock<span class="br0">&#40;</span><span class="st0">&#8216;mongo&#8217;</span><span class="br0">&#41;</span>.<span class="me1">as_null_object</span><br />
<span class="kw1">end</span></div>
<p>Which I call inside the <code>with_api(HttpLog)</code> block. I&#8217;ll leave it as an exercise to the reader to create some specs around the <code>record</code> method.</p>
<p>The default API server created inside <code>with_api</code> will be made available to the user through the <code>@api_server</code> variable.</p>
<p>With that, you should have a working proxy logger API. If you run webserver on port 8080 and execute the our http logger you can then make requests to port 9000 and see the results logged into mongo. Looking in mongo you should see something similar to:</p>
<pre>connecting to: test
&gt; use http_log
switched to db http_log
&gt; db.aggregators.find();
{ "_id" : ObjectId("4d6efbbad3547d28f3000001"),
  "request" : {
                "http_method" : "GET",
                "path" : "/",
                "headers" : {
                              "User-Agent" : "curl/7.19.7 (universal-apple-darwin10.0) libcurl/7.19.7 OpenSSL/0.9.8l zlib/1.2.3",
                              "Host" : "localhost:9000",
                              "Accept" : "*/*",
                              "Version" : "1.1" },
                "params" : { "echo" : "test" },
                "body" : "" },
   "response" : {
                "status" : 200,
                "length" : 19,
                "headers" : {
                              "Content-Type" : "application/json; charset=utf-8",
                              "Server" : "PostRank Goliath API Server",
                              "Vary" : "Accept",
                              "Content-Length" : "19",
                              "Date" : "Thu, 03 Mar 2011 02:23:54 GMT" },
                "body" : "{\"response\":\"test\"}" },
   "process_time" : 0.007593870162963867,
   "date" : 1299119034 }</pre>
<p>With that, have fun playing with Goliath. We&#8217;re hoping people find some interesting uses for the framework.</p>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F&amp;title=Stage+left%3A+Enter+Goliath" title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F&amp;title=Stage+left%3A+Enter+Goliath" title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F&amp;title=Stage+left%3A+Enter+Goliath" title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F&amp;title=Stage+left%3A+Enter+Goliath" title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F&amp;title=Stage+left%3A+Enter+Goliath', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F&amp;title=Stage+left%3A+Enter+Goliath" title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F&amp;title=Stage+left%3A+Enter+Goliath" title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></description>
			<content:encoded><![CDATA[<p><img class="alignleft size-thumbnail wp-image-760" title="_MG_9957" src="http://everburning.com/wp-content/uploads/2011/03/MG_9957-150x150.jpg" alt="" width="150" height="150" />Over at <a href="http://labs.postrank.com">PostRank Labs</a> we&#8217;ve released the current version of our API server framework to the wild. Allow me to introduce <a href="http://goliath.io">Goliath</a>. Goliath is built on the back of the work at <a href="http://postrank.com">PostRank</a>, <a href="http://code.macournoyer.com/thin/">Thin</a>, <a href="http://www.sinatrarb.com/">Sinatra</a>, <a href="https://github.com/copiousfreetime/http-parser.rb">http-parser.rb</a> and various other projects.</p>
<p>We&#8217;ve been using a version of Goliath internally for over a year serving a sustained 500 requests/sec and shuttling around gigabytes of data.</p>
<p>How did we get here you&#8217;re asking? Our API servers from the beginning have been built on the awesome  <a href="http://rubyeventmachine.org">EventMachine</a> library. The last several iterations have used a threaded model. A request comes in, a thread is grabbed from the pool, request is processed, thread goes back to the pool. This was cool and worked quite well for us for several years. The problem we ran into, we were getting spaghetti code. A lot of our APIs call other APIs internally. Handling the callbacks, error backs and various other delayed operations was getting difficult to maintain.</p>
<p>Then along wandered <a href="http://ruby-lang.org">Ruby 1.9</a> with these cool little things called <a href="http://www.ruby-doc.org/core-1.9/classes/Fiber.html">Fibers</a>. Fibers allow us to build synchronous looking code backed by asynchronous calls. To achieve this we&#8217;re using the <a href="http://github.com/igrigorik/em-synchrony">EM-Synchrony</a> library. Tying this back into Goliath, as each request comes in, a new Fiber is created, the request is processed within that Fiber and, when completed, the Fiber goes away.</p>
<p>Once we had the foundation in place we went a step further and built a few extra little goodies into Goliath including <a href="https://github.com/postrank-labs/goliath/blob/master/examples/async_upload.rb">streaming requests</a> and <a href="https://github.com/postrank-labs/goliath/blob/master/examples/stream.rb">responses</a>. This lets us do some cool things like hook AMQP exchanges up to a Goliath API to pipe content through a streaming response.</p>
<p>Using http-parser.rb allows us to open up the option of alternate Ruby VMs. At the time of writing, Goliath has been tested with MRI, JRuby and Rubinius. MRI is currently the fastest VM for Goliath but there is lots of work going on in the alternate VMs to make them faster.</p>
<p>Ok, ok, enough of the hype, lets see some code already.</p>
<p>Well, first we need to get Goliath installed. In general this is pretty simple, you can use your system Ruby, RVM or some other system and all you need to do is:</p>
<div class="dean_ch" style="white-space: wrap;">gem install goliath</div>
<p>What we&#8217;re going to build here is an HTTP proxy that stores information about each request/response into <a href="http://www.mongodb.org/">MongoDB</a>. (The full code listing can be found in <a href="https://gist.github.com/7c2fe52d73fb290690cf">this gist</a>.)</p>
<p>Before we dig into the code, a little on how Goliath works. By default, Goliath will execute your API when you run the API file. In order to do this, the API file has to have a snake cased name based on the class name. So, our <code>HttpLog</code> API will live in a file called <code>http_log.rb</code>. When you need to add configuration to your API you&#8217;ll do this by creating a <code>config</code> directory in the same directory as your file. You then create a file in the <code>config</code> directory with the same name as your API. In our case, we&#8217;ll create a <code>config/http_log.rb</code> file to hold our configuration.</p>
<p>Goliath has been built to be <a href="http://rack.rubyforge.org/">Rack</a>-aware. This means we can make use of Rack middlewares, as long as they&#8217;re async safe or have been wrapped by <a href="https://github.com/rkh/async-rack">async-rack</a>.</p>
<p>We&#8217;re going to build some specs along with our API. Create <code>spec/http_log_spec.rb</code> and we can get this party started.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw3">require</span> <span class="st0">&#8216;rubygems&#8217;</span><br />
<span class="kw3">require</span> <span class="st0">&#8216;goliath/test_helper&#8217;</span><br />
<span class="kw3">require</span> <span class="kw4">File</span>.<span class="me1">expand_path</span><span class="br0">&#40;</span><span class="kw4">File</span>.<span class="me1">join</span><span class="br0">&#40;</span><span class="kw4">File</span>.<span class="me1">dirname</span><span class="br0">&#40;</span><span class="kw2">__FILE__</span><span class="br0">&#41;</span>, <span class="st0">&#8216;..&#8217;</span>, <span class="st0">&#8216;http_log&#8217;</span><span class="br0">&#41;</span><span class="br0">&#41;</span></p>
<p>describe HttpLog <span class="kw1">do</span><br />
&nbsp; <span class="kw1">include</span> <span class="re2">Goliath::TestHelper</span></p>
<p>&nbsp; let<span class="br0">&#40;</span><span class="re3">:err</span><span class="br0">&#41;</span> <span class="br0">&#123;</span> <span class="kw3">Proc</span>.<span class="me1">new</span> <span class="br0">&#123;</span> |c| <span class="kw3">fail</span> <span class="st0">&quot;HTTP Request failed #{c.response}&quot;</span> <span class="br0">&#125;</span> <span class="br0">&#125;</span></p>
<p>&nbsp; it <span class="st0">&#8216;responds to requests&#8217;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; with_api<span class="br0">&#40;</span>HttpLog<span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; &nbsp; get_request<span class="br0">&#40;</span><span class="br0">&#123;</span><span class="br0">&#125;</span>, err<span class="br0">&#41;</span> <span class="kw1">do</span> |c|<br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span>.<span class="me1">status</span>.<span class="me1">should</span> == <span class="nu0">200</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>With Goliath we&#8217;ve provided a simple <code>goliath/test_helper</code> file that makes the testing of APIs a little easier. Once you&#8217;ve required the library, you just need to <code>include Goliath::TestHelper</code> and you can start using the helper methods. These include the <code>with_api</code> and <code>get_request</code> methods that are being used above. The <code>with_api</code> call will start an instance of our API running on port 9000 and anything done within its block will executing inside an EM reactor. This method will not return until <code>stop</code> is executed.</p>
<p>We&#8217;re, essentially, creating an integration test suite for our API. Everything except for the API launching will be tested by the test suite. The configuration files will be parsed, all the middlewares will be loaded and executed. Everything that your normal application would do except for the launching.</p>
<p>Calling <code>get_request</code> will send a GET to our API. The first parameter is any query parameters to send to the API. In our case, we send nothing. The second is an error handler to add. This is fired if we are unable to communicate with the API. For this example, we&#8217;re just using the RSpec <code>fail</code> method to fail the spec in the error handler.</p>
<p>The block passed to <code>get_request</code> will be executed and provided the request object when the request is complete. This allows us to look at the response headers and response body. In this case, we&#8217;re just verifying that server responds with a 200 code. When you&#8217;re using <code>get_request</code> or <code>post_request</code> a default error and callback handler will be installed that calls <code>stop</code> for you.</p>
<p>With that, running the spec should fail. Let&#8217;s create a simple API to make it pass. The following code in <code>http_log.rb</code> will give us a passing spec.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="co1">#!/usr/bin/env ruby</span></p>
<p><span class="kw3">require</span> <span class="st0">&#8216;rubygems&#8217;</span><br />
<span class="kw3">require</span> <span class="st0">&#8216;goliath&#8217;</span></p>
<p><span class="kw1">class</span> HttpLog &lt; <span class="re2">Goliath::API</span><br />
&nbsp; <span class="kw1">def</span> response<span class="br0">&#40;</span>env<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span><span class="nu0">200</span>, <span class="br0">&#123;</span><span class="br0">&#125;</span>, <span class="st0">&quot;Hello&quot;</span><span class="br0">&#93;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>You can execute the API with <code>./http_log.rb -sv</code> which will launch the API on a default port of 9000.</p>
<p>The <code>-sv</code> flags tell the API to log to STDOUT (s) and use verbose logging (v). You can run <code>./http_log.rb -h</code> to see a list of all the default options provided by Goliath. You can add <a href="https://github.com/postrank-labs/goliath/blob/master/examples/conf_test.rb">your own options</a> as well if you need special argument handling for your API.</p>
<p>With the API running we can query the API using curl.</p>
<div class="dean_ch" style="white-space: wrap;">dj2@titania ~ $ curl localhost:<span class="nu0">9000</span><br />
Hello</div>
<p>Running the spec test again our test case should also pass at this point.</p>
<p>To make our lives easier, we&#8217;re going to use the <code>Rack::Reloader</code> middleware to handle reloading our Rack middleware on each request.</p>
<p>This is done by adding <code>use ::Rack::Reloader, 0</code> into our API. Since we only want reloading when we&#8217;re doing development we&#8217;re going to make the <code>use</code> statement conditional on the Goliath environment. By default Goliath executes in a development environment. We can confirm this by calling <code>Goliath.dev?</code> which will return <code>true</code> if the current environment is development.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">class</span> HttpLog &lt; <span class="re2">Goliath::API</span><br />
&nbsp; use ::<span class="re2">Rack::Reloader</span>, <span class="nu0">0</span> <span class="kw1">if</span> Goliath.<span class="me1">dev</span>?</p>
<p>&nbsp; <span class="kw1">def</span> response<span class="br0">&#40;</span>env<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span><span class="nu0">200</span>, <span class="br0">&#123;</span><span class="br0">&#125;</span>, <span class="st0">&quot;Hello&quot;</span><span class="br0">&#93;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>You&#8217;ll notice that we didn&#8217;t create another file to place the middleware. With Goliath, the middleware is built into the API class itself. We&#8217;ve found this is a lot easier to deal with when you keep everything together in the same file. (You can also add <a href="https://github.com/postrank-labs/goliath/wiki/Plugins">plugins</a> in the same fashion by using the <code>plugin</code> keyword.)</p>
<p>The next step is to start forwarding to our backend server. To do this, we&#8217;ll specify in our configuration file the forwarder URL. Create <code>config/http_log.rb</code> and add the following:</p>
<div class="dean_ch" style="white-space: wrap;">config<span class="br0">&#91;</span><span class="st0">&#8216;forwarder&#8217;</span><span class="br0">&#93;</span> = <span class="st0">&#8216;http://localhost:8080&#8242;</span></div>
<p>The <code>config</code> hash allows us to store configuration data that will become available through the <code>env</code> parameter to the <code>response</code> method. In this case, we&#8217;ve specified we want our forwarder to send requests to <code>http://localhost:8080</code>.</p>
<p>In order to test this, I&#8217;m going to create a Goliath API server inside our spec files. I&#8217;ll run the server on port 8080 and check for our server response in the response from our API. Add the following to the top of the spec file.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">class</span> Responder &lt; <span class="re2">Goliath::API</span><br />
&nbsp; <span class="kw1">def</span> response<span class="br0">&#40;</span>env<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span><span class="nu0">200</span>, <span class="br0">&#123;</span><span class="st0">&quot;Special&quot;</span> =&gt; <span class="st0">&quot;Header&quot;</span><span class="br0">&#125;</span>, <span class="st0">&quot;Hello World&quot;</span><span class="br0">&#93;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>And the following spec:</p>
<div class="dean_ch" style="white-space: wrap;">it <span class="st0">&#8216;forwards to our API server&#8217;</span> <span class="kw1">do</span><br />
&nbsp; with_api<span class="br0">&#40;</span>HttpLog<span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; server<span class="br0">&#40;</span>Responder, <span class="nu0">8080</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; get_request<span class="br0">&#40;</span><span class="br0">&#123;</span><span class="br0">&#125;</span>, err<span class="br0">&#41;</span> <span class="kw1">do</span> |c|<br />
&nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span>.<span class="me1">status</span>.<span class="me1">should</span> == <span class="nu0">200</span><br />
&nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span><span class="br0">&#91;</span><span class="st0">&#8216;Special&#8217;</span><span class="br0">&#93;</span>.<span class="me1">should</span> == <span class="st0">&#8216;Header&#8217;</span><br />
&nbsp; &nbsp; &nbsp; c.<span class="me1">response</span>.<span class="me1">should</span> == <span class="st0">&#8216;Hello from Responder&#8217;</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span></div>
<p>This is similar to our previous spec except I&#8217;ve added a <code>server(Responder, 8080)</code> call to launch an instance of our <code>Responder</code> API on port 8080. Running this test should fail since we aren&#8217;t sending or receiving any data to the proxied API.</p>
<p>As a first pass, we&#8217;ll send the request and proxy the responses back.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> response<span class="br0">&#40;</span>env<span class="br0">&#41;</span><br />
&nbsp; req = <span class="re2">EM::HttpRequest</span>.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&quot;#{env.forwarder}&quot;</span><span class="br0">&#41;</span>.<span class="me1">get</span></p>
<p>&nbsp; <span class="br0">&#91;</span>req.<span class="me1">response_header</span>.<span class="me1">status</span>, req.<span class="me1">response_header</span>, req.<span class="me1">response</span><span class="br0">&#93;</span><br />
<span class="kw1">end</span></div>
<p>So, that&#8217;s cool. (Make sure you add the <code>server(Responder, 8080)</code> to the first example after doing this or it will end up failing.) There is one, non-apparent problem. It turns out the headers get transformed by <code>EM-HTTP-Request</code>. When we go to log this stuff we&#8217;ll want the non-transformed headers so we need to transform them back into the normal HTTP header format. Let&#8217;s start with a couple tests:</p>
<div class="dean_ch" style="white-space: wrap;">context <span class="st0">&#8216;HTTP header handling&#8217;</span> <span class="kw1">do</span><br />
&nbsp; it <span class="st0">&#8216;transforms back properly&#8217;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; hl = HttpLog.<span class="me1">new</span><br />
&nbsp; &nbsp; hl.<span class="me1">to_http_header</span><span class="br0">&#40;</span><span class="st0">&quot;SPECIAL&quot;</span><span class="br0">&#41;</span>.<span class="me1">should</span> == <span class="st0">&#8216;Special&#8217;</span><br />
&nbsp; &nbsp; hl.<span class="me1">to_http_header</span><span class="br0">&#40;</span><span class="st0">&quot;CONTENT_TYPE&quot;</span><span class="br0">&#41;</span>.<span class="me1">should</span> == <span class="st0">&#8216;Content-Type&#8217;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>Simple enough, we need to fix the casing and change <code>_</code> into <code>-</code>. Let&#8217;s add that to our HttpLog class.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> to_http_header<span class="br0">&#40;</span>k<span class="br0">&#41;</span><br />
&nbsp; k.<span class="me1">downcase</span>.<span class="kw3">split</span><span class="br0">&#40;</span><span class="st0">&#8216;_&#8217;</span><span class="br0">&#41;</span>.<span class="me1">collect</span> <span class="br0">&#123;</span> |e| e.<span class="me1">capitalize</span> <span class="br0">&#125;</span>.<span class="me1">join</span><span class="br0">&#40;</span><span class="st0">&#8216;-&#8217;</span><span class="br0">&#41;</span><br />
<span class="kw1">end</span></div>
<p>That should make our transform tests pass and we can build the change into the <code>response</code> method by changing it to look like:</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> response<span class="br0">&#40;</span>env<span class="br0">&#41;</span><br />
&nbsp; req = <span class="re2">EM::HttpRequest</span>.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&quot;#{env.forwarder}&quot;</span><span class="br0">&#41;</span>.<span class="me1">get</span></p>
<p>&nbsp; response_headers = <span class="br0">&#123;</span><span class="br0">&#125;</span><br />
&nbsp; req.<span class="me1">response_header</span>.<span class="me1">each_pair</span> <span class="kw1">do</span> |k, v|<br />
&nbsp; &nbsp; response_headers<span class="br0">&#91;</span>to_http_header<span class="br0">&#40;</span>k<span class="br0">&#41;</span><span class="br0">&#93;</span> = v<br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; <span class="br0">&#91;</span>req.<span class="me1">response_header</span>.<span class="me1">status</span>, response_headers, req.<span class="me1">response</span><span class="br0">&#93;</span><br />
<span class="kw1">end</span></div>
<p>Cool, so now we&#8217;re forwarding all requests to the proxied server. There is a bit of missing information that we need, that being the headers, query parameters, request path and different types of requests from just GET.</p>
<p>Lets start with the query parameters. To test this I&#8217;m going to augment our <code>Responder</code> class to return the request parameters as part of the headers.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">class</span> Responder &lt; <span class="re2">Goliath::API</span><br />
&nbsp; use <span class="re2">Goliath::Rack::Params</span></p>
<p>&nbsp; <span class="kw1">def</span> response<span class="br0">&#40;</span>env<span class="br0">&#41;</span><br />
&nbsp; &nbsp; query_params = env.<span class="me1">params</span>.<span class="me1">collect</span> <span class="br0">&#123;</span> |param| param.<span class="me1">join</span><span class="br0">&#40;</span><span class="st0">&quot;: &quot;</span><span class="br0">&#41;</span> <span class="br0">&#125;</span><br />
&nbsp; &nbsp; headers = <span class="br0">&#123;</span><span class="st0">&quot;Special&quot;</span> =&gt; <span class="st0">&quot;Header&quot;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="st0">&quot;Params&quot;</span> =&gt; query_params.<span class="me1">join</span><span class="br0">&#40;</span><span class="st0">&quot;|&quot;</span><span class="br0">&#41;</span><span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span><span class="nu0">200</span>, headers, <span class="st0">&quot;Hello from Responder&quot;</span><span class="br0">&#93;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>We&#8217;re using another middleware here, <code>Goliath::Rack::Params</code><code>, which will parse the query and body parameters and put them into the </code><code>params</code> hash of the environment. Using that we can send the parameters back as a header in the response.</p>
<p>The test for query parameters is pretty simple, pass them in, make sure they come back:</p>
<div class="dean_ch" style="white-space: wrap;">context <span class="st0">&#8216;query parameters&#8217;</span> <span class="kw1">do</span><br />
&nbsp; it <span class="st0">&#8216;forwards the query parameters&#8217;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; with_api<span class="br0">&#40;</span>HttpLog<span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; &nbsp; server<span class="br0">&#40;</span>Responder, <span class="nu0">8080</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; &nbsp; get_request<span class="br0">&#40;</span><span class="br0">&#123;</span>:query =&gt; <span class="br0">&#123;</span>:first =&gt; <span class="re3">:foo</span>, <span class="re3">:second</span> =&gt; <span class="re3">:bar</span>, <span class="re3">:third</span> =&gt; <span class="re3">:baz</span><span class="br0">&#125;</span><span class="br0">&#125;</span>, err<span class="br0">&#41;</span> <span class="kw1">do</span> |c|<br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span>.<span class="me1">status</span>.<span class="me1">should</span> == <span class="nu0">200</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span><span class="br0">&#91;</span><span class="st0">&quot;PARAMS&quot;</span><span class="br0">&#93;</span>.<span class="me1">should</span> == <span class="st0">&quot;first: foo|second: bar|third: baz&quot;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>To make this pass, we just need to <code>use Goliath::Rack::Params</code> and change the <code>EM::HttpRequest#get</code> request to provide the params. This is done by using: <code>req = EM::HttpRequest.new("#{env.forwarder}").get({:query =&gt; env.params})</code>.</p>
<p>Next up, let&#8217;s handle the request path. We&#8217;ll start with the spec.</p>
<div class="dean_ch" style="white-space: wrap;">context <span class="st0">&#8216;request path&#8217;</span> <span class="kw1">do</span><br />
&nbsp; it <span class="st0">&#8216;forwards the request path&#8217;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; with_api<span class="br0">&#40;</span>HttpLog<span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; &nbsp; server<span class="br0">&#40;</span>Responder, <span class="nu0">8080</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; &nbsp; get_request<span class="br0">&#40;</span><span class="br0">&#123;</span>:path =&gt; <span class="st0">&#8216;/my/request/path&#8217;</span><span class="br0">&#125;</span>, err<span class="br0">&#41;</span> <span class="kw1">do</span> |c|<br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span>.<span class="me1">status</span>.<span class="me1">should</span> == <span class="nu0">200</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span><span class="br0">&#91;</span><span class="st0">&#8216;PATH&#8217;</span><span class="br0">&#93;</span>.<span class="me1">should</span> == <span class="st0">&#8216;/my/request/path&#8217;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>Our <code>Responder</code> needs to return the path in the headers. This is done by adding <code>"Path" =&gt; env[Goliath::Request::REQUEST_PATH]</code> into the headers return hash. Making this spec pass is as simple as using the same <code>env[Goliath::Request::REQUEST_PATH]</code> data in our forwarder request.</p>
<div class="dean_ch" style="white-space: wrap;">req = <span class="re2">EM::HttpRequest</span>.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&quot;#{env.forwarder}#{env[Goliath::Request::REQUEST_PATH]}&quot;</span><span class="br0">&#41;</span>.<span class="me1">get</span><span class="br0">&#40;</span>params<span class="br0">&#41;</span></div>
<p>With the request path out of the way, let&#8217;s look into the headers. Goliath has built in facilities to do streaming requests. As part of that, we can hook into the <code>on_headers</code> method to receive a callback with the headers when they&#8217;re available. We can then store them into the environment and return them in a similar fashion to the query parameters.</p>
<p>We&#8217;re going to use the same <code>on_headers</code> callback in the <code>Responder</code> class to give us access to the headers. Then, the same as with query params, we&#8217;ll return them in a header for the response.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">class</span> Responder &lt; <span class="re2">Goliath::API</span><br />
&nbsp; use <span class="re2">Goliath::Rack::Params</span></p>
<p>&nbsp; <span class="kw1">def</span> on_headers<span class="br0">&#40;</span>env, headers<span class="br0">&#41;</span><br />
&nbsp; &nbsp; env<span class="br0">&#91;</span><span class="st0">&#8216;client-headers&#8217;</span><span class="br0">&#93;</span> = headers<br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; <span class="kw1">def</span> response<span class="br0">&#40;</span>env<span class="br0">&#41;</span><br />
&nbsp; &nbsp; query_params = env.<span class="me1">params</span>.<span class="me1">collect</span> <span class="br0">&#123;</span> |param| param.<span class="me1">join</span><span class="br0">&#40;</span><span class="st0">&quot;: &quot;</span><span class="br0">&#41;</span> <span class="br0">&#125;</span><br />
&nbsp; &nbsp; query_headers = env<span class="br0">&#91;</span><span class="st0">&#8216;client-headers&#8217;</span><span class="br0">&#93;</span>.<span class="me1">collect</span> <span class="br0">&#123;</span> |param| param.<span class="me1">join</span><span class="br0">&#40;</span><span class="st0">&quot;: &quot;</span><span class="br0">&#41;</span> <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; headers = <span class="br0">&#123;</span><span class="st0">&quot;Special&quot;</span> =&gt; <span class="st0">&quot;Header&quot;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="st0">&quot;Params&quot;</span> =&gt; query_params.<span class="me1">join</span><span class="br0">&#40;</span><span class="st0">&quot;|&quot;</span><span class="br0">&#41;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="st0">&quot;Path&quot;</span> =&gt; env<span class="br0">&#91;</span><span class="re2">Goliath::Request::REQUEST_PATH</span><span class="br0">&#93;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="st0">&quot;Headers&quot;</span> =&gt; query_headers.<span class="me1">join</span><span class="br0">&#40;</span><span class="st0">&quot;|&quot;</span><span class="br0">&#41;</span><span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span><span class="nu0">200</span>, headers, <span class="st0">&quot;Hello from Responder&quot;</span><span class="br0">&#93;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>With a spec test similar to the query example as well.</p>
<div class="dean_ch" style="white-space: wrap;">context <span class="st0">&#8216;headers&#8217;</span> <span class="kw1">do</span><br />
&nbsp; it <span class="st0">&#8216;forwards the headers&#8217;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; with_api<span class="br0">&#40;</span>HttpLog<span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; &nbsp; server<span class="br0">&#40;</span>Responder, <span class="nu0">8080</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; &nbsp; get_request<span class="br0">&#40;</span><span class="br0">&#123;</span>:head =&gt; <span class="br0">&#123;</span>:first =&gt; <span class="re3">:foo</span>, <span class="re3">:second</span> =&gt; <span class="re3">:bar</span><span class="br0">&#125;</span><span class="br0">&#125;</span>, err<span class="br0">&#41;</span> <span class="kw1">do</span> |c|<br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span>.<span class="me1">status</span>.<span class="me1">should</span> == <span class="nu0">200</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span><span class="br0">&#91;</span><span class="st0">&quot;HEADERS&quot;</span><span class="br0">&#93;</span>.<span class="me1">should</span> =~ /First: foo\|Second: bar/<br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>Let&#8217;s make the tests pass:</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> on_headers<span class="br0">&#40;</span>env, headers<span class="br0">&#41;</span><br />
&nbsp; env.<span class="me1">logger</span>.<span class="me1">info</span> <span class="st0">&#8216;proxying new request: &#8216;</span> + headers.<span class="me1">inspect</span><br />
&nbsp; env<span class="br0">&#91;</span><span class="st0">&#8216;client-headers&#8217;</span><span class="br0">&#93;</span> = headers<br />
<span class="kw1">end</span></div>
<p>You&#8217;ll notice I added a call to <code>env.logger.info</code>. Goliath comes with built in logging capabilities. The logger is based on <code>Log4r</code> and is accessed through the environment. The Goliath environment is a subclass of <code>Hash</code> and can be accessed as such to store and retrieve information.</p>
<p>Once we&#8217;ve got the headers stored, adding them to our request is a simple process.</p>
<div class="dean_ch" style="white-space: wrap;">params = <span class="br0">&#123;</span>:head =&gt; env<span class="br0">&#91;</span><span class="st0">&#8216;client-headers&#8217;</span><span class="br0">&#93;</span>, <span class="re3">:query</span> =&gt; env.<span class="me1">params</span><span class="br0">&#125;</span><br />
req = <span class="re2">EM::HttpRequest</span>.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&quot;#{env.forwarder}#{env[Goliath::Request::REQUEST_PATH]}&quot;</span><span class="br0">&#41;</span>.<span class="me1">get</span><span class="br0">&#40;</span>params<span class="br0">&#41;</span></div>
<p>One step left and our proxy is complete. Let&#8217;s send the right request method through to the proxied server (we&#8217;re just going to do GET and POST in this example). First step, add <code>"Method" =&gt; env[Goliath::Request::REQUEST_METHOD]</code> to the headers returned from our <code>Responder</code> API.</p>
<p>We can then add the specs:</p>
<div class="dean_ch" style="white-space: wrap;">context <span class="st0">&#8216;request method&#8217;</span> <span class="kw1">do</span><br />
&nbsp; it <span class="st0">&#8216;forwards GET requests&#8217;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; with_api<span class="br0">&#40;</span>HttpLog<span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; &nbsp; server<span class="br0">&#40;</span>Responder, <span class="nu0">8080</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; &nbsp; get_request<span class="br0">&#40;</span><span class="br0">&#123;</span><span class="br0">&#125;</span>, err<span class="br0">&#41;</span> <span class="kw1">do</span> |c|<br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span>.<span class="me1">status</span>.<span class="me1">should</span> == <span class="nu0">200</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span><span class="br0">&#91;</span><span class="st0">&quot;METHOD&quot;</span><span class="br0">&#93;</span>.<span class="me1">should</span> == <span class="st0">&quot;GET&quot;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; it <span class="st0">&#8216;forwards POST requests&#8217;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; with_api<span class="br0">&#40;</span>HttpLog<span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; &nbsp; server<span class="br0">&#40;</span>Responder, <span class="nu0">8080</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; &nbsp; post_request<span class="br0">&#40;</span><span class="br0">&#123;</span><span class="br0">&#125;</span>, err<span class="br0">&#41;</span> <span class="kw1">do</span> |c|<br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span>.<span class="me1">status</span>.<span class="me1">should</span> == <span class="nu0">200</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; c.<span class="me1">response_header</span><span class="br0">&#91;</span><span class="st0">&quot;METHOD&quot;</span><span class="br0">&#93;</span>.<span class="me1">should</span> == <span class="st0">&quot;POST&quot;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>To make this pass we&#8217;ll use the <code>Goliath::Request::REQUEST_METHOD</code> to determine the right type of request to make. You&#8217;ll notice I&#8217;m putting the response into a <code>resp</code> variable, so the references to <code>req</code> in the rest of the method need to be update as well.</p>
<div class="dean_ch" style="white-space: wrap;">req = <span class="re2">EM::HttpRequest</span>.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&quot;#{forwarder}#{env[Goliath::Request::REQUEST_PATH]}&quot;</span><span class="br0">&#41;</span><br />
resp = <span class="kw1">case</span><span class="br0">&#40;</span>env<span class="br0">&#91;</span><span class="re2">Goliath::Request::REQUEST_METHOD</span><span class="br0">&#93;</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">when</span> <span class="st0">&#8216;GET&#8217;</span> &nbsp;<span class="kw1">then</span> req.<span class="me1">get</span><span class="br0">&#40;</span>params<span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">when</span> <span class="st0">&#8216;POST&#8217;</span> <span class="kw1">then</span> req.<span class="me1">post</span><span class="br0">&#40;</span>params.<span class="me1">merge</span><span class="br0">&#40;</span><span class="re3">:body</span> =&gt; env<span class="br0">&#91;</span><span class="re2">Goliath::Request::RACK_INPUT</span><span class="br0">&#93;</span>.<span class="me1">read</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">else</span> <span class="kw3">p</span> <span class="st0">&quot;UNKNOWN METHOD #{env[Goliath::Request::REQUEST_METHOD]}&quot;</span><br />
<span class="kw1">end</span></div>
<p>The only significant change from the previous version, in the POST request, we need to send the body data through to the forwarded server. The body is stored in a <code>StringIO</code> object which we can <code>read</code>. The object is stored in the environment under the <code>Goliath::Request::RACK_INPUT</code> key.</p>
<p>With that done, we should now have a functioning HTTP proxy.</p>
<p>Let&#8217;s take a look at how we can hook this up to <em>MongoDB</em> to give us the <em>Log</em> part of our the API name.</p>
<p>In order to talk to MongoDB we&#8217;re going to use the <em>em-mongo</em> gem. Since we&#8217;re working in an asynchronous environment we may have several requests being processed at the same time, so we&#8217;re going to wrap our MongoDB connection into a connection pool. There is connection pool logic built into <em>em-synchrony</em> for us to utilize.</p>
<p>I&#8217;m also, for the sake of this tutorial, going to say we don&#8217;t want to create a connection to a real Mongo instance when we&#8217;re running our spec tests. So, I&#8217;m going to restrict the connection creation to only happen in development mode.</p>
<p>Make sense? Ok, add the following to your <em>config/http_log.rb file.</em></p>
<div class="dean_ch" style="white-space: wrap;">environment<span class="br0">&#40;</span><span class="re3">:development</span><span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; config<span class="br0">&#91;</span><span class="st0">&#8216;mongo&#8217;</span><span class="br0">&#93;</span> = <span class="re2">EventMachine::Synchrony::ConnectionPool</span>.<span class="me1">new</span><span class="br0">&#40;</span>size: <span class="nu0">20</span><span class="br0">&#41;</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; conn = <span class="re2">EM::Mongo::Connection</span>.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&#8216;localhost&#8217;</span>, <span class="nu0">27017</span>, <span class="nu0">1</span>, <span class="br0">&#123;</span>:reconnect_in =&gt; <span class="nu0">1</span><span class="br0">&#125;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; conn.<span class="me1">db</span><span class="br0">&#40;</span><span class="st0">&#8216;http_log&#8217;</span><span class="br0">&#41;</span>.<span class="me1">collection</span><span class="br0">&#40;</span><span class="st0">&#8216;aggregators&#8217;</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>The <code>environment(:development)</code> will cause this code block to only execute if we&#8217;re in development mode. We could have also passed <code>:test</code> or <code>:production</code> depending on which mode we want. Along with a single option, you can also provide an array. So, we could have said <code>environment([:test, :development])</code> to execute in both test and development mode but not in production mode.</p>
<p>I&#8217;m going to leave it as an exercise to you, my reader, to figure out the synchrony and mongo code we&#8217;re using in the connection. Let&#8217;s just leave it at, we will now have access to a MongoDB collection object in <code>config['mongo']</code> which we can use in our application.</p>
<p>Just before we return the status, headers and body we&#8217;re going to record the request into Mongo. We&#8217;ll do that by adding:</p>
<div class="dean_ch" style="white-space: wrap;">record<span class="br0">&#40;</span>process_time, resp, env<span class="br0">&#91;</span><span class="st0">&#8216;client-headers&#8217;</span><span class="br0">&#93;</span>, response_headers<span class="br0">&#41;</span></div>
<p>The implementation of <code>record</code> uses the various environment variables we&#8217;ve seen above to do it&#8217;s work:</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> record<span class="br0">&#40;</span>resp, client_headers, response_headers<span class="br0">&#41;</span><br />
&nbsp; e = env<br />
&nbsp; EM.<span class="me1">next_tick</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; doc = <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; request: <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; http_method: e<span class="br0">&#91;</span><span class="re2">Goliath::Request::REQUEST_METHOD</span><span class="br0">&#93;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; path: e<span class="br0">&#91;</span><span class="re2">Goliath::Request::REQUEST_PATH</span><span class="br0">&#93;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; headers: client_headers,<br />
&nbsp; &nbsp; &nbsp; &nbsp; params: e.<span class="me1">params</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span>,<br />
&nbsp; &nbsp; &nbsp; response: <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; status: resp.<span class="me1">response_header</span>.<span class="me1">status</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; length: resp.<span class="me1">response</span>.<span class="me1">length</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; headers: response_headers,<br />
&nbsp; &nbsp; &nbsp; &nbsp; body: resp.<span class="me1">response</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span>,<br />
&nbsp; &nbsp; &nbsp; date: <span class="kw4">Time</span>.<span class="me1">now</span>.<span class="me1">to_i</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">if</span> e<span class="br0">&#91;</span><span class="re2">Goliath::Request::RACK_INPUT</span><span class="br0">&#93;</span><br />
&nbsp; &nbsp; &nbsp; doc<span class="br0">&#91;</span><span class="re3">:request</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="re3">:body</span><span class="br0">&#93;</span> = e<span class="br0">&#91;</span><span class="re2">Goliath::Request::RACK_INPUT</span><span class="br0">&#93;</span>.<span class="me1">read</span><br />
&nbsp; &nbsp; <span class="kw1">end</span></p>
<p>&nbsp; &nbsp; e.<span class="me1">mongo</span>.<span class="me1">insert</span><span class="br0">&#40;</span>doc<span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>There are a couple of things to note, first, I&#8217;m doing this work in an <code>EM.next_tick</code> block so that I don&#8217;t block the response from returning to the client. Because I&#8217;m doing this in the next tick I&#8217;m going to lose access to the <em>env</em> so I tuck it away into a <code>e</code> variable which gets bound up with the <code>next_tick</code> block.</p>
<p>Finally, the last thing we do is call <code>e.mongo.insert(doc)</code> which will access the <code>config['mongo']</code> object we created earlier and call the <code>insert</code> method.</p>
<p>With that code added, all of our tests fail. We&#8217;re going to need to create a <em>mongo</em> key in the environment that the tests can use.</p>
<p>To do that, I&#8217;ve created a <code>mock_mongo</code> method:</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> mock_mongo<br />
&nbsp; <span class="re1">@api_server</span>.<span class="me1">config</span><span class="br0">&#91;</span><span class="st0">&#8216;mongo&#8217;</span><span class="br0">&#93;</span> = mock<span class="br0">&#40;</span><span class="st0">&#8216;mongo&#8217;</span><span class="br0">&#41;</span>.<span class="me1">as_null_object</span><br />
<span class="kw1">end</span></div>
<p>Which I call inside the <code>with_api(HttpLog)</code> block. I&#8217;ll leave it as an exercise to the reader to create some specs around the <code>record</code> method.</p>
<p>The default API server created inside <code>with_api</code> will be made available to the user through the <code>@api_server</code> variable.</p>
<p>With that, you should have a working proxy logger API. If you run webserver on port 8080 and execute the our http logger you can then make requests to port 9000 and see the results logged into mongo. Looking in mongo you should see something similar to:</p>
<pre>connecting to: test
&gt; use http_log
switched to db http_log
&gt; db.aggregators.find();
{ "_id" : ObjectId("4d6efbbad3547d28f3000001"),
  "request" : {
                "http_method" : "GET",
                "path" : "/",
                "headers" : {
                              "User-Agent" : "curl/7.19.7 (universal-apple-darwin10.0) libcurl/7.19.7 OpenSSL/0.9.8l zlib/1.2.3",
                              "Host" : "localhost:9000",
                              "Accept" : "*/*",
                              "Version" : "1.1" },
                "params" : { "echo" : "test" },
                "body" : "" },
   "response" : {
                "status" : 200,
                "length" : 19,
                "headers" : {
                              "Content-Type" : "application/json; charset=utf-8",
                              "Server" : "PostRank Goliath API Server",
                              "Vary" : "Accept",
                              "Content-Length" : "19",
                              "Date" : "Thu, 03 Mar 2011 02:23:54 GMT" },
                "body" : "{\"response\":\"test\"}" },
   "process_time" : 0.007593870162963867,
   "date" : 1299119034 }</pre>
<p>With that, have fun playing with Goliath. We&#8217;re hoping people find some interesting uses for the framework.</p>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F&amp;title=Stage+left%3A+Enter+Goliath" title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F&amp;title=Stage+left%3A+Enter+Goliath" title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F&amp;title=Stage+left%3A+Enter+Goliath" title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F&amp;title=Stage+left%3A+Enter+Goliath" title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F&amp;title=Stage+left%3A+Enter+Goliath', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F&amp;title=Stage+left%3A+Enter+Goliath" title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fstage-left-enter-goliath%2F&amp;title=Stage+left%3A+Enter+Goliath" title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></content:encoded>
			<wfw:commentRss>http://everburning.com/news/stage-left-enter-goliath/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Ramblings on programming Cocoa with Ruby</title>
		<link>http://everburning.com/news/ramblings-on-programming-cocoa-with-ruby/</link>
		<comments>http://everburning.com/news/ramblings-on-programming-cocoa-with-ruby/#comments</comments>
		<pubDate>Thu, 17 Jun 2010 04:51:46 +0000</pubDate>
		<dc:creator>dj2</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[Computers]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[HotCocoa]]></category>
		<category><![CDATA[MacRuby]]></category>

		<guid isPermaLink="false">http://everburning.com/?p=743</guid>
		<description><![CDATA[<p><img src="http://everburning.com/wp-content/uploads/2010/06/IMG_8072-150x150.jpg" alt="" title="IMG_8072" width="150" height="150" class="alignleft size-thumbnail wp-image-742" />This post started with a point. It didn&#8217;t end with one. So, I&#8217;m going to start it again and just get to the point. What makes a good Ruby Cocoa development environment. I&#8217;ll get into my rambling thoughts below, but quickly; straight up XCode/Interface Builder, <a href="http://www.macruby.org/hotcocoa.html">HotCocoa</a>, Interface Builder and command line building, some combination of all three or something else.</p>
<p>If you were going to do it, how, or what would you do?</p>
<p>I&#8217;ve been playing with the above three options as I&#8217;ve trying to figure this out myself. I&#8217;ve done a few straight HotCocoa applications (<a href="http://github.com/dj2/SilverLining">SilverLining</a>, <a href="http://github.com/dj2/Rife">Rife</a> and <a href="http://github.com/dj2/Postie">Postie</a>) and, while they worked out well, I found the need to do all the layout by hand tedious and boring. I really like Interface Builder for ease of UI creation.</p>
<p>Taking that, I tried the XCode/Interface Builder solution and went to the bundled <a href="http://macruby.org">MacRuby</a> XCode templates. When programming Objective-C I really enjoy XCode, it makes things easy when doing purely native Mac applications. That said, XCode irks me when doing Ruby development. Maybe it&#8217;s muscle memory, not sure, but I like using TextMate to do my Ruby work.</p>
<p>The approach I&#8217;ve been using in my latest fiddlings (<a href="http://github.com/dj2/Touchstone">Touchstone</a>) started with the XCode templates but I broke out, stole some of the rake tasks from HotCocoa, and switched to TextMate and the command line.</p>
<p>The biggest issue I&#8217;ve run into is the Interface Builder integration kinda sucks. It does work, but you have to reload the class files manually all the time. A bit frustrating.</p>
<p>On the flip side, I can organize my code however I want. The tendency for Objective-C programs to have all their code in the root directory drives me up the wall. Sure, XCode hides this, you can change it, but, by default, everything ends up in the root folder. Maddening.</p>
<p>With Touchstone, I&#8217;ve put all the application code, similar to Rails, under App/Controllers, App/Models, App/Helpers and App/Views. The same structure is copied into the .app file when it&#8217;s generated. Everything feels much cleaning. The .xib and .xcdatamodel files get compiled and moved as needed.</p>
<p>I know I could have used the XCode build stuff but having to launch XCode and getting the debugging output in an XCode window didn&#8217;t feel right for me. I wanted something a bit more contained.</p>
<p>Coming back to the original question, I think what I want to see, is something similar to Touchstone but with the convenience of HotCocoa. The HotCocoa mappings make a lot of things feel more Rubyish, feel more natural. Those bindings, combined with Interface Builder, I think, would make a kick-ass Cocoa programming environment.</p>
<p>Again, how would you do it?</p>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Framblings-on-programming-cocoa-with-ruby%2F&amp;title=Ramblings+on+programming+Cocoa+with+Ruby" title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Framblings-on-programming-cocoa-with-ruby%2F&amp;title=Ramblings+on+programming+Cocoa+with+Ruby" title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Framblings-on-programming-cocoa-with-ruby%2F&amp;title=Ramblings+on+programming+Cocoa+with+Ruby" title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Framblings-on-programming-cocoa-with-ruby%2F&amp;title=Ramblings+on+programming+Cocoa+with+Ruby" title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Framblings-on-programming-cocoa-with-ruby%2F&amp;title=Ramblings+on+programming+Cocoa+with+Ruby', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Framblings-on-programming-cocoa-with-ruby%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Framblings-on-programming-cocoa-with-ruby%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Framblings-on-programming-cocoa-with-ruby%2F&amp;title=Ramblings+on+programming+Cocoa+with+Ruby" title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Framblings-on-programming-cocoa-with-ruby%2F&amp;title=Ramblings+on+programming+Cocoa+with+Ruby" title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></description>
			<content:encoded><![CDATA[<p><img src="http://everburning.com/wp-content/uploads/2010/06/IMG_8072-150x150.jpg" alt="" title="IMG_8072" width="150" height="150" class="alignleft size-thumbnail wp-image-742" />This post started with a point. It didn&#8217;t end with one. So, I&#8217;m going to start it again and just get to the point. What makes a good Ruby Cocoa development environment. I&#8217;ll get into my rambling thoughts below, but quickly; straight up XCode/Interface Builder, <a href="http://www.macruby.org/hotcocoa.html">HotCocoa</a>, Interface Builder and command line building, some combination of all three or something else.</p>
<p>If you were going to do it, how, or what would you do?</p>
<p>I&#8217;ve been playing with the above three options as I&#8217;ve trying to figure this out myself. I&#8217;ve done a few straight HotCocoa applications (<a href="http://github.com/dj2/SilverLining">SilverLining</a>, <a href="http://github.com/dj2/Rife">Rife</a> and <a href="http://github.com/dj2/Postie">Postie</a>) and, while they worked out well, I found the need to do all the layout by hand tedious and boring. I really like Interface Builder for ease of UI creation.</p>
<p>Taking that, I tried the XCode/Interface Builder solution and went to the bundled <a href="http://macruby.org">MacRuby</a> XCode templates. When programming Objective-C I really enjoy XCode, it makes things easy when doing purely native Mac applications. That said, XCode irks me when doing Ruby development. Maybe it&#8217;s muscle memory, not sure, but I like using TextMate to do my Ruby work.</p>
<p>The approach I&#8217;ve been using in my latest fiddlings (<a href="http://github.com/dj2/Touchstone">Touchstone</a>) started with the XCode templates but I broke out, stole some of the rake tasks from HotCocoa, and switched to TextMate and the command line.</p>
<p>The biggest issue I&#8217;ve run into is the Interface Builder integration kinda sucks. It does work, but you have to reload the class files manually all the time. A bit frustrating.</p>
<p>On the flip side, I can organize my code however I want. The tendency for Objective-C programs to have all their code in the root directory drives me up the wall. Sure, XCode hides this, you can change it, but, by default, everything ends up in the root folder. Maddening.</p>
<p>With Touchstone, I&#8217;ve put all the application code, similar to Rails, under App/Controllers, App/Models, App/Helpers and App/Views. The same structure is copied into the .app file when it&#8217;s generated. Everything feels much cleaning. The .xib and .xcdatamodel files get compiled and moved as needed.</p>
<p>I know I could have used the XCode build stuff but having to launch XCode and getting the debugging output in an XCode window didn&#8217;t feel right for me. I wanted something a bit more contained.</p>
<p>Coming back to the original question, I think what I want to see, is something similar to Touchstone but with the convenience of HotCocoa. The HotCocoa mappings make a lot of things feel more Rubyish, feel more natural. Those bindings, combined with Interface Builder, I think, would make a kick-ass Cocoa programming environment.</p>
<p>Again, how would you do it?</p>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Framblings-on-programming-cocoa-with-ruby%2F&amp;title=Ramblings+on+programming+Cocoa+with+Ruby" title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Framblings-on-programming-cocoa-with-ruby%2F&amp;title=Ramblings+on+programming+Cocoa+with+Ruby" title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Framblings-on-programming-cocoa-with-ruby%2F&amp;title=Ramblings+on+programming+Cocoa+with+Ruby" title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Framblings-on-programming-cocoa-with-ruby%2F&amp;title=Ramblings+on+programming+Cocoa+with+Ruby" title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Framblings-on-programming-cocoa-with-ruby%2F&amp;title=Ramblings+on+programming+Cocoa+with+Ruby', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Framblings-on-programming-cocoa-with-ruby%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Framblings-on-programming-cocoa-with-ruby%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Framblings-on-programming-cocoa-with-ruby%2F&amp;title=Ramblings+on+programming+Cocoa+with+Ruby" title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Framblings-on-programming-cocoa-with-ruby%2F&amp;title=Ramblings+on+programming+Cocoa+with+Ruby" title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></content:encoded>
			<wfw:commentRss>http://everburning.com/news/ramblings-on-programming-cocoa-with-ruby/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Introducing QueryStringParser</title>
		<link>http://everburning.com/news/introducing-querystringparser/</link>
		<comments>http://everburning.com/news/introducing-querystringparser/#comments</comments>
		<pubDate>Thu, 17 Jun 2010 01:18:40 +0000</pubDate>
		<dc:creator>dj2</dc:creator>
				<category><![CDATA[Computers]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Library]]></category>
		<category><![CDATA[Query String Parser]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://everburning.com/?p=735</guid>
		<description><![CDATA[<p><img src="http://everburning.com/wp-content/uploads/2010/06/IMG_8227-150x150.jpg" alt="" title="IMG_8227" width="150" height="150" class="alignleft size-thumbnail wp-image-739" />Over at <a href="http://postrank.com">PostRank</a> we serve a lot of data through our API servers. All of those servers parse query strings. Along with, obviously, the query string, we also encode the post data in query string format. This means we&#8217;re doing a lot of parsing of query strings. Millions of query strings a day.</p>
<p>Parsing time for those query strings starts to add up. We started with the query string parser from <a href="http://camping.rubyforge.org/">Camping</a> and that served us well for over a year. Eventually, parsing the query data was over 80% of the processing time and something needed to be done. From there we briefly dabbled with the <a href="http://rack.rubyforge.org/doc/classes/Rack/Utils.html#M000084">Rack::Utils</a> query string parser. While faster, this still wasn&#8217;t fast enough.</p>
<p>From Rack, in a fit of desperation, we wrote our own. With the new version, written in C using Inline Ruby, we were finally fast enough. The parser has served us well but was starting to show some age with the Inline Ruby. We&#8217;ve started using Ruby 1.9 for our new API servers while the old servers were on 1.8. RVM doesn&#8217;t deal with Ruby Inline very well and we&#8217;d end up having to erase the Inline cache when switching between server versions. To that end, we converted the Inline Ruby version into a Ruby extension and, to help deal with <a href="hhttp://gembundler.com/">Bundler</a>, decided to push it out the door as a gem.</p>
<p>You can check it out on <a href="http://github.com/dj2/query_string_parser">Github</a> or install the gem from gemcutter. At this point, the gem does what we need it too. It doesn&#8217;t handle every case, but you should be able to update it, if needed, to handle what you need.</p>
<pre lang='ruby'>
require 'query_string_parser'
p QueryStringParser.qs_parse("my=query&#038;string=parser")
=> {"my"=>"query", "string"=>"parser"}

include QueryStringParser
qs_parse("does[]=array&#038;does[]=data&#038;does[3]=values&#038;does=more")
=> {"does"=>["array", "data", "values", "more"]}
</pre>
<p>As you can see from the example above individual params will be set their values. If a parameter exists multiple times it will be turned into an array of values. You can set a number into the brackets but it will be ignored by the parser.</p>
<p>If you&#8217;re parsing a lot of query strings give the parser a try. Let us know what you think.</p>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Fintroducing-querystringparser%2F&amp;title=Introducing+QueryStringParser" title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fintroducing-querystringparser%2F&amp;title=Introducing+QueryStringParser" title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fintroducing-querystringparser%2F&amp;title=Introducing+QueryStringParser" title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Fintroducing-querystringparser%2F&amp;title=Introducing+QueryStringParser" title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fintroducing-querystringparser%2F&amp;title=Introducing+QueryStringParser', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Fintroducing-querystringparser%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Fintroducing-querystringparser%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Fintroducing-querystringparser%2F&amp;title=Introducing+QueryStringParser" title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fintroducing-querystringparser%2F&amp;title=Introducing+QueryStringParser" title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></description>
			<content:encoded><![CDATA[<p><img src="http://everburning.com/wp-content/uploads/2010/06/IMG_8227-150x150.jpg" alt="" title="IMG_8227" width="150" height="150" class="alignleft size-thumbnail wp-image-739" />Over at <a href="http://postrank.com">PostRank</a> we serve a lot of data through our API servers. All of those servers parse query strings. Along with, obviously, the query string, we also encode the post data in query string format. This means we&#8217;re doing a lot of parsing of query strings. Millions of query strings a day.</p>
<p>Parsing time for those query strings starts to add up. We started with the query string parser from <a href="http://camping.rubyforge.org/">Camping</a> and that served us well for over a year. Eventually, parsing the query data was over 80% of the processing time and something needed to be done. From there we briefly dabbled with the <a href="http://rack.rubyforge.org/doc/classes/Rack/Utils.html#M000084">Rack::Utils</a> query string parser. While faster, this still wasn&#8217;t fast enough.</p>
<p>From Rack, in a fit of desperation, we wrote our own. With the new version, written in C using Inline Ruby, we were finally fast enough. The parser has served us well but was starting to show some age with the Inline Ruby. We&#8217;ve started using Ruby 1.9 for our new API servers while the old servers were on 1.8. RVM doesn&#8217;t deal with Ruby Inline very well and we&#8217;d end up having to erase the Inline cache when switching between server versions. To that end, we converted the Inline Ruby version into a Ruby extension and, to help deal with <a href="hhttp://gembundler.com/">Bundler</a>, decided to push it out the door as a gem.</p>
<p>You can check it out on <a href="http://github.com/dj2/query_string_parser">Github</a> or install the gem from gemcutter. At this point, the gem does what we need it too. It doesn&#8217;t handle every case, but you should be able to update it, if needed, to handle what you need.</p>
<pre lang='ruby'>
require 'query_string_parser'
p QueryStringParser.qs_parse("my=query&#038;string=parser")
=> {"my"=>"query", "string"=>"parser"}

include QueryStringParser
qs_parse("does[]=array&#038;does[]=data&#038;does[3]=values&#038;does=more")
=> {"does"=>["array", "data", "values", "more"]}
</pre>
<p>As you can see from the example above individual params will be set their values. If a parameter exists multiple times it will be turned into an array of values. You can set a number into the brackets but it will be ignored by the parser.</p>
<p>If you&#8217;re parsing a lot of query strings give the parser a try. Let us know what you think.</p>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Fintroducing-querystringparser%2F&amp;title=Introducing+QueryStringParser" title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fintroducing-querystringparser%2F&amp;title=Introducing+QueryStringParser" title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fintroducing-querystringparser%2F&amp;title=Introducing+QueryStringParser" title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Fintroducing-querystringparser%2F&amp;title=Introducing+QueryStringParser" title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fintroducing-querystringparser%2F&amp;title=Introducing+QueryStringParser', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Fintroducing-querystringparser%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Fintroducing-querystringparser%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Fintroducing-querystringparser%2F&amp;title=Introducing+QueryStringParser" title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fintroducing-querystringparser%2F&amp;title=Introducing+QueryStringParser" title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></content:encoded>
			<wfw:commentRss>http://everburning.com/news/introducing-querystringparser/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Download and XML parsing with HotCocoa</title>
		<link>http://everburning.com/news/download-and-xml-parsing-with-hotcocoa/</link>
		<comments>http://everburning.com/news/download-and-xml-parsing-with-hotcocoa/#comments</comments>
		<pubDate>Mon, 08 Feb 2010 05:47:16 +0000</pubDate>
		<dc:creator>dj2</dc:creator>
				<category><![CDATA[Article]]></category>
		<category><![CDATA[Computers]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[HotCocoa]]></category>
		<category><![CDATA[MacRuby]]></category>

		<guid isPermaLink="false">http://everburning.com/?p=700</guid>
		<description><![CDATA[<p><a href="http://everburning.com/wp-content/uploads/2010/02/IMG_7535.jpg"><img src="http://everburning.com/wp-content/uploads/2010/02/IMG_7535-150x150.jpg" alt="" title="IMG_7535" width="150" height="150" class="alignleft size-thumbnail wp-image-702" /></a>I&#8217;ve been working on <a href="http://github.com/dj2/Rife">Rife</a>, a <a href="http://reader.google.com">Google Reader</a> client, over the last few days and have been digging my way through some more <a href="http://github.com/richkilmer/hotcocoa">HotCocoa</a> mappings. I figured the best way to remember some of this stuff is to write it down so, the following will look at synchronous and asynchronous downloads and writing a XML parser in HotCocoa/<a href="http://macruby.org">MacRuby</a>.</p>
<p>So, what are we creating you ask? Well, as I said, I&#8217;ve been playing with Google Reader APIs and we&#8217;re going to do a synchronous request to Google for an identifier token. Once we&#8217;ve successfully authenticated we&#8217;re going to make an asynchronous request for our unread items. We&#8217;ll then parse the resulting XML document and spit the titles out to the console.</p>
<p>As with any HotCocoa application the easiest way to get started is to have system setup the shell of our application. We&#8217;ll use the <code>hotcocoa</code> command to create our application which I&#8217;m calling <em>titles</em>.</p>
<pre><code>titania:example dj2$ hotcocoa titles</code></pre>
<p>In order to authenticate to Google we&#8217;re going to need your username and password. Since I&#8217;m going to do the output to the console for demonstration purposes I&#8217;ll use the main application window to show the fields for username and password and a save button.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw3">require</span> <span class="st0">&#8216;rubygems&#8217;</span><br />
<span class="kw3">require</span> <span class="st0">&#8216;hotcocoa&#8217;</span></p>
<p><span class="kw1">class</span> Application<br />
&nbsp; <span class="kw1">include</span> HotCocoa<br />
&nbsp; <br />
&nbsp; <span class="kw1">def</span> start<br />
&nbsp; &nbsp; application<span class="br0">&#40;</span><span class="re3">:name</span> =&gt; <span class="st0">&quot;Titles&quot;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |app|<br />
&nbsp; &nbsp; &nbsp; app.<span class="me1">delegate</span> = <span class="kw2">self</span></p>
<p>&nbsp; &nbsp; &nbsp; window<span class="br0">&#40;</span><span class="re3">:frame</span> =&gt; <span class="br0">&#91;</span><span class="nu0">100</span>, <span class="nu0">100</span>, <span class="nu0">200</span>, <span class="nu0">200</span><span class="br0">&#93;</span>, <span class="re3">:title</span> =&gt; <span class="st0">&quot;Titles&quot;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |win|<br />
&nbsp; &nbsp; &nbsp; &nbsp; win.<span class="me1">center</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; win.<span class="me1">will_close</span> <span class="br0">&#123;</span> <span class="kw3">exit</span> <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; win &lt;&lt; label<span class="br0">&#40;</span><span class="re3">:text</span> =&gt; <span class="st0">&quot;Username&quot;</span>, <span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:start =&gt; <span class="kw2">false</span><span class="br0">&#125;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; win &lt;&lt; <span class="re1">@username_field</span> = text_field<span class="br0">&#40;</span><span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:start =&gt; <span class="kw2">false</span>, <span class="re3">:expand</span> =&gt; <span class="br0">&#91;</span><span class="re3">:width</span><span class="br0">&#93;</span><span class="br0">&#125;</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; win &lt;&lt; label<span class="br0">&#40;</span><span class="re3">:text</span> =&gt; <span class="st0">&quot;Password&quot;</span>, <span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:start =&gt; <span class="kw2">false</span><span class="br0">&#125;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; win &lt;&lt; <span class="re1">@password_field</span> = secure_text_field<span class="br0">&#40;</span><span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:start =&gt; <span class="kw2">false</span>, <span class="re3">:expand</span> =&gt; <span class="br0">&#91;</span><span class="re3">:width</span><span class="br0">&#93;</span><span class="br0">&#125;</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; win &lt;&lt; save = button<span class="br0">&#40;</span><span class="re3">:title</span> =&gt; <span class="st0">&quot;save&quot;</span>, <span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:start =&gt; <span class="kw2">true</span><span class="br0">&#125;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |button|<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; button.<span class="me1">on_action</span> <span class="br0">&#123;</span> authenticate <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">@username_field</span>.<span class="me1">setNextKeyView</span><span class="br0">&#40;</span>@password_field<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">@password_field</span>.<span class="me1">setNextKeyView</span><span class="br0">&#40;</span>save<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; save.<span class="me1">setNextKeyView</span><span class="br0">&#40;</span>@username_field<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; <span class="kw1">def</span> authenticate<br />
&nbsp; &nbsp; <span class="kw3">puts</span> <span class="st0">&quot;DO AUTH #{@username_field.to_s} #{@password_field.to_s}&quot;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></p>
<p>Application.<span class="me1">new</span>.<span class="me1">start</span></div>
<p>If you run the application by executing <code>macrake</code> in the <em>titles</em> directory you should see the main application window. Typing something into the <em>username</em> and <em>password</em> fields and pressing <em>save</em> you should see something similar to the following in your terminal.</p>
<pre><code>DO AUTH test test</code></pre>
<p>With our framework setup let&#8217;s get to the interesting stuff. First up, authenticating so we can retrieve our identifier from Google. We&#8217;re going to make a synchronous request to retrieve the identifier and, if successful, call a method to start retrieving our reading list.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> authenticate<br />
&nbsp; username = <span class="re1">@username_field</span>.<span class="me1">stringValue</span><br />
&nbsp; password = <span class="re1">@password_field</span>.<span class="me1">stringValue</span></p>
<p>&nbsp; query = <span class="st0">&quot;https://www.google.com/accounts/ClientLogin?&quot;</span> +<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&quot;Email=#{CGI.escape(username)}&amp;Passwd=#{CGI.escape(password.to_s)}&quot;</span> +<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&quot;&amp;source=HotCocoaExample&amp;service=reader&quot;</span></p>
<p>&nbsp; url = NSURL.<span class="me1">URLWithString</span><span class="br0">&#40;</span>query<span class="br0">&#41;</span><br />
&nbsp; request = NSMutableURLRequest.<span class="me1">requestWithURL</span><span class="br0">&#40;</span>url<span class="br0">&#41;</span><br />
&nbsp; request.<span class="me1">addValue</span><span class="br0">&#40;</span><span class="st0">&quot;HotCocoaExample&quot;</span>, forHTTPHeaderField:<span class="st0">&quot;source&quot;</span><span class="br0">&#41;</span><br />
&nbsp; request.<span class="me1">addValue</span><span class="br0">&#40;</span><span class="st0">&quot;2&quot;</span>, forHTTPHeaderField:<span class="st0">&quot;GData-Version&quot;</span><span class="br0">&#41;</span></p>
<p>&nbsp; response = Pointer.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&quot;@&quot;</span><span class="br0">&#41;</span><br />
&nbsp; data = NSURLConnection.<span class="me1">sendSynchronousRequest</span><span class="br0">&#40;</span>request, returningResponse:response, error:<span class="kw2">nil</span><span class="br0">&#41;</span><br />
&nbsp; data = NSString.<span class="me1">alloc</span>.<span class="me1">initWithData</span><span class="br0">&#40;</span>data, encoding:NSUTF8StringEncoding<span class="br0">&#41;</span></p>
<p>&nbsp; <span class="kw1">if</span> data =~ /^SID=<span class="br0">&#40;</span>.<span class="me1">*</span><span class="br0">&#41;</span>\n/<br />
&nbsp; &nbsp; <span class="re1">@sid</span> = $<span class="nu0">1</span></p>
<p>&nbsp; &nbsp; retrieve_reading_list<br />
&nbsp; <span class="kw1">else</span><br />
&nbsp; &nbsp; <span class="kw3">raise</span> <span class="kw4">Exception</span>.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&quot;Authentication failed with: #{data}&quot;</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></p>
<p><span class="kw1">def</span> retrieve_reading_list<br />
&nbsp; <span class="kw3">puts</span> <span class="re1">@sid</span><br />
<span class="kw1">end</span></div>
<p>If you add the above to your application, and add <code>require 'cgi'</code>, you should be able to run the program, put in your username and password and get a long line of characters spit out on the terminal. Those characters are your Google <em>SID</em>.</p>
<p>Let&#8217;s look a bit closer at what we&#8217;re doing in the <code>authenticate</code> method. We start by grabbing the <code>stringValue</code> for the username and password fields. Then, using these values, we build the query string needed for authentication. This query string is used to build a URL object by calling <code>NSURL.URLWithString(query)</code>. With the URL in hand we can start building our request object. This is done by calling <code>NSMutableURLRequest.requestWithURL(url)</code>. I&#8217;m using the mutable version of the request as I want to add a few extra header values. These are both added with <code>addValue(value, forHTTPHeaderField:field)</code>.</p>
<p>When we execute our request the system is going to want to put our response object somewhere. In the Cocoa version the method accepts a <code>NSURLResponse **response</code> parameter. In order to handle the response we need to create a <code>Pointer</code> object which is a MacRuby object for handling these pointers to objects. We want our pointer to point to an object so we use <code>Pointer.new("@")</code>.</p>
<p>With the response setup we call <code>NSURLConnection.sendSynchronousRequest</code> and provide our request and response objects. I don&#8217;t care about the error, but if you do, you&#8217;d want to pass in something similar to our response pointer. The request will return a <code>NSData</code> object which we convert to a string using the <code>initWithData</code> initialization method of <code>NSString</code>.</p>
<p>With the string in hand we try to extract our <em>SID</em> and, if successful, execute the <code>retrieve_reading_list</code> method which just spits out the <em>SID</em>.</p>
<p>OK, cool, we&#8217;ve now got our authentication token and are ready to move onto the asynchronous request to get our reading list.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> retrieve_reading_list<br />
&nbsp; query = <span class="st0">&quot;https://www.google.com/reader/atom/user/-/state/com.google/reading-list?&quot;</span> +<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&quot;xt=user/-/state/com.google/read&amp;ck=#{Time.now.to_i * 1000}&amp;n=2&quot;</span></p>
<p>&nbsp; url = NSURL.<span class="me1">URLWithString</span><span class="br0">&#40;</span>query<span class="br0">&#41;</span><br />
&nbsp; request = NSMutableURLRequest.<span class="me1">requestWithURL</span><span class="br0">&#40;</span>url<span class="br0">&#41;</span><br />
&nbsp; request.<span class="me1">addValue</span><span class="br0">&#40;</span><span class="st0">&quot;HotCocoaExample&quot;</span>, forHTTPHeaderField:<span class="st0">&quot;source&quot;</span><span class="br0">&#41;</span><br />
&nbsp; request.<span class="me1">addValue</span><span class="br0">&#40;</span><span class="st0">&quot;2&quot;</span>, forHTTPHeaderField:<span class="st0">&quot;GData-Version&quot;</span><span class="br0">&#41;</span><br />
&nbsp; request.<span class="me1">addValue</span><span class="br0">&#40;</span><span class="st0">&quot;SID=#{@sid}&quot;</span>, forHTTPHeaderField:<span class="st0">&quot;Cookie&quot;</span><span class="br0">&#41;</span></p>
<p>&nbsp; NSURLConnection.<span class="me1">connectionWithRequest</span><span class="br0">&#40;</span>request, delegate:<span class="kw2">self</span><span class="br0">&#41;</span><br />
<span class="kw1">end</span></p>
<p><span class="kw1">def</span> connectionDidFinishLoading<span class="br0">&#40;</span>conn<span class="br0">&#41;</span><br />
&nbsp; <span class="kw3">puts</span> NSString.<span class="me1">alloc</span>.<span class="me1">initWithData</span><span class="br0">&#40;</span>@receivedData, encoding:NSUTF8StringEncoding<span class="br0">&#41;</span><br />
<span class="kw1">end</span></p>
<p><span class="kw1">def</span> connection<span class="br0">&#40;</span>conn, didReceiveResponse:response<span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">if</span> response.<span class="me1">statusCode</span> != <span class="nu0">200</span><br />
&nbsp; &nbsp; <span class="kw3">puts</span> <span class="st0">&quot;BAD STATUS: #{response.statusCode}&quot;</span><br />
&nbsp; &nbsp; <span class="kw3">p</span> response.<span class="me1">allHeaderFields</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></p>
<p><span class="kw1">def</span> connection<span class="br0">&#40;</span>conn, didReceiveData:data<span class="br0">&#41;</span><br />
&nbsp; <span class="re1">@receivedData</span> ||= NSMutableData.<span class="me1">new</span><br />
&nbsp; <span class="re1">@receivedData</span>.<span class="me1">appendData</span><span class="br0">&#40;</span>data<span class="br0">&#41;</span><br />
<span class="kw1">end</span></div>
<p>Similar to the synchronous method we start by building our query string, <code>NSURL</code> and <code>NSMutableURLRequest</code>. We&#8217;ve added a cookie to our request object to hold the <em>SID</em> retrieved earlier from Google.</p>
<p>We fire the request by calling <code>NSURLConnection.connectionWithRequest(request, delegate:self)</code>. We specific ourselves as the delegate for the connection. There are a few delegate methods we can implement to receive data and get notified of request states. These are:</p>
<ul>
<li><code>connectionDidFinishLoading(connection)</code></li>
<li><code>connection(connection, didReceiveResponse:response)</code></li>
<li><code>connection(connection, didReceiveData:data)</code></li>
</ul>
<p>We&#8217;ll look at our implementation of each of these callbacks in turn. First, in <code>connectionDidFinishLoading(conn)</code> we&#8217;re just printing out the data retrieved. We need to convert the data, similar to what we did in the synchronous request, from a <code>NSData</code> object to a <code>NSString</code> object.</p>
<p>In <code>connection(conn, didReceiveResponse:response)</code> we&#8217;re just checking to see if we got a 200 response code from the server. In all other cases we print an error.</p>
<p>The main work is done in <code>connection(conn, didReceiveData:data)</code> where we create a <code>NSMutableData</code> object if needed and append any data received into the mutable data object.</p>
<p>Running the code at this point should dump the first two items in your reading list to the console. The data will be a big mess of XML but we&#8217;ll look at parsing that in the next step.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> connectionDidFinishLoading<span class="br0">&#40;</span>conn<span class="br0">&#41;</span><br />
&nbsp; xml = HotCocoa.<span class="me1">xml_parser</span><span class="br0">&#40;</span><span class="re3">:data</span> =&gt; <span class="re1">@receivedData</span><span class="br0">&#41;</span><br />
&nbsp; <span class="re1">@receivedData</span> = <span class="kw2">nil</span></p>
<p>&nbsp; xml.<span class="me1">on_start_document</span> <span class="br0">&#123;</span> <span class="kw3">puts</span> <span class="st0">&quot;Starting Parse&quot;</span> <span class="br0">&#125;</span><br />
&nbsp; xml.<span class="me1">on_end_document</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; HotCocoa.<span class="me1">notification</span><span class="br0">&#40;</span><span class="re3">:post</span> =&gt; <span class="kw2">true</span>, <span class="re3">:name</span> =&gt; <span class="st0">&quot;all_entries_loaded&quot;</span>, <span class="re3">:object</span> =&gt; <span class="kw2">nil</span>, <span class="re3">:info</span> =&gt; <span class="kw2">nil</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">end</span><br />
&nbsp; xml.<span class="me1">on_parse_error</span> <span class="br0">&#123;</span> |err| <span class="kw3">puts</span> <span class="st0">&quot;Parse error #{err.inspect}&quot;</span> <span class="br0">&#125;</span></p>
<p>&nbsp; xml.<span class="me1">on_cdata</span> <span class="br0">&#123;</span> |cdata| <span class="re1">@elem_text</span> += cdata.<span class="me1">to_s</span> <span class="br0">&#125;</span><br />
&nbsp; xml.<span class="me1">on_characters</span> <span class="br0">&#123;</span> |chars| <span class="re1">@elem_text</span> += chars.<span class="me1">to_s</span> <span class="br0">&#125;</span></p>
<p>&nbsp; xml.<span class="me1">on_start_element</span> <span class="kw1">do</span> |element, namespace, qualified_name, attributes|<br />
&nbsp; &nbsp; <span class="re1">@elem_text</span> = <span class="st0">&#8221;</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; xml.<span class="me1">on_end_element</span> <span class="kw1">do</span> |element, namespace, qualified_name|<br />
&nbsp; &nbsp; <span class="kw3">puts</span> <span class="re1">@elem_text</span> <span class="kw1">if</span> element == <span class="st0">&#8216;title&#8217;</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; xml.<span class="me1">parse</span><br />
<span class="kw1">end</span></div>
<p>We&#8217;re finally getting into some HotCocoa specific code with our XML parser. HotCocoa defines a mapping wrapper around <code>NSXMLParser</code> and provides a set of delegate methods. These delegates mean we don&#8217;t have to set our class as the delegate and create a bunch of methods. They mean we can attach our code as blocks on our XML object. All the better if you want to define a few parsers in one class.</p>
<p>We start off by creating a <code>HotCocoa.xml_parser</code>. The parser accepts <code>NSData</code> objects so we don&#8217;t need to convert our response data to a string. We then setup  eight callbacks. There are actually a bunch more callbacks that can be hooked up and you should look at the <code>xml_parser</code> mapping code to see if you need any of them. For our purposes, we only really care about eight.</p>
<p>The <code>on_start_document</code>, <code>on_end_document</code> and <code>on_parse_error</code> callbacks, as you can probably guess, get called when we start parsing, when we finish parsing and when we receive a parse error, respectively. We don&#8217;t really care about start in this example, but I put it in anyway. When we&#8217;ve completed parsing we send a notification and other application code can then listen for this notification and do anything it needs. If we wanted we could store the entries as they&#8217;re parsed and provide them to the <code>:object</code> key. This would make those entries available to anyone that receives the notification.</p>
<p>If we receive either CDATA, with <code>on_cdata</code>, or text, with <code>on_characters</code>, we append the content to our current elements text. When we receive the open tag of a new element, <code>on_start_element</code>, we dump our current element text as we&#8217;ve started a new element. We can also take a look at the elements name, attributes, namespace and qualified name, if desired.</p>
<p>Finally, in <code>on_end_element</code> we print out the current element text if the element we&#8217;re finishing has a name of <em>title</em>.</p>
<p>With all the callbacks configured we use <code>xml.parse</code> to start the parser. You should, if you run this example, see the titles and authors of the first two posts in your reading list. (The author name is also called title and I&#8217;m not bothering to check that the parent element is <em>entry</em> before spitting it out.)</p>
<p>That&#8217;s it. You can now make synchronous and asynchronous requests for content and parse any resulting XML.</p>
<p>One last thing before you go. Both of the requests we did above were <em>GET</em> requests. You can do other types of requests using the same methods as above you just need a slightly different setup for the request. You can see a <em>POST</em> request below.</p>
<div class="dean_ch" style="white-space: wrap;">request = NSMutableURLRequest.<span class="me1">requestWithURL</span><span class="br0">&#40;</span>url<span class="br0">&#41;</span><br />
request.<span class="me1">addValue</span><span class="br0">&#40;</span>SOURCE, forHTTPHeaderField:<span class="st0">&quot;source&quot;</span><span class="br0">&#41;</span><br />
request.<span class="me1">addValue</span><span class="br0">&#40;</span><span class="st0">&quot;2&quot;</span>, forHTTPHeaderField:<span class="st0">&quot;GData-Version&quot;</span><span class="br0">&#41;</span><br />
request.<span class="me1">addValue</span><span class="br0">&#40;</span><span class="st0">&quot;SID=#{@sid}&quot;</span>, forHTTPHeaderField:<span class="st0">&quot;Cookie&quot;</span><span class="br0">&#41;</span></p>
<p>body=<span class="st0">&quot;first=1&amp;second=2&amp;third=3&quot;</span></p>
<p>request.<span class="me1">setHTTPMethod</span><span class="br0">&#40;</span><span class="st0">&#8216;POST&#8217;</span><span class="br0">&#41;</span><br />
request.<span class="me1">setValue</span><span class="br0">&#40;</span><span class="st0">&#8216;application/x-www-form-urlencoded&#8217;</span>, forHTTPHeaderField:<span class="st0">&#8216;Content-Type&#8217;</span><span class="br0">&#41;</span><br />
request.<span class="me1">setValue</span><span class="br0">&#40;</span>body.<span class="me1">length</span>.<span class="me1">to_s</span>, forHTTPHeaderField:<span class="st0">&#8216;Content-Length&#8217;</span><span class="br0">&#41;</span><br />
request.<span class="me1">setHTTPBody</span><span class="br0">&#40;</span>body.<span class="me1">dataUsingEncoding</span><span class="br0">&#40;</span>NSASCIIStringEncoding<span class="br0">&#41;</span><span class="br0">&#41;</span></div>
<p>The first few lines should look familiar from creating our asynchronous request above. Since we&#8217;re going to be posting the data we use <code>setHTTPMethod('POST')</code> to setup the request method. We&#8217;ve form encoded the data so we set the appropriate <em>Content-Type</em> and set the <em>Content-Length</em>. Note, we convert the length to a string before sending to <code>setValue</code>. Finally, we set the body of the post with <code>setHTTPBody</code>. You need to convert the body string into a <code>NSData</code> object which we do with the <code>dataUsingEncoding</code> method. If you don&#8217;t convert the body to <code>NSData</code> you&#8217;ll end up sending a <code>nil</code> body with your post request.</p>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F&amp;title=Download+and+XML+parsing+with+HotCocoa" title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F&amp;title=Download+and+XML+parsing+with+HotCocoa" title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F&amp;title=Download+and+XML+parsing+with+HotCocoa" title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F&amp;title=Download+and+XML+parsing+with+HotCocoa" title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F&amp;title=Download+and+XML+parsing+with+HotCocoa', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F&amp;title=Download+and+XML+parsing+with+HotCocoa" title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F&amp;title=Download+and+XML+parsing+with+HotCocoa" title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></description>
			<content:encoded><![CDATA[<p><a href="http://everburning.com/wp-content/uploads/2010/02/IMG_7535.jpg"><img src="http://everburning.com/wp-content/uploads/2010/02/IMG_7535-150x150.jpg" alt="" title="IMG_7535" width="150" height="150" class="alignleft size-thumbnail wp-image-702" /></a>I&#8217;ve been working on <a href="http://github.com/dj2/Rife">Rife</a>, a <a href="http://reader.google.com">Google Reader</a> client, over the last few days and have been digging my way through some more <a href="http://github.com/richkilmer/hotcocoa">HotCocoa</a> mappings. I figured the best way to remember some of this stuff is to write it down so, the following will look at synchronous and asynchronous downloads and writing a XML parser in HotCocoa/<a href="http://macruby.org">MacRuby</a>.</p>
<p>So, what are we creating you ask? Well, as I said, I&#8217;ve been playing with Google Reader APIs and we&#8217;re going to do a synchronous request to Google for an identifier token. Once we&#8217;ve successfully authenticated we&#8217;re going to make an asynchronous request for our unread items. We&#8217;ll then parse the resulting XML document and spit the titles out to the console.</p>
<p>As with any HotCocoa application the easiest way to get started is to have system setup the shell of our application. We&#8217;ll use the <code>hotcocoa</code> command to create our application which I&#8217;m calling <em>titles</em>.</p>
<pre><code>titania:example dj2$ hotcocoa titles</code></pre>
<p>In order to authenticate to Google we&#8217;re going to need your username and password. Since I&#8217;m going to do the output to the console for demonstration purposes I&#8217;ll use the main application window to show the fields for username and password and a save button.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw3">require</span> <span class="st0">&#8216;rubygems&#8217;</span><br />
<span class="kw3">require</span> <span class="st0">&#8216;hotcocoa&#8217;</span></p>
<p><span class="kw1">class</span> Application<br />
&nbsp; <span class="kw1">include</span> HotCocoa<br />
&nbsp; <br />
&nbsp; <span class="kw1">def</span> start<br />
&nbsp; &nbsp; application<span class="br0">&#40;</span><span class="re3">:name</span> =&gt; <span class="st0">&quot;Titles&quot;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |app|<br />
&nbsp; &nbsp; &nbsp; app.<span class="me1">delegate</span> = <span class="kw2">self</span></p>
<p>&nbsp; &nbsp; &nbsp; window<span class="br0">&#40;</span><span class="re3">:frame</span> =&gt; <span class="br0">&#91;</span><span class="nu0">100</span>, <span class="nu0">100</span>, <span class="nu0">200</span>, <span class="nu0">200</span><span class="br0">&#93;</span>, <span class="re3">:title</span> =&gt; <span class="st0">&quot;Titles&quot;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |win|<br />
&nbsp; &nbsp; &nbsp; &nbsp; win.<span class="me1">center</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; win.<span class="me1">will_close</span> <span class="br0">&#123;</span> <span class="kw3">exit</span> <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; win &lt;&lt; label<span class="br0">&#40;</span><span class="re3">:text</span> =&gt; <span class="st0">&quot;Username&quot;</span>, <span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:start =&gt; <span class="kw2">false</span><span class="br0">&#125;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; win &lt;&lt; <span class="re1">@username_field</span> = text_field<span class="br0">&#40;</span><span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:start =&gt; <span class="kw2">false</span>, <span class="re3">:expand</span> =&gt; <span class="br0">&#91;</span><span class="re3">:width</span><span class="br0">&#93;</span><span class="br0">&#125;</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; win &lt;&lt; label<span class="br0">&#40;</span><span class="re3">:text</span> =&gt; <span class="st0">&quot;Password&quot;</span>, <span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:start =&gt; <span class="kw2">false</span><span class="br0">&#125;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; win &lt;&lt; <span class="re1">@password_field</span> = secure_text_field<span class="br0">&#40;</span><span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:start =&gt; <span class="kw2">false</span>, <span class="re3">:expand</span> =&gt; <span class="br0">&#91;</span><span class="re3">:width</span><span class="br0">&#93;</span><span class="br0">&#125;</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; win &lt;&lt; save = button<span class="br0">&#40;</span><span class="re3">:title</span> =&gt; <span class="st0">&quot;save&quot;</span>, <span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:start =&gt; <span class="kw2">true</span><span class="br0">&#125;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |button|<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; button.<span class="me1">on_action</span> <span class="br0">&#123;</span> authenticate <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">@username_field</span>.<span class="me1">setNextKeyView</span><span class="br0">&#40;</span>@password_field<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">@password_field</span>.<span class="me1">setNextKeyView</span><span class="br0">&#40;</span>save<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; save.<span class="me1">setNextKeyView</span><span class="br0">&#40;</span>@username_field<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; <span class="kw1">def</span> authenticate<br />
&nbsp; &nbsp; <span class="kw3">puts</span> <span class="st0">&quot;DO AUTH #{@username_field.to_s} #{@password_field.to_s}&quot;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></p>
<p>Application.<span class="me1">new</span>.<span class="me1">start</span></div>
<p>If you run the application by executing <code>macrake</code> in the <em>titles</em> directory you should see the main application window. Typing something into the <em>username</em> and <em>password</em> fields and pressing <em>save</em> you should see something similar to the following in your terminal.</p>
<pre><code>DO AUTH test test</code></pre>
<p>With our framework setup let&#8217;s get to the interesting stuff. First up, authenticating so we can retrieve our identifier from Google. We&#8217;re going to make a synchronous request to retrieve the identifier and, if successful, call a method to start retrieving our reading list.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> authenticate<br />
&nbsp; username = <span class="re1">@username_field</span>.<span class="me1">stringValue</span><br />
&nbsp; password = <span class="re1">@password_field</span>.<span class="me1">stringValue</span></p>
<p>&nbsp; query = <span class="st0">&quot;https://www.google.com/accounts/ClientLogin?&quot;</span> +<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&quot;Email=#{CGI.escape(username)}&amp;Passwd=#{CGI.escape(password.to_s)}&quot;</span> +<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&quot;&amp;source=HotCocoaExample&amp;service=reader&quot;</span></p>
<p>&nbsp; url = NSURL.<span class="me1">URLWithString</span><span class="br0">&#40;</span>query<span class="br0">&#41;</span><br />
&nbsp; request = NSMutableURLRequest.<span class="me1">requestWithURL</span><span class="br0">&#40;</span>url<span class="br0">&#41;</span><br />
&nbsp; request.<span class="me1">addValue</span><span class="br0">&#40;</span><span class="st0">&quot;HotCocoaExample&quot;</span>, forHTTPHeaderField:<span class="st0">&quot;source&quot;</span><span class="br0">&#41;</span><br />
&nbsp; request.<span class="me1">addValue</span><span class="br0">&#40;</span><span class="st0">&quot;2&quot;</span>, forHTTPHeaderField:<span class="st0">&quot;GData-Version&quot;</span><span class="br0">&#41;</span></p>
<p>&nbsp; response = Pointer.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&quot;@&quot;</span><span class="br0">&#41;</span><br />
&nbsp; data = NSURLConnection.<span class="me1">sendSynchronousRequest</span><span class="br0">&#40;</span>request, returningResponse:response, error:<span class="kw2">nil</span><span class="br0">&#41;</span><br />
&nbsp; data = NSString.<span class="me1">alloc</span>.<span class="me1">initWithData</span><span class="br0">&#40;</span>data, encoding:NSUTF8StringEncoding<span class="br0">&#41;</span></p>
<p>&nbsp; <span class="kw1">if</span> data =~ /^SID=<span class="br0">&#40;</span>.<span class="me1">*</span><span class="br0">&#41;</span>\n/<br />
&nbsp; &nbsp; <span class="re1">@sid</span> = $<span class="nu0">1</span></p>
<p>&nbsp; &nbsp; retrieve_reading_list<br />
&nbsp; <span class="kw1">else</span><br />
&nbsp; &nbsp; <span class="kw3">raise</span> <span class="kw4">Exception</span>.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&quot;Authentication failed with: #{data}&quot;</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></p>
<p><span class="kw1">def</span> retrieve_reading_list<br />
&nbsp; <span class="kw3">puts</span> <span class="re1">@sid</span><br />
<span class="kw1">end</span></div>
<p>If you add the above to your application, and add <code>require 'cgi'</code>, you should be able to run the program, put in your username and password and get a long line of characters spit out on the terminal. Those characters are your Google <em>SID</em>.</p>
<p>Let&#8217;s look a bit closer at what we&#8217;re doing in the <code>authenticate</code> method. We start by grabbing the <code>stringValue</code> for the username and password fields. Then, using these values, we build the query string needed for authentication. This query string is used to build a URL object by calling <code>NSURL.URLWithString(query)</code>. With the URL in hand we can start building our request object. This is done by calling <code>NSMutableURLRequest.requestWithURL(url)</code>. I&#8217;m using the mutable version of the request as I want to add a few extra header values. These are both added with <code>addValue(value, forHTTPHeaderField:field)</code>.</p>
<p>When we execute our request the system is going to want to put our response object somewhere. In the Cocoa version the method accepts a <code>NSURLResponse **response</code> parameter. In order to handle the response we need to create a <code>Pointer</code> object which is a MacRuby object for handling these pointers to objects. We want our pointer to point to an object so we use <code>Pointer.new("@")</code>.</p>
<p>With the response setup we call <code>NSURLConnection.sendSynchronousRequest</code> and provide our request and response objects. I don&#8217;t care about the error, but if you do, you&#8217;d want to pass in something similar to our response pointer. The request will return a <code>NSData</code> object which we convert to a string using the <code>initWithData</code> initialization method of <code>NSString</code>.</p>
<p>With the string in hand we try to extract our <em>SID</em> and, if successful, execute the <code>retrieve_reading_list</code> method which just spits out the <em>SID</em>.</p>
<p>OK, cool, we&#8217;ve now got our authentication token and are ready to move onto the asynchronous request to get our reading list.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> retrieve_reading_list<br />
&nbsp; query = <span class="st0">&quot;https://www.google.com/reader/atom/user/-/state/com.google/reading-list?&quot;</span> +<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">&quot;xt=user/-/state/com.google/read&amp;ck=#{Time.now.to_i * 1000}&amp;n=2&quot;</span></p>
<p>&nbsp; url = NSURL.<span class="me1">URLWithString</span><span class="br0">&#40;</span>query<span class="br0">&#41;</span><br />
&nbsp; request = NSMutableURLRequest.<span class="me1">requestWithURL</span><span class="br0">&#40;</span>url<span class="br0">&#41;</span><br />
&nbsp; request.<span class="me1">addValue</span><span class="br0">&#40;</span><span class="st0">&quot;HotCocoaExample&quot;</span>, forHTTPHeaderField:<span class="st0">&quot;source&quot;</span><span class="br0">&#41;</span><br />
&nbsp; request.<span class="me1">addValue</span><span class="br0">&#40;</span><span class="st0">&quot;2&quot;</span>, forHTTPHeaderField:<span class="st0">&quot;GData-Version&quot;</span><span class="br0">&#41;</span><br />
&nbsp; request.<span class="me1">addValue</span><span class="br0">&#40;</span><span class="st0">&quot;SID=#{@sid}&quot;</span>, forHTTPHeaderField:<span class="st0">&quot;Cookie&quot;</span><span class="br0">&#41;</span></p>
<p>&nbsp; NSURLConnection.<span class="me1">connectionWithRequest</span><span class="br0">&#40;</span>request, delegate:<span class="kw2">self</span><span class="br0">&#41;</span><br />
<span class="kw1">end</span></p>
<p><span class="kw1">def</span> connectionDidFinishLoading<span class="br0">&#40;</span>conn<span class="br0">&#41;</span><br />
&nbsp; <span class="kw3">puts</span> NSString.<span class="me1">alloc</span>.<span class="me1">initWithData</span><span class="br0">&#40;</span>@receivedData, encoding:NSUTF8StringEncoding<span class="br0">&#41;</span><br />
<span class="kw1">end</span></p>
<p><span class="kw1">def</span> connection<span class="br0">&#40;</span>conn, didReceiveResponse:response<span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">if</span> response.<span class="me1">statusCode</span> != <span class="nu0">200</span><br />
&nbsp; &nbsp; <span class="kw3">puts</span> <span class="st0">&quot;BAD STATUS: #{response.statusCode}&quot;</span><br />
&nbsp; &nbsp; <span class="kw3">p</span> response.<span class="me1">allHeaderFields</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></p>
<p><span class="kw1">def</span> connection<span class="br0">&#40;</span>conn, didReceiveData:data<span class="br0">&#41;</span><br />
&nbsp; <span class="re1">@receivedData</span> ||= NSMutableData.<span class="me1">new</span><br />
&nbsp; <span class="re1">@receivedData</span>.<span class="me1">appendData</span><span class="br0">&#40;</span>data<span class="br0">&#41;</span><br />
<span class="kw1">end</span></div>
<p>Similar to the synchronous method we start by building our query string, <code>NSURL</code> and <code>NSMutableURLRequest</code>. We&#8217;ve added a cookie to our request object to hold the <em>SID</em> retrieved earlier from Google.</p>
<p>We fire the request by calling <code>NSURLConnection.connectionWithRequest(request, delegate:self)</code>. We specific ourselves as the delegate for the connection. There are a few delegate methods we can implement to receive data and get notified of request states. These are:</p>
<ul>
<li><code>connectionDidFinishLoading(connection)</code></li>
<li><code>connection(connection, didReceiveResponse:response)</code></li>
<li><code>connection(connection, didReceiveData:data)</code></li>
</ul>
<p>We&#8217;ll look at our implementation of each of these callbacks in turn. First, in <code>connectionDidFinishLoading(conn)</code> we&#8217;re just printing out the data retrieved. We need to convert the data, similar to what we did in the synchronous request, from a <code>NSData</code> object to a <code>NSString</code> object.</p>
<p>In <code>connection(conn, didReceiveResponse:response)</code> we&#8217;re just checking to see if we got a 200 response code from the server. In all other cases we print an error.</p>
<p>The main work is done in <code>connection(conn, didReceiveData:data)</code> where we create a <code>NSMutableData</code> object if needed and append any data received into the mutable data object.</p>
<p>Running the code at this point should dump the first two items in your reading list to the console. The data will be a big mess of XML but we&#8217;ll look at parsing that in the next step.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> connectionDidFinishLoading<span class="br0">&#40;</span>conn<span class="br0">&#41;</span><br />
&nbsp; xml = HotCocoa.<span class="me1">xml_parser</span><span class="br0">&#40;</span><span class="re3">:data</span> =&gt; <span class="re1">@receivedData</span><span class="br0">&#41;</span><br />
&nbsp; <span class="re1">@receivedData</span> = <span class="kw2">nil</span></p>
<p>&nbsp; xml.<span class="me1">on_start_document</span> <span class="br0">&#123;</span> <span class="kw3">puts</span> <span class="st0">&quot;Starting Parse&quot;</span> <span class="br0">&#125;</span><br />
&nbsp; xml.<span class="me1">on_end_document</span> <span class="kw1">do</span><br />
&nbsp; &nbsp; HotCocoa.<span class="me1">notification</span><span class="br0">&#40;</span><span class="re3">:post</span> =&gt; <span class="kw2">true</span>, <span class="re3">:name</span> =&gt; <span class="st0">&quot;all_entries_loaded&quot;</span>, <span class="re3">:object</span> =&gt; <span class="kw2">nil</span>, <span class="re3">:info</span> =&gt; <span class="kw2">nil</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">end</span><br />
&nbsp; xml.<span class="me1">on_parse_error</span> <span class="br0">&#123;</span> |err| <span class="kw3">puts</span> <span class="st0">&quot;Parse error #{err.inspect}&quot;</span> <span class="br0">&#125;</span></p>
<p>&nbsp; xml.<span class="me1">on_cdata</span> <span class="br0">&#123;</span> |cdata| <span class="re1">@elem_text</span> += cdata.<span class="me1">to_s</span> <span class="br0">&#125;</span><br />
&nbsp; xml.<span class="me1">on_characters</span> <span class="br0">&#123;</span> |chars| <span class="re1">@elem_text</span> += chars.<span class="me1">to_s</span> <span class="br0">&#125;</span></p>
<p>&nbsp; xml.<span class="me1">on_start_element</span> <span class="kw1">do</span> |element, namespace, qualified_name, attributes|<br />
&nbsp; &nbsp; <span class="re1">@elem_text</span> = <span class="st0">&#8221;</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; xml.<span class="me1">on_end_element</span> <span class="kw1">do</span> |element, namespace, qualified_name|<br />
&nbsp; &nbsp; <span class="kw3">puts</span> <span class="re1">@elem_text</span> <span class="kw1">if</span> element == <span class="st0">&#8216;title&#8217;</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; xml.<span class="me1">parse</span><br />
<span class="kw1">end</span></div>
<p>We&#8217;re finally getting into some HotCocoa specific code with our XML parser. HotCocoa defines a mapping wrapper around <code>NSXMLParser</code> and provides a set of delegate methods. These delegates mean we don&#8217;t have to set our class as the delegate and create a bunch of methods. They mean we can attach our code as blocks on our XML object. All the better if you want to define a few parsers in one class.</p>
<p>We start off by creating a <code>HotCocoa.xml_parser</code>. The parser accepts <code>NSData</code> objects so we don&#8217;t need to convert our response data to a string. We then setup  eight callbacks. There are actually a bunch more callbacks that can be hooked up and you should look at the <code>xml_parser</code> mapping code to see if you need any of them. For our purposes, we only really care about eight.</p>
<p>The <code>on_start_document</code>, <code>on_end_document</code> and <code>on_parse_error</code> callbacks, as you can probably guess, get called when we start parsing, when we finish parsing and when we receive a parse error, respectively. We don&#8217;t really care about start in this example, but I put it in anyway. When we&#8217;ve completed parsing we send a notification and other application code can then listen for this notification and do anything it needs. If we wanted we could store the entries as they&#8217;re parsed and provide them to the <code>:object</code> key. This would make those entries available to anyone that receives the notification.</p>
<p>If we receive either CDATA, with <code>on_cdata</code>, or text, with <code>on_characters</code>, we append the content to our current elements text. When we receive the open tag of a new element, <code>on_start_element</code>, we dump our current element text as we&#8217;ve started a new element. We can also take a look at the elements name, attributes, namespace and qualified name, if desired.</p>
<p>Finally, in <code>on_end_element</code> we print out the current element text if the element we&#8217;re finishing has a name of <em>title</em>.</p>
<p>With all the callbacks configured we use <code>xml.parse</code> to start the parser. You should, if you run this example, see the titles and authors of the first two posts in your reading list. (The author name is also called title and I&#8217;m not bothering to check that the parent element is <em>entry</em> before spitting it out.)</p>
<p>That&#8217;s it. You can now make synchronous and asynchronous requests for content and parse any resulting XML.</p>
<p>One last thing before you go. Both of the requests we did above were <em>GET</em> requests. You can do other types of requests using the same methods as above you just need a slightly different setup for the request. You can see a <em>POST</em> request below.</p>
<div class="dean_ch" style="white-space: wrap;">request = NSMutableURLRequest.<span class="me1">requestWithURL</span><span class="br0">&#40;</span>url<span class="br0">&#41;</span><br />
request.<span class="me1">addValue</span><span class="br0">&#40;</span>SOURCE, forHTTPHeaderField:<span class="st0">&quot;source&quot;</span><span class="br0">&#41;</span><br />
request.<span class="me1">addValue</span><span class="br0">&#40;</span><span class="st0">&quot;2&quot;</span>, forHTTPHeaderField:<span class="st0">&quot;GData-Version&quot;</span><span class="br0">&#41;</span><br />
request.<span class="me1">addValue</span><span class="br0">&#40;</span><span class="st0">&quot;SID=#{@sid}&quot;</span>, forHTTPHeaderField:<span class="st0">&quot;Cookie&quot;</span><span class="br0">&#41;</span></p>
<p>body=<span class="st0">&quot;first=1&amp;second=2&amp;third=3&quot;</span></p>
<p>request.<span class="me1">setHTTPMethod</span><span class="br0">&#40;</span><span class="st0">&#8216;POST&#8217;</span><span class="br0">&#41;</span><br />
request.<span class="me1">setValue</span><span class="br0">&#40;</span><span class="st0">&#8216;application/x-www-form-urlencoded&#8217;</span>, forHTTPHeaderField:<span class="st0">&#8216;Content-Type&#8217;</span><span class="br0">&#41;</span><br />
request.<span class="me1">setValue</span><span class="br0">&#40;</span>body.<span class="me1">length</span>.<span class="me1">to_s</span>, forHTTPHeaderField:<span class="st0">&#8216;Content-Length&#8217;</span><span class="br0">&#41;</span><br />
request.<span class="me1">setHTTPBody</span><span class="br0">&#40;</span>body.<span class="me1">dataUsingEncoding</span><span class="br0">&#40;</span>NSASCIIStringEncoding<span class="br0">&#41;</span><span class="br0">&#41;</span></div>
<p>The first few lines should look familiar from creating our asynchronous request above. Since we&#8217;re going to be posting the data we use <code>setHTTPMethod('POST')</code> to setup the request method. We&#8217;ve form encoded the data so we set the appropriate <em>Content-Type</em> and set the <em>Content-Length</em>. Note, we convert the length to a string before sending to <code>setValue</code>. Finally, we set the body of the post with <code>setHTTPBody</code>. You need to convert the body string into a <code>NSData</code> object which we do with the <code>dataUsingEncoding</code> method. If you don&#8217;t convert the body to <code>NSData</code> you&#8217;ll end up sending a <code>nil</code> body with your post request.</p>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F&amp;title=Download+and+XML+parsing+with+HotCocoa" title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F&amp;title=Download+and+XML+parsing+with+HotCocoa" title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F&amp;title=Download+and+XML+parsing+with+HotCocoa" title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F&amp;title=Download+and+XML+parsing+with+HotCocoa" title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F&amp;title=Download+and+XML+parsing+with+HotCocoa', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F&amp;title=Download+and+XML+parsing+with+HotCocoa" title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fdownload-and-xml-parsing-with-hotcocoa%2F&amp;title=Download+and+XML+parsing+with+HotCocoa" title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></content:encoded>
			<wfw:commentRss>http://everburning.com/news/download-and-xml-parsing-with-hotcocoa/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Poking Objective-C with a Testing Stick</title>
		<link>http://everburning.com/news/poking-objective-c-with-a-testing-stick/</link>
		<comments>http://everburning.com/news/poking-objective-c-with-a-testing-stick/#comments</comments>
		<pubDate>Mon, 26 Oct 2009 20:32:14 +0000</pubDate>
		<dc:creator>dj2</dc:creator>
				<category><![CDATA[Computers]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[Objective-C]]></category>
		<category><![CDATA[OCMock]]></category>
		<category><![CDATA[OCUnit]]></category>
		<category><![CDATA[Testing]]></category>

		<guid isPermaLink="false">http://everburning.com/?p=669</guid>
		<description><![CDATA[<p><img src="http://everburning.com/wp-content/uploads/2009/10/4012929364_54af446306_b-300x199.jpg" alt="4012929364_54af446306_b" title="4012929364_54af446306_b" width="300" height="199" class="alignleft size-medium wp-image-670" />I&#8217;ve wandered back into <a href="http://en.wikipedia.org/wiki/Objective-C">Objective-C</a> coding land recently. After spending so much time doing <a href="http://en.wikipedia.org/wiki/Ruby_(programming_language)">Ruby</a> work I&#8217;ve gotten used to writing <a href="http://en.wikipedia.org/wiki/Unit_test">unit tests</a> and using <a href="http://en.wikipedia.org/wiki/Mock_object">mock objects</a>. To that end, I spent a bit of time figuring out OCUnit (built into XCode) and OCMock. I figured I&#8217;d write some of this down so I don&#8217;t forget for the next time I try to set this all up.</p>
<p><a href="http://everburning.com/wp-content/uploads/2009/10/Build-Results-1.png"><img src="http://everburning.com/wp-content/uploads/2009/10/Build-Results-1-150x150.png" alt="Build Results 1" title="Build Results 1" width="150" height="150" class="alignright size-thumbnail wp-image-671" /></a>We&#8217;re going to work with a simple Cocoa Application that connects to an external web resource, in this case, Google Reader authentication. We&#8217;ll setup the testing bundle to run when our main application is built and create a mock object so we don&#8217;t hit the external resource on every test run. I&#8217;m going to be using XCode version 3.2 and create a Cocoa Application called <em>UnitTesting</em>. If you build and run this application you should see an empty window on screen. If you bring up the Build Results window (Build -> Build Results) you&#8217;ll see some information about the current build.</p>
<p><a href="http://everburning.com/wp-content/uploads/2009/10/Add-Unit-Test-Target.png"><img src="http://everburning.com/wp-content/uploads/2009/10/Add-Unit-Test-Target-150x150.png" alt="Add Unit Test Target" title="Add Unit Test Target" width="150" height="150" class="alignright size-thumbnail wp-image-673" /></a>With our app setup, we&#8217;ll start integrating the unit tests. Click on the <em>Targets</em> item in the project folder. Select <em>Add -&gt; New Target</em>. Select <em>Unit Test Bundle and press </em><em>Next</em> (Note, make sure you&#8217;re in the <em>Cocoa</em> and not the <em>Cocoa Touch</em> section when selecting <em>Unit Test Bundle</em>). I named my target <em>Unit Tests</em> and hit <em>Finish</em>.</p>
<p>At this point if you right click on the <em>UnitTesting</em> target and select <em>Build &#8220;UnitTesting&#8221;</em> everything should work correctly. If you right click on <em>Unit Tests</em> and select <em>Build &#8220;Unit Tests&#8221;</em> you should receive a build failure.</p>
<p><a href="http://everburning.com/wp-content/uploads/2009/10/Add-Test-Group.png"><img src="http://everburning.com/wp-content/uploads/2009/10/Add-Test-Group-150x150.png" alt="Add Test Group" title="Add Test Group" width="150" height="150" class="alignleft size-thumbnail wp-image-674" /></a>Let&#8217;s add some tests. The first step is to create a new group to hold our test files. Right click on the <em>UnitTesting</em> item in the project draw and select <em>Add -&gt; New Group</em>. Name the new group <em>Tests</em>. Now, right click on the <em>Tests</em> group and select <em>Add -&gt; New File&#8230;</em>. You&#8217;ll want to add an <em>Objective-C test case class</em> (Note, make sure you&#8217;re in the <em>Cocoa Class</em> section of the new dialog and not the <em>Cocoa Touch Class</em> section or you&#8217;ll get an iPhone test class). Name the test <em>GReaderTest.m</em> and de-select the <em>Also create &#8220;GReaderTest.h&#8221;</em> option. The reason for this is that the headers are usually empty so there is no point in creating the extra file.  You&#8217;ll also want to make sure you have the <em>Unit Tests</em> target selected.<a href="http://everburning.com/wp-content/uploads/2009/10/Add-Test-Case.png"><img src="http://everburning.com/wp-content/uploads/2009/10/Add-Test-Case-150x150.png" alt="Add Test Case" title="Add Test Case" width="150" height="150" class="alignright size-thumbnail wp-image-675" /></a></p>
<p>When working with OCUnit you need to name each of your test classes <em>SomethingTest</em>. The trailing <em>Test</em> is required. In a similar vein, each of the tests themselves needs to start with <em>test</em>. So, something like <em>testAuthentication</em>.</p>
<p>Since we didn&#8217;t create a header file we&#8217;ll need to setup the interface for the test in the <em>.m</em> file.</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="co2">#import &lt;SenTestingKit/SenTestingKit.h&gt;</span></p>
<p>@interface GReaderTest : SenTestCase<br />
@end</p>
<p>@implementation GReaderTest<br />
@end</div>
<p><a href="http://everburning.com/wp-content/uploads/2009/10/Bundle-Build-Failure.png"><img src="http://everburning.com/wp-content/uploads/2009/10/Bundle-Build-Failure-150x150.png" alt="Bundle Build Failure" title="Bundle Build Failure" width="150" height="150" class="alignright size-thumbnail wp-image-676" /></a>You should be able to build the <em>Unit Tests</em> target now and have the build succeed. If the build doesn&#8217;t succeed, and you see a message about <em>UIKit</em> then you selected the <em>Cocoa Touch</em> unit test bundle instead of the <em>Cocoa</em> unit test bundle.</p>
<p><a href="http://everburning.com/wp-content/uploads/2009/10/Adding-dependencies.png"><img src="http://everburning.com/wp-content/uploads/2009/10/Adding-dependencies-150x150.png" alt="Adding dependencies" title="Adding dependencies" width="150" height="150" class="alignleft size-thumbnail wp-image-679" /></a>With the unit tests building we can add them into our main build as a dependency. Right click on the <em>UnitTesting</em> target in the project drawer and select <em>Get Info</em>. In the General section add a new <em>Direct Dependency</em> for the <em>Unit Tests</em> target.</p>
<p>Now, when you press apple-B to build the project you should see your unit tests executed before the main build phase.</p>
<p><a href="http://everburning.com/wp-content/uploads/2009/10/Build-Results-2.png"><img src="http://everburning.com/wp-content/uploads/2009/10/Build-Results-2-150x150.png" alt="Build Results 2" title="Build Results 2" width="150" height="150" class="alignright size-thumbnail wp-image-680" /></a>Ok, with everything setup we can start testing. First step, each test in this set will be using our <em>GReader</em> object. So, we&#8217;ll add <em>setUp</em> and <em>tearDown</em> methods that will be executed before and after each test, respectively.</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="co2">#import &lt;SenTestingKit/SenTestingKit.h&gt;</span><br />
<span class="co2">#import &quot;GReader.h&quot;</span></p>
<p>@interface GReaderTest : SenTestCase <span class="br0">&#123;</span><br />
&nbsp; &nbsp; GReader *gr;<br />
<span class="br0">&#125;</span><br />
@end</p>
<p>@implementation GReaderTest<br />
- <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>setUp <span class="br0">&#123;</span><br />
&nbsp; &nbsp; gr = <span class="br0">&#91;</span><span class="br0">&#91;</span>GReader alloc<span class="br0">&#93;</span> init<span class="br0">&#93;</span>;<br />
<span class="br0">&#125;</span></p>
<p>- <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>tearDown <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span>gr release<span class="br0">&#93;</span>;<br />
<span class="br0">&#125;</span><br />
@end</div>
<p>This, of course, won&#8217;t execute as we haven&#8217;t created our <em>GReader</em> object yet. Let&#8217;s do that now.</p>
<p>Right click on the <em>Classes</em> group and select <em>Add -&gt; New File&#8230;</em>. Add a new <em>Objective-C class</em> which is a subclass of <em>NSObject</em>. Call this new class <em>GReader</em>. The new class should be attached to both our main target and the <em>Unit Tests</em> target.</p>
<p>Now, for our little app, the first thing we&#8217;ll need to do is authenticate with Google Reader.  There is a really good document on the <a href="http://code.google.com/p/pyrfeed/wiki/GoogleReaderAPI">Reader API</a> from the <a href="http://code.google.com/p/pyrfeed/">pyrfeed</a> project.  We&#8217;ll need to post some specific data to a given end point and parse the response.</p>
<p>For our unit tests, we don&#8217;t actually want to hit the Google endpoint. There is too much time involved and we want our unit tests to be fast. So, we&#8217;ll need to do some mocking in order to verify the call is happening, but not actually make the call itself.</p>
<p>For this we&#8217;ll use OCMock. I&#8217;m going to add the test first and then we&#8217;ll add the OCMock.framework into the project.  First, we need to import OCMock. This is done by adding <code>#import &lt;OCMock.h&gt;</code> to the beginning of the <em>GReaderTest.m</em> file.</p>
<p>The test is defined as follows.</p>
<div class="dean_ch" style="white-space: wrap;">
- <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>testAuthentication <span class="br0">&#123;</span><br />
&nbsp; &nbsp; id mock = <span class="br0">&#91;</span>OCMockObject partialMockForObject:gr<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#91;</span><span class="br0">&#91;</span><span class="br0">&#91;</span>mock stub<span class="br0">&#93;</span> andCall:@selector<span class="br0">&#40;</span>fakeAuthenticationPost:<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;onObject:self<span class="br0">&#93;</span> post:<span class="br0">&#91;</span>OCMArg any<span class="br0">&#93;</span><span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span class="br0">&#91;</span>gr authenticateWithUsername:@<span class="st0">&quot;dan&quot;</span> password:@<span class="st0">&quot;password&quot;</span><span class="br0">&#93;</span>;<br />
<span class="br0">&#125;</span></p>
<p>- <span class="br0">&#40;</span>NSString *<span class="br0">&#41;</span>fakeAuthenticationPost:<span class="br0">&#40;</span>NSString *<span class="br0">&#41;</span>request <span class="br0">&#123;</span><br />
&nbsp; &nbsp; NSArray *d = <span class="br0">&#91;</span>request componentsSeparatedByString:@<span class="st0">&quot;&amp;&quot;</span><span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; STAssertTrue<span class="br0">&#40;</span><span class="br0">&#91;</span>d containsObject:@<span class="st0">&quot;Email=dan&quot;</span><span class="br0">&#93;</span>, @<span class="st0">&quot;Username not set correclty into request&quot;</span><span class="br0">&#41;</span>;<br />
&nbsp; &nbsp; STAssertTrue<span class="br0">&#40;</span><span class="br0">&#91;</span>d containsObject:@<span class="st0">&quot;Passwd=password&quot;</span><span class="br0">&#93;</span>, @<span class="st0">&quot;Password not set correctly in request&quot;</span><span class="br0">&#41;</span>;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span class="kw1">return</span> @<span class="st0">&quot;SID=mysid<span class="es0">\n</span>LSID=mylsid<span class="es0">\n</span>Auth=myauth&quot;</span>;<br />
<span class="br0">&#125;</span></div>
<p>As you can probably tell, we&#8217;ve added two methods. There is only one test, <em>testAuthentication</em> but we needed a helper function to deal with the mock post call.  Let&#8217;s take a look at what&#8217;s going on in these methods. We know that the <em>GReader</em> object will be making a call that we want to mock. So, we need to create a partial mock object that sits around our GReader object.  This is done as: <code>id mock = [OCMockObject partialMockForObject:gr];</code>. With the mock created, we can stub out specific methods of the GReader object, this is called <a href="http://www.cocoadev.com/index.pl?MethodSwizzling">Method Swizzling</a> in Objective-C land.</p>
<p>In order to swizzle the method we have <code>[[[mock stub] andCall:@selector(fakeAuthenticationPost:) onObject:self] post:[OCMArg any]];</code>.  So, we create a new <code>stub</code> on our mock object. We then tell the stub to call the <code>fakeAuthenticationPost</code> defined in the current object whenever the <code>post</code> method is called on the GReader object. Our post method takes one parameter so we specify <code>[OCMArg any]</code> to allow any argument through.</p>
<p>Finally, we call the <code>authenticateWithUsername:password:</code> on the GReader object to kick off the authentication.</p>
<p>The second method we defined, <code>fakeAuthenticationPost</code>, will be called instead of the post method in the <em>GReader</em> object. To that end, it will receive the same parameter, the <code>NSString</code> that is pass to <code>post</code>. To be on the safe side, I&#8217;m verifying that we&#8217;re properly passing the required email and password fields in the string. I then fake some return data that is similar to a successful response from Google.</p>
<p>If we try to build the project at this point we&#8217;re going to get a lot of errors. First, since we haven&#8217;t added the OCMock framework and second, we haven&#8217;t created the <code>authenticate</code> or <code>post</code> methods for our GReader object.</p>
<p>First things first, let&#8217;s get the OCMock framework setup.  To do that, you&#8217;ll need to download OCMock. You can grab the <em>.dmg</em> file <a href="http://www.mulle-kybernetik.com/software/OCMock/">off the OCMock pages</a>.  When you extract the archive you&#8217;ll see the <em>OCMock.framework</em> and the <em>source</em> directories. In order to keep everything in Git, I created a <em>Framework</em> directory in my <em>UnitTesting</em> directory. I then copied the <em>OCMock.framework</em> directory into this new <em>Framework</em> directory. </p>
<p>You could also install OCMock in the <em>/Library/Frameworks</em> directory but I like having it in Git as not all the developers may have OCMock installed.</p>
<p>Back in XCode, we need to add the framework to our project. Right click on the <em>Frameworks</em> group in the project directory. Select <em>Add -&gt; New Group</em>. Name the new group <em>Testing Frameworks</em>. Note, this isn&#8217;t required, I just like the separation it provides. Finally, right click on <em>Testing Frameworks</em> and select <code>Add -&gt; Existing Frameworks...</code> then press the <em>Add Other&#8230;</em> button. Navigate to the OCMock.framework directory you just copied into <code>Frameworks</code> directory and press <em>Add</em>. Make sure the new framework is hooked up to the <em>Unit Tests</em> target. Right click on the framework and hit <em>Get Info</em> in the general tab you can verify the targets.</p>
<p>Building the project at this point will get some warnings about missing functions and a crash executing the unit tests. In order to fix the crash we need to setup the build to copy the OCMock framework into our build directory. I&#8217;m not entirely sure why this is needed, something about one of the paths set in the framework, but it does get things working.</p>
<p><a href="http://everburning.com/wp-content/uploads/2009/10/Copy-Build-Phase.png"><img src="http://everburning.com/wp-content/uploads/2009/10/Copy-Build-Phase-150x150.png" alt="Copy Build Phase" title="Copy Build Phase" width="150" height="150" class="alignright size-thumbnail wp-image-683" /></a>Right click on the <em>Unit Tests</em> target and select <em>Add -&gt; New Build Phase -&gt; New Copy Files Build Phase</em>.</p>
<p>You need to set the <em>Destination</em> to <em>Absolute Path</em> and the <em>Full Path</em> to <em>$(BUILT_PRODUCTS_DIR)</em>. Once the Copy Files phase is created drag the <em>OCMock.framework</em> from the <em>Test Frameworks</em> group into the copy phase. The copy phase needs to be placed between <em>Compile Sources</em> and <em>Link Binary With Library</em> phases.</p>
<p><a href="http://everburning.com/wp-content/uploads/2009/10/Built-Products-dir.png"><img src="http://everburning.com/wp-content/uploads/2009/10/Built-Products-dir-150x150.png" alt="Built Products dir" title="Built Products dir" width="150" height="150" class="alignleft size-thumbnail wp-image-684" /></a>At this point, we should be able to execute our build again and we&#8217;ll get a failure <code>[NSProxy doesNotRecognizeSelector:post]</code>. This is good. This means our mock is setup correctly and is trying to hook into our non-existant <code>post</code> method.</p>
<p>Let&#8217;s go create a couple methods so things compile correctly. First we update the <em>GReader.h</em> file as follows.</p>
<div class="dean_ch" style="white-space: wrap;">@interface GReader : NSObject<br />
- <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>authenticateWithUsername:<span class="br0">&#40;</span>NSString *<span class="br0">&#41;</span>username password:<span class="br0">&#40;</span>NSString *<span class="br0">&#41;</span>password;<br />
- <span class="br0">&#40;</span>NSString *<span class="br0">&#41;</span>post:<span class="br0">&#40;</span>NSString *<span class="br0">&#41;</span>request;<br />
@end</div>
<p>And the implementation.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="co2">#import &quot;GReader.h&quot;</span><br />
@implementation GReader<br />
- <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>authenticateWithUsername:<span class="br0">&#40;</span>NSString *<span class="br0">&#41;</span>username password:<span class="br0">&#40;</span>NSString *<span class="br0">&#41;</span>password <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span>self post:<span class="br0">&#91;</span>NSString stringWithFormat:@<span class="st0">&quot;Email=%@&amp;Passwd=%@&quot;</span>, username, password<span class="br0">&#93;</span><span class="br0">&#93;</span>;<br />
<span class="br0">&#125;</span></p>
<p>- <span class="br0">&#40;</span>NSString *<span class="br0">&#41;</span>post:<span class="br0">&#40;</span>NSString *<span class="br0">&#41;</span>request <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> @<span class="st0">&quot;My post data&quot;</span>;<br />
<span class="br0">&#125;</span><br />
@end</div>
<p>With that in place our build should succeed. You can make sure that things are working correctly by modifying <code>[gr authenticateWithUsername:@"dan" password:@"password"];</code> to something similar to <code>[gr authenticateWithUsername:@"stan" password:@"password"];</code> and you should see a build failure.</p>
<p>That&#8217;s it. We have the build system setup and the mock objects hooked up. We can now continue down our TDD path. You can see an example project that I&#8217;ve <a href="http://github.com/dj2/ObjectiveC-UnitTest-Example">uploaded to GitHub</a>.</p>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Fpoking-objective-c-with-a-testing-stick%2F&amp;title=Poking+Objective-C+with+a+Testing+Stick" title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fpoking-objective-c-with-a-testing-stick%2F&amp;title=Poking+Objective-C+with+a+Testing+Stick" title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fpoking-objective-c-with-a-testing-stick%2F&amp;title=Poking+Objective-C+with+a+Testing+Stick" title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Fpoking-objective-c-with-a-testing-stick%2F&amp;title=Poking+Objective-C+with+a+Testing+Stick" title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fpoking-objective-c-with-a-testing-stick%2F&amp;title=Poking+Objective-C+with+a+Testing+Stick', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Fpoking-objective-c-with-a-testing-stick%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Fpoking-objective-c-with-a-testing-stick%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Fpoking-objective-c-with-a-testing-stick%2F&amp;title=Poking+Objective-C+with+a+Testing+Stick" title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fpoking-objective-c-with-a-testing-stick%2F&amp;title=Poking+Objective-C+with+a+Testing+Stick" title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></description>
			<content:encoded><![CDATA[<p><img src="http://everburning.com/wp-content/uploads/2009/10/4012929364_54af446306_b-300x199.jpg" alt="4012929364_54af446306_b" title="4012929364_54af446306_b" width="300" height="199" class="alignleft size-medium wp-image-670" />I&#8217;ve wandered back into <a href="http://en.wikipedia.org/wiki/Objective-C">Objective-C</a> coding land recently. After spending so much time doing <a href="http://en.wikipedia.org/wiki/Ruby_(programming_language)">Ruby</a> work I&#8217;ve gotten used to writing <a href="http://en.wikipedia.org/wiki/Unit_test">unit tests</a> and using <a href="http://en.wikipedia.org/wiki/Mock_object">mock objects</a>. To that end, I spent a bit of time figuring out OCUnit (built into XCode) and OCMock. I figured I&#8217;d write some of this down so I don&#8217;t forget for the next time I try to set this all up.</p>
<p><a href="http://everburning.com/wp-content/uploads/2009/10/Build-Results-1.png"><img src="http://everburning.com/wp-content/uploads/2009/10/Build-Results-1-150x150.png" alt="Build Results 1" title="Build Results 1" width="150" height="150" class="alignright size-thumbnail wp-image-671" /></a>We&#8217;re going to work with a simple Cocoa Application that connects to an external web resource, in this case, Google Reader authentication. We&#8217;ll setup the testing bundle to run when our main application is built and create a mock object so we don&#8217;t hit the external resource on every test run. I&#8217;m going to be using XCode version 3.2 and create a Cocoa Application called <em>UnitTesting</em>. If you build and run this application you should see an empty window on screen. If you bring up the Build Results window (Build -> Build Results) you&#8217;ll see some information about the current build.</p>
<p><a href="http://everburning.com/wp-content/uploads/2009/10/Add-Unit-Test-Target.png"><img src="http://everburning.com/wp-content/uploads/2009/10/Add-Unit-Test-Target-150x150.png" alt="Add Unit Test Target" title="Add Unit Test Target" width="150" height="150" class="alignright size-thumbnail wp-image-673" /></a>With our app setup, we&#8217;ll start integrating the unit tests. Click on the <em>Targets</em> item in the project folder. Select <em>Add -&gt; New Target</em>. Select <em>Unit Test Bundle and press </em><em>Next</em> (Note, make sure you&#8217;re in the <em>Cocoa</em> and not the <em>Cocoa Touch</em> section when selecting <em>Unit Test Bundle</em>). I named my target <em>Unit Tests</em> and hit <em>Finish</em>.</p>
<p>At this point if you right click on the <em>UnitTesting</em> target and select <em>Build &#8220;UnitTesting&#8221;</em> everything should work correctly. If you right click on <em>Unit Tests</em> and select <em>Build &#8220;Unit Tests&#8221;</em> you should receive a build failure.</p>
<p><a href="http://everburning.com/wp-content/uploads/2009/10/Add-Test-Group.png"><img src="http://everburning.com/wp-content/uploads/2009/10/Add-Test-Group-150x150.png" alt="Add Test Group" title="Add Test Group" width="150" height="150" class="alignleft size-thumbnail wp-image-674" /></a>Let&#8217;s add some tests. The first step is to create a new group to hold our test files. Right click on the <em>UnitTesting</em> item in the project draw and select <em>Add -&gt; New Group</em>. Name the new group <em>Tests</em>. Now, right click on the <em>Tests</em> group and select <em>Add -&gt; New File&#8230;</em>. You&#8217;ll want to add an <em>Objective-C test case class</em> (Note, make sure you&#8217;re in the <em>Cocoa Class</em> section of the new dialog and not the <em>Cocoa Touch Class</em> section or you&#8217;ll get an iPhone test class). Name the test <em>GReaderTest.m</em> and de-select the <em>Also create &#8220;GReaderTest.h&#8221;</em> option. The reason for this is that the headers are usually empty so there is no point in creating the extra file.  You&#8217;ll also want to make sure you have the <em>Unit Tests</em> target selected.<a href="http://everburning.com/wp-content/uploads/2009/10/Add-Test-Case.png"><img src="http://everburning.com/wp-content/uploads/2009/10/Add-Test-Case-150x150.png" alt="Add Test Case" title="Add Test Case" width="150" height="150" class="alignright size-thumbnail wp-image-675" /></a></p>
<p>When working with OCUnit you need to name each of your test classes <em>SomethingTest</em>. The trailing <em>Test</em> is required. In a similar vein, each of the tests themselves needs to start with <em>test</em>. So, something like <em>testAuthentication</em>.</p>
<p>Since we didn&#8217;t create a header file we&#8217;ll need to setup the interface for the test in the <em>.m</em> file.</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="co2">#import &lt;SenTestingKit/SenTestingKit.h&gt;</span></p>
<p>@interface GReaderTest : SenTestCase<br />
@end</p>
<p>@implementation GReaderTest<br />
@end</div>
<p><a href="http://everburning.com/wp-content/uploads/2009/10/Bundle-Build-Failure.png"><img src="http://everburning.com/wp-content/uploads/2009/10/Bundle-Build-Failure-150x150.png" alt="Bundle Build Failure" title="Bundle Build Failure" width="150" height="150" class="alignright size-thumbnail wp-image-676" /></a>You should be able to build the <em>Unit Tests</em> target now and have the build succeed. If the build doesn&#8217;t succeed, and you see a message about <em>UIKit</em> then you selected the <em>Cocoa Touch</em> unit test bundle instead of the <em>Cocoa</em> unit test bundle.</p>
<p><a href="http://everburning.com/wp-content/uploads/2009/10/Adding-dependencies.png"><img src="http://everburning.com/wp-content/uploads/2009/10/Adding-dependencies-150x150.png" alt="Adding dependencies" title="Adding dependencies" width="150" height="150" class="alignleft size-thumbnail wp-image-679" /></a>With the unit tests building we can add them into our main build as a dependency. Right click on the <em>UnitTesting</em> target in the project drawer and select <em>Get Info</em>. In the General section add a new <em>Direct Dependency</em> for the <em>Unit Tests</em> target.</p>
<p>Now, when you press apple-B to build the project you should see your unit tests executed before the main build phase.</p>
<p><a href="http://everburning.com/wp-content/uploads/2009/10/Build-Results-2.png"><img src="http://everburning.com/wp-content/uploads/2009/10/Build-Results-2-150x150.png" alt="Build Results 2" title="Build Results 2" width="150" height="150" class="alignright size-thumbnail wp-image-680" /></a>Ok, with everything setup we can start testing. First step, each test in this set will be using our <em>GReader</em> object. So, we&#8217;ll add <em>setUp</em> and <em>tearDown</em> methods that will be executed before and after each test, respectively.</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="co2">#import &lt;SenTestingKit/SenTestingKit.h&gt;</span><br />
<span class="co2">#import &quot;GReader.h&quot;</span></p>
<p>@interface GReaderTest : SenTestCase <span class="br0">&#123;</span><br />
&nbsp; &nbsp; GReader *gr;<br />
<span class="br0">&#125;</span><br />
@end</p>
<p>@implementation GReaderTest<br />
- <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>setUp <span class="br0">&#123;</span><br />
&nbsp; &nbsp; gr = <span class="br0">&#91;</span><span class="br0">&#91;</span>GReader alloc<span class="br0">&#93;</span> init<span class="br0">&#93;</span>;<br />
<span class="br0">&#125;</span></p>
<p>- <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>tearDown <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span>gr release<span class="br0">&#93;</span>;<br />
<span class="br0">&#125;</span><br />
@end</div>
<p>This, of course, won&#8217;t execute as we haven&#8217;t created our <em>GReader</em> object yet. Let&#8217;s do that now.</p>
<p>Right click on the <em>Classes</em> group and select <em>Add -&gt; New File&#8230;</em>. Add a new <em>Objective-C class</em> which is a subclass of <em>NSObject</em>. Call this new class <em>GReader</em>. The new class should be attached to both our main target and the <em>Unit Tests</em> target.</p>
<p>Now, for our little app, the first thing we&#8217;ll need to do is authenticate with Google Reader.  There is a really good document on the <a href="http://code.google.com/p/pyrfeed/wiki/GoogleReaderAPI">Reader API</a> from the <a href="http://code.google.com/p/pyrfeed/">pyrfeed</a> project.  We&#8217;ll need to post some specific data to a given end point and parse the response.</p>
<p>For our unit tests, we don&#8217;t actually want to hit the Google endpoint. There is too much time involved and we want our unit tests to be fast. So, we&#8217;ll need to do some mocking in order to verify the call is happening, but not actually make the call itself.</p>
<p>For this we&#8217;ll use OCMock. I&#8217;m going to add the test first and then we&#8217;ll add the OCMock.framework into the project.  First, we need to import OCMock. This is done by adding <code>#import &lt;OCMock.h&gt;</code> to the beginning of the <em>GReaderTest.m</em> file.</p>
<p>The test is defined as follows.</p>
<div class="dean_ch" style="white-space: wrap;">
- <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>testAuthentication <span class="br0">&#123;</span><br />
&nbsp; &nbsp; id mock = <span class="br0">&#91;</span>OCMockObject partialMockForObject:gr<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#91;</span><span class="br0">&#91;</span><span class="br0">&#91;</span>mock stub<span class="br0">&#93;</span> andCall:@selector<span class="br0">&#40;</span>fakeAuthenticationPost:<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;onObject:self<span class="br0">&#93;</span> post:<span class="br0">&#91;</span>OCMArg any<span class="br0">&#93;</span><span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span class="br0">&#91;</span>gr authenticateWithUsername:@<span class="st0">&quot;dan&quot;</span> password:@<span class="st0">&quot;password&quot;</span><span class="br0">&#93;</span>;<br />
<span class="br0">&#125;</span></p>
<p>- <span class="br0">&#40;</span>NSString *<span class="br0">&#41;</span>fakeAuthenticationPost:<span class="br0">&#40;</span>NSString *<span class="br0">&#41;</span>request <span class="br0">&#123;</span><br />
&nbsp; &nbsp; NSArray *d = <span class="br0">&#91;</span>request componentsSeparatedByString:@<span class="st0">&quot;&amp;&quot;</span><span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; STAssertTrue<span class="br0">&#40;</span><span class="br0">&#91;</span>d containsObject:@<span class="st0">&quot;Email=dan&quot;</span><span class="br0">&#93;</span>, @<span class="st0">&quot;Username not set correclty into request&quot;</span><span class="br0">&#41;</span>;<br />
&nbsp; &nbsp; STAssertTrue<span class="br0">&#40;</span><span class="br0">&#91;</span>d containsObject:@<span class="st0">&quot;Passwd=password&quot;</span><span class="br0">&#93;</span>, @<span class="st0">&quot;Password not set correctly in request&quot;</span><span class="br0">&#41;</span>;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span class="kw1">return</span> @<span class="st0">&quot;SID=mysid<span class="es0">\n</span>LSID=mylsid<span class="es0">\n</span>Auth=myauth&quot;</span>;<br />
<span class="br0">&#125;</span></div>
<p>As you can probably tell, we&#8217;ve added two methods. There is only one test, <em>testAuthentication</em> but we needed a helper function to deal with the mock post call.  Let&#8217;s take a look at what&#8217;s going on in these methods. We know that the <em>GReader</em> object will be making a call that we want to mock. So, we need to create a partial mock object that sits around our GReader object.  This is done as: <code>id mock = [OCMockObject partialMockForObject:gr];</code>. With the mock created, we can stub out specific methods of the GReader object, this is called <a href="http://www.cocoadev.com/index.pl?MethodSwizzling">Method Swizzling</a> in Objective-C land.</p>
<p>In order to swizzle the method we have <code>[[[mock stub] andCall:@selector(fakeAuthenticationPost:) onObject:self] post:[OCMArg any]];</code>.  So, we create a new <code>stub</code> on our mock object. We then tell the stub to call the <code>fakeAuthenticationPost</code> defined in the current object whenever the <code>post</code> method is called on the GReader object. Our post method takes one parameter so we specify <code>[OCMArg any]</code> to allow any argument through.</p>
<p>Finally, we call the <code>authenticateWithUsername:password:</code> on the GReader object to kick off the authentication.</p>
<p>The second method we defined, <code>fakeAuthenticationPost</code>, will be called instead of the post method in the <em>GReader</em> object. To that end, it will receive the same parameter, the <code>NSString</code> that is pass to <code>post</code>. To be on the safe side, I&#8217;m verifying that we&#8217;re properly passing the required email and password fields in the string. I then fake some return data that is similar to a successful response from Google.</p>
<p>If we try to build the project at this point we&#8217;re going to get a lot of errors. First, since we haven&#8217;t added the OCMock framework and second, we haven&#8217;t created the <code>authenticate</code> or <code>post</code> methods for our GReader object.</p>
<p>First things first, let&#8217;s get the OCMock framework setup.  To do that, you&#8217;ll need to download OCMock. You can grab the <em>.dmg</em> file <a href="http://www.mulle-kybernetik.com/software/OCMock/">off the OCMock pages</a>.  When you extract the archive you&#8217;ll see the <em>OCMock.framework</em> and the <em>source</em> directories. In order to keep everything in Git, I created a <em>Framework</em> directory in my <em>UnitTesting</em> directory. I then copied the <em>OCMock.framework</em> directory into this new <em>Framework</em> directory. </p>
<p>You could also install OCMock in the <em>/Library/Frameworks</em> directory but I like having it in Git as not all the developers may have OCMock installed.</p>
<p>Back in XCode, we need to add the framework to our project. Right click on the <em>Frameworks</em> group in the project directory. Select <em>Add -&gt; New Group</em>. Name the new group <em>Testing Frameworks</em>. Note, this isn&#8217;t required, I just like the separation it provides. Finally, right click on <em>Testing Frameworks</em> and select <code>Add -&gt; Existing Frameworks...</code> then press the <em>Add Other&#8230;</em> button. Navigate to the OCMock.framework directory you just copied into <code>Frameworks</code> directory and press <em>Add</em>. Make sure the new framework is hooked up to the <em>Unit Tests</em> target. Right click on the framework and hit <em>Get Info</em> in the general tab you can verify the targets.</p>
<p>Building the project at this point will get some warnings about missing functions and a crash executing the unit tests. In order to fix the crash we need to setup the build to copy the OCMock framework into our build directory. I&#8217;m not entirely sure why this is needed, something about one of the paths set in the framework, but it does get things working.</p>
<p><a href="http://everburning.com/wp-content/uploads/2009/10/Copy-Build-Phase.png"><img src="http://everburning.com/wp-content/uploads/2009/10/Copy-Build-Phase-150x150.png" alt="Copy Build Phase" title="Copy Build Phase" width="150" height="150" class="alignright size-thumbnail wp-image-683" /></a>Right click on the <em>Unit Tests</em> target and select <em>Add -&gt; New Build Phase -&gt; New Copy Files Build Phase</em>.</p>
<p>You need to set the <em>Destination</em> to <em>Absolute Path</em> and the <em>Full Path</em> to <em>$(BUILT_PRODUCTS_DIR)</em>. Once the Copy Files phase is created drag the <em>OCMock.framework</em> from the <em>Test Frameworks</em> group into the copy phase. The copy phase needs to be placed between <em>Compile Sources</em> and <em>Link Binary With Library</em> phases.</p>
<p><a href="http://everburning.com/wp-content/uploads/2009/10/Built-Products-dir.png"><img src="http://everburning.com/wp-content/uploads/2009/10/Built-Products-dir-150x150.png" alt="Built Products dir" title="Built Products dir" width="150" height="150" class="alignleft size-thumbnail wp-image-684" /></a>At this point, we should be able to execute our build again and we&#8217;ll get a failure <code>[NSProxy doesNotRecognizeSelector:post]</code>. This is good. This means our mock is setup correctly and is trying to hook into our non-existant <code>post</code> method.</p>
<p>Let&#8217;s go create a couple methods so things compile correctly. First we update the <em>GReader.h</em> file as follows.</p>
<div class="dean_ch" style="white-space: wrap;">@interface GReader : NSObject<br />
- <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>authenticateWithUsername:<span class="br0">&#40;</span>NSString *<span class="br0">&#41;</span>username password:<span class="br0">&#40;</span>NSString *<span class="br0">&#41;</span>password;<br />
- <span class="br0">&#40;</span>NSString *<span class="br0">&#41;</span>post:<span class="br0">&#40;</span>NSString *<span class="br0">&#41;</span>request;<br />
@end</div>
<p>And the implementation.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="co2">#import &quot;GReader.h&quot;</span><br />
@implementation GReader<br />
- <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>authenticateWithUsername:<span class="br0">&#40;</span>NSString *<span class="br0">&#41;</span>username password:<span class="br0">&#40;</span>NSString *<span class="br0">&#41;</span>password <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span>self post:<span class="br0">&#91;</span>NSString stringWithFormat:@<span class="st0">&quot;Email=%@&amp;Passwd=%@&quot;</span>, username, password<span class="br0">&#93;</span><span class="br0">&#93;</span>;<br />
<span class="br0">&#125;</span></p>
<p>- <span class="br0">&#40;</span>NSString *<span class="br0">&#41;</span>post:<span class="br0">&#40;</span>NSString *<span class="br0">&#41;</span>request <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> @<span class="st0">&quot;My post data&quot;</span>;<br />
<span class="br0">&#125;</span><br />
@end</div>
<p>With that in place our build should succeed. You can make sure that things are working correctly by modifying <code>[gr authenticateWithUsername:@"dan" password:@"password"];</code> to something similar to <code>[gr authenticateWithUsername:@"stan" password:@"password"];</code> and you should see a build failure.</p>
<p>That&#8217;s it. We have the build system setup and the mock objects hooked up. We can now continue down our TDD path. You can see an example project that I&#8217;ve <a href="http://github.com/dj2/ObjectiveC-UnitTest-Example">uploaded to GitHub</a>.</p>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Fpoking-objective-c-with-a-testing-stick%2F&amp;title=Poking+Objective-C+with+a+Testing+Stick" title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fpoking-objective-c-with-a-testing-stick%2F&amp;title=Poking+Objective-C+with+a+Testing+Stick" title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fpoking-objective-c-with-a-testing-stick%2F&amp;title=Poking+Objective-C+with+a+Testing+Stick" title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Fpoking-objective-c-with-a-testing-stick%2F&amp;title=Poking+Objective-C+with+a+Testing+Stick" title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fpoking-objective-c-with-a-testing-stick%2F&amp;title=Poking+Objective-C+with+a+Testing+Stick', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Fpoking-objective-c-with-a-testing-stick%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Fpoking-objective-c-with-a-testing-stick%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Fpoking-objective-c-with-a-testing-stick%2F&amp;title=Poking+Objective-C+with+a+Testing+Stick" title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fpoking-objective-c-with-a-testing-stick%2F&amp;title=Poking+Objective-C+with+a+Testing+Stick" title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></content:encoded>
			<wfw:commentRss>http://everburning.com/news/poking-objective-c-with-a-testing-stick/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>What&#8217;s your moniker</title>
		<link>http://everburning.com/news/whats-your-moniker/</link>
		<comments>http://everburning.com/news/whats-your-moniker/#comments</comments>
		<pubDate>Fri, 09 Oct 2009 02:49:15 +0000</pubDate>
		<dc:creator>dj2</dc:creator>
				<category><![CDATA[Computers]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Moniker]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://everburning.com/?p=664</guid>
		<description><![CDATA[<p><img src="http://everburning.com/wp-content/uploads/2009/10/IMG_6929-300x200.jpg" alt="IMG_6929" title="IMG_6929" width="300" height="200" class="alignleft size-medium wp-image-665" />We were playing with some ideas over at <a href="http://postrank.com">PostRank</a> and realized we needed a way to give out some unique codes. We didn&#8217;t want some random string of letters and numbers as that isn&#8217;t very memorable. We wanted real words. We wanted something entertaining.</p>
<p>I spent a bit of time looking around. The closest thing I found to what we wanted was <a href="http://github.com/dancroak/webster">Webster</a>. While Webster would work, I wasn&#8217;t a big fan of the words we were getting out of the system in some quick tests.  After some more fruitless searching, in a fit of not invented here syndrome, I created my own. </p>
<p>Enter, <a href="http://github.com/dj2/moniker">Moniker</a>. Moniker will take a list of descriptive words and a set of animals and give you a string. There are, currently, just over 42 thousand combinations. Enough for what we needed. The system is pretty simplistic and it&#8217;s up to you to make sure you aren&#8217;t getting duplicates. </p>
<div class="dean_ch" style="white-space: wrap;">titania:~ dj2$ irb<br />
&gt;&gt; <span class="kw3">require</span> <span class="st0">&#8216;rubygems&#8217;</span><br />
&gt;&gt; <span class="kw3">require</span> <span class="st0">&#8216;moniker&#8217;</span><br />
=&gt; <span class="kw2">true</span><br />
&gt;&gt; Moniker.<span class="me1">name</span><br />
=&gt; <span class="st0">&quot;octagon-zebra&quot;</span><br />
&gt;&gt; Moniker.<span class="me1">name</span><br />
=&gt; <span class="st0">&quot;shallow-lion&quot;</span><br />
&gt;&gt; Moniker.<span class="me1">name</span><br />
=&gt; <span class="st0">&quot;concave-parrot&quot;</span></div>
<p>The code is all up on <a href="http://github.com">GitHub</a> so take a look and feel free to play. Let me know if you&#8217;ve got any ideas for improvements</p>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Fwhats-your-moniker%2F&amp;title=What%26%238217%3Bs+your+moniker" title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fwhats-your-moniker%2F&amp;title=What%26%238217%3Bs+your+moniker" title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fwhats-your-moniker%2F&amp;title=What%26%238217%3Bs+your+moniker" title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Fwhats-your-moniker%2F&amp;title=What%26%238217%3Bs+your+moniker" title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fwhats-your-moniker%2F&amp;title=What%26%238217%3Bs+your+moniker', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Fwhats-your-moniker%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Fwhats-your-moniker%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Fwhats-your-moniker%2F&amp;title=What%26%238217%3Bs+your+moniker" title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fwhats-your-moniker%2F&amp;title=What%26%238217%3Bs+your+moniker" title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></description>
			<content:encoded><![CDATA[<p><img src="http://everburning.com/wp-content/uploads/2009/10/IMG_6929-300x200.jpg" alt="IMG_6929" title="IMG_6929" width="300" height="200" class="alignleft size-medium wp-image-665" />We were playing with some ideas over at <a href="http://postrank.com">PostRank</a> and realized we needed a way to give out some unique codes. We didn&#8217;t want some random string of letters and numbers as that isn&#8217;t very memorable. We wanted real words. We wanted something entertaining.</p>
<p>I spent a bit of time looking around. The closest thing I found to what we wanted was <a href="http://github.com/dancroak/webster">Webster</a>. While Webster would work, I wasn&#8217;t a big fan of the words we were getting out of the system in some quick tests.  After some more fruitless searching, in a fit of not invented here syndrome, I created my own. </p>
<p>Enter, <a href="http://github.com/dj2/moniker">Moniker</a>. Moniker will take a list of descriptive words and a set of animals and give you a string. There are, currently, just over 42 thousand combinations. Enough for what we needed. The system is pretty simplistic and it&#8217;s up to you to make sure you aren&#8217;t getting duplicates. </p>
<div class="dean_ch" style="white-space: wrap;">titania:~ dj2$ irb<br />
&gt;&gt; <span class="kw3">require</span> <span class="st0">&#8216;rubygems&#8217;</span><br />
&gt;&gt; <span class="kw3">require</span> <span class="st0">&#8216;moniker&#8217;</span><br />
=&gt; <span class="kw2">true</span><br />
&gt;&gt; Moniker.<span class="me1">name</span><br />
=&gt; <span class="st0">&quot;octagon-zebra&quot;</span><br />
&gt;&gt; Moniker.<span class="me1">name</span><br />
=&gt; <span class="st0">&quot;shallow-lion&quot;</span><br />
&gt;&gt; Moniker.<span class="me1">name</span><br />
=&gt; <span class="st0">&quot;concave-parrot&quot;</span></div>
<p>The code is all up on <a href="http://github.com">GitHub</a> so take a look and feel free to play. Let me know if you&#8217;ve got any ideas for improvements</p>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Fwhats-your-moniker%2F&amp;title=What%26%238217%3Bs+your+moniker" title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fwhats-your-moniker%2F&amp;title=What%26%238217%3Bs+your+moniker" title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fwhats-your-moniker%2F&amp;title=What%26%238217%3Bs+your+moniker" title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Fwhats-your-moniker%2F&amp;title=What%26%238217%3Bs+your+moniker" title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fwhats-your-moniker%2F&amp;title=What%26%238217%3Bs+your+moniker', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Fwhats-your-moniker%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Fwhats-your-moniker%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Fwhats-your-moniker%2F&amp;title=What%26%238217%3Bs+your+moniker" title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fwhats-your-moniker%2F&amp;title=What%26%238217%3Bs+your+moniker" title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></content:encoded>
			<wfw:commentRss>http://everburning.com/news/whats-your-moniker/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Flicking the iPhone</title>
		<link>http://everburning.com/news/flicking-the-iphone/</link>
		<comments>http://everburning.com/news/flicking-the-iphone/#comments</comments>
		<pubDate>Thu, 13 Aug 2009 14:18:24 +0000</pubDate>
		<dc:creator>dj2</dc:creator>
				<category><![CDATA[Computers]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[Objective-C]]></category>

		<guid isPermaLink="false">http://everburning.com/?p=629</guid>
		<description><![CDATA[<p><img src="http://everburning.com/wp-content/uploads/2009/08/IMG_4589-300x200.jpg" alt="IMG_4589" title="IMG_4589" width="300" height="200" class="alignleft size-medium wp-image-649" />I&#8217;ve been playing with my iPhone a bit lately. Trying to work out an application that I think will be useful (more on that some other time). In the mean time, I wanted to let the user of the application flick between views to see different detailed view screens. Each flick should take the user either forward or back in the list. The transitions should push the new item into view and have a little bounce on the end. </p>
<p>Turns out, this wasn&#8217;t trivial. I&#8217;m not sure now why I thought it would be trivial. Maybe because I&#8217;ve seen it in a few apps.</p>
<p>Anyway, what follows is how I did it. Maybe there is an easier way, if so, let me know, but this appears to work.</p>
<p>I&#8217;m going to create a simple Core Data backed, Navigation-based iPhone application. There will be two screens, the main list screen and a detailed view. The user will be able to flick between detailed views. Note, there is no real error checking, and possibly memory leaks in this code. You&#8217;ll need to fix up as appropriate for your application.</p>
<p>Firing up XCode, create a new project. Under the iPhone OS Applications template section, you&#8217;ll want to select <i>Navigation-based Application</i>. Make sure you&#8217;ve got <i>Use Core Data for storage</i> selected as well. In my case I&#8217;m naming the application <i>Flick</i>.<a href="http://everburning.com/wp-content/uploads/2009/08/choose_template.png"><img src="http://everburning.com/wp-content/uploads/2009/08/choose_template-300x167.png" alt="choose_template" title="choose_template" width="300" height="167" class="alignright size-medium wp-image-630" /></a></p>
<p>With the application created you should be able to execute it in the simulator and add a series of timestamps to the main view.</p>
<p><a href="http://everburning.com/wp-content/uploads/2009/08/managd_object_class.png"><img src="http://everburning.com/wp-content/uploads/2009/08/managd_object_class-300x133.png" alt="managd_object_class" title="managd_object_class" width="300" height="133" class="alignleft size-medium wp-image-632" /></a>Before we start adding screens, let&#8217;s create a class to hold our model entity. Double click on the <i>Flick.xcdatamodel</i> file to open the data modeler. Click on the <i>Event</i> entity and press command-n. Select, in the Mac OS X Cocoa section, <i>Managed Object Class</i>. Click through to finish the creation as the rest of the defaults will suffice. You should now have <i>Event.h</i> and <i>Event.m</i> files created in your project.<a href="http://everburning.com/wp-content/uploads/2009/08/create-view-controller.png"><img src="http://everburning.com/wp-content/uploads/2009/08/create-view-controller-300x151.png" alt="create view controller" title="create view controller" width="300" height="151" class="alignright size-medium wp-image-631" /></a></p>
<p>With the base application in place we still need to create a screen to allow viewing of our events. I&#8217;m backing this screen with a <i>UITableViewController</i> because that&#8217;s how my application does it. You could probably use a plain <i>UIViewController</i> if you don&#8217;t need to extra functionality of the table view controller.</p>
<p>Add a new file to the project and, under iPhone OS Cocoa Touch Class, select Objective-C class and set a Subclass of <i>UITableViewController</i>. Name the file <i>EventViewController.m</i> and have the <i>.h</i> file auto generated.</p>
<p>Opening up <i>RootViewController.m</i> we need to hookup the code to create our new view when one of the rows is selected.</p>
<p>First, lets mark the rows as having another view. Add <code>cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;</code> into the <code>tableView:cellForRowAtIndexPath:</code> method right after the UITableViewCell is created.</p>
<div class="dean_ch" style="white-space: wrap;">- <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>tableView:<span class="br0">&#40;</span>UITableView *<span class="br0">&#41;</span>tableView didSelectRowAtIndexPath:<span class="br0">&#40;</span>NSIndexPath *<span class="br0">&#41;</span>indexPath<br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; Event *event = <span class="br0">&#40;</span>Event *<span class="br0">&#41;</span><span class="br0">&#91;</span>fetchedResultsController objectAtIndexPath:indexPath<span class="br0">&#93;</span>;</p>
<p>&nbsp; &nbsp; EventViewController *controller = <span class="br0">&#91;</span><span class="br0">&#91;</span>EventViewController alloc<span class="br0">&#93;</span> initWithStyle:UITableViewStyleGrouped<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; controller.event = event;</p>
<p>&nbsp; &nbsp; <span class="br0">&#91;</span>self.navigationController pushViewController:controller animated:YES<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#91;</span>controller release<span class="br0">&#93;</span>;<br />
<span class="br0">&#125;</span></div>
<p>Using the <i>tableView:didSelectRowAtIndexPath:</i> delegate method we retrieve the selected event from our <i>fetchedResultsController</i>. We then create a new <i>EventViewController</i> with a table style of <i>UITableViewStyleGrouped</i>. There are two styles to tables Grouped and Plain.</p>
<dl>
<dt>UITableViewStylePlain</dt>
<dd>A plain table view. Any section headers or footers are displayed as inline separators and float when the table view is scrolled.</dd>
<dt>UITableViewStyleGrouped</dt>
<dd>A table view whose sections present distinct groups of rows. The section headers and footers do not float.</dd>
</dl>
<p>With the controller created we assign the selected event to the controller. Finally, we push the new view controller into the navigation stack using <code>[self.navigationController pushViewController:controller animated:YES]</code>. This will set our new view as the view to be displayed. Oh, and don&#8217;t forget to import <i>EventViewController.h</i> and <i>Event.h</i>.</p>
<p>We want our new view controller to have a back button. <del datetime="2009-08-14T19:05:05+00:00">If you don&#8217;t set the name in the <i>MainWindow.xib</i> file the back button will be available, but invisible. Double click the <i>MainWindow.xib</i> file to open it in Interface Builder. Click on the navigation bar in the view and press shift-command-i to open the inspector. Press command-1 to switch to the attributes panel. In the <i>Back Button</i> field enter <i>Events</i>.  You can now save and exit Interface Builder.</del> Thanks to <a href="http://streamingcolour.com">Owen</a>, setting <code>self.navigationItem.title = @"Events"</code> in the <code>viewDidLoad</code> method of <em>RootViewController</em> will set the correct text into the back button.</p>
<p>Continuing on, we can fill in the <i>EventViewController</i> files, starting with the header.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="co2">#import &quot;Event.h&quot;</span></p>
<p><span class="kw4">@interface</span> EventViewController : UITableViewController <span class="br0">&#123;</span><br />
&nbsp; &nbsp; Event *event;<br />
<span class="br0">&#125;</span><br />
@property <span class="br0">&#40;</span>nonatomic, retain<span class="br0">&#41;</span> Event *event;<br />
<span class="kw4">@end</span></div>
<p>The view controller has one attribute, the event that was selected. We setup a property for the event to handle the getters and setters for us.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="co2">#import &quot;EventViewController.h&quot;</span></p>
<p><span class="kw4">@implementation</span> EventViewController</p>
<p>@synthesize event;</p>
<p>- <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>viewDidLoad <span class="br0">&#123;</span><br />
&nbsp; &nbsp; self.navigationItem.title = @<span class="st0">&quot;Event Info&quot;</span>;<br />
<span class="br0">&#125;</span></p>
<p>- <span class="br0">&#40;</span>NSInteger<span class="br0">&#41;</span>numberOfSectionsInTableView:<span class="br0">&#40;</span>UITableView *<span class="br0">&#41;</span>tableView <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> <span class="nu0">1</span>;<br />
<span class="br0">&#125;</span></p>
<p>- <span class="br0">&#40;</span>NSInteger<span class="br0">&#41;</span>tableView:<span class="br0">&#40;</span>UITableView *<span class="br0">&#41;</span>tableView numberOfRowsInSection:<span class="br0">&#40;</span>NSInteger<span class="br0">&#41;</span>section <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> <span class="nu0">1</span>;<br />
<span class="br0">&#125;</span></p>
<p>- <span class="br0">&#40;</span>UITableViewCell *<span class="br0">&#41;</span>tableView:<span class="br0">&#40;</span>UITableView *<span class="br0">&#41;</span>tableView cellForRowAtIndexPath:<span class="br0">&#40;</span>NSIndexPath *<span class="br0">&#41;</span>indexPath <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw4">static</span> <a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/ObjC_classic/Classes/NSString.html"><span class="kw5">NSString</span></a> *LocationCellIdentifier = @<span class="st0">&quot;Timestamp&quot;</span>;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; UITableViewCell *cell = <span class="br0">&#91;</span>tableView dequeueReusableCellWithIdentifier:LocationCellIdentifier<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>cell == <span class="kw2">nil</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; cell = <span class="br0">&#91;</span><span class="br0">&#91;</span><span class="br0">&#91;</span>UITableViewCell alloc<span class="br0">&#93;</span> initWithStyle:UITableViewCellStyleSubtitle<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;reuseIdentifier:LocationCellIdentifier<span class="br0">&#93;</span> autorelease<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; cell.accessoryType = UITableViewCellAccessoryNone;<br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/ObjC_classic/Classes/NSDateFormatter.html"><span class="kw5">NSDateFormatter</span></a> *dateFormat = <span class="br0">&#91;</span><span class="br0">&#91;</span><span class="br0">&#91;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/ObjC_classic/Classes/NSDateFormatter.html"><span class="kw5">NSDateFormatter</span></a> alloc<span class="br0">&#93;</span> init<span class="br0">&#93;</span> autorelease<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#91;</span>dateFormat setDateStyle:NSDateFormatterLongStyle<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#91;</span>dateFormat setTimeStyle:NSDateFormatterLongStyle<span class="br0">&#93;</span>;</p>
<p>&nbsp; &nbsp; cell.textLabel.text = <span class="br0">&#91;</span>dateFormat stringFromDate:event.timeStamp<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span class="kw1">return</span> cell;<br />
<span class="br0">&#125;</span></p>
<p>- <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>dealloc <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span>super dealloc<span class="br0">&#93;</span>;<br />
<span class="br0">&#125;</span><br />
<span class="kw4">@end</span></div>
<p>Next up is the implementation file. When the view finishes loading we want to set the title to display <em>Event Info</em>. This is done in <code>viewDidLoad</code> by calling <code>self.navigationItem.title = @"Event Info"</code>.</p>
<p>We&#8217;re only going to show one thing in our table, the timestamp, so we setup the table to have a single section and a single row. We then create a cell to display the time stamp.</p>
<p>You should be able to build and run the application at this point. You can click on the events to move to the subview. From there you can navigate back to the main list.</p>
<p>With that, we&#8217;re ready to add some flicking to our application.</p>
<p>There is no <em>flick</em> event built into the iPhone APIs that I could find. The way to achieve a flick is to listen for the <code>touchesEnded:withEvent:</code> delegate method and determine if a flicked happened. The one catch, this delegate is available on <code>UIView</code> objects. We don&#8217;t, currently, have a <code>UIView</code> object we can attach the delegate method too.</p>
<p>So, we&#8217;re going to subclass <code>UIView</code> deal with the flicking, then let our parent <code>EventViewController</code> know when the flick happened so we can switch views. Sound good? Ok, let&#8217;s go.</p>
<p>Add a new class to the project. Under iPhone OS Cocoa Touch Class select Object-C class. Make sure <i>UIView</i> is selected in the drop down. Name the new file <em>EventView.m</em>.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw4">@protocol</span> EventViewDelegate;</p>
<p><span class="kw4">@interface</span> EventView : UITableView <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw4">id</span> &lt;UITableViewDelegate, EventViewDelegate&gt; delegate;<br />
<span class="br0">&#125;</span><br />
@property <span class="br0">&#40;</span>nonatomic, assign<span class="br0">&#41;</span> <span class="kw4">id</span> &lt;UITableViewDelegate, EventViewDelegate&gt; delegate;<br />
<span class="kw4">@end</span></p>
<p><span class="kw4">@protocol</span> EventViewDelegate &lt;NSObject&gt;<br />
- <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>eventView:<span class="br0">&#40;</span>EventView *<span class="br0">&#41;</span>view flickDirection:<span class="br0">&#40;</span><span class="kw4">int</span><span class="br0">&#41;</span>dir;<br />
<span class="kw4">@end</span></div>
<p>There are a couple things going on here. First, I switched the inherited class to <code>UITableView</code> as that&#8217;s what we&#8217;re actually dealing with.</p>
<p>We add one attribute to the <code>EventView</code> class. We need to be able to assign the controller as a delegate for our view so we can notify the controller of the flick. In order for the table to continue working, we also need to make sure we maintain the <code>UITableViewDelegate</code> protocol already attached to the delegate object in the super class. </p>
<p>Our protocol is pretty simple, we need one method implemented, <code>eventView:flickDirection:</code>. This will be called when the view is flicked and set the <em>flickDirection</em> to -1 for a left flick and 1 for a right flick.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="co2">#import &quot;EventView.h&quot;</span></p>
<p><span class="co2">#define MIN_FLICK_DISTANCE 10.0</span></p>
<p><span class="kw4">@implementation</span> EventView<br />
@synthesize delegate;</p>
<p>- <span class="br0">&#40;</span><span class="kw4">int</span><span class="br0">&#41;</span>flicked:<span class="br0">&#40;</span>UITouch *<span class="br0">&#41;</span>touch <span class="br0">&#123;</span><br />
&nbsp; &nbsp; CGPoint start = <span class="br0">&#91;</span>touch previousLocationInView:self<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; CGPoint end = <span class="br0">&#91;</span>touch locationInView:self<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span class="kw4">double</span> x = end.x &#8211; start.x;<br />
&nbsp; &nbsp; <span class="kw4">double</span> y = end.y &#8211; start.y;<br />
&nbsp; &nbsp; <span class="kw4">double</span> dist = <a href="http://www.opengroup.org/onlinepubs/009695399/functions/sqrt.html"><span class="kw3">sqrt</span></a><span class="br0">&#40;</span><span class="br0">&#40;</span>x * x<span class="br0">&#41;</span> + <span class="br0">&#40;</span>y * y<span class="br0">&#41;</span><span class="br0">&#41;</span>;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>dist &gt; MIN_FLICK_DISTANCE<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>end.x &lt; start.x<span class="br0">&#41;</span> <span class="kw1">return</span> <span class="nu0">1</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">else</span> <span class="kw1">return</span> <span class="nu0">-1</span>;<br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> <span class="nu0">0</span>;<br />
<span class="br0">&#125;</span></p>
<p>- <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>touchesEnded:<span class="br0">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/ObjC_classic/Classes/NSSet.html"><span class="kw5">NSSet</span></a> *<span class="br0">&#41;</span>touches withEvent:<span class="br0">&#40;</span>UIEvent *<span class="br0">&#41;</span>event <span class="br0">&#123;</span><br />
&nbsp; &nbsp; UITouch *touch = <span class="br0">&#91;</span>touches anyObject<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="kw4">int</span> dir = <span class="br0">&#91;</span>self flicked:touch<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>dir != <span class="nu0">0</span><span class="br0">&#41;</span> <span class="br0">&#91;</span>self.delegate eventView:self flickDirection:dir<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span class="br0">&#91;</span>super touchesEnded:touches withEvent:event<span class="br0">&#93;</span>;<br />
<span class="br0">&#125;</span></p>
<p>- <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>dealloc <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span>super dealloc<span class="br0">&#93;</span>;<br />
<span class="br0">&#125;</span><br />
<span class="kw4">@end</span></div>
<p>With the header out of the way we can implement the class. The <code>touchesEnded:withEvent:</code> method will be executed when the touch is complete. We can access the touch event, using <code>[touches anyObject]</code> and look at the <code>[touch previousLocationInView:self]</code> and <code>[touch locationInView:self]</code> methods to access the two touch points.</p>
<p>If these points are more then <code>MAX_FLICK_DISTANCE</code> apart then we have a flick. As long as we have a flick we notify our delegate by calling the <code>eventView:flickDirection:</code> method we defined in our protocol.</p>
<p>With the view created, we can hook it up to our <code>EventViewController</code>. Open up <code>EventViewController.h</code>. We need to add an import for the <code>EventView.h</code> file and then add the <code>EventViewDelegate</code> protocol to the view controller.</p>
</pre>
<div class="dean_ch" style="white-space: wrap;"><span class="kw4">@interface</span> EventViewController : UITableViewController &lt;EventViewDelegate&gt;</div>
<p>Moving over to <code>EventViewController.h</code> we need to add an import for the <code>RootViewController.h</code>. We'll be accessing it's fetched results in order to get the new view information.</p>
<p>We need to load our new <code>EventView</code> as the view to use for the controller. We do this by overriding <code>loadView</code></p>
<div class="dean_ch" style="white-space: wrap;">- <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>loadView<br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; EventView *eventView = <span class="br0">&#91;</span><span class="br0">&#91;</span><span class="br0">&#91;</span>EventView alloc<span class="br0">&#93;</span> initWithFrame:CGRectZero<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;style:UITableViewStyleGrouped<span class="br0">&#93;</span> autorelease<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#91;</span>eventView setDelegate:self<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#91;</span>eventView setDataSource:self<span class="br0">&#93;</span>;</p>
<p>&nbsp; &nbsp; self.view = eventView;<br />
&nbsp; &nbsp; self.tableView = eventView;<br />
<span class="br0">&#125;</span></div>
<p>Nothing too special here, we create our <code>EventView</code> and tell it that the current controller is the delegate and data source for the view. Finally, we assign the new view to the controllers <code>view</code> and <code>tableView</code> methods. You need to assign both of these for things to work properly.</p>
<p>The last step is to implement the <code>eventView:flickDirection:</code> method.</p>
<div class="dean_ch" style="white-space: wrap;">- <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>eventView:<span class="br0">&#40;</span>EventView *<span class="br0">&#41;</span>view flickDirection:<span class="br0">&#40;</span><span class="kw4">int</span><span class="br0">&#41;</span>dir<br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; NSFetchedResultsController *frc = <span class="br0">&#91;</span><span class="br0">&#91;</span><span class="br0">&#91;</span>self.navigationController viewControllers<span class="br0">&#93;</span> objectAtIndex:<span class="nu0">0</span><span class="br0">&#93;</span> fetchedResultsController<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; NSIndexPath *path = <span class="br0">&#91;</span>frc indexPathForObject:<span class="br0">&#91;</span>self event<span class="br0">&#93;</span><span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; NSInteger row = <span class="br0">&#91;</span>path row<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="br0">&#40;</span>dir == <span class="nu0">-1</span><span class="br0">&#41;</span> &amp;&amp; <span class="br0">&#40;</span>row &gt; <span class="nu0">0</span><span class="br0">&#41;</span><span class="br0">&#41;</span> row --;<br />
&nbsp; &nbsp; <span class="kw1">else</span> <span class="kw1">if</span> <span class="br0">&#40;</span><span class="br0">&#40;</span>dir == <span class="nu0">1</span><span class="br0">&#41;</span> &amp;&amp; <span class="br0">&#40;</span>row &lt; <span class="br0">&#40;</span><span class="br0">&#91;</span><span class="br0">&#91;</span>frc fetchedObjects<span class="br0">&#93;</span> count<span class="br0">&#93;</span> - <span class="nu0">1</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> row ++;</p>
<p>&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>row == <span class="br0">&#91;</span>path row<span class="br0">&#93;</span><span class="br0">&#41;</span> <span class="kw1">return</span>;</p>
<p>&nbsp; &nbsp; NSIndexPath *newPath = <span class="br0">&#91;</span>NSIndexPath indexPathForRow:row inSection:<span class="br0">&#91;</span>path section<span class="br0">&#93;</span><span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#91;</span>self setEvent:<span class="br0">&#91;</span>frc objectAtIndexPath:newPath<span class="br0">&#93;</span><span class="br0">&#93;</span>;</p>
<p>&nbsp; &nbsp; <span class="br0">&#91;</span>self.tableView reloadData<span class="br0">&#93;</span>; &nbsp; &nbsp;<br />
<span class="br0">&#125;</span></div>
<p>The idea here is simple, grab the fetched results from the root view using <code>[[[self.navigationController viewControllers] objectAtIndex:0] fetchedResultsController]</code>. The root view is always at the bottom of the view stack. The fetched results controller can then be used to find the index for the event that we're currently viewing. We can then get the next view, either left or right based on flick direction, to be displayed. We set that event into the view and reload the table data.</p>
<p>Building and running you should be able to flick between views. Although, there is an issue, the visuals really suck. Let's do something about that.</p>
<p>In order to make things look good we should push the new views into place and, if we're on the end view, bounce a little to show the user they can't go any further.</p>
<p>For this, we're going to dip into a little but of Core Animation. To that end, we need to import <code>&lt;QuartzCore/QuartzCore.h&gt;</code> into the <code>EventViewController.h</code> file and add the <code>QuartzCore.framework</code> into our project.</p>
<p>We'll start with a little helper method.</p>
</pre>
<div class="dean_ch" style="white-space: wrap;">- <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>addAnimation:<span class="br0">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/ObjC_classic/Classes/NSString.html"><span class="kw5">NSString</span></a> *<span class="br0">&#41;</span>name subtype:<span class="br0">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/ObjC_classic/Classes/NSString.html"><span class="kw5">NSString</span></a> *<span class="br0">&#41;</span>subtype duration:<span class="br0">&#40;</span><span class="kw4">float</span><span class="br0">&#41;</span>duration<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;start:<span class="br0">&#40;</span><span class="kw4">float</span><span class="br0">&#41;</span>start end:<span class="br0">&#40;</span><span class="kw4">float</span><span class="br0">&#41;</span>end delegate:<span class="br0">&#40;</span><span class="kw4">id</span><span class="br0">&#41;</span>animDelegate <span class="br0">&#123;</span><br />
&nbsp; &nbsp; CATransition *anim = <span class="br0">&#91;</span>CATransition animation<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span class="br0">&#91;</span>anim setDuration:duration<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#91;</span>anim setType:kCATransitionPush<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#91;</span>anim setSubtype:subtype<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#91;</span>anim setStartProgress:start<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#91;</span>anim setEndProgress:end<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#91;</span>anim setDelegate:animDelegate<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#91;</span>anim setTimingFunction:<span class="br0">&#91;</span>CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut<span class="br0">&#93;</span><span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span class="br0">&#91;</span><span class="br0">&#91;</span><span class="br0">&#91;</span>self.view superview<span class="br0">&#93;</span> layer<span class="br0">&#93;</span> addAnimation:anim forKey:name<span class="br0">&#93;</span>;<br />
<span class="br0">&#125;</span></div>
<p><code>addAnimation:subtype:duration:start:end:delegate:</code> adds a new animation to the system. We're always using push animations and always using the same ease timing function. Otherwise, we can set things up as we need.</p>
<p>To start, add the following to the end of <code>eventView:flickDirection:</code></p>
<div class="dean_ch" style="white-space: wrap;"> &nbsp; &nbsp;<span class="br0">&#91;</span>self addAnimation:@<span class="st0">&quot;SwitchView&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;subtype:<span class="br0">&#40;</span>dir == <span class="nu0">-1</span> ? kCATransitionFromLeft : kCATransitionFromRight<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; duration:<span class="nu0">0.5</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;start:<span class="nu0">0.0</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;end:<span class="nu0">1.0</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; delegate:<span class="kw2">nil</span><span class="br0">&#93;</span>;</div>
<p>If you run this, you should be able to flick between views with a nice push animation. You can change the <em>duration</em> to make the transition slower or faster as desired.</p>
<p>Now, let's add the bounce when we're at the end of our data. Inside <code>eventView:flickDirection:</code> change <code>if (row == [path row]) return;</code> to the following.</p>
<div class="dean_ch" style="white-space: wrap;"> &nbsp; &nbsp;<span class="kw1">if</span> <span class="br0">&#40;</span>row == <span class="br0">&#91;</span>path row<span class="br0">&#93;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#91;</span>self addAnimation:@<span class="st0">&quot;PushHalfOut&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;subtype:<span class="br0">&#40;</span>dir == <span class="nu0">-1</span> ? kCATransitionFromLeft : kCATransitionFromRight<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; duration:<span class="nu0">0.25</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;start:<span class="nu0">0.0</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;end:<span class="nu0">0.25</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; delegate:self<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span>;<br />
&nbsp; &nbsp; <span class="br0">&#125;</span></div>
<p>You'll notice we set the <em>delegate</em> in this instance to ourselves. This will cause <code>animationDidStop:finished:</code> to be called when the animation is finished. We're going to add that callback next.</p>
<div class="dean_ch" style="white-space: wrap;">- <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>animationDidStop:<span class="br0">&#40;</span>CAAnimation *<span class="br0">&#41;</span>animation finished:<span class="br0">&#40;</span><span class="kw4">BOOL</span><span class="br0">&#41;</span>flag <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span>self addAnimation:@<span class="st0">&quot;PushHalfIn&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;subtype:<span class="br0">&#91;</span><span class="br0">&#40;</span>CATransition *<span class="br0">&#41;</span>animation subtype<span class="br0">&#93;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; duration:<span class="nu0">0.25</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;start:<span class="nu0">0.25</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;end:<span class="nu0">0.0</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; delegate:<span class="kw2">nil</span><span class="br0">&#93;</span>;<br />
<span class="br0">&#125;</span></div>
<p>With the first animation we're shifting the view off screen by a quarter of the screen. Then, when that animation is finished, we trigger a second animation to shift the view back on screen again. Giving the appearance of a slight bounce.</p>
<p>With that, we have our views flicking and bouncing. You can grab the full source to this tutorial at <a href="http://github.com/dj2/Flick">http://github.com/dj2/Flick</a>.</p>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Fflicking-the-iphone%2F&amp;title=Flicking+the+iPhone" title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fflicking-the-iphone%2F&amp;title=Flicking+the+iPhone" title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fflicking-the-iphone%2F&amp;title=Flicking+the+iPhone" title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Fflicking-the-iphone%2F&amp;title=Flicking+the+iPhone" title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fflicking-the-iphone%2F&amp;title=Flicking+the+iPhone', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Fflicking-the-iphone%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Fflicking-the-iphone%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Fflicking-the-iphone%2F&amp;title=Flicking+the+iPhone" title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fflicking-the-iphone%2F&amp;title=Flicking+the+iPhone" title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></description>
			<content:encoded><![CDATA[<p><img src="http://everburning.com/wp-content/uploads/2009/08/IMG_4589-300x200.jpg" alt="IMG_4589" title="IMG_4589" width="300" height="200" class="alignleft size-medium wp-image-649" />I&#8217;ve been playing with my iPhone a bit lately. Trying to work out an application that I think will be useful (more on that some other time). In the mean time, I wanted to let the user of the application flick between views to see different detailed view screens. Each flick should take the user either forward or back in the list. The transitions should push the new item into view and have a little bounce on the end. </p>
<p>Turns out, this wasn&#8217;t trivial. I&#8217;m not sure now why I thought it would be trivial. Maybe because I&#8217;ve seen it in a few apps.</p>
<p>Anyway, what follows is how I did it. Maybe there is an easier way, if so, let me know, but this appears to work.</p>
<p>I&#8217;m going to create a simple Core Data backed, Navigation-based iPhone application. There will be two screens, the main list screen and a detailed view. The user will be able to flick between detailed views. Note, there is no real error checking, and possibly memory leaks in this code. You&#8217;ll need to fix up as appropriate for your application.</p>
<p>Firing up XCode, create a new project. Under the iPhone OS Applications template section, you&#8217;ll want to select <i>Navigation-based Application</i>. Make sure you&#8217;ve got <i>Use Core Data for storage</i> selected as well. In my case I&#8217;m naming the application <i>Flick</i>.<a href="http://everburning.com/wp-content/uploads/2009/08/choose_template.png"><img src="http://everburning.com/wp-content/uploads/2009/08/choose_template-300x167.png" alt="choose_template" title="choose_template" width="300" height="167" class="alignright size-medium wp-image-630" /></a></p>
<p>With the application created you should be able to execute it in the simulator and add a series of timestamps to the main view.</p>
<p><a href="http://everburning.com/wp-content/uploads/2009/08/managd_object_class.png"><img src="http://everburning.com/wp-content/uploads/2009/08/managd_object_class-300x133.png" alt="managd_object_class" title="managd_object_class" width="300" height="133" class="alignleft size-medium wp-image-632" /></a>Before we start adding screens, let&#8217;s create a class to hold our model entity. Double click on the <i>Flick.xcdatamodel</i> file to open the data modeler. Click on the <i>Event</i> entity and press command-n. Select, in the Mac OS X Cocoa section, <i>Managed Object Class</i>. Click through to finish the creation as the rest of the defaults will suffice. You should now have <i>Event.h</i> and <i>Event.m</i> files created in your project.<a href="http://everburning.com/wp-content/uploads/2009/08/create-view-controller.png"><img src="http://everburning.com/wp-content/uploads/2009/08/create-view-controller-300x151.png" alt="create view controller" title="create view controller" width="300" height="151" class="alignright size-medium wp-image-631" /></a></p>
<p>With the base application in place we still need to create a screen to allow viewing of our events. I&#8217;m backing this screen with a <i>UITableViewController</i> because that&#8217;s how my application does it. You could probably use a plain <i>UIViewController</i> if you don&#8217;t need to extra functionality of the table view controller.</p>
<p>Add a new file to the project and, under iPhone OS Cocoa Touch Class, select Objective-C class and set a Subclass of <i>UITableViewController</i>. Name the file <i>EventViewController.m</i> and have the <i>.h</i> file auto generated.</p>
<p>Opening up <i>RootViewController.m</i> we need to hookup the code to create our new view when one of the rows is selected.</p>
<p>First, lets mark the rows as having another view. Add <code>cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;</code> into the <code>tableView:cellForRowAtIndexPath:</code> method right after the UITableViewCell is created.</p>
<div class="dean_ch" style="white-space: wrap;">- <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>tableView:<span class="br0">&#40;</span>UITableView *<span class="br0">&#41;</span>tableView didSelectRowAtIndexPath:<span class="br0">&#40;</span>NSIndexPath *<span class="br0">&#41;</span>indexPath<br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; Event *event = <span class="br0">&#40;</span>Event *<span class="br0">&#41;</span><span class="br0">&#91;</span>fetchedResultsController objectAtIndexPath:indexPath<span class="br0">&#93;</span>;</p>
<p>&nbsp; &nbsp; EventViewController *controller = <span class="br0">&#91;</span><span class="br0">&#91;</span>EventViewController alloc<span class="br0">&#93;</span> initWithStyle:UITableViewStyleGrouped<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; controller.event = event;</p>
<p>&nbsp; &nbsp; <span class="br0">&#91;</span>self.navigationController pushViewController:controller animated:YES<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#91;</span>controller release<span class="br0">&#93;</span>;<br />
<span class="br0">&#125;</span></div>
<p>Using the <i>tableView:didSelectRowAtIndexPath:</i> delegate method we retrieve the selected event from our <i>fetchedResultsController</i>. We then create a new <i>EventViewController</i> with a table style of <i>UITableViewStyleGrouped</i>. There are two styles to tables Grouped and Plain.</p>
<dl>
<dt>UITableViewStylePlain</dt>
<dd>A plain table view. Any section headers or footers are displayed as inline separators and float when the table view is scrolled.</dd>
<dt>UITableViewStyleGrouped</dt>
<dd>A table view whose sections present distinct groups of rows. The section headers and footers do not float.</dd>
</dl>
<p>With the controller created we assign the selected event to the controller. Finally, we push the new view controller into the navigation stack using <code>[self.navigationController pushViewController:controller animated:YES]</code>. This will set our new view as the view to be displayed. Oh, and don&#8217;t forget to import <i>EventViewController.h</i> and <i>Event.h</i>.</p>
<p>We want our new view controller to have a back button. <del datetime="2009-08-14T19:05:05+00:00">If you don&#8217;t set the name in the <i>MainWindow.xib</i> file the back button will be available, but invisible. Double click the <i>MainWindow.xib</i> file to open it in Interface Builder. Click on the navigation bar in the view and press shift-command-i to open the inspector. Press command-1 to switch to the attributes panel. In the <i>Back Button</i> field enter <i>Events</i>.  You can now save and exit Interface Builder.</del> Thanks to <a href="http://streamingcolour.com">Owen</a>, setting <code>self.navigationItem.title = @"Events"</code> in the <code>viewDidLoad</code> method of <em>RootViewController</em> will set the correct text into the back button.</p>
<p>Continuing on, we can fill in the <i>EventViewController</i> files, starting with the header.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="co2">#import &quot;Event.h&quot;</span></p>
<p><span class="kw4">@interface</span> EventViewController : UITableViewController <span class="br0">&#123;</span><br />
&nbsp; &nbsp; Event *event;<br />
<span class="br0">&#125;</span><br />
@property <span class="br0">&#40;</span>nonatomic, retain<span class="br0">&#41;</span> Event *event;<br />
<span class="kw4">@end</span></div>
<p>The view controller has one attribute, the event that was selected. We setup a property for the event to handle the getters and setters for us.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="co2">#import &quot;EventViewController.h&quot;</span></p>
<p><span class="kw4">@implementation</span> EventViewController</p>
<p>@synthesize event;</p>
<p>- <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>viewDidLoad <span class="br0">&#123;</span><br />
&nbsp; &nbsp; self.navigationItem.title = @<span class="st0">&quot;Event Info&quot;</span>;<br />
<span class="br0">&#125;</span></p>
<p>- <span class="br0">&#40;</span>NSInteger<span class="br0">&#41;</span>numberOfSectionsInTableView:<span class="br0">&#40;</span>UITableView *<span class="br0">&#41;</span>tableView <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> <span class="nu0">1</span>;<br />
<span class="br0">&#125;</span></p>
<p>- <span class="br0">&#40;</span>NSInteger<span class="br0">&#41;</span>tableView:<span class="br0">&#40;</span>UITableView *<span class="br0">&#41;</span>tableView numberOfRowsInSection:<span class="br0">&#40;</span>NSInteger<span class="br0">&#41;</span>section <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> <span class="nu0">1</span>;<br />
<span class="br0">&#125;</span></p>
<p>- <span class="br0">&#40;</span>UITableViewCell *<span class="br0">&#41;</span>tableView:<span class="br0">&#40;</span>UITableView *<span class="br0">&#41;</span>tableView cellForRowAtIndexPath:<span class="br0">&#40;</span>NSIndexPath *<span class="br0">&#41;</span>indexPath <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw4">static</span> <a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/ObjC_classic/Classes/NSString.html"><span class="kw5">NSString</span></a> *LocationCellIdentifier = @<span class="st0">&quot;Timestamp&quot;</span>;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; UITableViewCell *cell = <span class="br0">&#91;</span>tableView dequeueReusableCellWithIdentifier:LocationCellIdentifier<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>cell == <span class="kw2">nil</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; cell = <span class="br0">&#91;</span><span class="br0">&#91;</span><span class="br0">&#91;</span>UITableViewCell alloc<span class="br0">&#93;</span> initWithStyle:UITableViewCellStyleSubtitle<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;reuseIdentifier:LocationCellIdentifier<span class="br0">&#93;</span> autorelease<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; cell.accessoryType = UITableViewCellAccessoryNone;<br />
&nbsp; &nbsp; <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; <a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/ObjC_classic/Classes/NSDateFormatter.html"><span class="kw5">NSDateFormatter</span></a> *dateFormat = <span class="br0">&#91;</span><span class="br0">&#91;</span><span class="br0">&#91;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/ObjC_classic/Classes/NSDateFormatter.html"><span class="kw5">NSDateFormatter</span></a> alloc<span class="br0">&#93;</span> init<span class="br0">&#93;</span> autorelease<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#91;</span>dateFormat setDateStyle:NSDateFormatterLongStyle<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#91;</span>dateFormat setTimeStyle:NSDateFormatterLongStyle<span class="br0">&#93;</span>;</p>
<p>&nbsp; &nbsp; cell.textLabel.text = <span class="br0">&#91;</span>dateFormat stringFromDate:event.timeStamp<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span class="kw1">return</span> cell;<br />
<span class="br0">&#125;</span></p>
<p>- <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>dealloc <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span>super dealloc<span class="br0">&#93;</span>;<br />
<span class="br0">&#125;</span><br />
<span class="kw4">@end</span></div>
<p>Next up is the implementation file. When the view finishes loading we want to set the title to display <em>Event Info</em>. This is done in <code>viewDidLoad</code> by calling <code>self.navigationItem.title = @"Event Info"</code>.</p>
<p>We&#8217;re only going to show one thing in our table, the timestamp, so we setup the table to have a single section and a single row. We then create a cell to display the time stamp.</p>
<p>You should be able to build and run the application at this point. You can click on the events to move to the subview. From there you can navigate back to the main list.</p>
<p>With that, we&#8217;re ready to add some flicking to our application.</p>
<p>There is no <em>flick</em> event built into the iPhone APIs that I could find. The way to achieve a flick is to listen for the <code>touchesEnded:withEvent:</code> delegate method and determine if a flicked happened. The one catch, this delegate is available on <code>UIView</code> objects. We don&#8217;t, currently, have a <code>UIView</code> object we can attach the delegate method too.</p>
<p>So, we&#8217;re going to subclass <code>UIView</code> deal with the flicking, then let our parent <code>EventViewController</code> know when the flick happened so we can switch views. Sound good? Ok, let&#8217;s go.</p>
<p>Add a new class to the project. Under iPhone OS Cocoa Touch Class select Object-C class. Make sure <i>UIView</i> is selected in the drop down. Name the new file <em>EventView.m</em>.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw4">@protocol</span> EventViewDelegate;</p>
<p><span class="kw4">@interface</span> EventView : UITableView <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw4">id</span> &lt;UITableViewDelegate, EventViewDelegate&gt; delegate;<br />
<span class="br0">&#125;</span><br />
@property <span class="br0">&#40;</span>nonatomic, assign<span class="br0">&#41;</span> <span class="kw4">id</span> &lt;UITableViewDelegate, EventViewDelegate&gt; delegate;<br />
<span class="kw4">@end</span></p>
<p><span class="kw4">@protocol</span> EventViewDelegate &lt;NSObject&gt;<br />
- <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>eventView:<span class="br0">&#40;</span>EventView *<span class="br0">&#41;</span>view flickDirection:<span class="br0">&#40;</span><span class="kw4">int</span><span class="br0">&#41;</span>dir;<br />
<span class="kw4">@end</span></div>
<p>There are a couple things going on here. First, I switched the inherited class to <code>UITableView</code> as that&#8217;s what we&#8217;re actually dealing with.</p>
<p>We add one attribute to the <code>EventView</code> class. We need to be able to assign the controller as a delegate for our view so we can notify the controller of the flick. In order for the table to continue working, we also need to make sure we maintain the <code>UITableViewDelegate</code> protocol already attached to the delegate object in the super class. </p>
<p>Our protocol is pretty simple, we need one method implemented, <code>eventView:flickDirection:</code>. This will be called when the view is flicked and set the <em>flickDirection</em> to -1 for a left flick and 1 for a right flick.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="co2">#import &quot;EventView.h&quot;</span></p>
<p><span class="co2">#define MIN_FLICK_DISTANCE 10.0</span></p>
<p><span class="kw4">@implementation</span> EventView<br />
@synthesize delegate;</p>
<p>- <span class="br0">&#40;</span><span class="kw4">int</span><span class="br0">&#41;</span>flicked:<span class="br0">&#40;</span>UITouch *<span class="br0">&#41;</span>touch <span class="br0">&#123;</span><br />
&nbsp; &nbsp; CGPoint start = <span class="br0">&#91;</span>touch previousLocationInView:self<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; CGPoint end = <span class="br0">&#91;</span>touch locationInView:self<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span class="kw4">double</span> x = end.x &#8211; start.x;<br />
&nbsp; &nbsp; <span class="kw4">double</span> y = end.y &#8211; start.y;<br />
&nbsp; &nbsp; <span class="kw4">double</span> dist = <a href="http://www.opengroup.org/onlinepubs/009695399/functions/sqrt.html"><span class="kw3">sqrt</span></a><span class="br0">&#40;</span><span class="br0">&#40;</span>x * x<span class="br0">&#41;</span> + <span class="br0">&#40;</span>y * y<span class="br0">&#41;</span><span class="br0">&#41;</span>;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>dist &gt; MIN_FLICK_DISTANCE<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>end.x &lt; start.x<span class="br0">&#41;</span> <span class="kw1">return</span> <span class="nu0">1</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">else</span> <span class="kw1">return</span> <span class="nu0">-1</span>;<br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> <span class="nu0">0</span>;<br />
<span class="br0">&#125;</span></p>
<p>- <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>touchesEnded:<span class="br0">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/ObjC_classic/Classes/NSSet.html"><span class="kw5">NSSet</span></a> *<span class="br0">&#41;</span>touches withEvent:<span class="br0">&#40;</span>UIEvent *<span class="br0">&#41;</span>event <span class="br0">&#123;</span><br />
&nbsp; &nbsp; UITouch *touch = <span class="br0">&#91;</span>touches anyObject<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="kw4">int</span> dir = <span class="br0">&#91;</span>self flicked:touch<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>dir != <span class="nu0">0</span><span class="br0">&#41;</span> <span class="br0">&#91;</span>self.delegate eventView:self flickDirection:dir<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span class="br0">&#91;</span>super touchesEnded:touches withEvent:event<span class="br0">&#93;</span>;<br />
<span class="br0">&#125;</span></p>
<p>- <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>dealloc <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span>super dealloc<span class="br0">&#93;</span>;<br />
<span class="br0">&#125;</span><br />
<span class="kw4">@end</span></div>
<p>With the header out of the way we can implement the class. The <code>touchesEnded:withEvent:</code> method will be executed when the touch is complete. We can access the touch event, using <code>[touches anyObject]</code> and look at the <code>[touch previousLocationInView:self]</code> and <code>[touch locationInView:self]</code> methods to access the two touch points.</p>
<p>If these points are more then <code>MAX_FLICK_DISTANCE</code> apart then we have a flick. As long as we have a flick we notify our delegate by calling the <code>eventView:flickDirection:</code> method we defined in our protocol.</p>
<p>With the view created, we can hook it up to our <code>EventViewController</code>. Open up <code>EventViewController.h</code>. We need to add an import for the <code>EventView.h</code> file and then add the <code>EventViewDelegate</code> protocol to the view controller.</p>
</pre>
<div class="dean_ch" style="white-space: wrap;"><span class="kw4">@interface</span> EventViewController : UITableViewController &lt;EventViewDelegate&gt;</div>
<p>Moving over to <code>EventViewController.h</code> we need to add an import for the <code>RootViewController.h</code>. We'll be accessing it's fetched results in order to get the new view information.</p>
<p>We need to load our new <code>EventView</code> as the view to use for the controller. We do this by overriding <code>loadView</code></p>
<div class="dean_ch" style="white-space: wrap;">- <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>loadView<br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; EventView *eventView = <span class="br0">&#91;</span><span class="br0">&#91;</span><span class="br0">&#91;</span>EventView alloc<span class="br0">&#93;</span> initWithFrame:CGRectZero<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;style:UITableViewStyleGrouped<span class="br0">&#93;</span> autorelease<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#91;</span>eventView setDelegate:self<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#91;</span>eventView setDataSource:self<span class="br0">&#93;</span>;</p>
<p>&nbsp; &nbsp; self.view = eventView;<br />
&nbsp; &nbsp; self.tableView = eventView;<br />
<span class="br0">&#125;</span></div>
<p>Nothing too special here, we create our <code>EventView</code> and tell it that the current controller is the delegate and data source for the view. Finally, we assign the new view to the controllers <code>view</code> and <code>tableView</code> methods. You need to assign both of these for things to work properly.</p>
<p>The last step is to implement the <code>eventView:flickDirection:</code> method.</p>
<div class="dean_ch" style="white-space: wrap;">- <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>eventView:<span class="br0">&#40;</span>EventView *<span class="br0">&#41;</span>view flickDirection:<span class="br0">&#40;</span><span class="kw4">int</span><span class="br0">&#41;</span>dir<br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; NSFetchedResultsController *frc = <span class="br0">&#91;</span><span class="br0">&#91;</span><span class="br0">&#91;</span>self.navigationController viewControllers<span class="br0">&#93;</span> objectAtIndex:<span class="nu0">0</span><span class="br0">&#93;</span> fetchedResultsController<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; NSIndexPath *path = <span class="br0">&#91;</span>frc indexPathForObject:<span class="br0">&#91;</span>self event<span class="br0">&#93;</span><span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; NSInteger row = <span class="br0">&#91;</span>path row<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="br0">&#40;</span>dir == <span class="nu0">-1</span><span class="br0">&#41;</span> &amp;&amp; <span class="br0">&#40;</span>row &gt; <span class="nu0">0</span><span class="br0">&#41;</span><span class="br0">&#41;</span> row --;<br />
&nbsp; &nbsp; <span class="kw1">else</span> <span class="kw1">if</span> <span class="br0">&#40;</span><span class="br0">&#40;</span>dir == <span class="nu0">1</span><span class="br0">&#41;</span> &amp;&amp; <span class="br0">&#40;</span>row &lt; <span class="br0">&#40;</span><span class="br0">&#91;</span><span class="br0">&#91;</span>frc fetchedObjects<span class="br0">&#93;</span> count<span class="br0">&#93;</span> - <span class="nu0">1</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> row ++;</p>
<p>&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>row == <span class="br0">&#91;</span>path row<span class="br0">&#93;</span><span class="br0">&#41;</span> <span class="kw1">return</span>;</p>
<p>&nbsp; &nbsp; NSIndexPath *newPath = <span class="br0">&#91;</span>NSIndexPath indexPathForRow:row inSection:<span class="br0">&#91;</span>path section<span class="br0">&#93;</span><span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#91;</span>self setEvent:<span class="br0">&#91;</span>frc objectAtIndexPath:newPath<span class="br0">&#93;</span><span class="br0">&#93;</span>;</p>
<p>&nbsp; &nbsp; <span class="br0">&#91;</span>self.tableView reloadData<span class="br0">&#93;</span>; &nbsp; &nbsp;<br />
<span class="br0">&#125;</span></div>
<p>The idea here is simple, grab the fetched results from the root view using <code>[[[self.navigationController viewControllers] objectAtIndex:0] fetchedResultsController]</code>. The root view is always at the bottom of the view stack. The fetched results controller can then be used to find the index for the event that we're currently viewing. We can then get the next view, either left or right based on flick direction, to be displayed. We set that event into the view and reload the table data.</p>
<p>Building and running you should be able to flick between views. Although, there is an issue, the visuals really suck. Let's do something about that.</p>
<p>In order to make things look good we should push the new views into place and, if we're on the end view, bounce a little to show the user they can't go any further.</p>
<p>For this, we're going to dip into a little but of Core Animation. To that end, we need to import <code>&lt;QuartzCore/QuartzCore.h&gt;</code> into the <code>EventViewController.h</code> file and add the <code>QuartzCore.framework</code> into our project.</p>
<p>We'll start with a little helper method.</p>
</pre>
<div class="dean_ch" style="white-space: wrap;">- <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>addAnimation:<span class="br0">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/ObjC_classic/Classes/NSString.html"><span class="kw5">NSString</span></a> *<span class="br0">&#41;</span>name subtype:<span class="br0">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/ObjC_classic/Classes/NSString.html"><span class="kw5">NSString</span></a> *<span class="br0">&#41;</span>subtype duration:<span class="br0">&#40;</span><span class="kw4">float</span><span class="br0">&#41;</span>duration<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;start:<span class="br0">&#40;</span><span class="kw4">float</span><span class="br0">&#41;</span>start end:<span class="br0">&#40;</span><span class="kw4">float</span><span class="br0">&#41;</span>end delegate:<span class="br0">&#40;</span><span class="kw4">id</span><span class="br0">&#41;</span>animDelegate <span class="br0">&#123;</span><br />
&nbsp; &nbsp; CATransition *anim = <span class="br0">&#91;</span>CATransition animation<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span class="br0">&#91;</span>anim setDuration:duration<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#91;</span>anim setType:kCATransitionPush<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#91;</span>anim setSubtype:subtype<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#91;</span>anim setStartProgress:start<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#91;</span>anim setEndProgress:end<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#91;</span>anim setDelegate:animDelegate<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <span class="br0">&#91;</span>anim setTimingFunction:<span class="br0">&#91;</span>CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut<span class="br0">&#93;</span><span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span class="br0">&#91;</span><span class="br0">&#91;</span><span class="br0">&#91;</span>self.view superview<span class="br0">&#93;</span> layer<span class="br0">&#93;</span> addAnimation:anim forKey:name<span class="br0">&#93;</span>;<br />
<span class="br0">&#125;</span></div>
<p><code>addAnimation:subtype:duration:start:end:delegate:</code> adds a new animation to the system. We're always using push animations and always using the same ease timing function. Otherwise, we can set things up as we need.</p>
<p>To start, add the following to the end of <code>eventView:flickDirection:</code></p>
<div class="dean_ch" style="white-space: wrap;"> &nbsp; &nbsp;<span class="br0">&#91;</span>self addAnimation:@<span class="st0">&quot;SwitchView&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;subtype:<span class="br0">&#40;</span>dir == <span class="nu0">-1</span> ? kCATransitionFromLeft : kCATransitionFromRight<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; duration:<span class="nu0">0.5</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;start:<span class="nu0">0.0</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;end:<span class="nu0">1.0</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; delegate:<span class="kw2">nil</span><span class="br0">&#93;</span>;</div>
<p>If you run this, you should be able to flick between views with a nice push animation. You can change the <em>duration</em> to make the transition slower or faster as desired.</p>
<p>Now, let's add the bounce when we're at the end of our data. Inside <code>eventView:flickDirection:</code> change <code>if (row == [path row]) return;</code> to the following.</p>
<div class="dean_ch" style="white-space: wrap;"> &nbsp; &nbsp;<span class="kw1">if</span> <span class="br0">&#40;</span>row == <span class="br0">&#91;</span>path row<span class="br0">&#93;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#91;</span>self addAnimation:@<span class="st0">&quot;PushHalfOut&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;subtype:<span class="br0">&#40;</span>dir == <span class="nu0">-1</span> ? kCATransitionFromLeft : kCATransitionFromRight<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; duration:<span class="nu0">0.25</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;start:<span class="nu0">0.0</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;end:<span class="nu0">0.25</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; delegate:self<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span>;<br />
&nbsp; &nbsp; <span class="br0">&#125;</span></div>
<p>You'll notice we set the <em>delegate</em> in this instance to ourselves. This will cause <code>animationDidStop:finished:</code> to be called when the animation is finished. We're going to add that callback next.</p>
<div class="dean_ch" style="white-space: wrap;">- <span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>animationDidStop:<span class="br0">&#40;</span>CAAnimation *<span class="br0">&#41;</span>animation finished:<span class="br0">&#40;</span><span class="kw4">BOOL</span><span class="br0">&#41;</span>flag <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#91;</span>self addAnimation:@<span class="st0">&quot;PushHalfIn&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;subtype:<span class="br0">&#91;</span><span class="br0">&#40;</span>CATransition *<span class="br0">&#41;</span>animation subtype<span class="br0">&#93;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; duration:<span class="nu0">0.25</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;start:<span class="nu0">0.25</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;end:<span class="nu0">0.0</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; delegate:<span class="kw2">nil</span><span class="br0">&#93;</span>;<br />
<span class="br0">&#125;</span></div>
<p>With the first animation we're shifting the view off screen by a quarter of the screen. Then, when that animation is finished, we trigger a second animation to shift the view back on screen again. Giving the appearance of a slight bounce.</p>
<p>With that, we have our views flicking and bouncing. You can grab the full source to this tutorial at <a href="http://github.com/dj2/Flick">http://github.com/dj2/Flick</a>.</p>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Fflicking-the-iphone%2F&amp;title=Flicking+the+iPhone" title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fflicking-the-iphone%2F&amp;title=Flicking+the+iPhone" title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fflicking-the-iphone%2F&amp;title=Flicking+the+iPhone" title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Fflicking-the-iphone%2F&amp;title=Flicking+the+iPhone" title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fflicking-the-iphone%2F&amp;title=Flicking+the+iPhone', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Fflicking-the-iphone%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Fflicking-the-iphone%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Fflicking-the-iphone%2F&amp;title=Flicking+the+iPhone" title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fflicking-the-iphone%2F&amp;title=Flicking+the+iPhone" title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></content:encoded>
			<wfw:commentRss>http://everburning.com/news/flicking-the-iphone/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Toolbars with HotCocoa</title>
		<link>http://everburning.com/news/toolbars-with-hotcocoa/</link>
		<comments>http://everburning.com/news/toolbars-with-hotcocoa/#comments</comments>
		<pubDate>Tue, 21 Jul 2009 20:28:09 +0000</pubDate>
		<dc:creator>dj2</dc:creator>
				<category><![CDATA[Computers]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[HotCocoa]]></category>
		<category><![CDATA[MacRuby]]></category>
		<category><![CDATA[NSToolbar]]></category>

		<guid isPermaLink="false">http://everburning.com/?p=612</guid>
		<description><![CDATA[<p><img src="http://everburning.com/wp-content/uploads/2009/07/3407707273_c05017779f-300x277.jpg" alt="3407707273_c05017779f" title="3407707273_c05017779f" width="300" height="277" class="alignleft size-medium wp-image-613" />I had the opportunity to add a toolbar to my <a href="http://github.com/dj2/SilverLining">Silver Lining</a> app the other day and though I&#8217;d write down how I did it.  This would, probably, have been easier if I had an understanding of the Cocoa Toolbar.</p>
<p>The main thing that kept tripping me up is that you don&#8217;t add toolbar items directly to the toolbar. Each item you want to add to the toolbar is given an identifier (HotCocoa will generate one from the label if needed). Then, when you create your toolbar, you specify which toolbar items are in the toolbar by default and which items are available to be put in the bar.</p>
<p>Once the toolbar is created we don&#8217;t just append it to the window with the normal <code>&lt;&lt;</code> operator. We need to use <code>toolbar=</code> to assign the toolbar to the window.</p>
<p><a href="http://everburning.com/wp-content/uploads/2009/07/silverlining_toolbar.png"><img src="http://everburning.com/wp-content/uploads/2009/07/silverlining_toolbar-300x23.png" alt="silverlining_toolbar" title="silverlining_toolbar" width="300" height="23" class="alignright size-medium wp-image-614" /></a>The toolbar we&#8217;re going to create is fairly simple. We want a button, with image, on the left for reloading and a search field on the right. We&#8217;ll put a flexible spacer in the middle to put them on opposite sides of the screen. (Note, there is currently a bug in HotCocoa where our specified items will be to the right and the spacer to the left. Things may not look correct until fixed.)</p>
<div class="dean_ch" style="white-space: wrap;">
reload_item = toolbar_item<span class="br0">&#40;</span><span class="re3">:label</span> =&gt; <span class="st0">&quot;Reload&quot;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="re3">:image</span> =&gt; image<span class="br0">&#40;</span><span class="re3">:named</span> =&gt; <span class="st0">&quot;reload&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
reload_item.<span class="me1">on_action</span> <span class="br0">&#123;</span> reload_instances <span class="br0">&#125;</span></div>
<p>The first toolbar item we create is the reload button.  We set the image on the button to a file in the resources folder called <em>reload.png</em>. When the button is clicked, we want to execute the <code>reload_instances</code> method.</p>
<div class="dean_ch" style="white-space: wrap;">
search_item = toolbar_item<span class="br0">&#40;</span><span class="re3">:identifier</span> =&gt; <span class="st0">&quot;Search&quot;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |si|<br />
&nbsp; search = search_field<span class="br0">&#40;</span><span class="re3">:frame</span> =&gt; <span class="br0">&#91;</span><span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">250</span>, <span class="nu0">30</span><span class="br0">&#93;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:align =&gt; <span class="re3">:right</span>, <span class="re3">:start</span> =&gt; <span class="kw2">false</span><span class="br0">&#125;</span><span class="br0">&#41;</span><br />
&nbsp; search.<span class="me1">on_action</span> <span class="br0">&#123;</span> |sender| filter_instances<span class="br0">&#40;</span>search<span class="br0">&#41;</span> <span class="br0">&#125;</span></p>
<p>&nbsp; si.<span class="me1">view</span> = search<br />
<span class="kw1">end</span></div>
<p>The second item is the search box. You&#8217;ll notice we&#8217;re setting the <em>:identifier</em> instead of the <em>:label</em> as we did for the reload button. The difference being, the label will appear below the button and we don&#8217;t want <em>Search</em> appearing below the search box.  The reload button will have an identifier of <em>Reload</em> created for it by HotCocoa.</p>
<p>With the search toolbar item in hand we create a <code>search_field</code>.  The search field is then assigned as the view in the search toolbar item using  <em>si.view=</em>.</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="re1">@toolbar</span> = toolbar<span class="br0">&#40;</span><span class="re3">:default</span> =&gt; <span class="br0">&#91;</span>reload_item, <span class="re3">:flexible_space</span>, search_item<span class="br0">&#93;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |tb|<br />
&nbsp; win.<span class="me1">toolbar</span> = tb<br />
<span class="kw1">end</span></div>
<p>Finally, we create our toolbar. This is done with the <code>toolbar</code> method. We want our default set of items to be the <code>reload_item</code>, <code>:flexible_space</code> and <code>search_item</code>. We then assign the toolbar to our window with <code>win.toolbar = tb</code>.</p>
<p>There are a few default toolbar items you can use, similar to <em>:flexible_space</em>. They are:</p>
<ul>
<li>:separator</li>
<li>:space</li>
<li>:flexible_space</li>
<li>:show_colors</li>
<li>:show_fonts</li>
<li>:customize</li>
<li>:print</li>
</ul>
<p>That&#8217;s all folks. Have fun.</p>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Ftoolbars-with-hotcocoa%2F&amp;title=Toolbars+with+HotCocoa" title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Ftoolbars-with-hotcocoa%2F&amp;title=Toolbars+with+HotCocoa" title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Ftoolbars-with-hotcocoa%2F&amp;title=Toolbars+with+HotCocoa" title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Ftoolbars-with-hotcocoa%2F&amp;title=Toolbars+with+HotCocoa" title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Ftoolbars-with-hotcocoa%2F&amp;title=Toolbars+with+HotCocoa', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Ftoolbars-with-hotcocoa%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Ftoolbars-with-hotcocoa%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Ftoolbars-with-hotcocoa%2F&amp;title=Toolbars+with+HotCocoa" title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Ftoolbars-with-hotcocoa%2F&amp;title=Toolbars+with+HotCocoa" title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></description>
			<content:encoded><![CDATA[<p><img src="http://everburning.com/wp-content/uploads/2009/07/3407707273_c05017779f-300x277.jpg" alt="3407707273_c05017779f" title="3407707273_c05017779f" width="300" height="277" class="alignleft size-medium wp-image-613" />I had the opportunity to add a toolbar to my <a href="http://github.com/dj2/SilverLining">Silver Lining</a> app the other day and though I&#8217;d write down how I did it.  This would, probably, have been easier if I had an understanding of the Cocoa Toolbar.</p>
<p>The main thing that kept tripping me up is that you don&#8217;t add toolbar items directly to the toolbar. Each item you want to add to the toolbar is given an identifier (HotCocoa will generate one from the label if needed). Then, when you create your toolbar, you specify which toolbar items are in the toolbar by default and which items are available to be put in the bar.</p>
<p>Once the toolbar is created we don&#8217;t just append it to the window with the normal <code>&lt;&lt;</code> operator. We need to use <code>toolbar=</code> to assign the toolbar to the window.</p>
<p><a href="http://everburning.com/wp-content/uploads/2009/07/silverlining_toolbar.png"><img src="http://everburning.com/wp-content/uploads/2009/07/silverlining_toolbar-300x23.png" alt="silverlining_toolbar" title="silverlining_toolbar" width="300" height="23" class="alignright size-medium wp-image-614" /></a>The toolbar we&#8217;re going to create is fairly simple. We want a button, with image, on the left for reloading and a search field on the right. We&#8217;ll put a flexible spacer in the middle to put them on opposite sides of the screen. (Note, there is currently a bug in HotCocoa where our specified items will be to the right and the spacer to the left. Things may not look correct until fixed.)</p>
<div class="dean_ch" style="white-space: wrap;">
reload_item = toolbar_item<span class="br0">&#40;</span><span class="re3">:label</span> =&gt; <span class="st0">&quot;Reload&quot;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="re3">:image</span> =&gt; image<span class="br0">&#40;</span><span class="re3">:named</span> =&gt; <span class="st0">&quot;reload&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
reload_item.<span class="me1">on_action</span> <span class="br0">&#123;</span> reload_instances <span class="br0">&#125;</span></div>
<p>The first toolbar item we create is the reload button.  We set the image on the button to a file in the resources folder called <em>reload.png</em>. When the button is clicked, we want to execute the <code>reload_instances</code> method.</p>
<div class="dean_ch" style="white-space: wrap;">
search_item = toolbar_item<span class="br0">&#40;</span><span class="re3">:identifier</span> =&gt; <span class="st0">&quot;Search&quot;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |si|<br />
&nbsp; search = search_field<span class="br0">&#40;</span><span class="re3">:frame</span> =&gt; <span class="br0">&#91;</span><span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">250</span>, <span class="nu0">30</span><span class="br0">&#93;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:align =&gt; <span class="re3">:right</span>, <span class="re3">:start</span> =&gt; <span class="kw2">false</span><span class="br0">&#125;</span><span class="br0">&#41;</span><br />
&nbsp; search.<span class="me1">on_action</span> <span class="br0">&#123;</span> |sender| filter_instances<span class="br0">&#40;</span>search<span class="br0">&#41;</span> <span class="br0">&#125;</span></p>
<p>&nbsp; si.<span class="me1">view</span> = search<br />
<span class="kw1">end</span></div>
<p>The second item is the search box. You&#8217;ll notice we&#8217;re setting the <em>:identifier</em> instead of the <em>:label</em> as we did for the reload button. The difference being, the label will appear below the button and we don&#8217;t want <em>Search</em> appearing below the search box.  The reload button will have an identifier of <em>Reload</em> created for it by HotCocoa.</p>
<p>With the search toolbar item in hand we create a <code>search_field</code>.  The search field is then assigned as the view in the search toolbar item using  <em>si.view=</em>.</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="re1">@toolbar</span> = toolbar<span class="br0">&#40;</span><span class="re3">:default</span> =&gt; <span class="br0">&#91;</span>reload_item, <span class="re3">:flexible_space</span>, search_item<span class="br0">&#93;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |tb|<br />
&nbsp; win.<span class="me1">toolbar</span> = tb<br />
<span class="kw1">end</span></div>
<p>Finally, we create our toolbar. This is done with the <code>toolbar</code> method. We want our default set of items to be the <code>reload_item</code>, <code>:flexible_space</code> and <code>search_item</code>. We then assign the toolbar to our window with <code>win.toolbar = tb</code>.</p>
<p>There are a few default toolbar items you can use, similar to <em>:flexible_space</em>. They are:</p>
<ul>
<li>:separator</li>
<li>:space</li>
<li>:flexible_space</li>
<li>:show_colors</li>
<li>:show_fonts</li>
<li>:customize</li>
<li>:print</li>
</ul>
<p>That&#8217;s all folks. Have fun.</p>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Ftoolbars-with-hotcocoa%2F&amp;title=Toolbars+with+HotCocoa" title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Ftoolbars-with-hotcocoa%2F&amp;title=Toolbars+with+HotCocoa" title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Ftoolbars-with-hotcocoa%2F&amp;title=Toolbars+with+HotCocoa" title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Ftoolbars-with-hotcocoa%2F&amp;title=Toolbars+with+HotCocoa" title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Ftoolbars-with-hotcocoa%2F&amp;title=Toolbars+with+HotCocoa', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Ftoolbars-with-hotcocoa%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Ftoolbars-with-hotcocoa%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Ftoolbars-with-hotcocoa%2F&amp;title=Toolbars+with+HotCocoa" title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Ftoolbars-with-hotcocoa%2F&amp;title=Toolbars+with+HotCocoa" title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></content:encoded>
			<wfw:commentRss>http://everburning.com/news/toolbars-with-hotcocoa/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>EventMachine Screencast &#8212; EM-HTTP-Request</title>
		<link>http://everburning.com/news/eventmachine-screencast-em-http-request/</link>
		<comments>http://everburning.com/news/eventmachine-screencast-em-http-request/#comments</comments>
		<pubDate>Thu, 16 Jul 2009 00:52:42 +0000</pubDate>
		<dc:creator>dj2</dc:creator>
				<category><![CDATA[Computers]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Screencasts]]></category>
		<category><![CDATA[em-http-request]]></category>
		<category><![CDATA[EventMachine]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://everburning.com/?p=606</guid>
		<description><![CDATA[<p><img src="http://everburning.com/wp-content/uploads/2009/07/IMG_6037-300x200.jpg" alt="IMG_6037" title="IMG_6037" width="300" height="200" class="alignleft size-medium wp-image-608" />I decided to try my hand at creating a screencast the other day. I took a look at the EventMachine EM-HTTP-Request library and created a simple shell program to do single and multi requests. Take a look and let me know what you think.</p>
<ul>
<li><a href='http://everburning.com/wp-content/uploads/2009/07/eventmachine-em_http_request-screencast_1.m4v' >EventMachine Screencast &#8212; EM-HTTP-Request</a></li>
</ul>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw3">require</span> <span class="st0">&#8216;rubygems&#8217;</span><br />
<span class="kw3">require</span> <span class="st0">&#8216;eventmachine&#8217;</span><br />
<span class="kw3">require</span> <span class="st0">&#8216;em-http&#8217;</span><br />
<span class="kw3">require</span> <span class="st0">&#8216;pp&#8217;</span></p>
<p><span class="re0">$stdout</span>.<span class="me1">sync</span> = <span class="kw2">true</span></p>
<p><span class="kw1">class</span> KeyboardHandler &lt; <span class="re2">EM::Connection</span><br />
&nbsp; <span class="kw1">include</span> <span class="re2">EM::Protocols::LineText2</span><br />
&nbsp; <br />
&nbsp; <span class="kw1">def</span> post_init<br />
&nbsp; &nbsp; <span class="kw3">print</span> <span class="st0">&quot;&gt; &quot;</span><br />
&nbsp; <span class="kw1">end</span><br />
&nbsp; <br />
&nbsp; <span class="kw1">def</span> receive_line<span class="br0">&#40;</span>line<span class="br0">&#41;</span><br />
&nbsp; &nbsp; line.<span class="kw3">chomp</span>!<br />
&nbsp; &nbsp; line.<span class="kw3">gsub</span>!<span class="br0">&#40;</span>/^\s+/, <span class="st0">&#8221;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span class="kw1">case</span><span class="br0">&#40;</span>line<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">when</span> /^get <span class="br0">&#40;</span>.<span class="me1">*</span><span class="br0">&#41;</span>$/ <span class="kw1">then</span><br />
&nbsp; &nbsp; &nbsp; site = $<span class="nu0">1</span>.<span class="kw3">chomp</span><br />
&nbsp; &nbsp; &nbsp; sites = site.<span class="kw3">split</span><span class="br0">&#40;</span><span class="st0">&#8216;,&#8217;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; multi = <span class="re2">EM::MultiRequest</span>.<span class="me1">new</span><br />
&nbsp; &nbsp; &nbsp; sites.<span class="me1">each</span> <span class="kw1">do</span> |s|<br />
&nbsp; &nbsp; &nbsp; &nbsp; multi.<span class="me1">add</span><span class="br0">&#40;</span><span class="re2">EM::HttpRequest</span>.<span class="me1">new</span><span class="br0">&#40;</span>s<span class="br0">&#41;</span>.<span class="me1">get</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; multi.<span class="me1">callback</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">puts</span> <span class="st0">&quot;&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; multi.<span class="me1">responses</span><span class="br0">&#91;</span><span class="re3">:succeeded</span><span class="br0">&#93;</span>.<span class="me1">each</span> <span class="kw1">do</span> |h|<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pp h.<span class="me1">response_header</span>.<span class="me1">status</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pp h.<span class="me1">response_header</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; multi.<span class="me1">responses</span><span class="br0">&#91;</span><span class="re3">:failed</span><span class="br0">&#93;</span>.<span class="me1">each</span> <span class="kw1">do</span> |h|<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">puts</span> <span class="st0">&quot;#{h.inspect} failed&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">print</span> <span class="st0">&quot;&gt; &quot;</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw3">print</span> <span class="st0">&quot;&gt; &quot;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">when</span> /^exit$/ <span class="kw1">then</span><br />
&nbsp; &nbsp; &nbsp; EM.<span class="me1">stop</span></p>
<p>&nbsp; &nbsp; <span class="kw1">when</span> /^help$/ <span class="kw1">then</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw3">puts</span> <span class="st0">&quot;get URL[,URL]* &nbsp; &#8211; gets a URL&quot;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw3">puts</span> <span class="st0">&quot;exit &nbsp; &nbsp; &nbsp;- exits the app&quot;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw3">puts</span> <span class="st0">&quot;help &nbsp; &nbsp; &nbsp;- this help&quot;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw3">print</span> <span class="st0">&quot;&gt; &quot;</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></p>
<p>EM::run <span class="br0">&#123;</span><br />
&nbsp; EM.<span class="me1">open_keyboard</span><span class="br0">&#40;</span>KeyboardHandler<span class="br0">&#41;</span><br />
<span class="br0">&#125;</span><br />
<span class="kw3">puts</span> <span class="st0">&quot;Finished&quot;</span></div>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Feventmachine-screencast-em-http-request%2F&amp;title=EventMachine+Screencast+%26%238212%3B+EM-HTTP-Request" title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Feventmachine-screencast-em-http-request%2F&amp;title=EventMachine+Screencast+%26%238212%3B+EM-HTTP-Request" title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Feventmachine-screencast-em-http-request%2F&amp;title=EventMachine+Screencast+%26%238212%3B+EM-HTTP-Request" title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Feventmachine-screencast-em-http-request%2F&amp;title=EventMachine+Screencast+%26%238212%3B+EM-HTTP-Request" title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Feventmachine-screencast-em-http-request%2F&amp;title=EventMachine+Screencast+%26%238212%3B+EM-HTTP-Request', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Feventmachine-screencast-em-http-request%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Feventmachine-screencast-em-http-request%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Feventmachine-screencast-em-http-request%2F&amp;title=EventMachine+Screencast+%26%238212%3B+EM-HTTP-Request" title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Feventmachine-screencast-em-http-request%2F&amp;title=EventMachine+Screencast+%26%238212%3B+EM-HTTP-Request" title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></description>
			<content:encoded><![CDATA[<p><img src="http://everburning.com/wp-content/uploads/2009/07/IMG_6037-300x200.jpg" alt="IMG_6037" title="IMG_6037" width="300" height="200" class="alignleft size-medium wp-image-608" />I decided to try my hand at creating a screencast the other day. I took a look at the EventMachine EM-HTTP-Request library and created a simple shell program to do single and multi requests. Take a look and let me know what you think.</p>
<ul>
<li><a href='http://everburning.com/wp-content/uploads/2009/07/eventmachine-em_http_request-screencast_1.m4v' >EventMachine Screencast &#8212; EM-HTTP-Request</a></li>
</ul>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw3">require</span> <span class="st0">&#8216;rubygems&#8217;</span><br />
<span class="kw3">require</span> <span class="st0">&#8216;eventmachine&#8217;</span><br />
<span class="kw3">require</span> <span class="st0">&#8216;em-http&#8217;</span><br />
<span class="kw3">require</span> <span class="st0">&#8216;pp&#8217;</span></p>
<p><span class="re0">$stdout</span>.<span class="me1">sync</span> = <span class="kw2">true</span></p>
<p><span class="kw1">class</span> KeyboardHandler &lt; <span class="re2">EM::Connection</span><br />
&nbsp; <span class="kw1">include</span> <span class="re2">EM::Protocols::LineText2</span><br />
&nbsp; <br />
&nbsp; <span class="kw1">def</span> post_init<br />
&nbsp; &nbsp; <span class="kw3">print</span> <span class="st0">&quot;&gt; &quot;</span><br />
&nbsp; <span class="kw1">end</span><br />
&nbsp; <br />
&nbsp; <span class="kw1">def</span> receive_line<span class="br0">&#40;</span>line<span class="br0">&#41;</span><br />
&nbsp; &nbsp; line.<span class="kw3">chomp</span>!<br />
&nbsp; &nbsp; line.<span class="kw3">gsub</span>!<span class="br0">&#40;</span>/^\s+/, <span class="st0">&#8221;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span class="kw1">case</span><span class="br0">&#40;</span>line<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">when</span> /^get <span class="br0">&#40;</span>.<span class="me1">*</span><span class="br0">&#41;</span>$/ <span class="kw1">then</span><br />
&nbsp; &nbsp; &nbsp; site = $<span class="nu0">1</span>.<span class="kw3">chomp</span><br />
&nbsp; &nbsp; &nbsp; sites = site.<span class="kw3">split</span><span class="br0">&#40;</span><span class="st0">&#8216;,&#8217;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; multi = <span class="re2">EM::MultiRequest</span>.<span class="me1">new</span><br />
&nbsp; &nbsp; &nbsp; sites.<span class="me1">each</span> <span class="kw1">do</span> |s|<br />
&nbsp; &nbsp; &nbsp; &nbsp; multi.<span class="me1">add</span><span class="br0">&#40;</span><span class="re2">EM::HttpRequest</span>.<span class="me1">new</span><span class="br0">&#40;</span>s<span class="br0">&#41;</span>.<span class="me1">get</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; multi.<span class="me1">callback</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">puts</span> <span class="st0">&quot;&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; multi.<span class="me1">responses</span><span class="br0">&#91;</span><span class="re3">:succeeded</span><span class="br0">&#93;</span>.<span class="me1">each</span> <span class="kw1">do</span> |h|<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pp h.<span class="me1">response_header</span>.<span class="me1">status</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pp h.<span class="me1">response_header</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; multi.<span class="me1">responses</span><span class="br0">&#91;</span><span class="re3">:failed</span><span class="br0">&#93;</span>.<span class="me1">each</span> <span class="kw1">do</span> |h|<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">puts</span> <span class="st0">&quot;#{h.inspect} failed&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">print</span> <span class="st0">&quot;&gt; &quot;</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw3">print</span> <span class="st0">&quot;&gt; &quot;</span></p>
<p>&nbsp; &nbsp; <span class="kw1">when</span> /^exit$/ <span class="kw1">then</span><br />
&nbsp; &nbsp; &nbsp; EM.<span class="me1">stop</span></p>
<p>&nbsp; &nbsp; <span class="kw1">when</span> /^help$/ <span class="kw1">then</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw3">puts</span> <span class="st0">&quot;get URL[,URL]* &nbsp; &#8211; gets a URL&quot;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw3">puts</span> <span class="st0">&quot;exit &nbsp; &nbsp; &nbsp;- exits the app&quot;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw3">puts</span> <span class="st0">&quot;help &nbsp; &nbsp; &nbsp;- this help&quot;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw3">print</span> <span class="st0">&quot;&gt; &quot;</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></p>
<p>EM::run <span class="br0">&#123;</span><br />
&nbsp; EM.<span class="me1">open_keyboard</span><span class="br0">&#40;</span>KeyboardHandler<span class="br0">&#41;</span><br />
<span class="br0">&#125;</span><br />
<span class="kw3">puts</span> <span class="st0">&quot;Finished&quot;</span></div>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Feventmachine-screencast-em-http-request%2F&amp;title=EventMachine+Screencast+%26%238212%3B+EM-HTTP-Request" title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Feventmachine-screencast-em-http-request%2F&amp;title=EventMachine+Screencast+%26%238212%3B+EM-HTTP-Request" title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Feventmachine-screencast-em-http-request%2F&amp;title=EventMachine+Screencast+%26%238212%3B+EM-HTTP-Request" title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Feventmachine-screencast-em-http-request%2F&amp;title=EventMachine+Screencast+%26%238212%3B+EM-HTTP-Request" title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Feventmachine-screencast-em-http-request%2F&amp;title=EventMachine+Screencast+%26%238212%3B+EM-HTTP-Request', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Feventmachine-screencast-em-http-request%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Feventmachine-screencast-em-http-request%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Feventmachine-screencast-em-http-request%2F&amp;title=EventMachine+Screencast+%26%238212%3B+EM-HTTP-Request" title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Feventmachine-screencast-em-http-request%2F&amp;title=EventMachine+Screencast+%26%238212%3B+EM-HTTP-Request" title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></content:encoded>
			<wfw:commentRss>http://everburning.com/news/eventmachine-screencast-em-http-request/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
<enclosure url="http://everburning.com/wp-content/uploads/2009/07/eventmachine-em_http_request-screencast_1.m4v" length="22405958" type="video/x-m4v" />
		</item>
		<item>
		<title>Google Analytics, OAuth and Ruby. Oh, my.</title>
		<link>http://everburning.com/news/google-analytics-oauth-and-ruby-oh-my/</link>
		<comments>http://everburning.com/news/google-analytics-oauth-and-ruby-oh-my/#comments</comments>
		<pubDate>Wed, 08 Jul 2009 03:28:25 +0000</pubDate>
		<dc:creator>dj2</dc:creator>
				<category><![CDATA[Computers]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[OAuth]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://everburning.com/?p=594</guid>
		<description><![CDATA[<p><img src="http://everburning.com/wp-content/uploads/2009/07/IMG_6658-300x282.jpg" alt="IMG_6658" title="IMG_6658" width="300" height="282" class="alignleft size-medium wp-image-595" />I recently had the opportunity to take a peek at accessing <a href="http://google.com/analytics">Google Analytics</a> data using their <a href="http://oauth.net/">OAuth</a> endpoint.  We did this in Ruby using the Ruby OAuth gem.  There were a few snags along the way so I figured I&#8217;d put up some notes for other people to take a peek.</p>
<p>First, you&#8217;re going to need a newer version of the OAuth gem (0.3.5 seems to work). Google uses some features from the 1.0a version of the OAuth spec, the <code>oauth_verifier</code> specifically, that aren&#8217;t available in older versions of the OAuth gem.  That took a while to track down.</p>
<p>The second thing you&#8217;re going to need is a consumer token and secret from google. You can retrieve this from your domain management page (<a href="https://www.google.com/accounts/ManageDomains">here</a> as it&#8217;s a bit difficult to find.)</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw3">require</span> <span class="st0">&#8216;oauth&#8217;</span><br />
con = <span class="re2">OAuth::Consumer</span>.<span class="me1">new</span><span class="br0">&#40;</span>CONSUMER_TOKEN, CONSUMER_SECRET,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span>:site =&gt; <span class="st0">&#8216;https://www.google.com&#8217;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="re3">:request_token_path</span> =&gt; <span class="st0">&#8216;/accounts/OAuthGetRequestToken&#8217;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="re3">:access_token_path</span> =&gt; <span class="st0">&#8216;/accounts/OAuthGetAccessToken&#8217;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="re3">:authorize_path</span> =&gt; <span class="st0">&#8216;/accounts/OAuthAuthorizeToken&#8217;</span><span class="br0">&#125;</span><span class="br0">&#41;</span></div>
<p>To start we create our OAuth connection. You&#8217;ll need to substitute your token and secret for the consumer constants above. We need to specify all of the OAuth paths to match the endpoints provided by Google.</p>
<div class="dean_ch" style="white-space: wrap;">
rt = con.<span class="me1">get_request_token</span><span class="br0">&#40;</span><span class="br0">&#123;</span><span class="br0">&#125;</span>, <span class="br0">&#123;</span>:scope =&gt; <span class="st0">&#8216;https://www.google.com/analytics/feeds&#8217;</span><span class="br0">&#125;</span><span class="br0">&#41;</span></div>
<p>We use the connection to create a request token. <code>get_request_token</code> takes two parameters. The token parameters and other parameters. Above, I&#8217;m not specifying any request parameters but, if you want Google to redirect the user to a site of your choice you would provide <code>{:oauth_callback => URL}</code> as the token parameter. After authenticating the user would be redirected to the provided URL and the parameters <code>oauth_token=TOKEN</code> and <code>oauth_verifier=VERIFIER</code> will be passed to the URL. You&#8217;ll need to use those parameters when creating the access token.</p>
<p>The second parameter when creating the request token is used by Google to restrict the users access to specific portions of Google. In our case we&#8217;re requesting access to the Analytics data. A list of available scopes is <a href="http://code.google.com/apis/gdata/faq.html#AuthScopes">here</a>. You can specify multiple scope values by putting a space between them. e.g. <code>"http://www.google.com/calendar/feeds/%20http://docs.google.com/feeds/"</code>.</p>
<div class="dean_ch" style="white-space: wrap;">
rt.<span class="me1">authorize_url</span> &nbsp; &nbsp; &nbsp; =&gt; <span class="st0">&quot;https://www.google.com/accounts/OAuthAuthorizeToken?oauth_token=4%2F-1o2aZgHxJmjhaExv9htRsGWLlwy&quot;</span></div>
<p><a href="http://everburning.com/wp-content/uploads/2009/07/google-1.png"><img src="http://everburning.com/wp-content/uploads/2009/07/google-1-300x82.png" alt="google-1" title="google-1" width="300" height="82" class="alignright size-medium wp-image-596" /></a>With our request token in hand we can use it to generate the authorization URL. You&#8217;ll need to redirect the user to this URL to do the authentication.</p>
<p>Since we aren&#8217;t providing a callback URL our users will just get redirected to a standard Google page and you&#8217;ll need to copy their verifier code into the application.</p>
<div class="dean_ch" style="white-space: wrap;">
at = rt.<span class="me1">get_access_token</span><span class="br0">&#40;</span><span class="re3">:oauth_verifier</span> =&gt; <span class="st0">&#8216;vQH9bpXateNpsISpEDZax&#8217;</span><span class="br0">&#41;</span></div>
<p><a href="http://everburning.com/wp-content/uploads/2009/07/google-2.png"><img src="http://everburning.com/wp-content/uploads/2009/07/google-2-300x69.png" alt="google-2" title="google-2" width="300" height="69" class="alignleft size-medium wp-image-597" /></a>When the user has authorized we either get them to paste the verifier code or we can retrieve it from the parameters passed to our <code>oauth_callback</code>. We use the verifier code to create an access token. This is the token we&#8217;ll be using to access the Google resources. We can save the <code>at.token</code> and <code>at.secret</code> values into our database to create new access tokens next time the user needs data from Google. We don&#8217;t need to authenticate each time.</p>
<div class="dean_ch" style="white-space: wrap;">
at = <span class="re2">OAuth::AccessToken</span>.<span class="me1">new</span><span class="br0">&#40;</span>con, USER_TOKEN, USER_SECRET<span class="br0">&#41;</span></div>
<p>We can also, if we&#8217;re using the <code>oauth_callback</code> store some of our request token information to the session. If we store <code>rt.token</code> and <code>rt.secret</code> we can re-build our request token in a simlar fashion to the access token.</p>
<div class="dean_ch" style="white-space: wrap;">
rt = <span class="re2">OAuth::RequestToken</span>.<span class="me1">new</span><span class="br0">&#40;</span>con, REQUEST_TOKEN, REQUEST_SECRET<span class="br0">&#41;</span></div>
<p>With the authentication out of the way we can get on with accessing the analytics data. </p>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw3">puts</span> at.<span class="me1">get</span><span class="br0">&#40;</span><span class="st0">&quot;https://www.google.com/analytics/feeds/accounts/default?prettyprint=true&quot;</span><span class="br0">&#41;</span>.<span class="me1">body</span></div>
<p>We grab our profile data first. You&#8217;ll need the <code>dxp:tableId</code> value in order to make calls to get profile specific analytic data. (The <code>prettyprint=true</code> is a handy little flag to get Google to nicely indent the XML returned. Makes for easier reading/debugging.)</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="sc3"><span class="re1">&lt;</span> ?xml <span class="re0">version</span>=<span class="st0">&#8217;1.0&#8242;</span> <span class="re0">encoding</span>=<span class="st0">&#8216;UTF-8&#8242;</span><span class="re2">?&gt;</span></span><br />
<span class="sc3"><span class="re1">&lt;feed</span> <span class="re0">xmlns</span>=<span class="st0">&#8216;http://www.w3.org/2005/Atom&#8217;</span> <span class="re0">xmlns:openSearch</span>=<span class="st0">&#8216;http://a9.com/-/spec/opensearchrss/1.0/&#8217;</span> <span class="re0">xmlns:dxp</span>=<span class="st0">&#8216;http://schemas.google.com/analytics/2009&#8242;</span><span class="re2">&gt;</span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;id<span class="re2">&gt;</span></span></span>http://www.google.com/analytics/feeds/accounts/dan.sinclair@gmail.com<span class="sc3"><span class="re1">&lt;/id<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;updated<span class="re2">&gt;</span></span></span>2009-07-06T11:00:09.000-07:00<span class="sc3"><span class="re1">&lt;/updated<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;title</span> <span class="re0">type</span>=<span class="st0">&#8216;text&#8217;</span><span class="re2">&gt;</span></span>Profile list for dan.sinclair@gmail.com<span class="sc3"><span class="re1">&lt;/title<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;link</span> <span class="re0">rel</span>=<span class="st0">&#8216;self&#8217;</span> <span class="re0">type</span>=<span class="st0">&#8216;application/atom+xml&#8217;</span> <span class="re0">href</span>=<span class="st0">&#8216;http://www.google.com/analytics/feeds/accounts/default&#8217;</span><span class="re2">/&gt;</span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;author<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;name<span class="re2">&gt;</span></span></span>Google Analytics<span class="sc3"><span class="re1">&lt;/name<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;/author<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;generator</span> <span class="re0">version</span>=<span class="st0">&#8217;1.0&#8242;</span><span class="re2">&gt;</span></span>Google Analytics<span class="sc3"><span class="re1">&lt;/generator<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;opensearch</span> :totalResults<span class="re2">&gt;</span></span>1<span class="sc3"><span class="re1">&lt;/opensearch<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;opensearch</span> :startIndex<span class="re2">&gt;</span></span>1<span class="sc3"><span class="re1">&lt;/opensearch<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;opensearch</span> :itemsPerPage<span class="re2">&gt;</span></span>1<span class="sc3"><span class="re1">&lt;/opensearch<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;entry<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;id<span class="re2">&gt;</span></span></span>http://www.google.com/analytics/feeds/accounts/ga:123xxxx<span class="sc3"><span class="re1">&lt;/id<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;updated<span class="re2">&gt;</span></span></span>2009-07-06T11:00:09.000-07:00<span class="sc3"><span class="re1">&lt;/updated<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;title</span> <span class="re0">type</span>=<span class="st0">&#8216;text&#8217;</span><span class="re2">&gt;</span></span>everburning<span class="sc3"><span class="re1">&lt;/title<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;link</span> <span class="re0">rel</span>=<span class="st0">&#8216;alternate&#8217;</span> <span class="re0">type</span>=<span class="st0">&#8216;text/html&#8217;</span> <span class="re0">href</span>=<span class="st0">&#8216;http://www.google.com/analytics&#8217;</span><span class="re2">/&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :tableId<span class="re2">&gt;</span></span>ga:123xxxx<span class="sc3"><span class="re1">&lt;/dxp<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :property <span class="re0">name</span>=<span class="st0">&#8216;ga:accountId&#8217;</span> <span class="re0">value</span>=<span class="st0">&#8217;73xxxx&#8217;</span><span class="re2">/&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :property <span class="re0">name</span>=<span class="st0">&#8216;ga:accountName&#8217;</span> <span class="re0">value</span>=<span class="st0">&#8216;everburning&#8217;</span><span class="re2">/&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :property <span class="re0">name</span>=<span class="st0">&#8216;ga:profileId&#8217;</span> <span class="re0">value</span>=<span class="st0">&#8217;123xxxx&#8217;</span><span class="re2">/&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :property <span class="re0">name</span>=<span class="st0">&#8216;ga:webPropertyId&#8217;</span> <span class="re0">value</span>=<span class="st0">&#8216;UA-73xxxx-1&#8242;</span><span class="re2">/&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :property <span class="re0">name</span>=<span class="st0">&#8216;ga:currency&#8217;</span> <span class="re0">value</span>=<span class="st0">&#8216;USD&#8217;</span><span class="re2">/&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :property <span class="re0">name</span>=<span class="st0">&#8216;ga:timezone&#8217;</span> <span class="re0">value</span>=<span class="st0">&#8216;America/Toronto&#8217;</span><span class="re2">/&gt;</span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;/entry<span class="re2">&gt;</span></span></span><br />
<span class="sc3"><span class="re1">&lt;/feed<span class="re2">&gt;</span></span></span></div>
<p></p>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw3">puts</span> at.<span class="me1">get</span><span class="br0">&#40;</span><span class="st0">&quot;https://www.google.com/analytics/feeds/data?ids=ga:123xxxx&amp;start-date=2009-06-05&amp;end-date=2009-06-06&amp;dimensions=ga:pagePath&amp;metrics=ga:pageviews,ga:uniquePageviews&amp;prettyprint=true&quot;</span><span class="br0">&#41;</span>.<span class="me1">body</span></div>
<p>The data I want from Google Analytics is the number of <code>ga:pageviews</code> and <code>ga:uniquePageviews</code> for each <code>ga:pagePath</code> between June 5th and 6th.</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="sc3"><span class="re1">&lt;</span> ?xml <span class="re0">version</span>=<span class="st0">&#8217;1.0&#8242;</span> <span class="re0">encoding</span>=<span class="st0">&#8216;UTF-8&#8242;</span><span class="re2">?&gt;</span></span><br />
<span class="sc3"><span class="re1">&lt;feed</span> <span class="re0">xmlns</span>=<span class="st0">&#8216;http://www.w3.org/2005/Atom&#8217;</span> <span class="re0">xmlns:openSearch</span>=<span class="st0">&#8216;http://a9.com/-/spec/opensearchrss/1.0/&#8217;</span> <span class="re0">xmlns:dxp</span>=<span class="st0">&#8216;http://schemas.google.com/analytics/2009&#8242;</span><span class="re2">&gt;</span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;id<span class="re2">&gt;</span></span></span>http://www.google.com/analytics/feeds/data?ids=ga:123xxxx<span class="sc1">&amp;amp;</span>dimensions=ga:pagePath<span class="sc1">&amp;amp;</span>metrics=ga:pageviews,ga:uniquePageviews<span class="sc1">&amp;amp;</span>start-date=2009-06-05<span class="sc1">&amp;amp;</span>end-date=2009-06-06<span class="sc3"><span class="re1">&lt;/id<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;updated<span class="re2">&gt;</span></span></span>2009-06-06T16:59:59.999-07:00<span class="sc3"><span class="re1">&lt;/updated<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;title</span> <span class="re0">type</span>=<span class="st0">&#8216;text&#8217;</span><span class="re2">&gt;</span></span>Google Analytics Data for Profile 123xxxx<span class="sc3"><span class="re1">&lt;/title<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;link</span> <span class="re0">rel</span>=<span class="st0">&#8216;self&#8217;</span> <span class="re0">type</span>=<span class="st0">&#8216;application/atom+xml&#8217;</span> <span class="re0">href</span>=<span class="st0">&#8216;http://www.google.com/analytics/feeds/data?end-date=2009-06-06&amp;amp;start-date=2009-06-05&amp;amp;metrics=ga%3Apageviews%2Cga%3AuniquePageviews&amp;amp;ids=ga%3A123xxxx&amp;amp;dimensions=ga%3ApagePath&#8217;</span><span class="re2">/&gt;</span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;author<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;name<span class="re2">&gt;</span></span></span>Google Analytics<span class="sc3"><span class="re1">&lt;/name<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;/author<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;generator</span> <span class="re0">version</span>=<span class="st0">&#8217;1.0&#8242;</span><span class="re2">&gt;</span></span>Google Analytics<span class="sc3"><span class="re1">&lt;/generator<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;opensearch</span> :totalResults<span class="re2">&gt;</span></span>52<span class="sc3"><span class="re1">&lt;/opensearch<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;opensearch</span> :startIndex<span class="re2">&gt;</span></span>1<span class="sc3"><span class="re1">&lt;/opensearch<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;opensearch</span> :itemsPerPage<span class="re2">&gt;</span></span>52<span class="sc3"><span class="re1">&lt;/opensearch<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :startDate<span class="re2">&gt;</span></span>2009-06-05<span class="sc3"><span class="re1">&lt;/dxp<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :endDate<span class="re2">&gt;</span></span>2009-06-06<span class="sc3"><span class="re1">&lt;/dxp<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :aggregates<span class="re2">&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :metric <span class="re0">confidenceInterval</span>=<span class="st0">&#8217;0.0&#8242;</span> <span class="re0">name</span>=<span class="st0">&#8216;ga:pageviews&#8217;</span> <span class="re0">type</span>=<span class="st0">&#8216;integer&#8217;</span> <span class="re0">value</span>=<span class="st0">&#8217;162&#8242;</span><span class="re2">/&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :metric <span class="re0">confidenceInterval</span>=<span class="st0">&#8217;0.0&#8242;</span> <span class="re0">name</span>=<span class="st0">&#8216;ga:uniquePageviews&#8217;</span> <span class="re0">type</span>=<span class="st0">&#8216;integer&#8217;</span> <span class="re0">value</span>=<span class="st0">&#8217;152&#8242;</span><span class="re2">/&gt;</span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;/dxp<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :dataSource<span class="re2">&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/dxp<span class="re2">&gt;</span></span></span><span class="sc3"><span class="re1">&lt;dxp</span> :tableId<span class="re2">&gt;</span></span>ga:1239xxxx<span class="sc3"><span class="re1">&lt;/dxp<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :tableName<span class="re2">&gt;</span></span>everburning<span class="sc3"><span class="re1">&lt;/dxp<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :property <span class="re0">name</span>=<span class="st0">&#8216;ga:profileId&#8217;</span> <span class="re0">value</span>=<span class="st0">&#8217;123xxxx5&#8242;</span><span class="re2">/&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :property <span class="re0">name</span>=<span class="st0">&#8216;ga:webPropertyId&#8217;</span> <span class="re0">value</span>=<span class="st0">&#8216;UA-73xxxx-1&#8242;</span><span class="re2">/&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :property <span class="re0">name</span>=<span class="st0">&#8216;ga:accountName&#8217;</span> <span class="re0">value</span>=<span class="st0">&#8216;everburning&#8217;</span><span class="re2">/&gt;</span></span><br />
&nbsp; <br />
&nbsp; <span class="sc3"><span class="re1">&lt;entry<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;id<span class="re2">&gt;</span></span></span>http://www.google.com/analytics/feeds/data?ids=ga:123xxxx<span class="sc1">&amp;amp;</span>ga:pagePath=/<span class="sc1">&amp;amp;</span>start-date=2009-06-05<span class="sc1">&amp;amp;</span>end-date=2009-06-06<span class="sc3"><span class="re1">&lt;/id<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;updated<span class="re2">&gt;</span></span></span>2009-06-05T17:00:00.001-07:00<span class="sc3"><span class="re1">&lt;/updated<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;title</span> <span class="re0">type</span>=<span class="st0">&#8216;text&#8217;</span><span class="re2">&gt;</span></span>ga:pagePath=/<span class="sc3"><span class="re1">&lt;/title<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;link</span> <span class="re0">rel</span>=<span class="st0">&#8216;alternate&#8217;</span> <span class="re0">type</span>=<span class="st0">&#8216;text/html&#8217;</span> <span class="re0">href</span>=<span class="st0">&#8216;http://www.google.com/analytics&#8217;</span><span class="re2">/&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :dimension <span class="re0">name</span>=<span class="st0">&#8216;ga:pagePath&#8217;</span> <span class="re0">value</span>=<span class="st0">&#8216;/&#8217;</span><span class="re2">/&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :metric <span class="re0">confidenceInterval</span>=<span class="st0">&#8217;0.0&#8242;</span> <span class="re0">name</span>=<span class="st0">&#8216;ga:pageviews&#8217;</span> <span class="re0">type</span>=<span class="st0">&#8216;integer&#8217;</span> <span class="re0">value</span>=<span class="st0">&#8217;9&#8242;</span><span class="re2">/&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :metric <span class="re0">confidenceInterval</span>=<span class="st0">&#8217;0.0&#8242;</span> <span class="re0">name</span>=<span class="st0">&#8216;ga:uniquePageviews&#8217;</span> <span class="re0">type</span>=<span class="st0">&#8216;integer&#8217;</span> <span class="re0">value</span>=<span class="st0">&#8217;8&#8242;</span><span class="re2">/&gt;</span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;/entry<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;entry<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;id<span class="re2">&gt;</span></span></span>http://www.google.com/analytics/feeds/data?ids=ga:123xxxx<span class="sc1">&amp;amp;</span>ga:pagePath=/quotes/<span class="sc1">&amp;amp;</span>start-date=2009-06-05<span class="sc1">&amp;amp;</span>end-date=2009-06-06<span class="sc3"><span class="re1">&lt;/id<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;updated<span class="re2">&gt;</span></span></span>2009-06-05T17:00:00.001-07:00<span class="sc3"><span class="re1">&lt;/updated<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;title</span> <span class="re0">type</span>=<span class="st0">&#8216;text&#8217;</span><span class="re2">&gt;</span></span>ga:pagePath=/quotes/<span class="sc3"><span class="re1">&lt;/title<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;link</span> <span class="re0">rel</span>=<span class="st0">&#8216;alternate&#8217;</span> <span class="re0">type</span>=<span class="st0">&#8216;text/html&#8217;</span> <span class="re0">href</span>=<span class="st0">&#8216;http://www.google.com/analytics&#8217;</span><span class="re2">/&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :dimension <span class="re0">name</span>=<span class="st0">&#8216;ga:pagePath&#8217;</span> <span class="re0">value</span>=<span class="st0">&#8216;/quotes/&#8217;</span><span class="re2">/&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :metric <span class="re0">confidenceInterval</span>=<span class="st0">&#8217;0.0&#8242;</span> <span class="re0">name</span>=<span class="st0">&#8216;ga:pageviews&#8217;</span> <span class="re0">type</span>=<span class="st0">&#8216;integer&#8217;</span> <span class="re0">value</span>=<span class="st0">&#8217;6&#8242;</span><span class="re2">/&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :metric <span class="re0">confidenceInterval</span>=<span class="st0">&#8217;0.0&#8242;</span> <span class="re0">name</span>=<span class="st0">&#8216;ga:uniquePageviews&#8217;</span> <span class="re0">type</span>=<span class="st0">&#8216;integer&#8217;</span> <span class="re0">value</span>=<span class="st0">&#8217;6&#8242;</span><span class="re2">/&gt;</span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;/entry<span class="re2">&gt;</span></span></span><br />
<span class="sc3"><span class="re1">&lt;/feed<span class="re2">&gt;</span></span></span></div>
<p>You may notice that all of the paths returned are relative to some base URL. I haven&#8217;t been able to figure out how to get Google Analytics to tell me what that base URL happens to be. If you figure it out, please leave a comment so I can update my code.</p>
<p>That&#8217;s it. You can take a deeper look at the <a href="http://code.google.com/apis/analytics/docs/gdata/gdataDeveloperGuide.html">Google Analytics API</a> documentation to learn about the different <code>dimensions</code> and <code>metrics</code> that can be queried.</p>
<p>If you&#8217;re doing this in Ruby you may also want to take a look at the <a href="http://railstips.org/2008/11/17/happymapper-making-xml-fun-again">Happy Mapper</a> gem to make your XML using life easier.</p>
<p>Have fun.</p>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Fgoogle-analytics-oauth-and-ruby-oh-my%2F&amp;title=Google+Analytics%2C+OAuth+and+Ruby.+Oh%2C+my." title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fgoogle-analytics-oauth-and-ruby-oh-my%2F&amp;title=Google+Analytics%2C+OAuth+and+Ruby.+Oh%2C+my." title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fgoogle-analytics-oauth-and-ruby-oh-my%2F&amp;title=Google+Analytics%2C+OAuth+and+Ruby.+Oh%2C+my." title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Fgoogle-analytics-oauth-and-ruby-oh-my%2F&amp;title=Google+Analytics%2C+OAuth+and+Ruby.+Oh%2C+my." title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fgoogle-analytics-oauth-and-ruby-oh-my%2F&amp;title=Google+Analytics%2C+OAuth+and+Ruby.+Oh%2C+my.', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Fgoogle-analytics-oauth-and-ruby-oh-my%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Fgoogle-analytics-oauth-and-ruby-oh-my%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Fgoogle-analytics-oauth-and-ruby-oh-my%2F&amp;title=Google+Analytics%2C+OAuth+and+Ruby.+Oh%2C+my." title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fgoogle-analytics-oauth-and-ruby-oh-my%2F&amp;title=Google+Analytics%2C+OAuth+and+Ruby.+Oh%2C+my." title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></description>
			<content:encoded><![CDATA[<p><img src="http://everburning.com/wp-content/uploads/2009/07/IMG_6658-300x282.jpg" alt="IMG_6658" title="IMG_6658" width="300" height="282" class="alignleft size-medium wp-image-595" />I recently had the opportunity to take a peek at accessing <a href="http://google.com/analytics">Google Analytics</a> data using their <a href="http://oauth.net/">OAuth</a> endpoint.  We did this in Ruby using the Ruby OAuth gem.  There were a few snags along the way so I figured I&#8217;d put up some notes for other people to take a peek.</p>
<p>First, you&#8217;re going to need a newer version of the OAuth gem (0.3.5 seems to work). Google uses some features from the 1.0a version of the OAuth spec, the <code>oauth_verifier</code> specifically, that aren&#8217;t available in older versions of the OAuth gem.  That took a while to track down.</p>
<p>The second thing you&#8217;re going to need is a consumer token and secret from google. You can retrieve this from your domain management page (<a href="https://www.google.com/accounts/ManageDomains">here</a> as it&#8217;s a bit difficult to find.)</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw3">require</span> <span class="st0">&#8216;oauth&#8217;</span><br />
con = <span class="re2">OAuth::Consumer</span>.<span class="me1">new</span><span class="br0">&#40;</span>CONSUMER_TOKEN, CONSUMER_SECRET,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span>:site =&gt; <span class="st0">&#8216;https://www.google.com&#8217;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="re3">:request_token_path</span> =&gt; <span class="st0">&#8216;/accounts/OAuthGetRequestToken&#8217;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="re3">:access_token_path</span> =&gt; <span class="st0">&#8216;/accounts/OAuthGetAccessToken&#8217;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="re3">:authorize_path</span> =&gt; <span class="st0">&#8216;/accounts/OAuthAuthorizeToken&#8217;</span><span class="br0">&#125;</span><span class="br0">&#41;</span></div>
<p>To start we create our OAuth connection. You&#8217;ll need to substitute your token and secret for the consumer constants above. We need to specify all of the OAuth paths to match the endpoints provided by Google.</p>
<div class="dean_ch" style="white-space: wrap;">
rt = con.<span class="me1">get_request_token</span><span class="br0">&#40;</span><span class="br0">&#123;</span><span class="br0">&#125;</span>, <span class="br0">&#123;</span>:scope =&gt; <span class="st0">&#8216;https://www.google.com/analytics/feeds&#8217;</span><span class="br0">&#125;</span><span class="br0">&#41;</span></div>
<p>We use the connection to create a request token. <code>get_request_token</code> takes two parameters. The token parameters and other parameters. Above, I&#8217;m not specifying any request parameters but, if you want Google to redirect the user to a site of your choice you would provide <code>{:oauth_callback => URL}</code> as the token parameter. After authenticating the user would be redirected to the provided URL and the parameters <code>oauth_token=TOKEN</code> and <code>oauth_verifier=VERIFIER</code> will be passed to the URL. You&#8217;ll need to use those parameters when creating the access token.</p>
<p>The second parameter when creating the request token is used by Google to restrict the users access to specific portions of Google. In our case we&#8217;re requesting access to the Analytics data. A list of available scopes is <a href="http://code.google.com/apis/gdata/faq.html#AuthScopes">here</a>. You can specify multiple scope values by putting a space between them. e.g. <code>"http://www.google.com/calendar/feeds/%20http://docs.google.com/feeds/"</code>.</p>
<div class="dean_ch" style="white-space: wrap;">
rt.<span class="me1">authorize_url</span> &nbsp; &nbsp; &nbsp; =&gt; <span class="st0">&quot;https://www.google.com/accounts/OAuthAuthorizeToken?oauth_token=4%2F-1o2aZgHxJmjhaExv9htRsGWLlwy&quot;</span></div>
<p><a href="http://everburning.com/wp-content/uploads/2009/07/google-1.png"><img src="http://everburning.com/wp-content/uploads/2009/07/google-1-300x82.png" alt="google-1" title="google-1" width="300" height="82" class="alignright size-medium wp-image-596" /></a>With our request token in hand we can use it to generate the authorization URL. You&#8217;ll need to redirect the user to this URL to do the authentication.</p>
<p>Since we aren&#8217;t providing a callback URL our users will just get redirected to a standard Google page and you&#8217;ll need to copy their verifier code into the application.</p>
<div class="dean_ch" style="white-space: wrap;">
at = rt.<span class="me1">get_access_token</span><span class="br0">&#40;</span><span class="re3">:oauth_verifier</span> =&gt; <span class="st0">&#8216;vQH9bpXateNpsISpEDZax&#8217;</span><span class="br0">&#41;</span></div>
<p><a href="http://everburning.com/wp-content/uploads/2009/07/google-2.png"><img src="http://everburning.com/wp-content/uploads/2009/07/google-2-300x69.png" alt="google-2" title="google-2" width="300" height="69" class="alignleft size-medium wp-image-597" /></a>When the user has authorized we either get them to paste the verifier code or we can retrieve it from the parameters passed to our <code>oauth_callback</code>. We use the verifier code to create an access token. This is the token we&#8217;ll be using to access the Google resources. We can save the <code>at.token</code> and <code>at.secret</code> values into our database to create new access tokens next time the user needs data from Google. We don&#8217;t need to authenticate each time.</p>
<div class="dean_ch" style="white-space: wrap;">
at = <span class="re2">OAuth::AccessToken</span>.<span class="me1">new</span><span class="br0">&#40;</span>con, USER_TOKEN, USER_SECRET<span class="br0">&#41;</span></div>
<p>We can also, if we&#8217;re using the <code>oauth_callback</code> store some of our request token information to the session. If we store <code>rt.token</code> and <code>rt.secret</code> we can re-build our request token in a simlar fashion to the access token.</p>
<div class="dean_ch" style="white-space: wrap;">
rt = <span class="re2">OAuth::RequestToken</span>.<span class="me1">new</span><span class="br0">&#40;</span>con, REQUEST_TOKEN, REQUEST_SECRET<span class="br0">&#41;</span></div>
<p>With the authentication out of the way we can get on with accessing the analytics data. </p>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw3">puts</span> at.<span class="me1">get</span><span class="br0">&#40;</span><span class="st0">&quot;https://www.google.com/analytics/feeds/accounts/default?prettyprint=true&quot;</span><span class="br0">&#41;</span>.<span class="me1">body</span></div>
<p>We grab our profile data first. You&#8217;ll need the <code>dxp:tableId</code> value in order to make calls to get profile specific analytic data. (The <code>prettyprint=true</code> is a handy little flag to get Google to nicely indent the XML returned. Makes for easier reading/debugging.)</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="sc3"><span class="re1">&lt;</span> ?xml <span class="re0">version</span>=<span class="st0">&#8217;1.0&#8242;</span> <span class="re0">encoding</span>=<span class="st0">&#8216;UTF-8&#8242;</span><span class="re2">?&gt;</span></span><br />
<span class="sc3"><span class="re1">&lt;feed</span> <span class="re0">xmlns</span>=<span class="st0">&#8216;http://www.w3.org/2005/Atom&#8217;</span> <span class="re0">xmlns:openSearch</span>=<span class="st0">&#8216;http://a9.com/-/spec/opensearchrss/1.0/&#8217;</span> <span class="re0">xmlns:dxp</span>=<span class="st0">&#8216;http://schemas.google.com/analytics/2009&#8242;</span><span class="re2">&gt;</span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;id<span class="re2">&gt;</span></span></span>http://www.google.com/analytics/feeds/accounts/dan.sinclair@gmail.com<span class="sc3"><span class="re1">&lt;/id<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;updated<span class="re2">&gt;</span></span></span>2009-07-06T11:00:09.000-07:00<span class="sc3"><span class="re1">&lt;/updated<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;title</span> <span class="re0">type</span>=<span class="st0">&#8216;text&#8217;</span><span class="re2">&gt;</span></span>Profile list for dan.sinclair@gmail.com<span class="sc3"><span class="re1">&lt;/title<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;link</span> <span class="re0">rel</span>=<span class="st0">&#8216;self&#8217;</span> <span class="re0">type</span>=<span class="st0">&#8216;application/atom+xml&#8217;</span> <span class="re0">href</span>=<span class="st0">&#8216;http://www.google.com/analytics/feeds/accounts/default&#8217;</span><span class="re2">/&gt;</span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;author<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;name<span class="re2">&gt;</span></span></span>Google Analytics<span class="sc3"><span class="re1">&lt;/name<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;/author<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;generator</span> <span class="re0">version</span>=<span class="st0">&#8217;1.0&#8242;</span><span class="re2">&gt;</span></span>Google Analytics<span class="sc3"><span class="re1">&lt;/generator<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;opensearch</span> :totalResults<span class="re2">&gt;</span></span>1<span class="sc3"><span class="re1">&lt;/opensearch<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;opensearch</span> :startIndex<span class="re2">&gt;</span></span>1<span class="sc3"><span class="re1">&lt;/opensearch<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;opensearch</span> :itemsPerPage<span class="re2">&gt;</span></span>1<span class="sc3"><span class="re1">&lt;/opensearch<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;entry<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;id<span class="re2">&gt;</span></span></span>http://www.google.com/analytics/feeds/accounts/ga:123xxxx<span class="sc3"><span class="re1">&lt;/id<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;updated<span class="re2">&gt;</span></span></span>2009-07-06T11:00:09.000-07:00<span class="sc3"><span class="re1">&lt;/updated<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;title</span> <span class="re0">type</span>=<span class="st0">&#8216;text&#8217;</span><span class="re2">&gt;</span></span>everburning<span class="sc3"><span class="re1">&lt;/title<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;link</span> <span class="re0">rel</span>=<span class="st0">&#8216;alternate&#8217;</span> <span class="re0">type</span>=<span class="st0">&#8216;text/html&#8217;</span> <span class="re0">href</span>=<span class="st0">&#8216;http://www.google.com/analytics&#8217;</span><span class="re2">/&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :tableId<span class="re2">&gt;</span></span>ga:123xxxx<span class="sc3"><span class="re1">&lt;/dxp<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :property <span class="re0">name</span>=<span class="st0">&#8216;ga:accountId&#8217;</span> <span class="re0">value</span>=<span class="st0">&#8217;73xxxx&#8217;</span><span class="re2">/&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :property <span class="re0">name</span>=<span class="st0">&#8216;ga:accountName&#8217;</span> <span class="re0">value</span>=<span class="st0">&#8216;everburning&#8217;</span><span class="re2">/&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :property <span class="re0">name</span>=<span class="st0">&#8216;ga:profileId&#8217;</span> <span class="re0">value</span>=<span class="st0">&#8217;123xxxx&#8217;</span><span class="re2">/&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :property <span class="re0">name</span>=<span class="st0">&#8216;ga:webPropertyId&#8217;</span> <span class="re0">value</span>=<span class="st0">&#8216;UA-73xxxx-1&#8242;</span><span class="re2">/&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :property <span class="re0">name</span>=<span class="st0">&#8216;ga:currency&#8217;</span> <span class="re0">value</span>=<span class="st0">&#8216;USD&#8217;</span><span class="re2">/&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :property <span class="re0">name</span>=<span class="st0">&#8216;ga:timezone&#8217;</span> <span class="re0">value</span>=<span class="st0">&#8216;America/Toronto&#8217;</span><span class="re2">/&gt;</span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;/entry<span class="re2">&gt;</span></span></span><br />
<span class="sc3"><span class="re1">&lt;/feed<span class="re2">&gt;</span></span></span></div>
<p></p>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw3">puts</span> at.<span class="me1">get</span><span class="br0">&#40;</span><span class="st0">&quot;https://www.google.com/analytics/feeds/data?ids=ga:123xxxx&amp;start-date=2009-06-05&amp;end-date=2009-06-06&amp;dimensions=ga:pagePath&amp;metrics=ga:pageviews,ga:uniquePageviews&amp;prettyprint=true&quot;</span><span class="br0">&#41;</span>.<span class="me1">body</span></div>
<p>The data I want from Google Analytics is the number of <code>ga:pageviews</code> and <code>ga:uniquePageviews</code> for each <code>ga:pagePath</code> between June 5th and 6th.</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="sc3"><span class="re1">&lt;</span> ?xml <span class="re0">version</span>=<span class="st0">&#8217;1.0&#8242;</span> <span class="re0">encoding</span>=<span class="st0">&#8216;UTF-8&#8242;</span><span class="re2">?&gt;</span></span><br />
<span class="sc3"><span class="re1">&lt;feed</span> <span class="re0">xmlns</span>=<span class="st0">&#8216;http://www.w3.org/2005/Atom&#8217;</span> <span class="re0">xmlns:openSearch</span>=<span class="st0">&#8216;http://a9.com/-/spec/opensearchrss/1.0/&#8217;</span> <span class="re0">xmlns:dxp</span>=<span class="st0">&#8216;http://schemas.google.com/analytics/2009&#8242;</span><span class="re2">&gt;</span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;id<span class="re2">&gt;</span></span></span>http://www.google.com/analytics/feeds/data?ids=ga:123xxxx<span class="sc1">&amp;amp;</span>dimensions=ga:pagePath<span class="sc1">&amp;amp;</span>metrics=ga:pageviews,ga:uniquePageviews<span class="sc1">&amp;amp;</span>start-date=2009-06-05<span class="sc1">&amp;amp;</span>end-date=2009-06-06<span class="sc3"><span class="re1">&lt;/id<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;updated<span class="re2">&gt;</span></span></span>2009-06-06T16:59:59.999-07:00<span class="sc3"><span class="re1">&lt;/updated<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;title</span> <span class="re0">type</span>=<span class="st0">&#8216;text&#8217;</span><span class="re2">&gt;</span></span>Google Analytics Data for Profile 123xxxx<span class="sc3"><span class="re1">&lt;/title<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;link</span> <span class="re0">rel</span>=<span class="st0">&#8216;self&#8217;</span> <span class="re0">type</span>=<span class="st0">&#8216;application/atom+xml&#8217;</span> <span class="re0">href</span>=<span class="st0">&#8216;http://www.google.com/analytics/feeds/data?end-date=2009-06-06&amp;amp;start-date=2009-06-05&amp;amp;metrics=ga%3Apageviews%2Cga%3AuniquePageviews&amp;amp;ids=ga%3A123xxxx&amp;amp;dimensions=ga%3ApagePath&#8217;</span><span class="re2">/&gt;</span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;author<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;name<span class="re2">&gt;</span></span></span>Google Analytics<span class="sc3"><span class="re1">&lt;/name<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;/author<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;generator</span> <span class="re0">version</span>=<span class="st0">&#8217;1.0&#8242;</span><span class="re2">&gt;</span></span>Google Analytics<span class="sc3"><span class="re1">&lt;/generator<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;opensearch</span> :totalResults<span class="re2">&gt;</span></span>52<span class="sc3"><span class="re1">&lt;/opensearch<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;opensearch</span> :startIndex<span class="re2">&gt;</span></span>1<span class="sc3"><span class="re1">&lt;/opensearch<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;opensearch</span> :itemsPerPage<span class="re2">&gt;</span></span>52<span class="sc3"><span class="re1">&lt;/opensearch<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :startDate<span class="re2">&gt;</span></span>2009-06-05<span class="sc3"><span class="re1">&lt;/dxp<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :endDate<span class="re2">&gt;</span></span>2009-06-06<span class="sc3"><span class="re1">&lt;/dxp<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :aggregates<span class="re2">&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :metric <span class="re0">confidenceInterval</span>=<span class="st0">&#8217;0.0&#8242;</span> <span class="re0">name</span>=<span class="st0">&#8216;ga:pageviews&#8217;</span> <span class="re0">type</span>=<span class="st0">&#8216;integer&#8217;</span> <span class="re0">value</span>=<span class="st0">&#8217;162&#8242;</span><span class="re2">/&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :metric <span class="re0">confidenceInterval</span>=<span class="st0">&#8217;0.0&#8242;</span> <span class="re0">name</span>=<span class="st0">&#8216;ga:uniquePageviews&#8217;</span> <span class="re0">type</span>=<span class="st0">&#8216;integer&#8217;</span> <span class="re0">value</span>=<span class="st0">&#8217;152&#8242;</span><span class="re2">/&gt;</span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;/dxp<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :dataSource<span class="re2">&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/dxp<span class="re2">&gt;</span></span></span><span class="sc3"><span class="re1">&lt;dxp</span> :tableId<span class="re2">&gt;</span></span>ga:1239xxxx<span class="sc3"><span class="re1">&lt;/dxp<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :tableName<span class="re2">&gt;</span></span>everburning<span class="sc3"><span class="re1">&lt;/dxp<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :property <span class="re0">name</span>=<span class="st0">&#8216;ga:profileId&#8217;</span> <span class="re0">value</span>=<span class="st0">&#8217;123xxxx5&#8242;</span><span class="re2">/&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :property <span class="re0">name</span>=<span class="st0">&#8216;ga:webPropertyId&#8217;</span> <span class="re0">value</span>=<span class="st0">&#8216;UA-73xxxx-1&#8242;</span><span class="re2">/&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :property <span class="re0">name</span>=<span class="st0">&#8216;ga:accountName&#8217;</span> <span class="re0">value</span>=<span class="st0">&#8216;everburning&#8217;</span><span class="re2">/&gt;</span></span><br />
&nbsp; <br />
&nbsp; <span class="sc3"><span class="re1">&lt;entry<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;id<span class="re2">&gt;</span></span></span>http://www.google.com/analytics/feeds/data?ids=ga:123xxxx<span class="sc1">&amp;amp;</span>ga:pagePath=/<span class="sc1">&amp;amp;</span>start-date=2009-06-05<span class="sc1">&amp;amp;</span>end-date=2009-06-06<span class="sc3"><span class="re1">&lt;/id<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;updated<span class="re2">&gt;</span></span></span>2009-06-05T17:00:00.001-07:00<span class="sc3"><span class="re1">&lt;/updated<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;title</span> <span class="re0">type</span>=<span class="st0">&#8216;text&#8217;</span><span class="re2">&gt;</span></span>ga:pagePath=/<span class="sc3"><span class="re1">&lt;/title<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;link</span> <span class="re0">rel</span>=<span class="st0">&#8216;alternate&#8217;</span> <span class="re0">type</span>=<span class="st0">&#8216;text/html&#8217;</span> <span class="re0">href</span>=<span class="st0">&#8216;http://www.google.com/analytics&#8217;</span><span class="re2">/&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :dimension <span class="re0">name</span>=<span class="st0">&#8216;ga:pagePath&#8217;</span> <span class="re0">value</span>=<span class="st0">&#8216;/&#8217;</span><span class="re2">/&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :metric <span class="re0">confidenceInterval</span>=<span class="st0">&#8217;0.0&#8242;</span> <span class="re0">name</span>=<span class="st0">&#8216;ga:pageviews&#8217;</span> <span class="re0">type</span>=<span class="st0">&#8216;integer&#8217;</span> <span class="re0">value</span>=<span class="st0">&#8217;9&#8242;</span><span class="re2">/&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :metric <span class="re0">confidenceInterval</span>=<span class="st0">&#8217;0.0&#8242;</span> <span class="re0">name</span>=<span class="st0">&#8216;ga:uniquePageviews&#8217;</span> <span class="re0">type</span>=<span class="st0">&#8216;integer&#8217;</span> <span class="re0">value</span>=<span class="st0">&#8217;8&#8242;</span><span class="re2">/&gt;</span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;/entry<span class="re2">&gt;</span></span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;entry<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;id<span class="re2">&gt;</span></span></span>http://www.google.com/analytics/feeds/data?ids=ga:123xxxx<span class="sc1">&amp;amp;</span>ga:pagePath=/quotes/<span class="sc1">&amp;amp;</span>start-date=2009-06-05<span class="sc1">&amp;amp;</span>end-date=2009-06-06<span class="sc3"><span class="re1">&lt;/id<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;updated<span class="re2">&gt;</span></span></span>2009-06-05T17:00:00.001-07:00<span class="sc3"><span class="re1">&lt;/updated<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;title</span> <span class="re0">type</span>=<span class="st0">&#8216;text&#8217;</span><span class="re2">&gt;</span></span>ga:pagePath=/quotes/<span class="sc3"><span class="re1">&lt;/title<span class="re2">&gt;</span></span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;link</span> <span class="re0">rel</span>=<span class="st0">&#8216;alternate&#8217;</span> <span class="re0">type</span>=<span class="st0">&#8216;text/html&#8217;</span> <span class="re0">href</span>=<span class="st0">&#8216;http://www.google.com/analytics&#8217;</span><span class="re2">/&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :dimension <span class="re0">name</span>=<span class="st0">&#8216;ga:pagePath&#8217;</span> <span class="re0">value</span>=<span class="st0">&#8216;/quotes/&#8217;</span><span class="re2">/&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :metric <span class="re0">confidenceInterval</span>=<span class="st0">&#8217;0.0&#8242;</span> <span class="re0">name</span>=<span class="st0">&#8216;ga:pageviews&#8217;</span> <span class="re0">type</span>=<span class="st0">&#8216;integer&#8217;</span> <span class="re0">value</span>=<span class="st0">&#8217;6&#8242;</span><span class="re2">/&gt;</span></span><br />
&nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;dxp</span> :metric <span class="re0">confidenceInterval</span>=<span class="st0">&#8217;0.0&#8242;</span> <span class="re0">name</span>=<span class="st0">&#8216;ga:uniquePageviews&#8217;</span> <span class="re0">type</span>=<span class="st0">&#8216;integer&#8217;</span> <span class="re0">value</span>=<span class="st0">&#8217;6&#8242;</span><span class="re2">/&gt;</span></span><br />
&nbsp; <span class="sc3"><span class="re1">&lt;/entry<span class="re2">&gt;</span></span></span><br />
<span class="sc3"><span class="re1">&lt;/feed<span class="re2">&gt;</span></span></span></div>
<p>You may notice that all of the paths returned are relative to some base URL. I haven&#8217;t been able to figure out how to get Google Analytics to tell me what that base URL happens to be. If you figure it out, please leave a comment so I can update my code.</p>
<p>That&#8217;s it. You can take a deeper look at the <a href="http://code.google.com/apis/analytics/docs/gdata/gdataDeveloperGuide.html">Google Analytics API</a> documentation to learn about the different <code>dimensions</code> and <code>metrics</code> that can be queried.</p>
<p>If you&#8217;re doing this in Ruby you may also want to take a look at the <a href="http://railstips.org/2008/11/17/happymapper-making-xml-fun-again">Happy Mapper</a> gem to make your XML using life easier.</p>
<p>Have fun.</p>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Fgoogle-analytics-oauth-and-ruby-oh-my%2F&amp;title=Google+Analytics%2C+OAuth+and+Ruby.+Oh%2C+my." title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fgoogle-analytics-oauth-and-ruby-oh-my%2F&amp;title=Google+Analytics%2C+OAuth+and+Ruby.+Oh%2C+my." title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fgoogle-analytics-oauth-and-ruby-oh-my%2F&amp;title=Google+Analytics%2C+OAuth+and+Ruby.+Oh%2C+my." title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Fgoogle-analytics-oauth-and-ruby-oh-my%2F&amp;title=Google+Analytics%2C+OAuth+and+Ruby.+Oh%2C+my." title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fgoogle-analytics-oauth-and-ruby-oh-my%2F&amp;title=Google+Analytics%2C+OAuth+and+Ruby.+Oh%2C+my.', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Fgoogle-analytics-oauth-and-ruby-oh-my%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Fgoogle-analytics-oauth-and-ruby-oh-my%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Fgoogle-analytics-oauth-and-ruby-oh-my%2F&amp;title=Google+Analytics%2C+OAuth+and+Ruby.+Oh%2C+my." title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fgoogle-analytics-oauth-and-ruby-oh-my%2F&amp;title=Google+Analytics%2C+OAuth+and+Ruby.+Oh%2C+my." title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></content:encoded>
			<wfw:commentRss>http://everburning.com/news/google-analytics-oauth-and-ruby-oh-my/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>HotCocoa and Core Data</title>
		<link>http://everburning.com/news/hotcocoa-and-core-data/</link>
		<comments>http://everburning.com/news/hotcocoa-and-core-data/#comments</comments>
		<pubDate>Sat, 13 Jun 2009 06:10:44 +0000</pubDate>
		<dc:creator>dj2</dc:creator>
				<category><![CDATA[Computers]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Core Data]]></category>
		<category><![CDATA[HotCocoa]]></category>
		<category><![CDATA[MacRuby]]></category>

		<guid isPermaLink="false">http://everburning.com/?p=567</guid>
		<description><![CDATA[<p><img src="http://everburning.com/wp-content/uploads/2009/06/IMG_6093-300x200.jpg" alt="IMG_6093" title="IMG_6093" width="300" height="200" class="alignleft size-medium wp-image-573" />I&#8217;ve been playing around a bit with Core Data and HotCocoa the last few days and thought I&#8217;d share what I&#8217;ve got so far.  When you install MacRuby you&#8217;re provided with an XCode template to start working with Core Data. I didn&#8217;t want to use XCode so I skipped the template. Some of this code is probably pretty similar to the template as I ported the Obj-C Core Data template to Ruby.</p>
<p>Note, this code is lightly tested, prototype, probably wrong and has already assassinated an unknown number of kitties. At this point, I&#8217;m still not sure if the approach taken is actually sane. Time will tell, but I thought it was interesting enough to share.</p>
<p>I stuffed all of the default Core Data stuff into a CoreData module that I&#8217;m including into my main application (mostly so I don&#8217;t have to look at the functions when working on my main code). The separation isn&#8217;t great at the moment as the <code>should_terminate?</code> method is currently in the module instead of the main application. I&#8217;ll be coming back to this setup once I&#8217;ve figured out how all the pieces work.</p>
<p>Along with the Core Data base methods I&#8217;ve also created a <code>ManagedObject</code> class.  Each of the entities in my model I&#8217;ve created as a model class. Those model classes inherit from <code>ManagedObject</code>. (I just realized, the objects returned from <code>self.create</code> in <code>ManagedObject</code> aren&#8217;t of my model types. They&#8217;ll be straight up <code>NSManagedObjects</code>. Oops. Will need to look at that as well. Can you tell this stuff is still pretty rough?) <code>ManagedObject</code> contains the common <code>create</code> method for all of the sub-classes. When <code>create</code> is called the entity will be retrieved from the Core Data model and have it&#8217;s attributes and relationships added as methods on the returned object.</p>
<p>What this means is that, in the end, I can do the following:</p>
<div class="dean_ch" style="white-space: wrap;">
&nbsp; <span class="kw3">p</span> = Post.<span class="me1">create</span><br />
&nbsp; <span class="kw3">p</span>.<span class="me1">title</span> = <span class="st0">&quot;HotData&quot;</span><br />
&nbsp; <span class="kw3">p</span>.<span class="me1">pub_date</span> = NSDate.<span class="me1">date</span><br />
&nbsp; <span class="kw3">p</span>.<span class="me1">content</span> = <span class="st0">&quot;Playing with Core Data from HotCocoa&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; <span class="kw3">p</span>.<span class="me1">author</span> = Author.<span class="me1">create</span><span class="br0">&#40;</span><span class="re3">:name</span> =&gt; <span class="st0">&#8216;dj2&#8242;</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw3">p</span>.<span class="me1">tags</span>.<span class="me1">addObject</span><span class="br0">&#40;</span>Tag.<span class="me1">create</span><span class="br0">&#40;</span><span class="re3">:name</span> =&gt; <span class="st0">&quot;Core Data&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw3">p</span>.<span class="me1">tags</span>.<span class="me1">addObject</span><span class="br0">&#40;</span>Tag.<span class="me1">create</span><span class="br0">&#40;</span><span class="re3">:name</span> =&gt; <span class="st0">&quot;HotCocoa&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; <span class="kw3">p</span>.<span class="me1">published</span> = <span class="kw2">true</span> <span class="kw1">if</span> !<span class="kw3">p</span>.<span class="me1">published</span>?</div>
<p>One thing to note, when you&#8217;re working with Core Data you&#8217;ll be working with a <code>.xcdatamodel</code> file. In order to use that from code this data model needs to be compiled to a <code>mom</code> file and put into your application bundle. I&#8217;ve created a patch, attached to <a href="https://www.macruby.org/trac/ticket/278">ticket 278</a> that builds this into the HotCocoa application builder. All you need is a <code>build.yml</code> similar to the following:</p>
<div class="dean_ch" style="white-space: wrap;">
name: Blog<br />
load: lib/application.rb<br />
version: &quot;1.0&quot;<br />
icon: resources/HotCocoa.icns<br />
resources:<br />
&nbsp; &#8211; resources/**/*.*<br />
data_models:<br />
&nbsp; &#8211; data/**/*.xcdatamodel<br />
sources: <br />
&nbsp; &#8211; lib/**/*.rb</div>
<p><a href="http://everburning.com/wp-content/uploads/2009/06/Picture-1.png"><img src="http://everburning.com/wp-content/uploads/2009/06/Picture-1-300x156.png" alt="Picture 1" title="Picture 1" width="300" height="156" class="alignright size-medium wp-image-568" /></a>Note the addition of the <code>data_models</code> key. This will take each of the models found, compile it, and place it in your bundles resource folder ready for use.</p>
<p>With that out of the way, what are we modeling? I figured I&#8217;d go simple and model a blog. I didn&#8217;t bother to write any real UI in front of it for the example as I just wanted to focus on the Core Data aspects.</p>
<p>The first step is to create the project: <code>hotcocoa Blog</code> will create the basis for us. I then updated the <code>build.yml</code> file to what&#8217;s seen above. </p>
<p>The next step was to create my data model which I saved to <code>data/Blog.xcdatamodel</code>. This needs to be done through XCode. Open up a new data model and create the following:</p>
<h3>Post</h3>
<ul>
<li>content  &#8211; string (not optional)</li>
<li>pub_date &#8211; date (not optional)</li>
<li>published &#8211; boolean (default NO)</li>
<li>title &#8211; string (not optional, default &#8220;- untitled -&#8221;)</li>
<li>author &#8211; relationship to Author table (not optional)</li>
<li>tags &#8211; relationship to the Tags table (optional, to-many)</li>
</ul>
<h3>Author</h3>
<ul>
<li>name &#8211; string (not optional)</li>
<li>posts &#8211; relationship to the Posts table (optional, to-many)</li>
</ul>
<h3>Tag</h3>
<ul>
<li>name &#8211; string (not optional)</li>
<li>posts &#8211; relationship to the Posts table (optional, to-many)</li>
</ul>
<p>With the model in place we need to copy in our supporting code. This is the <code>CoreData</code> module and the <code>ManagedObject</code> class.</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw3">require</span> <span class="st0">&#8216;fileutils&#8217;</span><br />
framework <span class="st0">&#8216;CoreData&#8217;</span></p>
<p><span class="kw1">module</span> CoreData<br />
&nbsp; <span class="kw1">def</span> application_support_folder<br />
&nbsp; &nbsp; <span class="kw2">return</span> <span class="re1">@application_support_folder</span> <span class="kw1">if</span> <span class="re1">@application_support_folder</span></p>
<p>&nbsp; &nbsp; paths = NSSearchPathForDirectoriesInDomains<span class="br0">&#40;</span>NSApplicationSupportDirectory, NSUserDomainMask, <span class="kw2">true</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; path = <span class="br0">&#40;</span>paths.<span class="me1">count</span> &gt; <span class="nu0">0</span><span class="br0">&#41;</span> ? paths<span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span> : NSTemporaryDirectory<br />
&nbsp; &nbsp; <span class="re1">@application_support_folder</span> = <span class="kw4">File</span>.<span class="me1">join</span><span class="br0">&#40;</span>path, NSApp.<span class="me1">name</span>.<span class="me1">downcase</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw4">FileUtils</span>.<span class="me1">mkdir_p</span><span class="br0">&#40;</span>@application_support_folder<span class="br0">&#41;</span> <span class="kw1">unless</span> <span class="kw4">File</span>.<span class="me1">exists</span>?<span class="br0">&#40;</span>@application_support_folder<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="re1">@application_support_folder</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; <span class="kw1">def</span> managed_object_model<br />
&nbsp; &nbsp; <span class="re1">@managed_object_model</span> ||= NSManagedObjectModel.<span class="me1">mergedModelFromBundles</span><span class="br0">&#40;</span><span class="kw2">nil</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; <span class="kw1">def</span> persistent_store_coordinator<br />
&nbsp; &nbsp; <span class="kw2">return</span> <span class="re1">@persistent_store_coordinator</span> <span class="kw1">if</span> <span class="re1">@persistent_store_coordinator</span></p>
<p>&nbsp; &nbsp; <span class="re1">@persistent_store_coordinator</span> = NSPersistentStoreCoordinator.<span class="me1">alloc</span>.<span class="me1">initWithManagedObjectModel</span><span class="br0">&#40;</span>managed_object_model<span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; error = Pointer.<span class="me1">new_with_type</span><span class="br0">&#40;</span><span class="st0">&#8216;@&#8217;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; url = NSURL.<span class="me1">fileURLWithPath</span><span class="br0">&#40;</span><span class="kw4">File</span>.<span class="me1">join</span><span class="br0">&#40;</span>application_support_folder, <span class="st0">&quot;#{NSApp.name}.xml&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">unless</span> <span class="re1">@persistent_store_coordinator</span>.<span class="me1">addPersistentStoreWithType</span><span class="br0">&#40;</span>NSXMLStoreType, configuration:<span class="kw2">nil</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; URL:url, options:<span class="kw2">nil</span>, error:error<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; NSApplication.<span class="me1">sharedApplication</span>.<span class="me1">presentError</span><span class="br0">&#40;</span>error<span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="re1">@persistent_store_coordinator</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; <span class="kw1">def</span> managed_object_context<br />
&nbsp; &nbsp; <span class="kw2">return</span> <span class="re1">@managed_object_context</span> <span class="kw1">if</span> <span class="re1">@managed_object_context</span></p>
<p>&nbsp; &nbsp; <span class="kw1">if</span> persistent_store_coordinator<br />
&nbsp; &nbsp; &nbsp; <span class="re1">@managed_object_context</span> = NSManagedObjectContext.<span class="me1">alloc</span>.<span class="me1">init</span><br />
&nbsp; &nbsp; &nbsp; <span class="re1">@managed_object_context</span>.<span class="me1">setPersistentStoreCoordinator</span><span class="br0">&#40;</span>persistent_store_coordinator<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="re1">@managed_object_context</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; <span class="kw1">def</span> returning_undo_manager<br />
&nbsp; &nbsp; managed_object_context.<span class="me1">undoManager</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; <span class="kw1">def</span> save<br />
&nbsp; &nbsp; error = Pointer.<span class="me1">new_with_type</span><span class="br0">&#40;</span><span class="st0">&#8216;@&#8217;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">unless</span> managed_object_context.<span class="me1">save</span><span class="br0">&#40;</span>error<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; NSApplication.<span class="me1">sharedApplication</span>.<span class="me1">presentError</span><span class="br0">&#40;</span>error<span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; <span class="kw1">def</span> should_terminate?<br />
&nbsp; &nbsp; reply = NSTerminateNow<br />
&nbsp; &nbsp; <span class="kw1">if</span> managed_object_context<br />
&nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>managed_object_context.<span class="me1">commitEditing</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; error = Pointer.<span class="me1">new_with_type</span><span class="br0">&#40;</span><span class="st0">&#8216;@&#8217;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>managed_object_context.<span class="me1">hasChanges</span> <span class="kw1">and</span> !managed_object_context.<span class="me1">save</span><span class="br0">&#40;</span>error<span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> NSApplication.<span class="me1">sharedApplication</span>.<span class="me1">presentError</span><span class="br0">&#40;</span>error<span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; reply = NSTerminateCancel<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">else</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; alertReturn = NSRunAlertPanel<span class="br0">&#40;</span><span class="kw2">nil</span>, <span class="st0">&quot;Could not save changes while quitting. Quit anyway?&quot;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="st0">&quot;Quit anyway&quot;</span>, <span class="st0">&quot;Cancel&quot;</span>, <span class="kw2">nil</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>alertReturn == NSAlertAlternateReturn<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; reply = NSTerminateCancel<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">else</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; reply = NSTerminateCancel<br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; reply<br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>There isn&#8217;t anything special here. This is, basically, the Obj-C Core Data template ported to Ruby.</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw3">require</span> <span class="st0">&#8216;hotcocoa&#8217;</span></p>
<p><span class="kw1">class</span> ManagedObject<br />
&nbsp; <span class="kw1">def</span> <span class="kw2">self</span>.<span class="me1">create</span><span class="br0">&#40;</span>vals=<span class="br0">&#123;</span><span class="br0">&#125;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; name = <span class="kw2">self</span>.<span class="me1">to_s</span>.<span class="kw3">gsub</span><span class="br0">&#40;</span>/^.<span class="me1">*</span>::/, <span class="st0">&#8221;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; obj = NSEntityDescription.<span class="me1">insertNewObjectForEntityForName</span><span class="br0">&#40;</span>name,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; inManagedObjectContext:NSApp.<span class="me1">delegate</span>.<span class="me1">managed_object_context</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; entity = NSEntityDescription.<span class="me1">entityForName</span><span class="br0">&#40;</span>name,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; inManagedObjectContext:NSApp.<span class="me1">delegate</span>.<span class="me1">managed_object_context</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; map_attributes<span class="br0">&#40;</span>obj, entity.<span class="me1">attributesByName</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; map_relationships<span class="br0">&#40;</span>obj, entity.<span class="me1">relationshipsByName</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; vals.<span class="me1">each_pair</span> <span class="kw1">do</span> |key, value|<br />
&nbsp; &nbsp; &nbsp; obj.<span class="me1">send</span><span class="br0">&#40;</span><span class="st0">&quot;#{key}=&quot;</span>, value<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; obj<br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; <span class="kw1">def</span> <span class="kw2">self</span>.<span class="me1">map_attributes</span><span class="br0">&#40;</span>obj, attributes_by_name<span class="br0">&#41;</span><br />
&nbsp; &nbsp; attributes_by_name.<span class="me1">each_pair</span> <span class="kw1">do</span> |attribute, value|<br />
&nbsp; &nbsp; &nbsp; obj.<span class="me1">instance_eval</span> %<span class="br0">&#91;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">def</span> <span class="co1">#{attribute}=(val)</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; setValue<span class="br0">&#40;</span>val, forKey:<span class="st0">&quot;#{attribute}&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">def</span> <span class="co1">#{attribute}</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; valueForKey<span class="br0">&#40;</span><span class="st0">&quot;#{attribute}&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#93;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">if</span> value.<span class="me1">attributeType</span> == NSBooleanAttributeType<br />
&nbsp; &nbsp; &nbsp; &nbsp; obj.<span class="me1">instance_eval</span> %<span class="br0">&#91;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">def</span> <span class="co1">#{attribute}?</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; send<span class="br0">&#40;</span><span class="st0">&quot;#{attribute}&quot;</span>.<span class="me1">to_sym</span><span class="br0">&#41;</span>.<span class="me1">boolValue</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#93;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span></p>
<p>&nbsp; &nbsp; &nbsp; obj.<span class="me1">send</span><span class="br0">&#40;</span><span class="st0">&quot;#{attribute}=&quot;</span>.<span class="me1">to_sym</span>, value.<span class="me1">defaultValue</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; <span class="kw1">def</span> <span class="kw2">self</span>.<span class="me1">map_relationships</span><span class="br0">&#40;</span>obj, relationships_by_name<span class="br0">&#41;</span><br />
&nbsp; &nbsp; relationships_by_name.<span class="me1">each_pair</span> <span class="kw1">do</span> |relationship, value|<br />
&nbsp; &nbsp; &nbsp; <span class="kw1">if</span> value.<span class="me1">isToMany</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; obj.<span class="me1">instance_eval</span> %<span class="br0">&#91;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">def</span> <span class="co1">#{relationship}</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; willAccessValueForKey<span class="br0">&#40;</span><span class="st0">&quot;#{relationship}&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v = primitiveValueForKey<span class="br0">&#40;</span><span class="st0">&quot;#{relationship}&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; didAccessValueForKey<span class="br0">&#40;</span><span class="st0">&quot;#{relationship}&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#93;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">else</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; obj.<span class="me1">instance_eval</span> %<span class="br0">&#91;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">def</span> <span class="co1">#{relationship}</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; valueForKey<span class="br0">&#40;</span><span class="st0">&quot;#{relationship}&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">def</span> <span class="co1">#{relationship}=(val)</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; setValue<span class="br0">&#40;</span>val, forKey:<span class="st0">&quot;#{relationship}&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#93;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p><code>ManagedObject</code> is a little more interesting. I didn&#8217;t want to have to use <code>setValue:forKey:</code>, <code>valueForKey:</code> and other <code>NSManagedObject</code> methods to work with my model. What <code>ManagedObject</code> does is create a new entity with <code>NSEntityDescription.insertNewObjectForEntityForName:inManagedObjectContext:</code> and then attach methods for the relationships and attributes of that entity.</p>
<p>We use <code>NSEntityDescription.entityForName:inManagedObjectContext:</code> to get the information on the entity. This allows us to call <code>entity.attributesByName</code> and <code>entity.relationshipsByName</code>. The last thing <code>self.create</code> does is take any provided options and send them to the appropriate keys. This allows us to do <code>Author.create(:name => "dj2")</code> instead of needing multiple lines.</p>
<p>The methods are created in <code>self.map_attributes</code> and <code>self.map_relationships</code>. For each attribute we attach an <code>attribute=</code> and <code>attribute</code> method. If the attribute is a boolean we also create an <code>attribute?</code> method. If the attribute assigns a default value we&#8217;ll send the default after the methods are added.</p>
<p><code>self.map_relationships</code> works in a similar fashion if the relation ship isn&#8217;t to-many. If it is to-many we only create an <code>attribute</code> method. Currently you&#8217;ll need to use the <code>NSSet</code> methods to access the values in the to-many set.</p>
<p>The relationship code needs a bit more work as it won&#8217;t try to lookup a relationship, just blindly create a new row. I need to decide if I want to build that into <code>create</code> or add a <code>find</code> method to lookup the entities.</p>
<p>I&#8217;ve been storing my models in a <code>lib/models</code> directory. For this all we create three, almost identical, models. <code>author.rb</code>, <code>post.rb</code> and <code>tag.rb</code></p>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw1">class</span> Author &lt; ManagedObject<br />
<span class="kw1">end</span></p>
<p><span class="kw1">class</span> Post &lt; ManagedObject<br />
<span class="kw1">end</span></p>
<p><span class="kw1">class</span> Tag &lt; ManagedObject<br />
<span class="kw1">end</span></div>
<p>The last changes are to <code>application.rb</code>. We need to require <code>lib/core_data</code>, <code>lib/managed_object</code> and our models.</p>
</pre>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw4">Dir</span>.<span class="me1">glob</span><span class="br0">&#40;</span><span class="st0">&quot;lib/models/*.rb&quot;</span><span class="br0">&#41;</span>.<span class="me1">each</span> <span class="kw1">do</span> |file|<br />
&nbsp; <span class="kw3">require</span> file<br />
<span class="kw1">end</span><br />
&nbsp;</div>
<p>Then, inside the application class we <code>include CoreData</code> and we're good to go. You can then do something similar to:</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw3">p</span> = Post.<span class="me1">create</span><br />
<span class="kw3">p</span>.<span class="me1">title</span> = <span class="st0">&quot;HotData&quot;</span><br />
<span class="kw3">p</span>.<span class="me1">pub_date</span> = NSDate.<span class="me1">date</span><br />
<span class="kw3">p</span>.<span class="me1">content</span> = <span class="st0">&quot;Playing with Core Data from HotCocoa&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <br />
<span class="kw3">p</span>.<span class="me1">author</span> = Author.<span class="me1">create</span><span class="br0">&#40;</span><span class="re3">:name</span> =&gt; <span class="st0">'dj2'</span><span class="br0">&#41;</span><br />
<span class="kw3">p</span>.<span class="me1">tags</span>.<span class="me1">addObject</span><span class="br0">&#40;</span>Tag.<span class="me1">create</span><span class="br0">&#40;</span><span class="re3">:name</span> =&gt; <span class="st0">&quot;Core Data&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
<span class="kw3">p</span>.<span class="me1">tags</span>.<span class="me1">addObject</span><span class="br0">&#40;</span>Tag.<span class="me1">create</span><span class="br0">&#40;</span><span class="re3">:name</span> =&gt; <span class="st0">&quot;HotCocoa&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <br />
<span class="kw3">p</span>.<span class="me1">published</span> = <span class="kw2">true</span> <span class="kw1">if</span> !<span class="kw3">p</span>.<span class="me1">published</span>?<br />
&nbsp; &nbsp; &nbsp; &nbsp; <br />
save</div>
<p>Which will create a <code>~/Library/Application Support/blog/Blog.xml</code> file with your new post inside.</p>
<p>I've <a href='http://everburning.com/wp-content/uploads/2009/06/Blog.tar.gz'>uploaded</a> a copy of my source if you want to take a look.</p>
<p>Questions? Comments? Let'em rip.</p>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Fhotcocoa-and-core-data%2F&amp;title=HotCocoa+and+Core+Data" title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fhotcocoa-and-core-data%2F&amp;title=HotCocoa+and+Core+Data" title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fhotcocoa-and-core-data%2F&amp;title=HotCocoa+and+Core+Data" title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Fhotcocoa-and-core-data%2F&amp;title=HotCocoa+and+Core+Data" title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fhotcocoa-and-core-data%2F&amp;title=HotCocoa+and+Core+Data', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Fhotcocoa-and-core-data%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Fhotcocoa-and-core-data%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Fhotcocoa-and-core-data%2F&amp;title=HotCocoa+and+Core+Data" title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fhotcocoa-and-core-data%2F&amp;title=HotCocoa+and+Core+Data" title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></description>
			<content:encoded><![CDATA[<p><img src="http://everburning.com/wp-content/uploads/2009/06/IMG_6093-300x200.jpg" alt="IMG_6093" title="IMG_6093" width="300" height="200" class="alignleft size-medium wp-image-573" />I&#8217;ve been playing around a bit with Core Data and HotCocoa the last few days and thought I&#8217;d share what I&#8217;ve got so far.  When you install MacRuby you&#8217;re provided with an XCode template to start working with Core Data. I didn&#8217;t want to use XCode so I skipped the template. Some of this code is probably pretty similar to the template as I ported the Obj-C Core Data template to Ruby.</p>
<p>Note, this code is lightly tested, prototype, probably wrong and has already assassinated an unknown number of kitties. At this point, I&#8217;m still not sure if the approach taken is actually sane. Time will tell, but I thought it was interesting enough to share.</p>
<p>I stuffed all of the default Core Data stuff into a CoreData module that I&#8217;m including into my main application (mostly so I don&#8217;t have to look at the functions when working on my main code). The separation isn&#8217;t great at the moment as the <code>should_terminate?</code> method is currently in the module instead of the main application. I&#8217;ll be coming back to this setup once I&#8217;ve figured out how all the pieces work.</p>
<p>Along with the Core Data base methods I&#8217;ve also created a <code>ManagedObject</code> class.  Each of the entities in my model I&#8217;ve created as a model class. Those model classes inherit from <code>ManagedObject</code>. (I just realized, the objects returned from <code>self.create</code> in <code>ManagedObject</code> aren&#8217;t of my model types. They&#8217;ll be straight up <code>NSManagedObjects</code>. Oops. Will need to look at that as well. Can you tell this stuff is still pretty rough?) <code>ManagedObject</code> contains the common <code>create</code> method for all of the sub-classes. When <code>create</code> is called the entity will be retrieved from the Core Data model and have it&#8217;s attributes and relationships added as methods on the returned object.</p>
<p>What this means is that, in the end, I can do the following:</p>
<div class="dean_ch" style="white-space: wrap;">
&nbsp; <span class="kw3">p</span> = Post.<span class="me1">create</span><br />
&nbsp; <span class="kw3">p</span>.<span class="me1">title</span> = <span class="st0">&quot;HotData&quot;</span><br />
&nbsp; <span class="kw3">p</span>.<span class="me1">pub_date</span> = NSDate.<span class="me1">date</span><br />
&nbsp; <span class="kw3">p</span>.<span class="me1">content</span> = <span class="st0">&quot;Playing with Core Data from HotCocoa&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; <span class="kw3">p</span>.<span class="me1">author</span> = Author.<span class="me1">create</span><span class="br0">&#40;</span><span class="re3">:name</span> =&gt; <span class="st0">&#8216;dj2&#8242;</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw3">p</span>.<span class="me1">tags</span>.<span class="me1">addObject</span><span class="br0">&#40;</span>Tag.<span class="me1">create</span><span class="br0">&#40;</span><span class="re3">:name</span> =&gt; <span class="st0">&quot;Core Data&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw3">p</span>.<span class="me1">tags</span>.<span class="me1">addObject</span><span class="br0">&#40;</span>Tag.<span class="me1">create</span><span class="br0">&#40;</span><span class="re3">:name</span> =&gt; <span class="st0">&quot;HotCocoa&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; <span class="kw3">p</span>.<span class="me1">published</span> = <span class="kw2">true</span> <span class="kw1">if</span> !<span class="kw3">p</span>.<span class="me1">published</span>?</div>
<p>One thing to note, when you&#8217;re working with Core Data you&#8217;ll be working with a <code>.xcdatamodel</code> file. In order to use that from code this data model needs to be compiled to a <code>mom</code> file and put into your application bundle. I&#8217;ve created a patch, attached to <a href="https://www.macruby.org/trac/ticket/278">ticket 278</a> that builds this into the HotCocoa application builder. All you need is a <code>build.yml</code> similar to the following:</p>
<div class="dean_ch" style="white-space: wrap;">
name: Blog<br />
load: lib/application.rb<br />
version: &quot;1.0&quot;<br />
icon: resources/HotCocoa.icns<br />
resources:<br />
&nbsp; &#8211; resources/**/*.*<br />
data_models:<br />
&nbsp; &#8211; data/**/*.xcdatamodel<br />
sources: <br />
&nbsp; &#8211; lib/**/*.rb</div>
<p><a href="http://everburning.com/wp-content/uploads/2009/06/Picture-1.png"><img src="http://everburning.com/wp-content/uploads/2009/06/Picture-1-300x156.png" alt="Picture 1" title="Picture 1" width="300" height="156" class="alignright size-medium wp-image-568" /></a>Note the addition of the <code>data_models</code> key. This will take each of the models found, compile it, and place it in your bundles resource folder ready for use.</p>
<p>With that out of the way, what are we modeling? I figured I&#8217;d go simple and model a blog. I didn&#8217;t bother to write any real UI in front of it for the example as I just wanted to focus on the Core Data aspects.</p>
<p>The first step is to create the project: <code>hotcocoa Blog</code> will create the basis for us. I then updated the <code>build.yml</code> file to what&#8217;s seen above. </p>
<p>The next step was to create my data model which I saved to <code>data/Blog.xcdatamodel</code>. This needs to be done through XCode. Open up a new data model and create the following:</p>
<h3>Post</h3>
<ul>
<li>content  &#8211; string (not optional)</li>
<li>pub_date &#8211; date (not optional)</li>
<li>published &#8211; boolean (default NO)</li>
<li>title &#8211; string (not optional, default &#8220;- untitled -&#8221;)</li>
<li>author &#8211; relationship to Author table (not optional)</li>
<li>tags &#8211; relationship to the Tags table (optional, to-many)</li>
</ul>
<h3>Author</h3>
<ul>
<li>name &#8211; string (not optional)</li>
<li>posts &#8211; relationship to the Posts table (optional, to-many)</li>
</ul>
<h3>Tag</h3>
<ul>
<li>name &#8211; string (not optional)</li>
<li>posts &#8211; relationship to the Posts table (optional, to-many)</li>
</ul>
<p>With the model in place we need to copy in our supporting code. This is the <code>CoreData</code> module and the <code>ManagedObject</code> class.</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw3">require</span> <span class="st0">&#8216;fileutils&#8217;</span><br />
framework <span class="st0">&#8216;CoreData&#8217;</span></p>
<p><span class="kw1">module</span> CoreData<br />
&nbsp; <span class="kw1">def</span> application_support_folder<br />
&nbsp; &nbsp; <span class="kw2">return</span> <span class="re1">@application_support_folder</span> <span class="kw1">if</span> <span class="re1">@application_support_folder</span></p>
<p>&nbsp; &nbsp; paths = NSSearchPathForDirectoriesInDomains<span class="br0">&#40;</span>NSApplicationSupportDirectory, NSUserDomainMask, <span class="kw2">true</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; path = <span class="br0">&#40;</span>paths.<span class="me1">count</span> &gt; <span class="nu0">0</span><span class="br0">&#41;</span> ? paths<span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span> : NSTemporaryDirectory<br />
&nbsp; &nbsp; <span class="re1">@application_support_folder</span> = <span class="kw4">File</span>.<span class="me1">join</span><span class="br0">&#40;</span>path, NSApp.<span class="me1">name</span>.<span class="me1">downcase</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw4">FileUtils</span>.<span class="me1">mkdir_p</span><span class="br0">&#40;</span>@application_support_folder<span class="br0">&#41;</span> <span class="kw1">unless</span> <span class="kw4">File</span>.<span class="me1">exists</span>?<span class="br0">&#40;</span>@application_support_folder<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="re1">@application_support_folder</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; <span class="kw1">def</span> managed_object_model<br />
&nbsp; &nbsp; <span class="re1">@managed_object_model</span> ||= NSManagedObjectModel.<span class="me1">mergedModelFromBundles</span><span class="br0">&#40;</span><span class="kw2">nil</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; <span class="kw1">def</span> persistent_store_coordinator<br />
&nbsp; &nbsp; <span class="kw2">return</span> <span class="re1">@persistent_store_coordinator</span> <span class="kw1">if</span> <span class="re1">@persistent_store_coordinator</span></p>
<p>&nbsp; &nbsp; <span class="re1">@persistent_store_coordinator</span> = NSPersistentStoreCoordinator.<span class="me1">alloc</span>.<span class="me1">initWithManagedObjectModel</span><span class="br0">&#40;</span>managed_object_model<span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; error = Pointer.<span class="me1">new_with_type</span><span class="br0">&#40;</span><span class="st0">&#8216;@&#8217;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; url = NSURL.<span class="me1">fileURLWithPath</span><span class="br0">&#40;</span><span class="kw4">File</span>.<span class="me1">join</span><span class="br0">&#40;</span>application_support_folder, <span class="st0">&quot;#{NSApp.name}.xml&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">unless</span> <span class="re1">@persistent_store_coordinator</span>.<span class="me1">addPersistentStoreWithType</span><span class="br0">&#40;</span>NSXMLStoreType, configuration:<span class="kw2">nil</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; URL:url, options:<span class="kw2">nil</span>, error:error<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; NSApplication.<span class="me1">sharedApplication</span>.<span class="me1">presentError</span><span class="br0">&#40;</span>error<span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="re1">@persistent_store_coordinator</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; <span class="kw1">def</span> managed_object_context<br />
&nbsp; &nbsp; <span class="kw2">return</span> <span class="re1">@managed_object_context</span> <span class="kw1">if</span> <span class="re1">@managed_object_context</span></p>
<p>&nbsp; &nbsp; <span class="kw1">if</span> persistent_store_coordinator<br />
&nbsp; &nbsp; &nbsp; <span class="re1">@managed_object_context</span> = NSManagedObjectContext.<span class="me1">alloc</span>.<span class="me1">init</span><br />
&nbsp; &nbsp; &nbsp; <span class="re1">@managed_object_context</span>.<span class="me1">setPersistentStoreCoordinator</span><span class="br0">&#40;</span>persistent_store_coordinator<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="re1">@managed_object_context</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; <span class="kw1">def</span> returning_undo_manager<br />
&nbsp; &nbsp; managed_object_context.<span class="me1">undoManager</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; <span class="kw1">def</span> save<br />
&nbsp; &nbsp; error = Pointer.<span class="me1">new_with_type</span><span class="br0">&#40;</span><span class="st0">&#8216;@&#8217;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">unless</span> managed_object_context.<span class="me1">save</span><span class="br0">&#40;</span>error<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; NSApplication.<span class="me1">sharedApplication</span>.<span class="me1">presentError</span><span class="br0">&#40;</span>error<span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; <span class="kw1">def</span> should_terminate?<br />
&nbsp; &nbsp; reply = NSTerminateNow<br />
&nbsp; &nbsp; <span class="kw1">if</span> managed_object_context<br />
&nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>managed_object_context.<span class="me1">commitEditing</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; error = Pointer.<span class="me1">new_with_type</span><span class="br0">&#40;</span><span class="st0">&#8216;@&#8217;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>managed_object_context.<span class="me1">hasChanges</span> <span class="kw1">and</span> !managed_object_context.<span class="me1">save</span><span class="br0">&#40;</span>error<span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> NSApplication.<span class="me1">sharedApplication</span>.<span class="me1">presentError</span><span class="br0">&#40;</span>error<span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; reply = NSTerminateCancel<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">else</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; alertReturn = NSRunAlertPanel<span class="br0">&#40;</span><span class="kw2">nil</span>, <span class="st0">&quot;Could not save changes while quitting. Quit anyway?&quot;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="st0">&quot;Quit anyway&quot;</span>, <span class="st0">&quot;Cancel&quot;</span>, <span class="kw2">nil</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>alertReturn == NSAlertAlternateReturn<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; reply = NSTerminateCancel<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">else</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; reply = NSTerminateCancel<br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; reply<br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>There isn&#8217;t anything special here. This is, basically, the Obj-C Core Data template ported to Ruby.</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw3">require</span> <span class="st0">&#8216;hotcocoa&#8217;</span></p>
<p><span class="kw1">class</span> ManagedObject<br />
&nbsp; <span class="kw1">def</span> <span class="kw2">self</span>.<span class="me1">create</span><span class="br0">&#40;</span>vals=<span class="br0">&#123;</span><span class="br0">&#125;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; name = <span class="kw2">self</span>.<span class="me1">to_s</span>.<span class="kw3">gsub</span><span class="br0">&#40;</span>/^.<span class="me1">*</span>::/, <span class="st0">&#8221;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; obj = NSEntityDescription.<span class="me1">insertNewObjectForEntityForName</span><span class="br0">&#40;</span>name,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; inManagedObjectContext:NSApp.<span class="me1">delegate</span>.<span class="me1">managed_object_context</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; entity = NSEntityDescription.<span class="me1">entityForName</span><span class="br0">&#40;</span>name,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; inManagedObjectContext:NSApp.<span class="me1">delegate</span>.<span class="me1">managed_object_context</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; map_attributes<span class="br0">&#40;</span>obj, entity.<span class="me1">attributesByName</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; map_relationships<span class="br0">&#40;</span>obj, entity.<span class="me1">relationshipsByName</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; vals.<span class="me1">each_pair</span> <span class="kw1">do</span> |key, value|<br />
&nbsp; &nbsp; &nbsp; obj.<span class="me1">send</span><span class="br0">&#40;</span><span class="st0">&quot;#{key}=&quot;</span>, value<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; obj<br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; <span class="kw1">def</span> <span class="kw2">self</span>.<span class="me1">map_attributes</span><span class="br0">&#40;</span>obj, attributes_by_name<span class="br0">&#41;</span><br />
&nbsp; &nbsp; attributes_by_name.<span class="me1">each_pair</span> <span class="kw1">do</span> |attribute, value|<br />
&nbsp; &nbsp; &nbsp; obj.<span class="me1">instance_eval</span> %<span class="br0">&#91;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">def</span> <span class="co1">#{attribute}=(val)</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; setValue<span class="br0">&#40;</span>val, forKey:<span class="st0">&quot;#{attribute}&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">def</span> <span class="co1">#{attribute}</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; valueForKey<span class="br0">&#40;</span><span class="st0">&quot;#{attribute}&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#93;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">if</span> value.<span class="me1">attributeType</span> == NSBooleanAttributeType<br />
&nbsp; &nbsp; &nbsp; &nbsp; obj.<span class="me1">instance_eval</span> %<span class="br0">&#91;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">def</span> <span class="co1">#{attribute}?</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; send<span class="br0">&#40;</span><span class="st0">&quot;#{attribute}&quot;</span>.<span class="me1">to_sym</span><span class="br0">&#41;</span>.<span class="me1">boolValue</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#93;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span></p>
<p>&nbsp; &nbsp; &nbsp; obj.<span class="me1">send</span><span class="br0">&#40;</span><span class="st0">&quot;#{attribute}=&quot;</span>.<span class="me1">to_sym</span>, value.<span class="me1">defaultValue</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; <span class="kw1">def</span> <span class="kw2">self</span>.<span class="me1">map_relationships</span><span class="br0">&#40;</span>obj, relationships_by_name<span class="br0">&#41;</span><br />
&nbsp; &nbsp; relationships_by_name.<span class="me1">each_pair</span> <span class="kw1">do</span> |relationship, value|<br />
&nbsp; &nbsp; &nbsp; <span class="kw1">if</span> value.<span class="me1">isToMany</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; obj.<span class="me1">instance_eval</span> %<span class="br0">&#91;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">def</span> <span class="co1">#{relationship}</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; willAccessValueForKey<span class="br0">&#40;</span><span class="st0">&quot;#{relationship}&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v = primitiveValueForKey<span class="br0">&#40;</span><span class="st0">&quot;#{relationship}&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; didAccessValueForKey<span class="br0">&#40;</span><span class="st0">&quot;#{relationship}&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#93;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">else</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; obj.<span class="me1">instance_eval</span> %<span class="br0">&#91;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">def</span> <span class="co1">#{relationship}</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; valueForKey<span class="br0">&#40;</span><span class="st0">&quot;#{relationship}&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">def</span> <span class="co1">#{relationship}=(val)</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; setValue<span class="br0">&#40;</span>val, forKey:<span class="st0">&quot;#{relationship}&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#93;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p><code>ManagedObject</code> is a little more interesting. I didn&#8217;t want to have to use <code>setValue:forKey:</code>, <code>valueForKey:</code> and other <code>NSManagedObject</code> methods to work with my model. What <code>ManagedObject</code> does is create a new entity with <code>NSEntityDescription.insertNewObjectForEntityForName:inManagedObjectContext:</code> and then attach methods for the relationships and attributes of that entity.</p>
<p>We use <code>NSEntityDescription.entityForName:inManagedObjectContext:</code> to get the information on the entity. This allows us to call <code>entity.attributesByName</code> and <code>entity.relationshipsByName</code>. The last thing <code>self.create</code> does is take any provided options and send them to the appropriate keys. This allows us to do <code>Author.create(:name => "dj2")</code> instead of needing multiple lines.</p>
<p>The methods are created in <code>self.map_attributes</code> and <code>self.map_relationships</code>. For each attribute we attach an <code>attribute=</code> and <code>attribute</code> method. If the attribute is a boolean we also create an <code>attribute?</code> method. If the attribute assigns a default value we&#8217;ll send the default after the methods are added.</p>
<p><code>self.map_relationships</code> works in a similar fashion if the relation ship isn&#8217;t to-many. If it is to-many we only create an <code>attribute</code> method. Currently you&#8217;ll need to use the <code>NSSet</code> methods to access the values in the to-many set.</p>
<p>The relationship code needs a bit more work as it won&#8217;t try to lookup a relationship, just blindly create a new row. I need to decide if I want to build that into <code>create</code> or add a <code>find</code> method to lookup the entities.</p>
<p>I&#8217;ve been storing my models in a <code>lib/models</code> directory. For this all we create three, almost identical, models. <code>author.rb</code>, <code>post.rb</code> and <code>tag.rb</code></p>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw1">class</span> Author &lt; ManagedObject<br />
<span class="kw1">end</span></p>
<p><span class="kw1">class</span> Post &lt; ManagedObject<br />
<span class="kw1">end</span></p>
<p><span class="kw1">class</span> Tag &lt; ManagedObject<br />
<span class="kw1">end</span></div>
<p>The last changes are to <code>application.rb</code>. We need to require <code>lib/core_data</code>, <code>lib/managed_object</code> and our models.</p>
</pre>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw4">Dir</span>.<span class="me1">glob</span><span class="br0">&#40;</span><span class="st0">&quot;lib/models/*.rb&quot;</span><span class="br0">&#41;</span>.<span class="me1">each</span> <span class="kw1">do</span> |file|<br />
&nbsp; <span class="kw3">require</span> file<br />
<span class="kw1">end</span><br />
&nbsp;</div>
<p>Then, inside the application class we <code>include CoreData</code> and we're good to go. You can then do something similar to:</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw3">p</span> = Post.<span class="me1">create</span><br />
<span class="kw3">p</span>.<span class="me1">title</span> = <span class="st0">&quot;HotData&quot;</span><br />
<span class="kw3">p</span>.<span class="me1">pub_date</span> = NSDate.<span class="me1">date</span><br />
<span class="kw3">p</span>.<span class="me1">content</span> = <span class="st0">&quot;Playing with Core Data from HotCocoa&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <br />
<span class="kw3">p</span>.<span class="me1">author</span> = Author.<span class="me1">create</span><span class="br0">&#40;</span><span class="re3">:name</span> =&gt; <span class="st0">'dj2'</span><span class="br0">&#41;</span><br />
<span class="kw3">p</span>.<span class="me1">tags</span>.<span class="me1">addObject</span><span class="br0">&#40;</span>Tag.<span class="me1">create</span><span class="br0">&#40;</span><span class="re3">:name</span> =&gt; <span class="st0">&quot;Core Data&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
<span class="kw3">p</span>.<span class="me1">tags</span>.<span class="me1">addObject</span><span class="br0">&#40;</span>Tag.<span class="me1">create</span><span class="br0">&#40;</span><span class="re3">:name</span> =&gt; <span class="st0">&quot;HotCocoa&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <br />
<span class="kw3">p</span>.<span class="me1">published</span> = <span class="kw2">true</span> <span class="kw1">if</span> !<span class="kw3">p</span>.<span class="me1">published</span>?<br />
&nbsp; &nbsp; &nbsp; &nbsp; <br />
save</div>
<p>Which will create a <code>~/Library/Application Support/blog/Blog.xml</code> file with your new post inside.</p>
<p>I've <a href='http://everburning.com/wp-content/uploads/2009/06/Blog.tar.gz'>uploaded</a> a copy of my source if you want to take a look.</p>
<p>Questions? Comments? Let'em rip.</p>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Fhotcocoa-and-core-data%2F&amp;title=HotCocoa+and+Core+Data" title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fhotcocoa-and-core-data%2F&amp;title=HotCocoa+and+Core+Data" title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fhotcocoa-and-core-data%2F&amp;title=HotCocoa+and+Core+Data" title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Fhotcocoa-and-core-data%2F&amp;title=HotCocoa+and+Core+Data" title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fhotcocoa-and-core-data%2F&amp;title=HotCocoa+and+Core+Data', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Fhotcocoa-and-core-data%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Fhotcocoa-and-core-data%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Fhotcocoa-and-core-data%2F&amp;title=HotCocoa+and+Core+Data" title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fhotcocoa-and-core-data%2F&amp;title=HotCocoa+and+Core+Data" title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></content:encoded>
			<wfw:commentRss>http://everburning.com/news/hotcocoa-and-core-data/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>MacRuby and NSError</title>
		<link>http://everburning.com/news/macruby-and-nserror/</link>
		<comments>http://everburning.com/news/macruby-and-nserror/#comments</comments>
		<pubDate>Fri, 12 Jun 2009 02:54:05 +0000</pubDate>
		<dc:creator>dj2</dc:creator>
				<category><![CDATA[Computers]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[MacRuby]]></category>

		<guid isPermaLink="false">http://everburning.com/?p=562</guid>
		<description><![CDATA[<p>Just a note for other people that might be cracking their heads trying to meld MacRuby and NSError ** parameters.  The answer was already sitting on my computer, I just never through to look at the MacRuby Core Data XCode templates, but that&#8217;s beside the point.  For those of you searching, you just need to do the following.</p>
<div class="dean_ch" style="white-space: wrap;">
&nbsp; &nbsp; &nbsp; error = Pointer.<span class="me1">new_with_type</span><span class="br0">&#40;</span><span class="st0">&#8216;@&#8217;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">unless</span> managed_object_context.<span class="me1">save</span><span class="br0">&#40;</span>error<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; NSLog error<span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span>.<span class="me1">inspect</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; NSApplication.<span class="me1">sharedApplication</span>.<span class="me1">presentError</span><span class="br0">&#40;</span>error<span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp;</div>
<p>The <code>NSLog error[0].inspect</code> will output <code>#&lt;NSError:0x80057a640&gt;</code>. Exactly what we want.</p>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Fmacruby-and-nserror%2F&amp;title=MacRuby+and+NSError" title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fmacruby-and-nserror%2F&amp;title=MacRuby+and+NSError" title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fmacruby-and-nserror%2F&amp;title=MacRuby+and+NSError" title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Fmacruby-and-nserror%2F&amp;title=MacRuby+and+NSError" title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fmacruby-and-nserror%2F&amp;title=MacRuby+and+NSError', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Fmacruby-and-nserror%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Fmacruby-and-nserror%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Fmacruby-and-nserror%2F&amp;title=MacRuby+and+NSError" title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fmacruby-and-nserror%2F&amp;title=MacRuby+and+NSError" title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></description>
			<content:encoded><![CDATA[<p>Just a note for other people that might be cracking their heads trying to meld MacRuby and NSError ** parameters.  The answer was already sitting on my computer, I just never through to look at the MacRuby Core Data XCode templates, but that&#8217;s beside the point.  For those of you searching, you just need to do the following.</p>
<div class="dean_ch" style="white-space: wrap;">
&nbsp; &nbsp; &nbsp; error = Pointer.<span class="me1">new_with_type</span><span class="br0">&#40;</span><span class="st0">&#8216;@&#8217;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">unless</span> managed_object_context.<span class="me1">save</span><span class="br0">&#40;</span>error<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; NSLog error<span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span>.<span class="me1">inspect</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; NSApplication.<span class="me1">sharedApplication</span>.<span class="me1">presentError</span><span class="br0">&#40;</span>error<span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp;</div>
<p>The <code>NSLog error[0].inspect</code> will output <code>#&lt;NSError:0x80057a640&gt;</code>. Exactly what we want.</p>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Fmacruby-and-nserror%2F&amp;title=MacRuby+and+NSError" title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fmacruby-and-nserror%2F&amp;title=MacRuby+and+NSError" title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fmacruby-and-nserror%2F&amp;title=MacRuby+and+NSError" title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Fmacruby-and-nserror%2F&amp;title=MacRuby+and+NSError" title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fmacruby-and-nserror%2F&amp;title=MacRuby+and+NSError', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Fmacruby-and-nserror%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Fmacruby-and-nserror%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Fmacruby-and-nserror%2F&amp;title=MacRuby+and+NSError" title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fmacruby-and-nserror%2F&amp;title=MacRuby+and+NSError" title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></content:encoded>
			<wfw:commentRss>http://everburning.com/news/macruby-and-nserror/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Heating up with HotCocoa on GitHub</title>
		<link>http://everburning.com/news/heating-up-with-hotcocoa-on-github/</link>
		<comments>http://everburning.com/news/heating-up-with-hotcocoa-on-github/#comments</comments>
		<pubDate>Tue, 26 May 2009 17:57:55 +0000</pubDate>
		<dc:creator>dj2</dc:creator>
				<category><![CDATA[Computers]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[GitHub]]></category>
		<category><![CDATA[HotCocoa]]></category>
		<category><![CDATA[MacRuby]]></category>

		<guid isPermaLink="false">http://everburning.com/?p=555</guid>
		<description><![CDATA[<p>I&#8217;ve imported <a href="http://github.com/dj2/Postie">Postie</a> to <a href="http://github.com">GitHub</a> to make it easier to follow along from home.  If you&#8217;re new here, you can see the development of Postie in the Heating up with HotCocoa articles (<a href="http://everburning.com/news/heating-up-with-hotcocoa-part-i/">part I</a>, <a href="http://everburning.com/news/heating-up-with-hotcocoa-part-ii/">part II</a> and <a href="http://everburning.com/news/heating-up-with-hotcocoa-part-iii/">part III</a>).</p>
<p>The code on GitHub is based off of the code from part III with a minor addition to the button action.</p>
<div class="dean_ch" style="white-space: wrap;">
b.<span class="me1">on_action</span> <span class="kw1">do</span><br />
&nbsp; load_feed<br />
&nbsp;<br />
&nbsp; <span class="re1">@timer</span>.<span class="me1">invalidate</span> <span class="kw1">unless</span> <span class="re1">@timer</span>.<span class="kw2">nil</span>?<br />
&nbsp; <span class="re1">@timer</span> = NSTimer.<span class="me1">scheduledTimerWithTimeInterval</span><span class="br0">&#40;</span><span class="nu0">30</span>, target:<span class="kw2">self</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; selector:<span class="st0">&quot;refresh&quot;</span>.<span class="me1">to_sym</span>, userInfo:<span class="kw2">nil</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; repeats:<span class="kw2">true</span><span class="br0">&#41;</span><br />
<span class="kw1">end</span></div>
<p>I wanted to have the application reload my metrics data automatically to feed my obsessive nature.  I added a <code>NSTimer</code> that will execute a <code>refresh</code> method every 30 seconds. The refresh method just calls <code>load_feed</code> to reload everything. Not perfect, but works as a quick hack to be cleaned up later.</p>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-on-github%2F&amp;title=Heating+up+with+HotCocoa+on+GitHub" title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-on-github%2F&amp;title=Heating+up+with+HotCocoa+on+GitHub" title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-on-github%2F&amp;title=Heating+up+with+HotCocoa+on+GitHub" title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-on-github%2F&amp;title=Heating+up+with+HotCocoa+on+GitHub" title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-on-github%2F&amp;title=Heating+up+with+HotCocoa+on+GitHub', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-on-github%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-on-github%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-on-github%2F&amp;title=Heating+up+with+HotCocoa+on+GitHub" title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-on-github%2F&amp;title=Heating+up+with+HotCocoa+on+GitHub" title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve imported <a href="http://github.com/dj2/Postie">Postie</a> to <a href="http://github.com">GitHub</a> to make it easier to follow along from home.  If you&#8217;re new here, you can see the development of Postie in the Heating up with HotCocoa articles (<a href="http://everburning.com/news/heating-up-with-hotcocoa-part-i/">part I</a>, <a href="http://everburning.com/news/heating-up-with-hotcocoa-part-ii/">part II</a> and <a href="http://everburning.com/news/heating-up-with-hotcocoa-part-iii/">part III</a>).</p>
<p>The code on GitHub is based off of the code from part III with a minor addition to the button action.</p>
<div class="dean_ch" style="white-space: wrap;">
b.<span class="me1">on_action</span> <span class="kw1">do</span><br />
&nbsp; load_feed<br />
&nbsp;<br />
&nbsp; <span class="re1">@timer</span>.<span class="me1">invalidate</span> <span class="kw1">unless</span> <span class="re1">@timer</span>.<span class="kw2">nil</span>?<br />
&nbsp; <span class="re1">@timer</span> = NSTimer.<span class="me1">scheduledTimerWithTimeInterval</span><span class="br0">&#40;</span><span class="nu0">30</span>, target:<span class="kw2">self</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; selector:<span class="st0">&quot;refresh&quot;</span>.<span class="me1">to_sym</span>, userInfo:<span class="kw2">nil</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; repeats:<span class="kw2">true</span><span class="br0">&#41;</span><br />
<span class="kw1">end</span></div>
<p>I wanted to have the application reload my metrics data automatically to feed my obsessive nature.  I added a <code>NSTimer</code> that will execute a <code>refresh</code> method every 30 seconds. The refresh method just calls <code>load_feed</code> to reload everything. Not perfect, but works as a quick hack to be cleaned up later.</p>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-on-github%2F&amp;title=Heating+up+with+HotCocoa+on+GitHub" title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-on-github%2F&amp;title=Heating+up+with+HotCocoa+on+GitHub" title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-on-github%2F&amp;title=Heating+up+with+HotCocoa+on+GitHub" title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-on-github%2F&amp;title=Heating+up+with+HotCocoa+on+GitHub" title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-on-github%2F&amp;title=Heating+up+with+HotCocoa+on+GitHub', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-on-github%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-on-github%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-on-github%2F&amp;title=Heating+up+with+HotCocoa+on+GitHub" title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-on-github%2F&amp;title=Heating+up+with+HotCocoa+on+GitHub" title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></content:encoded>
			<wfw:commentRss>http://everburning.com/news/heating-up-with-hotcocoa-on-github/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Heating up with HotCocoa Part III</title>
		<link>http://everburning.com/news/heating-up-with-hotcocoa-part-iii/</link>
		<comments>http://everburning.com/news/heating-up-with-hotcocoa-part-iii/#comments</comments>
		<pubDate>Mon, 25 May 2009 21:26:23 +0000</pubDate>
		<dc:creator>dj2</dc:creator>
				<category><![CDATA[Computers]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[HotCocoa]]></category>
		<category><![CDATA[MacRuby]]></category>

		<guid isPermaLink="false">http://everburning.com/?p=512</guid>
		<description><![CDATA[<p><img src="http://everburning.com/wp-content/uploads/2009/05/img_5183-300x200.jpg" alt="img_5183" title="img_5183" width="300" height="200" class="alignleft size-medium wp-image-514" />In the words of Homer Simpsons, &#8220;forward not backwards, upwards not forwards and always twirling, twirling, twirling towards freedom&#8221;. With that, we&#8217;re back for part III of my <a href="http://www.macruby.org/trac/wiki/HotCocoa">HotCocoa</a> tutorial. For those of you just joining the party, you&#8217;ll probably want to take a look at <a href="http://everburning.com/news/heating-up-with-hotcocoa-part-i/">part I</a> and <a href="http://everburning.com/news/heating-up-with-hotcocoa-part-ii/">part II</a>.</p>
<p>If you&#8217;d like to grab a copy of the code, it&#8217;s getting a bit big to post all of it in one go, you can grab the tar ball <a href='http://everburning.com/wp-content/uploads/2009/05/postietar.gz'>here</a>. The file contains all of the HotCocoa files along with the sprite image that I&#8217;ve shamelessly niced from <a href="http://postrank.com">PostRank</a>.</p>
<p><a href="http://everburning.com/wp-content/uploads/2009/05/picture-12.png"><img src="http://everburning.com/wp-content/uploads/2009/05/picture-12-300x234.png" alt="picture-12" title="picture-12" width="300" height="234" class="alignright size-medium wp-image-515" /></a>When we last left off we&#8217;d created the basic layout for our application with our button and table views setup. With this installment, we&#8217;re going to go a step further and get a fully working application. We&#8217;re going to use the feed entered into the text field to query PostRank to get the current posts in the feed along with there PostRank and metric information. I&#8217;m going to be skipping over sections of code that haven&#8217;t changed from part I or part II for the sake of brevity. </p>
<p>Note, we&#8217;re going to be using JSON when working with the PostRank APIs. There is a bug in MacRuby, as of revision 1594, where JSON.parse would crash MacRuby. You&#8217;ll need to apply the patch attached to <a href="https://www.macruby.org/trac/ticket/257">ticket 257</a> in order run this application.</p>
<p>OK, let&#8217;s go.</p>
<div class="dean_ch" style="white-space: wrap;">
POSTRANK_URL_BASE = <span class="st0">&quot;http://api.postrank.com/v2&quot;</span><br />
APPKEY = <span class="st0">&quot;appkey=Postie&quot;</span></div>
<p>All of our calls to PostRank will use the same URL prefix and we&#8217;ll need to provide our appkey. I&#8217;ve placed both of these into constants.</p>
<div class="dean_ch" style="white-space: wrap;">
vert &lt;&lt; scroll_view<span class="br0">&#40;</span><span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:expand =&gt; <span class="br0">&#91;</span><span class="re3">:width</span>, <span class="re3">:height</span><span class="br0">&#93;</span><span class="br0">&#125;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |scroll|<br />
&nbsp; scroll.<span class="me1">setAutohidesScrollers</span><span class="br0">&#40;</span><span class="kw2">true</span><span class="br0">&#41;</span></p>
<p>&nbsp; pr_column = column<span class="br0">&#40;</span><span class="re3">:id</span> =&gt; <span class="re3">:postrank</span>, <span class="re3">:title</span> =&gt; <span class="st0">&#8221;</span><span class="br0">&#41;</span><br />
&nbsp; pr_column.<span class="me1">setDataCell</span><span class="br0">&#40;</span>PostRankCell.<span class="me1">new</span><span class="br0">&#41;</span><br />
&nbsp; pr_column.<span class="me1">setMaxWidth</span><span class="br0">&#40;</span><span class="nu0">34</span><span class="br0">&#41;</span><br />
&nbsp; pr_column.<span class="me1">setMinWidth</span><span class="br0">&#40;</span><span class="nu0">34</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; info_column = column<span class="br0">&#40;</span><span class="re3">:id</span> =&gt; <span class="re3">:data</span>, <span class="re3">:title</span> =&gt; <span class="st0">&#8221;</span><span class="br0">&#41;</span><br />
&nbsp; info_column.<span class="me1">setDataCell</span><span class="br0">&#40;</span>PostCell.<span class="me1">new</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; scroll &lt;&lt; <span class="re1">@table</span> = table_view<span class="br0">&#40;</span><span class="re3">:columns</span> =&gt; <span class="br0">&#91;</span>pr_column, info_column<span class="br0">&#93;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re3">:data</span> =&gt; <span class="br0">&#91;</span><span class="br0">&#93;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |table|<br />
&nbsp; &nbsp; table.<span class="me1">setRowHeight</span><span class="br0">&#40;</span><span class="re2">PostCell::ROW_HEIGHT</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; table.<span class="me1">setUsesAlternatingRowBackgroundColors</span><span class="br0">&#40;</span><span class="kw2">true</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; table.<span class="me1">setGridStyleMask</span><span class="br0">&#40;</span>NSTableViewSolidHorizontalGridLineMask<span class="br0">&#41;</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; table.<span class="me1">setDelegate</span><span class="br0">&#40;</span><span class="kw2">self</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; table.<span class="me1">setDoubleAction</span><span class="br0">&#40;</span><span class="re3">:table_clicked</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>I&#8217;ve made one layout modification which was to add an extra column to our table to display the PostRank for each post. The PostRank column and post data columns use custom cell formatters so we can get the layout we want. I also wanted to constrain the PostRank column to a set size, 34 pixels seemed to look good.  In order to use my custom formatters I use <code>setDataCell</code> on the <code>column</code> objects. The parameter to <code>setDataCell</code> is an instantiated instance of our formatter class. I have two classes, <code>PostRankCell</code> and <code>PostCell</code> for the PostRank and post columns respectively.</p>
<p>Along with the column changes we&#8217;re also setting a default height on the table rows as defined in the <code>PostCell</code> class.  We set the Postie instance as the delegate for the table so we can receive the <code>tableView(table, heightOfRow:row)</code> callback (thanks <a href="http://www.twitter.com/macruby">@macruby</a> for the pointer).  The last addition to the table is to hookup the double click action with <code>@table.setDoubleAction(:table_clicked)</code>. The parameter is the name of the method that will be called, as a symbol.</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw1">def</span> table_clicked<br />
&nbsp; url = NSURL.<span class="me1">URLWithString</span><span class="br0">&#40;</span>@table.<span class="me1">dataSource</span>.<span class="me1">data</span><span class="br0">&#91;</span>@table.<span class="me1">clickedRow</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="re3">:data</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="re3">:link</span><span class="br0">&#93;</span><span class="br0">&#41;</span><br />
&nbsp; NSWorkspace.<span class="me1">sharedWorkspace</span>.<span class="me1">openURL</span><span class="br0">&#40;</span>url<span class="br0">&#41;</span><br />
<span class="kw1">end</span></div>
<p>When a table row is double clicked we want to open the corresponding posts page in the users brower. We&#8217;ll be storing the link in the data attached to our table. We can get the clicked row with <code>@table.clickedRow</code> and access the link with <code>@table.dataSource.data[@table.clickedRow][:data][:link]</code>. We then create a <code>NSURL</code> with this string. The created URL object is passed to <code>NSWorkspace.sharedWorkspace.openURL(url)</code> causing the page to open in the browser.</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw1">def</span> tableView<span class="br0">&#40;</span>table, heightOfRow:row<span class="br0">&#41;</span><br />
&nbsp; metrics = <span class="re1">@table</span>.<span class="me1">dataSource</span>.<span class="me1">data</span><span class="br0">&#91;</span>row<span class="br0">&#93;</span><span class="br0">&#91;</span><span class="re3">:data</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="re3">:metrics</span><span class="br0">&#93;</span>.<span class="me1">keys</span>.<span class="me1">length</span><br />
&nbsp; &nbsp; <br />
&nbsp; num_rows = <span class="br0">&#40;</span>metrics / <span class="re2">PostCell::NUM_METRICS_PER_ROW</span><span class="br0">&#41;</span> + <span class="nu0">1</span><br />
&nbsp; num_rows -= <span class="nu0">1</span> <span class="kw1">if</span> metrics &gt; <span class="nu0">0</span> &amp;&amp; <span class="br0">&#40;</span>metrics % <span class="re2">PostCell::NUM_METRICS_PER_ROW</span><span class="br0">&#41;</span> == <span class="nu0">0</span><br />
&nbsp; num_rows = <span class="nu0">0</span> <span class="kw1">if</span> metrics == <span class="nu0">0</span></p>
<p>&nbsp; num_rows * <span class="re2">PostCell::ROW_HEIGHT</span> + <span class="re2">PostCell::ROW_HEIGHT</span> &nbsp;<span class="co1"># 2nd row height for the title</span><br />
<span class="kw1">end</span></div>
<p><a href="http://everburning.com/wp-content/uploads/2009/05/picture-2.png"><img src="http://everburning.com/wp-content/uploads/2009/05/picture-2-300x51.png" alt="picture-2" title="picture-2" width="300" height="51" class="alignright size-medium wp-image-516" /></a>The <code>tableView(table, heightOfRow:row)</code> callback is triggered each time the table is rendered out to determine the height for a given row. In the case of Postie we&#8217;re going to display the post title on the first line and the metrics on subsequent lines. I&#8217;ve constrained the metrics to allow a maximum of 6 metrics on each line.  All of the metrics are stored in a <code>:metrics</code> key of the data attached to our table. Both the number of metrics and a row and the row height are constants stored in the <code>PostCell</code> class.</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw1">def</span> load_feed<br />
&nbsp; <span class="re1">@table</span>.<span class="me1">data</span> = <span class="br0">&#91;</span><span class="br0">&#93;</span></p>
<p>&nbsp; str = <span class="re1">@feed_field</span>.<span class="me1">stringValue</span><br />
&nbsp; <span class="kw1">unless</span> str.<span class="kw2">nil</span>? || str =~ /^s*$/<br />
&nbsp; &nbsp; fetch_feed<span class="br0">&#40;</span>str<span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p><code>load_feed</code> has been updated to empty our tables data by assigning a new array and, assuming we&#8217;ve received a valid feed, call <code>fetch_feed</code> to start retrieving the feed data.</p>
<p>There are a few different ways we could go about querying the data from the PostRank APIs. We could use <em>Net::HTTP</em>, <em>Curb</em>, <em>NSXMLDocument</em> or, as I&#8217;ve done, <em>NSURLConnection</em>. The reason I used <code>NSURLConnection</code> is so that I can have the requests run asynchronously. As well, the UI won&#8217;t block as we&#8217;re off fetching the data. A handy feature when you want things to remain responsive.</p>
<p>Let&#8217;s take a quick look at the wrapper class I&#8217;ve put around <code>NSURLConnection</code> before looking at <code>fetch_feed</code>.  The reason I created a wrapper is that <code>NSURLConnection</code>, because it&#8217;s asynchronous, works through callbacks. I&#8217;m going to need to query three different PostRank APIs and take different actions for each query. Instead of trying to do some magic in the callbacks, I&#8217;ve created a wrapper class that accepts a block. The block is called when the data has been successfully retrieved. (The wrapper just spits out an error if something goes wrong, the block is never called.) For example, to download the Google.ca homepage we could do:</p>
<div class="dean_ch" style="white-space: wrap;">
DataRequest.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&quot;http://google.ca&quot;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |data|<br />
&nbsp; NSLog <span class="st0">&quot;Data: #{data}&quot;</span><br />
<span class="kw1">end</span></div>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw1">class</span> DataRequest<br />
&nbsp; <span class="kw1">def</span> get<span class="br0">&#40;</span>url, &amp;blk<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="re1">@buf</span> = NSMutableData.<span class="me1">new</span><br />
&nbsp; &nbsp; <span class="re1">@blk</span> = blk<br />
&nbsp; &nbsp; req = NSURLRequest.<span class="me1">requestWithURL</span><span class="br0">&#40;</span>NSURL.<span class="me1">URLWithString</span><span class="br0">&#40;</span>url<span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; NSURLConnection.<span class="me1">alloc</span>.<span class="me1">initWithRequest</span><span class="br0">&#40;</span>req, delegate:<span class="kw2">self</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">end</span><br />
&nbsp; <br />
&nbsp; <span class="kw1">def</span> connection<span class="br0">&#40;</span>conn, didReceiveResponse:resp<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="re1">@buf</span>.<span class="me1">setLength</span><span class="br0">&#40;</span><span class="nu0">0</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">end</span><br />
&nbsp; <br />
&nbsp; <span class="kw1">def</span> connection<span class="br0">&#40;</span>conn, didReceiveData:data<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="re1">@buf</span>.<span class="me1">appendData</span><span class="br0">&#40;</span>data<span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; <span class="kw1">def</span> connection<span class="br0">&#40;</span>conn, didFailWithError:err<span class="br0">&#41;</span><br />
&nbsp; &nbsp; NSLog <span class="st0">&quot;Request failed&quot;</span><br />
&nbsp; <span class="kw1">end</span><br />
&nbsp; <br />
&nbsp; <span class="kw1">def</span> connectionDidFinishLoading<span class="br0">&#40;</span>conn<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="re1">@blk</span>.<span class="me1">call</span><span class="br0">&#40;</span>NSString.<span class="me1">alloc</span>.<span class="me1">initWithData</span> <span class="re1">@buf</span>, encoding:NSUTF8StringEncoding<span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>As <code>NSURLConnection</code> executes it returns data to the application. We&#8217;re storing this data in a <code>NSMutableData</code> object called <code>@buf</code>.  The callbacks we&#8217;re interested in are:</p>
<dl>
<dt><code>connection(conn, didReceiveResponse:resp)</code></dt>
<dd>Called when we receive a response from the server. This can be called multiple times if there are any server redirects in place. We reset the length of our data buffer each time this callback is called.</dd>
<dt><code>connection(conn, didReceiveData:data)</code></dt>
<dd>Called each time data is received from the server. This can be called multiple times and we just append the data to our buffer each time.</dd>
<dt><code>connection(conn, didFailWithError:err)</code></dt>
<dd>Called if there is an error retrieving the data. We, basically, just ignore the error. You&#8217;d probably want to do something sane in your application.</dd>
<dt><code>connectionDidFinishLoading(conn)</code></dt>
<dd>Called when all of the data has been retrieved from the remote server. Since we&#8217;re not working with binary data I convert the <code>NSMutableData</code> to an <code>NSString</code> using the <code>initWithData:encoding</code> method. Note the use of <code>alloc</code> on the <code>NSString</code>. If you try to use <code>new</code> you&#8217;ll, like me, spend the next 30 minutes trying to figure out why your application is crashing.</dd>
</dl>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw1">def</span> fetch_feed<span class="br0">&#40;</span>url<span class="br0">&#41;</span><br />
&nbsp; DataRequest.<span class="me1">new</span>.<span class="me1">get</span><span class="br0">&#40;</span><span class="st0">&quot;#{POSTRANK_URL_BASE}/feed/info?id=#{url}&amp;#{APPKEY}&quot;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |data|<br />
&nbsp; &nbsp; feed_info = JSON.<span class="me1">parse</span><span class="br0">&#40;</span>data<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">unless</span> feed_info.<span class="me1">has_key</span>?<span class="br0">&#40;</span><span class="st0">&#8216;error&#8217;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; DataRequest.<span class="me1">new</span>.<span class="me1">get</span><span class="br0">&#40;</span><span class="st0">&quot;#{POSTRANK_URL_BASE}/feed/#{feed_info['id']}?#{APPKEY}&quot;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |data|<br />
&nbsp; &nbsp; &nbsp; &nbsp; feed = JSON.<span class="me1">parse</span><span class="br0">&#40;</span>data<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; feed<span class="br0">&#91;</span><span class="st0">&#8216;items&#8217;</span><span class="br0">&#93;</span>.<span class="me1">each</span> <span class="kw1">do</span> |item|<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; post_data = <span class="br0">&#123;</span>:title =&gt; item<span class="br0">&#91;</span><span class="st0">&#8216;title&#8217;</span><span class="br0">&#93;</span>, <span class="re3">:link</span> =&gt; item<span class="br0">&#91;</span><span class="st0">&#8216;original_link&#8217;</span><span class="br0">&#93;</span>, <span class="re3">:metrics</span> =&gt; <span class="br0">&#123;</span><span class="br0">&#125;</span><span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">@table</span>.<span class="me1">dataSource</span>.<span class="me1">data</span> &lt;&lt; <span class="br0">&#123;</span>:data =&gt; post_data,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="re3">:postrank</span> =&gt; <span class="br0">&#123;</span>:value =&gt; item<span class="br0">&#91;</span><span class="st0">&#8216;postrank&#8217;</span><span class="br0">&#93;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="re3">:color</span> =&gt; item<span class="br0">&#91;</span><span class="st0">&#8216;postrank_color&#8217;</span><span class="br0">&#93;</span><span class="br0">&#125;</span><span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; DataRequest.<span class="me1">new</span>.<span class="me1">get</span><span class="br0">&#40;</span><span class="st0">&quot;#{POSTRANK_URL_BASE}/entry/#{item['id']}/metrics?#{APPKEY}&quot;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |data|<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; metrics = JSON.<span class="me1">parse</span><span class="br0">&#40;</span>data<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; metrics<span class="br0">&#91;</span>item<span class="br0">&#91;</span><span class="st0">&#8216;id&#8217;</span><span class="br0">&#93;</span><span class="br0">&#93;</span>.<span class="me1">each_pair</span> <span class="kw1">do</span> |key, value|<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">next</span> <span class="kw1">if</span> key == <span class="st0">&#8216;friendfeed_comm&#8217;</span> || key == <span class="st0">&#8216;friendfeed_like&#8217;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; post_data<span class="br0">&#91;</span><span class="re3">:metrics</span><span class="br0">&#93;</span><span class="br0">&#91;</span>key.<span class="me1">to_sym</span><span class="br0">&#93;</span> = value<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">@table</span>.<span class="me1">reloadData</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>Most of the code in <code>fetch_feed</code> should probably be refactored into <code>Feed</code>, <code>Post</code> and <code>Metrics</code> classes, but, for the tutorial, I&#8217;m not going to bother.</p>
<p>You can see we&#8217;re doing three successive data requests. The first is to the <a href="http://www.postrank.com/developers/api#feed_info">Feed Info</a> API. From this call we can retrieve the <em>feed_hash</em> which allows us to uniquely identify our feed in the PostRank system. By default all the PostRank API calls will return the data in JSON format. We could, and I did this initially use <code>format=xml</code> and <code>NSXMLDocument.initWithContentsOfURL</code> to pullback and parse all the data (the problem being, metrics only responds in JSON).</p>
<p>Now, as long as the query to Feed Info didn&#8217;t return an error we use the <code>id</code> to access the <a href="http://www.postrank.com/developers/api#feed">Feed</a> API. The Feed API will return the posts in the given feed. The default is to return 10 posts which works for our purposes. We could, if we wished, add a button to retrieve the next set of posts from the API using the <code>start</code> and <code>num</code> parameters.</p>
<p>With the feed in hand we&#8217;re interested in the <code>items</code> attribute. This is an array of the posts in the feed. Using these items we can start to create our table data. For each item we&#8217;re going to create two hashes of data, one for each column of our table.  The PostRank column will contain the <code>:postrank</code> and <code>:postrank_color</code> and the post column will contain the <code>:title</code>, <code>:link</code> and <code>:metrics</code>.</p>
<p>Finally, we query the metrics API for each post to retrieve the metrics data. The metrics API will provide us with a hash with a single key based on our post&#8217;s ID. Under this key we receive a hash containing the metric source names and the values. We&#8217;re skipping <code>friendfeed_comm</code> and <code>friendfeed_like</code> as they&#8217;ve been renamed to <code>ff_comments</code> and <code>ff_links</code> and only remain as legacy.</p>
<p>Once we&#8217;ve got all the metrics source information packed into our <code>post_data</code> hash we call <code>@table.reloadData</code> so everything gets rendered properly.</p>
<p>Since the calls to <code>DataRequest</code> are asynchronous, we have to call reload  inside the metrics block. This guarantees the table will be reloaded after we&#8217;ve received our data.</p>
<p>With that out of the way, we&#8217;re onto our formatting cells. In order to get our custom table display we need to subclass <code>NSCell</code> and override the <code>drawInteriorWithFrame(frame, inView:view)</code> where we can layout our cell as desired.</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw1">class</span> PostRankCell &lt; NSCell<br />
&nbsp; <span class="kw1">def</span> drawInteriorWithFrame<span class="br0">&#40;</span>frame, inView:view<span class="br0">&#41;</span><br />
&nbsp; &nbsp; m = objectValue<span class="br0">&#91;</span><span class="re3">:color</span><span class="br0">&#93;</span>.<span class="me1">match</span><span class="br0">&#40;</span>/<span class="co1">#(..)(..)(..)/)</span><br />
&nbsp; &nbsp; NSColor.<span class="me1">colorWithCalibratedRed</span><span class="br0">&#40;</span>m<span class="br0">&#91;</span><span class="nu0">1</span><span class="br0">&#93;</span>.<span class="me1">hex</span>/ <span class="nu0">255.0</span>, green:m<span class="br0">&#91;</span><span class="nu0">2</span><span class="br0">&#93;</span>.<span class="me1">hex</span>/<span class="nu0">255.0</span>, blue:m<span class="br0">&#91;</span><span class="nu0">3</span><span class="br0">&#93;</span>.<span class="me1">hex</span>/<span class="nu0">255.0</span>, alpha:<span class="nu0">100</span><span class="br0">&#41;</span>.<span class="me1">set</span><br />
&nbsp; &nbsp; NSRectFill<span class="br0">&#40;</span>frame<span class="br0">&#41;</span><br />
&nbsp; <br />
&nbsp; &nbsp; rank_frame = NSMakeRect<span class="br0">&#40;</span>frame.<span class="me1">origin</span>.<span class="me1">x</span> + <span class="br0">&#40;</span>frame.<span class="me1">size</span>.<span class="me1">width</span> / <span class="nu0">2</span><span class="br0">&#41;</span> &#8211; <span class="nu0">12</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; frame.<span class="me1">origin</span>.<span class="me1">y</span> + <span class="br0">&#40;</span>frame.<span class="me1">size</span>.<span class="me1">height</span> / <span class="nu0">2</span><span class="br0">&#41;</span> &#8211; <span class="nu0">8</span>, frame.<span class="me1">size</span>.<span class="me1">width</span>, <span class="nu0">17</span><span class="br0">&#41;</span><br />
&nbsp; <br />
&nbsp; &nbsp; objectValue<span class="br0">&#91;</span><span class="re3">:value</span><span class="br0">&#93;</span>.<span class="me1">to_s</span>.<span class="me1">drawInRect</span><span class="br0">&#40;</span>rank_frame, withAttributes:<span class="kw2">nil</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>The <code>PostRankCell</code> is pretty simple. We parse the provided PostRank colour, which comes as <code>#ffffff</code> into separate red, green and blue values. These values are passed to <code>NSColor.colorWithCalibratedRed(red, green:green, blue:blue, alpha:alpha)</code> in order to create a <code>NSColor</code> object representing our PostRank colour. We need to divided each value by <code>255</code> as <code>colorWithCalibratedRed:green:blue:alpha:</code> expects a value between <code>0.0</code> and <code>1.0</code>.  Once we&#8217;ve got our colour we call <code>set</code> to make that colour active and, using <code>NSRectFill</code> we fill then entire frame with the provided <code>postrank_color</code>.</p>
<p>I&#8217;m, kinda, sorta, centering the PostRank values in the column so we need to create a <code>NSRect</code> to specify the box where we want to draw the numbers. This is done by calling <code>NSMakeRect</code> and providing the x, y, width and height values for the rectange. Once we&#8217;ve got our <code>NSRect</code> in hand we call <code>drawInRect(rank_frame, withAttributes:nil)</code> on the PostRank value. This will draw the string in the rectangle specified. We could set extra attributes on the string but, I don&#8217;t need any, so I just leave it <code>nil</code>.</p>
<p>You&#8217;ll notice I&#8217;m using <code>objectValue</code> in a few places. <code>objectValue</code> is a <code>NSCell</code> method that will return the value assigned to this cell as retrieved based on the column key from our table data source.</p>
</pre>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw1">class</span> PostCell &lt; NSCell<br />
&nbsp; ROW_HEIGHT = <span class="nu0">20</span><br />
&nbsp; NUM_METRICS_PER_ROW = <span class="nu0">6</span><br />
&nbsp; SPRITE_SIZE = <span class="nu0">16</span><br />
&nbsp; <br />
&nbsp; @@sprites = <span class="br0">&#123;</span>:default =&gt; <span class="nu0">0</span>, <span class="re3">:blogines</span> =&gt; <span class="nu0">16</span>, <span class="re3">:reddit</span> =&gt; <span class="nu0">32</span>, <span class="re3">:reddit_votes</span> =&gt; <span class="nu0">32</span>,<br />
&nbsp; &nbsp; &nbsp; <span class="re3">:technorati</span> =&gt; <span class="nu0">48</span>, <span class="re3">:magnolia</span> =&gt; <span class="nu0">64</span>, <span class="re3">:digg</span> =&gt; <span class="nu0">80</span>, <span class="re3">:twitter</span> =&gt; <span class="nu0">96</span>, <span class="re3">:comments</span> =&gt; <span class="nu0">112</span>,<br />
&nbsp; &nbsp; &nbsp; <span class="re3">:icerocket</span> =&gt; <span class="nu0">128</span>, <span class="re3">:delicious</span> =&gt; <span class="nu0">144</span>, <span class="re3">:google</span> =&gt; <span class="nu0">160</span>, <span class="re3">:pownce</span> =&gt; <span class="nu0">176</span>, <span class="re3">:views</span> =&gt; <span class="nu0">192</span>,<br />
&nbsp; &nbsp; &nbsp; <span class="re3">:bookmarks</span> =&gt; <span class="nu0">208</span>, <span class="re3">:clicks</span> =&gt; <span class="nu0">224</span>, <span class="re3">:jaiku</span> =&gt; <span class="nu0">240</span>, <span class="re3">:digg_comments</span> =&gt; <span class="nu0">256</span>,<br />
&nbsp; &nbsp; &nbsp; <span class="re3">:diigo</span> =&gt; <span class="nu0">272</span>, <span class="re3">:feecle</span> =&gt; <span class="nu0">288</span>, <span class="re3">:brightkite</span> =&gt; <span class="nu0">304</span>, <span class="re3">:furl</span> =&gt; <span class="nu0">320</span>, <span class="re3">:twitarmy</span> =&gt; <span class="nu0">336</span>,<br />
&nbsp; &nbsp; &nbsp; <span class="re3">:identica</span> =&gt; <span class="nu0">352</span>, <span class="re3">:ff_likes</span> =&gt; <span class="nu0">368</span>, <span class="re3">:blip</span> =&gt; <span class="nu0">384</span>, <span class="re3">:tumblr</span> =&gt; <span class="nu0">400</span>,<br />
&nbsp; &nbsp; &nbsp; <span class="re3">:reddit_comments</span> =&gt; <span class="nu0">416</span>, <span class="re3">:ff_comments</span> =&gt; <span class="nu0">432</span><span class="br0">&#125;</span><br />
&nbsp; @@sprite = <span class="kw2">nil</span></p>
<p>&nbsp; <span class="kw1">def</span> drawInteriorWithFrame<span class="br0">&#40;</span>frame, inView:view<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">unless</span> @@sprite<br />
&nbsp; &nbsp; &nbsp; bundle = NSBundle.<span class="me1">mainBundle</span><br />
&nbsp; &nbsp; &nbsp; @@sprite = NSImage.<span class="me1">alloc</span>.<span class="me1">initWithContentsOfFile</span><span class="br0">&#40;</span>bundle.<span class="me1">pathForResource</span><span class="br0">&#40;</span><span class="st0">&quot;sprites&quot;</span>, ofType:<span class="st0">&quot;png&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; @@sprite.<span class="me1">setFlipped</span><span class="br0">&#40;</span><span class="kw2">true</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">end</span></p>
<p>&nbsp; &nbsp; title_rect = NSMakeRect<span class="br0">&#40;</span>frame.<span class="me1">origin</span>.<span class="me1">x</span>, frame.<span class="me1">origin</span>.<span class="me1">y</span> + <span class="nu0">1</span>, frame.<span class="me1">size</span>.<span class="me1">width</span>, <span class="nu0">17</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; metrics_rect = NSMakeRect<span class="br0">&#40;</span>frame.<span class="me1">origin</span>.<span class="me1">x</span>, frame.<span class="me1">origin</span>.<span class="me1">y</span> + ROW_HEIGHT, frame.<span class="me1">size</span>.<span class="me1">width</span>, <span class="nu0">17</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; title_str = <span class="st0">&quot;#{objectValue[:title]}&quot;</span><br />
&nbsp; &nbsp; title_str.<span class="me1">drawInRect</span><span class="br0">&#40;</span>title_rect, withAttributes:<span class="kw2">nil</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; count = <span class="nu0">0</span><br />
&nbsp; &nbsp; orig_x_orign = metrics_rect.<span class="me1">origin</span>.<span class="me1">x</span><br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; objectValue<span class="br0">&#91;</span><span class="re3">:metrics</span><span class="br0">&#93;</span>.<span class="me1">each_pair</span> <span class="kw1">do</span> |key, value|<br />
&nbsp; &nbsp; &nbsp; s = metrics_rect.<span class="me1">size</span>.<span class="me1">width</span><br />
&nbsp; &nbsp; &nbsp; metrics_rect.<span class="me1">size</span>.<span class="me1">width</span> = SPRITE_SIZE<br />
&nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; y = <span class="kw1">if</span> @@sprites.<span class="me1">has_key</span>?<span class="br0">&#40;</span>key<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; @@sprites<span class="br0">&#91;</span>key.<span class="me1">to_sym</span><span class="br0">&#93;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">else</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="nu0">0</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; r = NSMakeRect<span class="br0">&#40;</span><span class="nu0">0</span>, y, SPRITE_SIZE, SPRITE_SIZE<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; @@sprite.<span class="me1">drawInRect</span><span class="br0">&#40;</span>metrics_rect, fromRect:r,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; operation:NSCompositeSourceOver, fraction:<span class="nu0">1.0</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; metrics_rect.<span class="me1">origin</span>.<span class="me1">x</span> += <span class="nu0">21</span><br />
&nbsp; &nbsp; &nbsp; metrics_rect.<span class="me1">size</span>.<span class="me1">width</span> = s - <span class="nu0">21</span><br />
&nbsp; &nbsp; &nbsp; &nbsp;<br />
&nbsp; &nbsp; &nbsp; <span class="st0">&quot;#{value}&quot;</span>.<span class="me1">drawInRect</span><span class="br0">&#40;</span>metrics_rect, withAttributes:<span class="kw2">nil</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; s = <span class="st0">&quot;#{value}&quot;</span>.<span class="me1">sizeWithAttributes</span><span class="br0">&#40;</span><span class="kw2">nil</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; metrics_rect.<span class="me1">origin</span>.<span class="me1">x</span> += s.<span class="me1">width</span> + <span class="nu0">15</span><br />
&nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; count += <span class="nu0">1</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">if</span> count == NUM_METRICS_PER_ROW<br />
&nbsp; &nbsp; &nbsp; &nbsp; metrics_rect.<span class="me1">origin</span>.<span class="me1">y</span> += ROW_HEIGHT<br />
&nbsp; &nbsp; &nbsp; &nbsp; metrics_rect.<span class="me1">origin</span>.<span class="me1">x</span> = orig_x_orign<br />
&nbsp; &nbsp; &nbsp; &nbsp; count = <span class="nu0">0</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p><code>PostRankCell</code> is similar to <code>PostCell</code> in that we're basically creating bounding rectangles and drawing into them. The extra little bit we're doing here is loading up a <code>NSImage</code> which is our sprite set and using that to pull out all of the individual service icons. <code>NSImage</code> makes it easy to work with our sprite image by providing <code>drawInRect(rect, fromRect:from_rect, operation:op, fraction:val)</code>. <code>drawInRect:fromRect:operation:fraction:</code> draws into the rectangle defined by <code>rect</code> retrieving the pixels in your <code>NSImage</code> that are inside the <code>from_rect</code>. I'm using <code>NSCompositeSourceOver</code> because some of my images are semi-transparent. The fraction parameter is a the alpha setting for the image.</p>
<p>With that, well, you'll probably need to download the source code to see it all in one file, you should have a working application that will query PostRank for a feed and display the posts and metrics for the feed.</p>
<p>As for the next installment. I've got a few things I still want to do, including: tabbing between widgets, submitting the text field on return, a progress indicator as the feed information is being retrieved and adding a tabbed interface to allow showing feed, post and top post information. I'm not sure which of these things I'll tackle next. There are a few helper methods I want to try to add to HotCocoa from this article that I'll probably do first.  So, until next time.</p>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-iii%2F&amp;title=Heating+up+with+HotCocoa+Part+III" title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-iii%2F&amp;title=Heating+up+with+HotCocoa+Part+III" title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-iii%2F&amp;title=Heating+up+with+HotCocoa+Part+III" title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-iii%2F&amp;title=Heating+up+with+HotCocoa+Part+III" title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-iii%2F&amp;title=Heating+up+with+HotCocoa+Part+III', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-iii%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-iii%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-iii%2F&amp;title=Heating+up+with+HotCocoa+Part+III" title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-iii%2F&amp;title=Heating+up+with+HotCocoa+Part+III" title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></description>
			<content:encoded><![CDATA[<p><img src="http://everburning.com/wp-content/uploads/2009/05/img_5183-300x200.jpg" alt="img_5183" title="img_5183" width="300" height="200" class="alignleft size-medium wp-image-514" />In the words of Homer Simpsons, &#8220;forward not backwards, upwards not forwards and always twirling, twirling, twirling towards freedom&#8221;. With that, we&#8217;re back for part III of my <a href="http://www.macruby.org/trac/wiki/HotCocoa">HotCocoa</a> tutorial. For those of you just joining the party, you&#8217;ll probably want to take a look at <a href="http://everburning.com/news/heating-up-with-hotcocoa-part-i/">part I</a> and <a href="http://everburning.com/news/heating-up-with-hotcocoa-part-ii/">part II</a>.</p>
<p>If you&#8217;d like to grab a copy of the code, it&#8217;s getting a bit big to post all of it in one go, you can grab the tar ball <a href='http://everburning.com/wp-content/uploads/2009/05/postietar.gz'>here</a>. The file contains all of the HotCocoa files along with the sprite image that I&#8217;ve shamelessly niced from <a href="http://postrank.com">PostRank</a>.</p>
<p><a href="http://everburning.com/wp-content/uploads/2009/05/picture-12.png"><img src="http://everburning.com/wp-content/uploads/2009/05/picture-12-300x234.png" alt="picture-12" title="picture-12" width="300" height="234" class="alignright size-medium wp-image-515" /></a>When we last left off we&#8217;d created the basic layout for our application with our button and table views setup. With this installment, we&#8217;re going to go a step further and get a fully working application. We&#8217;re going to use the feed entered into the text field to query PostRank to get the current posts in the feed along with there PostRank and metric information. I&#8217;m going to be skipping over sections of code that haven&#8217;t changed from part I or part II for the sake of brevity. </p>
<p>Note, we&#8217;re going to be using JSON when working with the PostRank APIs. There is a bug in MacRuby, as of revision 1594, where JSON.parse would crash MacRuby. You&#8217;ll need to apply the patch attached to <a href="https://www.macruby.org/trac/ticket/257">ticket 257</a> in order run this application.</p>
<p>OK, let&#8217;s go.</p>
<div class="dean_ch" style="white-space: wrap;">
POSTRANK_URL_BASE = <span class="st0">&quot;http://api.postrank.com/v2&quot;</span><br />
APPKEY = <span class="st0">&quot;appkey=Postie&quot;</span></div>
<p>All of our calls to PostRank will use the same URL prefix and we&#8217;ll need to provide our appkey. I&#8217;ve placed both of these into constants.</p>
<div class="dean_ch" style="white-space: wrap;">
vert &lt;&lt; scroll_view<span class="br0">&#40;</span><span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:expand =&gt; <span class="br0">&#91;</span><span class="re3">:width</span>, <span class="re3">:height</span><span class="br0">&#93;</span><span class="br0">&#125;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |scroll|<br />
&nbsp; scroll.<span class="me1">setAutohidesScrollers</span><span class="br0">&#40;</span><span class="kw2">true</span><span class="br0">&#41;</span></p>
<p>&nbsp; pr_column = column<span class="br0">&#40;</span><span class="re3">:id</span> =&gt; <span class="re3">:postrank</span>, <span class="re3">:title</span> =&gt; <span class="st0">&#8221;</span><span class="br0">&#41;</span><br />
&nbsp; pr_column.<span class="me1">setDataCell</span><span class="br0">&#40;</span>PostRankCell.<span class="me1">new</span><span class="br0">&#41;</span><br />
&nbsp; pr_column.<span class="me1">setMaxWidth</span><span class="br0">&#40;</span><span class="nu0">34</span><span class="br0">&#41;</span><br />
&nbsp; pr_column.<span class="me1">setMinWidth</span><span class="br0">&#40;</span><span class="nu0">34</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; info_column = column<span class="br0">&#40;</span><span class="re3">:id</span> =&gt; <span class="re3">:data</span>, <span class="re3">:title</span> =&gt; <span class="st0">&#8221;</span><span class="br0">&#41;</span><br />
&nbsp; info_column.<span class="me1">setDataCell</span><span class="br0">&#40;</span>PostCell.<span class="me1">new</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; scroll &lt;&lt; <span class="re1">@table</span> = table_view<span class="br0">&#40;</span><span class="re3">:columns</span> =&gt; <span class="br0">&#91;</span>pr_column, info_column<span class="br0">&#93;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re3">:data</span> =&gt; <span class="br0">&#91;</span><span class="br0">&#93;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |table|<br />
&nbsp; &nbsp; table.<span class="me1">setRowHeight</span><span class="br0">&#40;</span><span class="re2">PostCell::ROW_HEIGHT</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; table.<span class="me1">setUsesAlternatingRowBackgroundColors</span><span class="br0">&#40;</span><span class="kw2">true</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; table.<span class="me1">setGridStyleMask</span><span class="br0">&#40;</span>NSTableViewSolidHorizontalGridLineMask<span class="br0">&#41;</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; table.<span class="me1">setDelegate</span><span class="br0">&#40;</span><span class="kw2">self</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; table.<span class="me1">setDoubleAction</span><span class="br0">&#40;</span><span class="re3">:table_clicked</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>I&#8217;ve made one layout modification which was to add an extra column to our table to display the PostRank for each post. The PostRank column and post data columns use custom cell formatters so we can get the layout we want. I also wanted to constrain the PostRank column to a set size, 34 pixels seemed to look good.  In order to use my custom formatters I use <code>setDataCell</code> on the <code>column</code> objects. The parameter to <code>setDataCell</code> is an instantiated instance of our formatter class. I have two classes, <code>PostRankCell</code> and <code>PostCell</code> for the PostRank and post columns respectively.</p>
<p>Along with the column changes we&#8217;re also setting a default height on the table rows as defined in the <code>PostCell</code> class.  We set the Postie instance as the delegate for the table so we can receive the <code>tableView(table, heightOfRow:row)</code> callback (thanks <a href="http://www.twitter.com/macruby">@macruby</a> for the pointer).  The last addition to the table is to hookup the double click action with <code>@table.setDoubleAction(:table_clicked)</code>. The parameter is the name of the method that will be called, as a symbol.</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw1">def</span> table_clicked<br />
&nbsp; url = NSURL.<span class="me1">URLWithString</span><span class="br0">&#40;</span>@table.<span class="me1">dataSource</span>.<span class="me1">data</span><span class="br0">&#91;</span>@table.<span class="me1">clickedRow</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="re3">:data</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="re3">:link</span><span class="br0">&#93;</span><span class="br0">&#41;</span><br />
&nbsp; NSWorkspace.<span class="me1">sharedWorkspace</span>.<span class="me1">openURL</span><span class="br0">&#40;</span>url<span class="br0">&#41;</span><br />
<span class="kw1">end</span></div>
<p>When a table row is double clicked we want to open the corresponding posts page in the users brower. We&#8217;ll be storing the link in the data attached to our table. We can get the clicked row with <code>@table.clickedRow</code> and access the link with <code>@table.dataSource.data[@table.clickedRow][:data][:link]</code>. We then create a <code>NSURL</code> with this string. The created URL object is passed to <code>NSWorkspace.sharedWorkspace.openURL(url)</code> causing the page to open in the browser.</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw1">def</span> tableView<span class="br0">&#40;</span>table, heightOfRow:row<span class="br0">&#41;</span><br />
&nbsp; metrics = <span class="re1">@table</span>.<span class="me1">dataSource</span>.<span class="me1">data</span><span class="br0">&#91;</span>row<span class="br0">&#93;</span><span class="br0">&#91;</span><span class="re3">:data</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="re3">:metrics</span><span class="br0">&#93;</span>.<span class="me1">keys</span>.<span class="me1">length</span><br />
&nbsp; &nbsp; <br />
&nbsp; num_rows = <span class="br0">&#40;</span>metrics / <span class="re2">PostCell::NUM_METRICS_PER_ROW</span><span class="br0">&#41;</span> + <span class="nu0">1</span><br />
&nbsp; num_rows -= <span class="nu0">1</span> <span class="kw1">if</span> metrics &gt; <span class="nu0">0</span> &amp;&amp; <span class="br0">&#40;</span>metrics % <span class="re2">PostCell::NUM_METRICS_PER_ROW</span><span class="br0">&#41;</span> == <span class="nu0">0</span><br />
&nbsp; num_rows = <span class="nu0">0</span> <span class="kw1">if</span> metrics == <span class="nu0">0</span></p>
<p>&nbsp; num_rows * <span class="re2">PostCell::ROW_HEIGHT</span> + <span class="re2">PostCell::ROW_HEIGHT</span> &nbsp;<span class="co1"># 2nd row height for the title</span><br />
<span class="kw1">end</span></div>
<p><a href="http://everburning.com/wp-content/uploads/2009/05/picture-2.png"><img src="http://everburning.com/wp-content/uploads/2009/05/picture-2-300x51.png" alt="picture-2" title="picture-2" width="300" height="51" class="alignright size-medium wp-image-516" /></a>The <code>tableView(table, heightOfRow:row)</code> callback is triggered each time the table is rendered out to determine the height for a given row. In the case of Postie we&#8217;re going to display the post title on the first line and the metrics on subsequent lines. I&#8217;ve constrained the metrics to allow a maximum of 6 metrics on each line.  All of the metrics are stored in a <code>:metrics</code> key of the data attached to our table. Both the number of metrics and a row and the row height are constants stored in the <code>PostCell</code> class.</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw1">def</span> load_feed<br />
&nbsp; <span class="re1">@table</span>.<span class="me1">data</span> = <span class="br0">&#91;</span><span class="br0">&#93;</span></p>
<p>&nbsp; str = <span class="re1">@feed_field</span>.<span class="me1">stringValue</span><br />
&nbsp; <span class="kw1">unless</span> str.<span class="kw2">nil</span>? || str =~ /^s*$/<br />
&nbsp; &nbsp; fetch_feed<span class="br0">&#40;</span>str<span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p><code>load_feed</code> has been updated to empty our tables data by assigning a new array and, assuming we&#8217;ve received a valid feed, call <code>fetch_feed</code> to start retrieving the feed data.</p>
<p>There are a few different ways we could go about querying the data from the PostRank APIs. We could use <em>Net::HTTP</em>, <em>Curb</em>, <em>NSXMLDocument</em> or, as I&#8217;ve done, <em>NSURLConnection</em>. The reason I used <code>NSURLConnection</code> is so that I can have the requests run asynchronously. As well, the UI won&#8217;t block as we&#8217;re off fetching the data. A handy feature when you want things to remain responsive.</p>
<p>Let&#8217;s take a quick look at the wrapper class I&#8217;ve put around <code>NSURLConnection</code> before looking at <code>fetch_feed</code>.  The reason I created a wrapper is that <code>NSURLConnection</code>, because it&#8217;s asynchronous, works through callbacks. I&#8217;m going to need to query three different PostRank APIs and take different actions for each query. Instead of trying to do some magic in the callbacks, I&#8217;ve created a wrapper class that accepts a block. The block is called when the data has been successfully retrieved. (The wrapper just spits out an error if something goes wrong, the block is never called.) For example, to download the Google.ca homepage we could do:</p>
<div class="dean_ch" style="white-space: wrap;">
DataRequest.<span class="me1">new</span><span class="br0">&#40;</span><span class="st0">&quot;http://google.ca&quot;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |data|<br />
&nbsp; NSLog <span class="st0">&quot;Data: #{data}&quot;</span><br />
<span class="kw1">end</span></div>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw1">class</span> DataRequest<br />
&nbsp; <span class="kw1">def</span> get<span class="br0">&#40;</span>url, &amp;blk<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="re1">@buf</span> = NSMutableData.<span class="me1">new</span><br />
&nbsp; &nbsp; <span class="re1">@blk</span> = blk<br />
&nbsp; &nbsp; req = NSURLRequest.<span class="me1">requestWithURL</span><span class="br0">&#40;</span>NSURL.<span class="me1">URLWithString</span><span class="br0">&#40;</span>url<span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; NSURLConnection.<span class="me1">alloc</span>.<span class="me1">initWithRequest</span><span class="br0">&#40;</span>req, delegate:<span class="kw2">self</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">end</span><br />
&nbsp; <br />
&nbsp; <span class="kw1">def</span> connection<span class="br0">&#40;</span>conn, didReceiveResponse:resp<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="re1">@buf</span>.<span class="me1">setLength</span><span class="br0">&#40;</span><span class="nu0">0</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">end</span><br />
&nbsp; <br />
&nbsp; <span class="kw1">def</span> connection<span class="br0">&#40;</span>conn, didReceiveData:data<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="re1">@buf</span>.<span class="me1">appendData</span><span class="br0">&#40;</span>data<span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; <span class="kw1">def</span> connection<span class="br0">&#40;</span>conn, didFailWithError:err<span class="br0">&#41;</span><br />
&nbsp; &nbsp; NSLog <span class="st0">&quot;Request failed&quot;</span><br />
&nbsp; <span class="kw1">end</span><br />
&nbsp; <br />
&nbsp; <span class="kw1">def</span> connectionDidFinishLoading<span class="br0">&#40;</span>conn<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="re1">@blk</span>.<span class="me1">call</span><span class="br0">&#40;</span>NSString.<span class="me1">alloc</span>.<span class="me1">initWithData</span> <span class="re1">@buf</span>, encoding:NSUTF8StringEncoding<span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>As <code>NSURLConnection</code> executes it returns data to the application. We&#8217;re storing this data in a <code>NSMutableData</code> object called <code>@buf</code>.  The callbacks we&#8217;re interested in are:</p>
<dl>
<dt><code>connection(conn, didReceiveResponse:resp)</code></dt>
<dd>Called when we receive a response from the server. This can be called multiple times if there are any server redirects in place. We reset the length of our data buffer each time this callback is called.</dd>
<dt><code>connection(conn, didReceiveData:data)</code></dt>
<dd>Called each time data is received from the server. This can be called multiple times and we just append the data to our buffer each time.</dd>
<dt><code>connection(conn, didFailWithError:err)</code></dt>
<dd>Called if there is an error retrieving the data. We, basically, just ignore the error. You&#8217;d probably want to do something sane in your application.</dd>
<dt><code>connectionDidFinishLoading(conn)</code></dt>
<dd>Called when all of the data has been retrieved from the remote server. Since we&#8217;re not working with binary data I convert the <code>NSMutableData</code> to an <code>NSString</code> using the <code>initWithData:encoding</code> method. Note the use of <code>alloc</code> on the <code>NSString</code>. If you try to use <code>new</code> you&#8217;ll, like me, spend the next 30 minutes trying to figure out why your application is crashing.</dd>
</dl>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw1">def</span> fetch_feed<span class="br0">&#40;</span>url<span class="br0">&#41;</span><br />
&nbsp; DataRequest.<span class="me1">new</span>.<span class="me1">get</span><span class="br0">&#40;</span><span class="st0">&quot;#{POSTRANK_URL_BASE}/feed/info?id=#{url}&amp;#{APPKEY}&quot;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |data|<br />
&nbsp; &nbsp; feed_info = JSON.<span class="me1">parse</span><span class="br0">&#40;</span>data<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">unless</span> feed_info.<span class="me1">has_key</span>?<span class="br0">&#40;</span><span class="st0">&#8216;error&#8217;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; DataRequest.<span class="me1">new</span>.<span class="me1">get</span><span class="br0">&#40;</span><span class="st0">&quot;#{POSTRANK_URL_BASE}/feed/#{feed_info['id']}?#{APPKEY}&quot;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |data|<br />
&nbsp; &nbsp; &nbsp; &nbsp; feed = JSON.<span class="me1">parse</span><span class="br0">&#40;</span>data<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; feed<span class="br0">&#91;</span><span class="st0">&#8216;items&#8217;</span><span class="br0">&#93;</span>.<span class="me1">each</span> <span class="kw1">do</span> |item|<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; post_data = <span class="br0">&#123;</span>:title =&gt; item<span class="br0">&#91;</span><span class="st0">&#8216;title&#8217;</span><span class="br0">&#93;</span>, <span class="re3">:link</span> =&gt; item<span class="br0">&#91;</span><span class="st0">&#8216;original_link&#8217;</span><span class="br0">&#93;</span>, <span class="re3">:metrics</span> =&gt; <span class="br0">&#123;</span><span class="br0">&#125;</span><span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">@table</span>.<span class="me1">dataSource</span>.<span class="me1">data</span> &lt;&lt; <span class="br0">&#123;</span>:data =&gt; post_data,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="re3">:postrank</span> =&gt; <span class="br0">&#123;</span>:value =&gt; item<span class="br0">&#91;</span><span class="st0">&#8216;postrank&#8217;</span><span class="br0">&#93;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="re3">:color</span> =&gt; item<span class="br0">&#91;</span><span class="st0">&#8216;postrank_color&#8217;</span><span class="br0">&#93;</span><span class="br0">&#125;</span><span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; DataRequest.<span class="me1">new</span>.<span class="me1">get</span><span class="br0">&#40;</span><span class="st0">&quot;#{POSTRANK_URL_BASE}/entry/#{item['id']}/metrics?#{APPKEY}&quot;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |data|<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; metrics = JSON.<span class="me1">parse</span><span class="br0">&#40;</span>data<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; metrics<span class="br0">&#91;</span>item<span class="br0">&#91;</span><span class="st0">&#8216;id&#8217;</span><span class="br0">&#93;</span><span class="br0">&#93;</span>.<span class="me1">each_pair</span> <span class="kw1">do</span> |key, value|<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">next</span> <span class="kw1">if</span> key == <span class="st0">&#8216;friendfeed_comm&#8217;</span> || key == <span class="st0">&#8216;friendfeed_like&#8217;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; post_data<span class="br0">&#91;</span><span class="re3">:metrics</span><span class="br0">&#93;</span><span class="br0">&#91;</span>key.<span class="me1">to_sym</span><span class="br0">&#93;</span> = value<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re1">@table</span>.<span class="me1">reloadData</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>Most of the code in <code>fetch_feed</code> should probably be refactored into <code>Feed</code>, <code>Post</code> and <code>Metrics</code> classes, but, for the tutorial, I&#8217;m not going to bother.</p>
<p>You can see we&#8217;re doing three successive data requests. The first is to the <a href="http://www.postrank.com/developers/api#feed_info">Feed Info</a> API. From this call we can retrieve the <em>feed_hash</em> which allows us to uniquely identify our feed in the PostRank system. By default all the PostRank API calls will return the data in JSON format. We could, and I did this initially use <code>format=xml</code> and <code>NSXMLDocument.initWithContentsOfURL</code> to pullback and parse all the data (the problem being, metrics only responds in JSON).</p>
<p>Now, as long as the query to Feed Info didn&#8217;t return an error we use the <code>id</code> to access the <a href="http://www.postrank.com/developers/api#feed">Feed</a> API. The Feed API will return the posts in the given feed. The default is to return 10 posts which works for our purposes. We could, if we wished, add a button to retrieve the next set of posts from the API using the <code>start</code> and <code>num</code> parameters.</p>
<p>With the feed in hand we&#8217;re interested in the <code>items</code> attribute. This is an array of the posts in the feed. Using these items we can start to create our table data. For each item we&#8217;re going to create two hashes of data, one for each column of our table.  The PostRank column will contain the <code>:postrank</code> and <code>:postrank_color</code> and the post column will contain the <code>:title</code>, <code>:link</code> and <code>:metrics</code>.</p>
<p>Finally, we query the metrics API for each post to retrieve the metrics data. The metrics API will provide us with a hash with a single key based on our post&#8217;s ID. Under this key we receive a hash containing the metric source names and the values. We&#8217;re skipping <code>friendfeed_comm</code> and <code>friendfeed_like</code> as they&#8217;ve been renamed to <code>ff_comments</code> and <code>ff_links</code> and only remain as legacy.</p>
<p>Once we&#8217;ve got all the metrics source information packed into our <code>post_data</code> hash we call <code>@table.reloadData</code> so everything gets rendered properly.</p>
<p>Since the calls to <code>DataRequest</code> are asynchronous, we have to call reload  inside the metrics block. This guarantees the table will be reloaded after we&#8217;ve received our data.</p>
<p>With that out of the way, we&#8217;re onto our formatting cells. In order to get our custom table display we need to subclass <code>NSCell</code> and override the <code>drawInteriorWithFrame(frame, inView:view)</code> where we can layout our cell as desired.</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw1">class</span> PostRankCell &lt; NSCell<br />
&nbsp; <span class="kw1">def</span> drawInteriorWithFrame<span class="br0">&#40;</span>frame, inView:view<span class="br0">&#41;</span><br />
&nbsp; &nbsp; m = objectValue<span class="br0">&#91;</span><span class="re3">:color</span><span class="br0">&#93;</span>.<span class="me1">match</span><span class="br0">&#40;</span>/<span class="co1">#(..)(..)(..)/)</span><br />
&nbsp; &nbsp; NSColor.<span class="me1">colorWithCalibratedRed</span><span class="br0">&#40;</span>m<span class="br0">&#91;</span><span class="nu0">1</span><span class="br0">&#93;</span>.<span class="me1">hex</span>/ <span class="nu0">255.0</span>, green:m<span class="br0">&#91;</span><span class="nu0">2</span><span class="br0">&#93;</span>.<span class="me1">hex</span>/<span class="nu0">255.0</span>, blue:m<span class="br0">&#91;</span><span class="nu0">3</span><span class="br0">&#93;</span>.<span class="me1">hex</span>/<span class="nu0">255.0</span>, alpha:<span class="nu0">100</span><span class="br0">&#41;</span>.<span class="me1">set</span><br />
&nbsp; &nbsp; NSRectFill<span class="br0">&#40;</span>frame<span class="br0">&#41;</span><br />
&nbsp; <br />
&nbsp; &nbsp; rank_frame = NSMakeRect<span class="br0">&#40;</span>frame.<span class="me1">origin</span>.<span class="me1">x</span> + <span class="br0">&#40;</span>frame.<span class="me1">size</span>.<span class="me1">width</span> / <span class="nu0">2</span><span class="br0">&#41;</span> &#8211; <span class="nu0">12</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; frame.<span class="me1">origin</span>.<span class="me1">y</span> + <span class="br0">&#40;</span>frame.<span class="me1">size</span>.<span class="me1">height</span> / <span class="nu0">2</span><span class="br0">&#41;</span> &#8211; <span class="nu0">8</span>, frame.<span class="me1">size</span>.<span class="me1">width</span>, <span class="nu0">17</span><span class="br0">&#41;</span><br />
&nbsp; <br />
&nbsp; &nbsp; objectValue<span class="br0">&#91;</span><span class="re3">:value</span><span class="br0">&#93;</span>.<span class="me1">to_s</span>.<span class="me1">drawInRect</span><span class="br0">&#40;</span>rank_frame, withAttributes:<span class="kw2">nil</span><span class="br0">&#41;</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p>The <code>PostRankCell</code> is pretty simple. We parse the provided PostRank colour, which comes as <code>#ffffff</code> into separate red, green and blue values. These values are passed to <code>NSColor.colorWithCalibratedRed(red, green:green, blue:blue, alpha:alpha)</code> in order to create a <code>NSColor</code> object representing our PostRank colour. We need to divided each value by <code>255</code> as <code>colorWithCalibratedRed:green:blue:alpha:</code> expects a value between <code>0.0</code> and <code>1.0</code>.  Once we&#8217;ve got our colour we call <code>set</code> to make that colour active and, using <code>NSRectFill</code> we fill then entire frame with the provided <code>postrank_color</code>.</p>
<p>I&#8217;m, kinda, sorta, centering the PostRank values in the column so we need to create a <code>NSRect</code> to specify the box where we want to draw the numbers. This is done by calling <code>NSMakeRect</code> and providing the x, y, width and height values for the rectange. Once we&#8217;ve got our <code>NSRect</code> in hand we call <code>drawInRect(rank_frame, withAttributes:nil)</code> on the PostRank value. This will draw the string in the rectangle specified. We could set extra attributes on the string but, I don&#8217;t need any, so I just leave it <code>nil</code>.</p>
<p>You&#8217;ll notice I&#8217;m using <code>objectValue</code> in a few places. <code>objectValue</code> is a <code>NSCell</code> method that will return the value assigned to this cell as retrieved based on the column key from our table data source.</p>
</pre>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw1">class</span> PostCell &lt; NSCell<br />
&nbsp; ROW_HEIGHT = <span class="nu0">20</span><br />
&nbsp; NUM_METRICS_PER_ROW = <span class="nu0">6</span><br />
&nbsp; SPRITE_SIZE = <span class="nu0">16</span><br />
&nbsp; <br />
&nbsp; @@sprites = <span class="br0">&#123;</span>:default =&gt; <span class="nu0">0</span>, <span class="re3">:blogines</span> =&gt; <span class="nu0">16</span>, <span class="re3">:reddit</span> =&gt; <span class="nu0">32</span>, <span class="re3">:reddit_votes</span> =&gt; <span class="nu0">32</span>,<br />
&nbsp; &nbsp; &nbsp; <span class="re3">:technorati</span> =&gt; <span class="nu0">48</span>, <span class="re3">:magnolia</span> =&gt; <span class="nu0">64</span>, <span class="re3">:digg</span> =&gt; <span class="nu0">80</span>, <span class="re3">:twitter</span> =&gt; <span class="nu0">96</span>, <span class="re3">:comments</span> =&gt; <span class="nu0">112</span>,<br />
&nbsp; &nbsp; &nbsp; <span class="re3">:icerocket</span> =&gt; <span class="nu0">128</span>, <span class="re3">:delicious</span> =&gt; <span class="nu0">144</span>, <span class="re3">:google</span> =&gt; <span class="nu0">160</span>, <span class="re3">:pownce</span> =&gt; <span class="nu0">176</span>, <span class="re3">:views</span> =&gt; <span class="nu0">192</span>,<br />
&nbsp; &nbsp; &nbsp; <span class="re3">:bookmarks</span> =&gt; <span class="nu0">208</span>, <span class="re3">:clicks</span> =&gt; <span class="nu0">224</span>, <span class="re3">:jaiku</span> =&gt; <span class="nu0">240</span>, <span class="re3">:digg_comments</span> =&gt; <span class="nu0">256</span>,<br />
&nbsp; &nbsp; &nbsp; <span class="re3">:diigo</span> =&gt; <span class="nu0">272</span>, <span class="re3">:feecle</span> =&gt; <span class="nu0">288</span>, <span class="re3">:brightkite</span> =&gt; <span class="nu0">304</span>, <span class="re3">:furl</span> =&gt; <span class="nu0">320</span>, <span class="re3">:twitarmy</span> =&gt; <span class="nu0">336</span>,<br />
&nbsp; &nbsp; &nbsp; <span class="re3">:identica</span> =&gt; <span class="nu0">352</span>, <span class="re3">:ff_likes</span> =&gt; <span class="nu0">368</span>, <span class="re3">:blip</span> =&gt; <span class="nu0">384</span>, <span class="re3">:tumblr</span> =&gt; <span class="nu0">400</span>,<br />
&nbsp; &nbsp; &nbsp; <span class="re3">:reddit_comments</span> =&gt; <span class="nu0">416</span>, <span class="re3">:ff_comments</span> =&gt; <span class="nu0">432</span><span class="br0">&#125;</span><br />
&nbsp; @@sprite = <span class="kw2">nil</span></p>
<p>&nbsp; <span class="kw1">def</span> drawInteriorWithFrame<span class="br0">&#40;</span>frame, inView:view<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">unless</span> @@sprite<br />
&nbsp; &nbsp; &nbsp; bundle = NSBundle.<span class="me1">mainBundle</span><br />
&nbsp; &nbsp; &nbsp; @@sprite = NSImage.<span class="me1">alloc</span>.<span class="me1">initWithContentsOfFile</span><span class="br0">&#40;</span>bundle.<span class="me1">pathForResource</span><span class="br0">&#40;</span><span class="st0">&quot;sprites&quot;</span>, ofType:<span class="st0">&quot;png&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; @@sprite.<span class="me1">setFlipped</span><span class="br0">&#40;</span><span class="kw2">true</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="kw1">end</span></p>
<p>&nbsp; &nbsp; title_rect = NSMakeRect<span class="br0">&#40;</span>frame.<span class="me1">origin</span>.<span class="me1">x</span>, frame.<span class="me1">origin</span>.<span class="me1">y</span> + <span class="nu0">1</span>, frame.<span class="me1">size</span>.<span class="me1">width</span>, <span class="nu0">17</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; metrics_rect = NSMakeRect<span class="br0">&#40;</span>frame.<span class="me1">origin</span>.<span class="me1">x</span>, frame.<span class="me1">origin</span>.<span class="me1">y</span> + ROW_HEIGHT, frame.<span class="me1">size</span>.<span class="me1">width</span>, <span class="nu0">17</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; title_str = <span class="st0">&quot;#{objectValue[:title]}&quot;</span><br />
&nbsp; &nbsp; title_str.<span class="me1">drawInRect</span><span class="br0">&#40;</span>title_rect, withAttributes:<span class="kw2">nil</span><span class="br0">&#41;</span></p>
<p>&nbsp; &nbsp; count = <span class="nu0">0</span><br />
&nbsp; &nbsp; orig_x_orign = metrics_rect.<span class="me1">origin</span>.<span class="me1">x</span><br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; objectValue<span class="br0">&#91;</span><span class="re3">:metrics</span><span class="br0">&#93;</span>.<span class="me1">each_pair</span> <span class="kw1">do</span> |key, value|<br />
&nbsp; &nbsp; &nbsp; s = metrics_rect.<span class="me1">size</span>.<span class="me1">width</span><br />
&nbsp; &nbsp; &nbsp; metrics_rect.<span class="me1">size</span>.<span class="me1">width</span> = SPRITE_SIZE<br />
&nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; y = <span class="kw1">if</span> @@sprites.<span class="me1">has_key</span>?<span class="br0">&#40;</span>key<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; @@sprites<span class="br0">&#91;</span>key.<span class="me1">to_sym</span><span class="br0">&#93;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">else</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="nu0">0</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; r = NSMakeRect<span class="br0">&#40;</span><span class="nu0">0</span>, y, SPRITE_SIZE, SPRITE_SIZE<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; @@sprite.<span class="me1">drawInRect</span><span class="br0">&#40;</span>metrics_rect, fromRect:r,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; operation:NSCompositeSourceOver, fraction:<span class="nu0">1.0</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; metrics_rect.<span class="me1">origin</span>.<span class="me1">x</span> += <span class="nu0">21</span><br />
&nbsp; &nbsp; &nbsp; metrics_rect.<span class="me1">size</span>.<span class="me1">width</span> = s - <span class="nu0">21</span><br />
&nbsp; &nbsp; &nbsp; &nbsp;<br />
&nbsp; &nbsp; &nbsp; <span class="st0">&quot;#{value}&quot;</span>.<span class="me1">drawInRect</span><span class="br0">&#40;</span>metrics_rect, withAttributes:<span class="kw2">nil</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; s = <span class="st0">&quot;#{value}&quot;</span>.<span class="me1">sizeWithAttributes</span><span class="br0">&#40;</span><span class="kw2">nil</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; metrics_rect.<span class="me1">origin</span>.<span class="me1">x</span> += s.<span class="me1">width</span> + <span class="nu0">15</span><br />
&nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; count += <span class="nu0">1</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">if</span> count == NUM_METRICS_PER_ROW<br />
&nbsp; &nbsp; &nbsp; &nbsp; metrics_rect.<span class="me1">origin</span>.<span class="me1">y</span> += ROW_HEIGHT<br />
&nbsp; &nbsp; &nbsp; &nbsp; metrics_rect.<span class="me1">origin</span>.<span class="me1">x</span> = orig_x_orign<br />
&nbsp; &nbsp; &nbsp; &nbsp; count = <span class="nu0">0</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p><code>PostRankCell</code> is similar to <code>PostCell</code> in that we're basically creating bounding rectangles and drawing into them. The extra little bit we're doing here is loading up a <code>NSImage</code> which is our sprite set and using that to pull out all of the individual service icons. <code>NSImage</code> makes it easy to work with our sprite image by providing <code>drawInRect(rect, fromRect:from_rect, operation:op, fraction:val)</code>. <code>drawInRect:fromRect:operation:fraction:</code> draws into the rectangle defined by <code>rect</code> retrieving the pixels in your <code>NSImage</code> that are inside the <code>from_rect</code>. I'm using <code>NSCompositeSourceOver</code> because some of my images are semi-transparent. The fraction parameter is a the alpha setting for the image.</p>
<p>With that, well, you'll probably need to download the source code to see it all in one file, you should have a working application that will query PostRank for a feed and display the posts and metrics for the feed.</p>
<p>As for the next installment. I've got a few things I still want to do, including: tabbing between widgets, submitting the text field on return, a progress indicator as the feed information is being retrieved and adding a tabbed interface to allow showing feed, post and top post information. I'm not sure which of these things I'll tackle next. There are a few helper methods I want to try to add to HotCocoa from this article that I'll probably do first.  So, until next time.</p>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-iii%2F&amp;title=Heating+up+with+HotCocoa+Part+III" title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-iii%2F&amp;title=Heating+up+with+HotCocoa+Part+III" title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-iii%2F&amp;title=Heating+up+with+HotCocoa+Part+III" title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-iii%2F&amp;title=Heating+up+with+HotCocoa+Part+III" title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-iii%2F&amp;title=Heating+up+with+HotCocoa+Part+III', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-iii%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-iii%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-iii%2F&amp;title=Heating+up+with+HotCocoa+Part+III" title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-iii%2F&amp;title=Heating+up+with+HotCocoa+Part+III" title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></content:encoded>
			<wfw:commentRss>http://everburning.com/news/heating-up-with-hotcocoa-part-iii/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Heating up with HotCocoa Part II</title>
		<link>http://everburning.com/news/heating-up-with-hotcocoa-part-ii/</link>
		<comments>http://everburning.com/news/heating-up-with-hotcocoa-part-ii/#comments</comments>
		<pubDate>Sun, 24 May 2009 02:49:57 +0000</pubDate>
		<dc:creator>dj2</dc:creator>
				<category><![CDATA[Computers]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Cocoa]]></category>
		<category><![CDATA[HotCocoa]]></category>
		<category><![CDATA[MacRuby]]></category>

		<guid isPermaLink="false">http://everburning.com/?p=467</guid>
		<description><![CDATA[<p><img src="http://everburning.com/wp-content/uploads/2009/05/img_4292-300x200.jpg" alt="img_4292" title="img_4292" width="300" height="200" class="alignleft size-medium wp-image-472" />Welcome back. In <a href="http://everburning.com/news/heating-up-with-hotcocoa-part-i/">part I</a> we created a basic <a href="http://www.macruby.org/trac/wiki/HotCocoa">HotCocoa</a> application and took a look at the created files and some of their workings. In part II we&#8217;re going get our UI laid out and hooked up.</p>
<p>We&#8217;ll be creating a text field, button, scroll view and a table. When the button is pressed, for now, the content of the text field will be appended to the table view.</p>
<p>A good thing to keep in mind when hacking around with MacRuby and HotCocoa is that the methods available in the OS X frameworks you&#8217;re using are available in Ruby. This means, if you don&#8217;t know how to do something, the Apple documentation is awesome at trying to figure out the right methods use.</p>
<p>HotCocoa doesn&#8217;t have wrappers for all of the methods we need so, sometimes, we&#8217;ll drop down and call the Cocoa methods directly. Hopefully as HotCocoa matures more of these methods will have wrappers in Ruby land. I&#8217;ve created few patches, <a href="https://www.macruby.org/trac/ticket/258">here</a>, <a href="https://www.macruby.org/trac/ticket/259">here</a>, <a href="https://www.macruby.org/trac/ticket/260">here</a> and <a href="https://www.macruby.org/trac/ticket/261">here</a> for some of the wrappings I thought would be handy as I worked on this portion of our tale.</p>
<p>I&#8217;m going to start off with the complete listing of the code. Don&#8217;t worry if this doesn&#8217;t make any sense at the moment as we&#8217;re going to go through all of the relevant portions. Some of this should look familiar from the basic HotCocoa generated code seen in part I.</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw3">require</span> <span class="st0">&#8216;hotcocoa&#8217;</span></p>
<p><span class="kw1">class</span> Postie<br />
&nbsp; <span class="kw1">include</span> HotCocoa</p>
<p>&nbsp; <span class="kw1">def</span> start<br />
&nbsp; &nbsp; application<span class="br0">&#40;</span><span class="re3">:name</span> =&gt; <span class="st0">&quot;Postie&quot;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |app|<br />
&nbsp; &nbsp; &nbsp; app.<span class="me1">delegate</span> = <span class="kw2">self</span><br />
&nbsp; &nbsp; &nbsp; window<span class="br0">&#40;</span><span class="re3">:size</span> =&gt; <span class="br0">&#91;</span><span class="nu0">640</span>, <span class="nu0">480</span><span class="br0">&#93;</span>, <span class="re3">:center</span> =&gt; <span class="kw2">true</span>, <span class="re3">:title</span> =&gt; <span class="st0">&quot;Postie&quot;</span>, <span class="re3">:view</span> =&gt; <span class="re3">:nolayout</span><span class="br0">&#41;</span> <span class="kw1">do</span> |win|<br />
&nbsp; &nbsp; &nbsp; &nbsp; win.<span class="me1">will_close</span> <span class="br0">&#123;</span> <span class="kw3">exit</span> <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; win.<span class="me1">view</span> = layout_view<span class="br0">&#40;</span><span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:expand =&gt; <span class="br0">&#91;</span><span class="re3">:width</span>, <span class="re3">:height</span><span class="br0">&#93;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="re3">:padding</span> =&gt; <span class="nu0">0</span>, <span class="re3">:margin</span> =&gt; <span class="nu0">0</span><span class="br0">&#125;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |vert|<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; vert &lt;&lt; layout_view<span class="br0">&#40;</span><span class="re3">:frame</span> =&gt; <span class="br0">&#91;</span><span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">40</span><span class="br0">&#93;</span>, <span class="re3">:mode</span> =&gt; <span class="re3">:horizontal</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:padding =&gt; <span class="nu0">0</span>, <span class="re3">:margin</span> =&gt; <span class="nu0">0</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re3">:start</span> =&gt; <span class="kw2">false</span>, <span class="re3">:expand</span> =&gt; <span class="br0">&#91;</span><span class="re3">:width</span><span class="br0">&#93;</span><span class="br0">&#125;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |horiz|<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; horiz &lt;&lt; label<span class="br0">&#40;</span><span class="re3">:text</span> =&gt; <span class="st0">&quot;Feed&quot;</span>, <span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:align =&gt; <span class="re3">:center</span><span class="br0">&#125;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; horiz &lt;&lt; <span class="re1">@feed_field</span> = text_field<span class="br0">&#40;</span><span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:expand =&gt; <span class="br0">&#91;</span><span class="re3">:width</span><span class="br0">&#93;</span><span class="br0">&#125;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; horiz &lt;&lt; button<span class="br0">&#40;</span><span class="re3">:title</span> =&gt; <span class="st0">&#8216;go&#8217;</span>, <span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:align =&gt; <span class="re3">:center</span><span class="br0">&#125;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |b|<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; b.<span class="me1">on_action</span> <span class="br0">&#123;</span> load_feed <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; vert &lt;&lt; scroll_view<span class="br0">&#40;</span><span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:expand =&gt; <span class="br0">&#91;</span><span class="re3">:width</span>, <span class="re3">:height</span><span class="br0">&#93;</span><span class="br0">&#125;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |scroll|<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; scroll.<span class="me1">setAutohidesScrollers</span><span class="br0">&#40;</span><span class="kw2">true</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; scroll &lt;&lt; <span class="re1">@table</span> = table_view<span class="br0">&#40;</span><span class="re3">:columns</span> =&gt; <span class="br0">&#91;</span>column<span class="br0">&#40;</span><span class="re3">:id</span> =&gt; <span class="re3">:data</span>, <span class="re3">:title</span> =&gt; <span class="st0">&#8221;</span><span class="br0">&#41;</span><span class="br0">&#93;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re3">:data</span> =&gt; <span class="br0">&#91;</span><span class="br0">&#93;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |table|<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;table.<span class="me1">setUsesAlternatingRowBackgroundColors</span><span class="br0">&#40;</span><span class="kw2">true</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;table.<span class="me1">setGridStyleMask</span><span class="br0">&#40;</span>NSTableViewSolidHorizontalGridLineMask<span class="br0">&#41;</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; <span class="kw1">def</span> load_feed<br />
&nbsp; &nbsp; str = <span class="re1">@feed_field</span>.<span class="me1">stringValue</span><br />
&nbsp; &nbsp; <span class="kw1">unless</span> str.<span class="kw2">nil</span>? || str =~ /^\s*$/<br />
&nbsp; &nbsp; &nbsp; <span class="re1">@table</span>.<span class="me1">dataSource</span>.<span class="me1">data</span> &lt;&lt; <span class="br0">&#123;</span>:data =&gt; str<span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; <span class="re1">@table</span>.<span class="me1">reloadData</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></p>
<p>Postie.<span class="me1">new</span>.<span class="me1">start</span></div>
<p>I&#8217;m going to skip the parts that we saw in part I and just mention the changes and new additions in part II.</p>
<div class="dean_ch" style="white-space: wrap;">
window<span class="br0">&#40;</span>&lt;em&gt;:size =&gt; <span class="br0">&#91;</span><span class="nu0">640</span>, <span class="nu0">480</span><span class="br0">&#93;</span>, <span class="re3">:center</span> =&gt; true&lt;/em&gt;, <span class="re3">:title</span> =&gt; <span class="st0">&quot;Postie&quot;</span>, <span class="re3">:view</span> =&gt; <span class="re3">:nolayout</span><span class="br0">&#41;</span></div>
<p>Instead of using the <code>:frame =&gt; [100, 100, 500, 500]</code> as seen in part I, I prefer to use <code>:size =&gt; [640, 480]</code><code> and </code><code>:center =&gt; true</code> to set the window with a starting size of 640&#215;480 position in the center of the screen.</p>
<p>You&#8217;ll also notice the addition of <code>:view =&gt; :nolayout</code> tacked on the end of the <code>window</code> method. This isn&#8217;t strictly necessary but saves the creation of an object we&#8217;re just going to destroy anyway. By default when a window is created a <code>LayoutView</code> will be created and appended to the window. I&#8217;m going to be creating my own layout and overriding the created one so I&#8217;m just telling the window to skip the creation of the default view.</p>
<p>Before we dig into the next chunk of code lets take a little diversion to look at <code>layout_view</code>. The <code>layout_view</code> is one of the basic building blocks for organizing your application layout. The <code>layout_view</code> method will create a <code>LayoutView</code> object which is a subclass of <code>NSView</code>. Any Cocoa methods available for an <code>NSView</code> can be called on a <code>LayoutView</code>.</p>
<p>When working with <code>layout_view</code> there are a few parameters we&#8217;re interested in. The first, similar to <code>window</code> is <code>:frame</code>. As with <code>window</code> the <code>:frame</code> parameter allows us to set the frame position and size for the view. Note, if you don&#8217;t set a <code>:frame</code> then, it appears, that the view may have 0 size. This can be changed by the children placed in the view but not always. I spent a while trying to figure out why removing the <code>:frame =&gt; [0, 0, 0, 40]</code> from the code above caused my label, text field and button to disappear.</p>
<p>A handy little trick when working with <code>layout_view</code> is to run your application in the  <code>$DEBUG</code> mode of Ruby. When <code>$DEBUG</code> is active each layout will have a red border.</p>
<p>To execute your application in <code>$DEBUG</code> you can do:</p>
<pre><code>titania:Postie dj2$ macruby -d lib/application.rb </code></pre>
<p>Other parameters we&#8217;re using for the <code>layout_view</code> calls are <code>:mode</code>, <code>:margin</code>, <code>:spacing</code> and <code>:layout</code>.</p>
<dl>
<dt><code>:mode</code></dt>
<dd>Lets us specify if this view has a <code>:vertical</code> or <code>:horizontal</code> layout. The default layout is <code>:vertical</code>.</dd>
<dt><code>:margin</code></dt>
<dd>Allows us to specify a margin size for the layout. The provided value is a single integer which will be applied to top, bottom, left and right margins of the view.</dd>
<dt><code>:spacing</code></dt>
<dd>Allows us to set the spacing for items placed into the view. The value is a single integer.</dd>
</dl>
<p>The last option we&#8217;re going to look at is <code>:layout</code>. The layout option isn&#8217;t restricted to just <code>layout_view</code> calls and is available on all of the other widgets I&#8217;ve created so far.</p>
<p>The <code>:layout</code> hash will be turned into a <code>LayoutOptions</code> object. The available keys are: <code>:start</code>, <code>:expand</code>, <code>:padding</code>, <code>:[left | right | bottom | top]_padding</code> and <code>:align</code>.</p>
<dl>
<dt><code>:start</code></dt>
<dd>Signifies if the view is packed at the start or end of the packing view. I&#8217;ll admit, I don&#8217;t really know what that means. I&#8217;m stealing it from the <code>LayoutOption docs</code>. It appears, in my limited fiddling, that setting it to <code>false</code> causes your subviews to end up at the top of the layout. The default value is <code>true</code>.</dd>
<dt><code>:expand</code></dt>
<dd>Specifies how the view will grow when the window is resized. The available options are: <code>:height</code>, <code>:width</code>, <code>[:height, :width]</code> and <code>nil</code>. The default setting is <code>nil</code>.</dd>
<dt><code>:*padding</code></dt>
<dd>Allows you to set the padding around the view. The padding values are specified as a float with a default of <code>0.0</code>.</dd>
<dt><code>:align</code></dt>
<dd>Allows us to specify the alignment of the view as long as it isn&#8217;t set to <code>:expand</code> in the other direction. The available options are: <code>:left</code>, <code>:center</code>, <code>:right</code>, <code>:top</code> and <code>:bottom</code>.</dd>
</dl>
<p>With that out of the way, back to our code. As you can see by the layout image, our layout isn&#8217;t overly complicated. All of the layout is handled by two layout views and a scroll view.</p>
<div class="dean_ch" style="white-space: wrap;">
win.<span class="me1">view</span> = layout_view<span class="br0">&#40;</span><span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:expand =&gt; <span class="br0">&#91;</span><span class="re3">:width</span>, <span class="re3">:height</span><span class="br0">&#93;</span>, <span class="re3">:padding</span> =&gt; <span class="nu0">0</span><span class="br0">&#125;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |vert|</div>
<p><a href="http://everburning.com/wp-content/uploads/2009/05/postie_layout.png"><img src="http://everburning.com/wp-content/uploads/2009/05/postie_layout-263x300.png" alt="postie_layout" title="postie_layout" width="263" height="300" class="alignright size-medium wp-image-466" /></a>We start by creating the main window view. If you remember, we created the window with <code>:view =&gt; :nolayout</code> so there is currently no view in our window. We assign the new <code>layout_view</code> to the <code>win.view</code> instead of using <code>&lt;&lt;</code> to pack it into the view. We remove the padding on the view and set the expand to <code>[:width, :height]</code> so the view will fill the entire window and resize correctly.</p>
<p>Since we didn&#8217;t specify a <code>:mode</code> the main window will layout its packed widgets in a vertical fashion.</p>
<p>As each layout view is created you can attach a block to the <code>layout_view</code> call. The block will be called with the <code>LayoutView</code> object that was just created. This makes it easy to pack subviews into the views as they&#8217;re created.</p>
<div class="dean_ch" style="white-space: wrap;">
vert &lt;&lt; layout_view<span class="br0">&#40;</span><span class="re3">:frame</span> =&gt; <span class="br0">&#91;</span><span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">40</span><span class="br0">&#93;</span>, <span class="re3">:mode</span> =&gt; <span class="re3">:horizontal</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:padding =&gt; <span class="nu0">0</span>, <span class="re3">:start</span> =&gt; <span class="kw2">false</span>, <span class="re3">:expand</span> =&gt; <span class="br0">&#91;</span><span class="re3">:width</span><span class="br0">&#93;</span><span class="br0">&#125;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |horiz|</div>
<p>Next we pack a horizontal view (<code>:mode =&gt; :horizontal</code>) to hold the label, text field and button. We set the view to <code>:expand =&gt; [:width]</code> so we&#8217;ll only get horizontal expansion and maintain the height specified in our <code>:frame</code> parameter. You&#8217;ll notice we&#8217;re setting a <code>:frame</code> on this <code>layout_view</code>. If we don&#8217;t have this parameter it appears that the view will be drawn with 0 height. Effectively making the view invisible.</p>
<div class="dean_ch" style="white-space: wrap;">
horiz &lt;&lt; label<span class="br0">&#40;</span><span class="re3">:text</span> =&gt; <span class="st0">&quot;Feed&quot;</span>, <span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:align =&gt; <span class="re3">:center</span><span class="br0">&#125;</span><span class="br0">&#41;</span><br />
horiz &lt;&lt; <span class="re1">@feed_field</span> = text_field<span class="br0">&#40;</span><span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:expand =&gt; <span class="br0">&#91;</span><span class="re3">:width</span><span class="br0">&#93;</span><span class="br0">&#125;</span><span class="br0">&#41;</span><br />
horiz &lt;&lt; button<span class="br0">&#40;</span><span class="re3">:title</span> =&gt; <span class="st0">&#8216;go&#8217;</span>, <span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:align =&gt; <span class="re3">:center</span><span class="br0">&#125;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |b|<br />
&nbsp; b.<span class="me1">on_action</span> <span class="br0">&#123;</span> load_feed <span class="br0">&#125;</span><br />
<span class="kw1">end</span></div>
<p>Into the horizontal layout we pack our <code>label</code>, <code>text_field</code> and <code>button</code>. For both the label and button we&#8217;re specifying an <code>:align =&gt; :center</code>  to line them up with the center of the text field. The only item we&#8217;re setting an expand on is the <code>text_field</code>. The other two widgets will maintain their positions and sizes when the window is resized.</p>
<p>When we create the <code>button</code> we attach a block for the <code>on_action</code> callback. This will be triggered when the button is pressed. In our case we&#8217;re just calling the <code>load_feed</code> method.</p>
<div class="dean_ch" style="white-space: wrap;">
vert &lt;&lt; scroll_view<span class="br0">&#40;</span><span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:expand =&gt; <span class="br0">&#91;</span><span class="re3">:width</span>, <span class="re3">:height</span><span class="br0">&#93;</span><span class="br0">&#125;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |scroll|<br />
&nbsp; scroll.<span class="me1">setAutohidesScrollers</span><span class="br0">&#40;</span><span class="kw2">true</span><span class="br0">&#41;</span></div>
<p>With the horizontal view out of the way we create a <code>scroll_view</code> and pack it into our main vertical view. We want the scroll view to <code>:expand =&gt; [:width, :height]</code> so it fills the entire window on resize. There is currently no exposed HotCocoa sugar for auto hiding the scrollbars so we drop down and call <code>setAutohidesScrollers</code> to set the scrollbars to auto hide.</p>
<div class="dean_ch" style="white-space: wrap;">
scroll &lt;&lt; <span class="re1">@table</span> = table_view<span class="br0">&#40;</span><span class="re3">:columns</span> =&gt; <span class="br0">&#91;</span>column<span class="br0">&#40;</span><span class="re3">:id</span> =&gt; <span class="re3">:data</span>, <span class="re3">:title</span> =&gt; <span class="st0">&#8221;</span><span class="br0">&#41;</span><span class="br0">&#93;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re3">:data</span> =&gt; <span class="br0">&#91;</span><span class="br0">&#93;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |table|<br />
&nbsp; table.<span class="me1">setUsesAlternatingRowBackgroundColors</span><span class="br0">&#40;</span><span class="kw2">true</span><span class="br0">&#41;</span><br />
&nbsp; table.<span class="me1">setGridStyleMask</span><span class="br0">&#40;</span>NSTableViewSolidHorizontalGridLineMask<span class="br0">&#41;</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
<span class="kw1">end</span></div>
<p>The last view we pack is a <code>table_view</code>.  We&#8217;re again dropping down to Cocoa for the <code>setUsesAlternatingRowBackgroundColors</code> and <code>setGridStyleMask</code>.</p>
<p>Now, the <code>table_view</code> itself. The table view is more complicated then the other widgets we&#8217;ve looked at in that it requires column and data source information.</p>
<p>The column information is provided by the <code>:columns =&gt; [column(:id =&gt; :data, :title =&gt; '')]</code> parameter. You provide an array of <code>column</code> objects which define the table columns. The <code>:title</code> will be displayed at the top of a column. If you don&#8217;t provide a <code>:title</code> the default is <em>Column</em>. We also providing an <code>:id</code> parameter to our column method. This parameter will be passed when we&#8217;re accessing our data source, and, if you&#8217;re using the default data source, used to access the specific column information. We&#8217;ll dig into columns and their relations to data sources in a moment.</p>
<p>There are two ways to provide data source information. You can either pass an array, which is what we&#8217;re doing here or provide your own data source object. </p>
<p>If you decide to define your own data source then it must respond to <code>numberOfRowsInTable(tableView) => integer</code> and <code>tableView(view, objectValueForTableColumn:column, row:i) => string</code>. </p>
<p>If you opt to use the default data source then you provide an array of hashes. The keys into the hash are the <code>:id</code> values set when we created our columns.</p>
<p><img src="http://everburning.com/wp-content/uploads/2009/05/picture-11-300x64.png" alt="picture-11" title="picture-11" width="300" height="64" class="alignright size-medium wp-image-489" />Hopefully an example will make this a bit clearer. Lets say we want to create a table with three columns, name, age and sex. We would define our table as:</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="re1">@table</span> = table_view<span class="br0">&#40;</span><span class="re3">:columns</span> =&gt; <span class="br0">&#91;</span>column<span class="br0">&#40;</span><span class="re3">:id</span> =&gt; <span class="re3">:name</span>, <span class="re3">:title</span> =&gt; <span class="st0">&#8216;Name&#8217;</span><span class="br0">&#41;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;column<span class="br0">&#40;</span><span class="re3">:id</span> =&gt; <span class="re3">:age</span>, <span class="re3">:title</span> =&gt; <span class="st0">&#8216;Age&#8217;</span><span class="br0">&#41;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;column<span class="br0">&#40;</span><span class="re3">:id</span> =&gt; <span class="re3">:sex</span>, <span class="re3">:title</span> =&gt; <span class="st0">&#8216;Sex&#8217;</span><span class="br0">&#41;</span><span class="br0">&#93;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re3">:data</span> =&gt; <span class="br0">&#91;</span><span class="br0">&#123;</span> <span class="re3">:name</span> =&gt; <span class="st0">&#8216;Betty Sue&#8217;</span>, <span class="re3">:age</span> =&gt; <span class="nu0">29</span>, <span class="re3">:sex</span> =&gt; <span class="st0">&#8216;F&#8217;</span> <span class="br0">&#125;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span> <span class="re3">:name</span> =&gt; <span class="st0">&#8216;Brandon Oberon&#8217;</span>, <span class="re3">:age</span> =&gt; <span class="nu0">0.005</span>, <span class="re3">:sex</span> =&gt; <span class="st0">&#8216;M&#8217;</span> <span class="br0">&#125;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span> <span class="re3">:name</span> =&gt; <span class="st0">&#8216;Sally Joe&#8217;</span>, <span class="re3">:age</span> =&gt; <span class="nu0">48</span>, <span class="re3">:sex</span> =&gt; <span class="st0">&#8216;F&#8217;</span><span class="br0">&#125;</span><span class="br0">&#93;</span><span class="br0">&#41;</span></div>
<p>With the table view finished all that&#8217;s left is to define the <code>load_feed</code> method. For this first iteration we&#8217;re just going to take the content of the feed text field and load it into our table view.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> load_feed<br />
&nbsp; str = <span class="re1">@feed_field</span>.<span class="me1">stringValue</span><br />
&nbsp; <span class="kw1">unless</span> str.<span class="kw2">nil</span>? || str =~ /^\s*$/<br />
&nbsp; &nbsp; <span class="re1">@table</span>.<span class="me1">dataSource</span>.<span class="me1">data</span> &lt;&lt; <span class="br0">&#123;</span>:data =&gt; str<span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="re1">@table</span>.<span class="me1">reloadData</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p><img src="http://everburning.com/wp-content/uploads/2009/05/picture-1-276x300.png" alt="Postie V1" title="Postie V1" width="276" height="300" class="alignleft size-medium wp-image-465" />We use the Cocoa <code>stringValue</code> method to retrieve the value set into the text field. As long as it isn&#8217;t blank we append a new hash (with our <code>:data</code> key as defined in our <code>column</code> setup) into the <code>@table.dataSource.data</code>. We then call <code>@table.reloadData</code> to get the table to re-display its content.</p>
<p>That&#8217;s it. You should be able to run the application, enter some text in the text field, hit go and see the text appear in the table view. As you add more table rows, scrollbars should appear and you can scroll in the table as needed.</p>
<p>The third part of this series will be, hopefully, pulling real data back from <a href="http://postrank.com">PostRank</a> and displaying it in a custom cell in our table.</p>
<p><strong>Update:</strong> <a href="http://everburning.com/news/heating-up-with-hotcocoa-part-iii/">Part III</a> is now available.</p>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-ii%2F&amp;title=Heating+up+with+HotCocoa+Part+II" title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-ii%2F&amp;title=Heating+up+with+HotCocoa+Part+II" title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-ii%2F&amp;title=Heating+up+with+HotCocoa+Part+II" title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-ii%2F&amp;title=Heating+up+with+HotCocoa+Part+II" title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-ii%2F&amp;title=Heating+up+with+HotCocoa+Part+II', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-ii%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-ii%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-ii%2F&amp;title=Heating+up+with+HotCocoa+Part+II" title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-ii%2F&amp;title=Heating+up+with+HotCocoa+Part+II" title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></description>
			<content:encoded><![CDATA[<p><img src="http://everburning.com/wp-content/uploads/2009/05/img_4292-300x200.jpg" alt="img_4292" title="img_4292" width="300" height="200" class="alignleft size-medium wp-image-472" />Welcome back. In <a href="http://everburning.com/news/heating-up-with-hotcocoa-part-i/">part I</a> we created a basic <a href="http://www.macruby.org/trac/wiki/HotCocoa">HotCocoa</a> application and took a look at the created files and some of their workings. In part II we&#8217;re going get our UI laid out and hooked up.</p>
<p>We&#8217;ll be creating a text field, button, scroll view and a table. When the button is pressed, for now, the content of the text field will be appended to the table view.</p>
<p>A good thing to keep in mind when hacking around with MacRuby and HotCocoa is that the methods available in the OS X frameworks you&#8217;re using are available in Ruby. This means, if you don&#8217;t know how to do something, the Apple documentation is awesome at trying to figure out the right methods use.</p>
<p>HotCocoa doesn&#8217;t have wrappers for all of the methods we need so, sometimes, we&#8217;ll drop down and call the Cocoa methods directly. Hopefully as HotCocoa matures more of these methods will have wrappers in Ruby land. I&#8217;ve created few patches, <a href="https://www.macruby.org/trac/ticket/258">here</a>, <a href="https://www.macruby.org/trac/ticket/259">here</a>, <a href="https://www.macruby.org/trac/ticket/260">here</a> and <a href="https://www.macruby.org/trac/ticket/261">here</a> for some of the wrappings I thought would be handy as I worked on this portion of our tale.</p>
<p>I&#8217;m going to start off with the complete listing of the code. Don&#8217;t worry if this doesn&#8217;t make any sense at the moment as we&#8217;re going to go through all of the relevant portions. Some of this should look familiar from the basic HotCocoa generated code seen in part I.</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw3">require</span> <span class="st0">&#8216;hotcocoa&#8217;</span></p>
<p><span class="kw1">class</span> Postie<br />
&nbsp; <span class="kw1">include</span> HotCocoa</p>
<p>&nbsp; <span class="kw1">def</span> start<br />
&nbsp; &nbsp; application<span class="br0">&#40;</span><span class="re3">:name</span> =&gt; <span class="st0">&quot;Postie&quot;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |app|<br />
&nbsp; &nbsp; &nbsp; app.<span class="me1">delegate</span> = <span class="kw2">self</span><br />
&nbsp; &nbsp; &nbsp; window<span class="br0">&#40;</span><span class="re3">:size</span> =&gt; <span class="br0">&#91;</span><span class="nu0">640</span>, <span class="nu0">480</span><span class="br0">&#93;</span>, <span class="re3">:center</span> =&gt; <span class="kw2">true</span>, <span class="re3">:title</span> =&gt; <span class="st0">&quot;Postie&quot;</span>, <span class="re3">:view</span> =&gt; <span class="re3">:nolayout</span><span class="br0">&#41;</span> <span class="kw1">do</span> |win|<br />
&nbsp; &nbsp; &nbsp; &nbsp; win.<span class="me1">will_close</span> <span class="br0">&#123;</span> <span class="kw3">exit</span> <span class="br0">&#125;</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; win.<span class="me1">view</span> = layout_view<span class="br0">&#40;</span><span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:expand =&gt; <span class="br0">&#91;</span><span class="re3">:width</span>, <span class="re3">:height</span><span class="br0">&#93;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="re3">:padding</span> =&gt; <span class="nu0">0</span>, <span class="re3">:margin</span> =&gt; <span class="nu0">0</span><span class="br0">&#125;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |vert|<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; vert &lt;&lt; layout_view<span class="br0">&#40;</span><span class="re3">:frame</span> =&gt; <span class="br0">&#91;</span><span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">40</span><span class="br0">&#93;</span>, <span class="re3">:mode</span> =&gt; <span class="re3">:horizontal</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:padding =&gt; <span class="nu0">0</span>, <span class="re3">:margin</span> =&gt; <span class="nu0">0</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re3">:start</span> =&gt; <span class="kw2">false</span>, <span class="re3">:expand</span> =&gt; <span class="br0">&#91;</span><span class="re3">:width</span><span class="br0">&#93;</span><span class="br0">&#125;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |horiz|<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; horiz &lt;&lt; label<span class="br0">&#40;</span><span class="re3">:text</span> =&gt; <span class="st0">&quot;Feed&quot;</span>, <span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:align =&gt; <span class="re3">:center</span><span class="br0">&#125;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; horiz &lt;&lt; <span class="re1">@feed_field</span> = text_field<span class="br0">&#40;</span><span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:expand =&gt; <span class="br0">&#91;</span><span class="re3">:width</span><span class="br0">&#93;</span><span class="br0">&#125;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; horiz &lt;&lt; button<span class="br0">&#40;</span><span class="re3">:title</span> =&gt; <span class="st0">&#8216;go&#8217;</span>, <span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:align =&gt; <span class="re3">:center</span><span class="br0">&#125;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |b|<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; b.<span class="me1">on_action</span> <span class="br0">&#123;</span> load_feed <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; vert &lt;&lt; scroll_view<span class="br0">&#40;</span><span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:expand =&gt; <span class="br0">&#91;</span><span class="re3">:width</span>, <span class="re3">:height</span><span class="br0">&#93;</span><span class="br0">&#125;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |scroll|<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; scroll.<span class="me1">setAutohidesScrollers</span><span class="br0">&#40;</span><span class="kw2">true</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; scroll &lt;&lt; <span class="re1">@table</span> = table_view<span class="br0">&#40;</span><span class="re3">:columns</span> =&gt; <span class="br0">&#91;</span>column<span class="br0">&#40;</span><span class="re3">:id</span> =&gt; <span class="re3">:data</span>, <span class="re3">:title</span> =&gt; <span class="st0">&#8221;</span><span class="br0">&#41;</span><span class="br0">&#93;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re3">:data</span> =&gt; <span class="br0">&#91;</span><span class="br0">&#93;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |table|<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;table.<span class="me1">setUsesAlternatingRowBackgroundColors</span><span class="br0">&#40;</span><span class="kw2">true</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;table.<span class="me1">setGridStyleMask</span><span class="br0">&#40;</span>NSTableViewSolidHorizontalGridLineMask<span class="br0">&#41;</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span></p>
<p>&nbsp; <span class="kw1">def</span> load_feed<br />
&nbsp; &nbsp; str = <span class="re1">@feed_field</span>.<span class="me1">stringValue</span><br />
&nbsp; &nbsp; <span class="kw1">unless</span> str.<span class="kw2">nil</span>? || str =~ /^\s*$/<br />
&nbsp; &nbsp; &nbsp; <span class="re1">@table</span>.<span class="me1">dataSource</span>.<span class="me1">data</span> &lt;&lt; <span class="br0">&#123;</span>:data =&gt; str<span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; <span class="re1">@table</span>.<span class="me1">reloadData</span><br />
&nbsp; &nbsp; <span class="kw1">end</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></p>
<p>Postie.<span class="me1">new</span>.<span class="me1">start</span></div>
<p>I&#8217;m going to skip the parts that we saw in part I and just mention the changes and new additions in part II.</p>
<div class="dean_ch" style="white-space: wrap;">
window<span class="br0">&#40;</span>&lt;em&gt;:size =&gt; <span class="br0">&#91;</span><span class="nu0">640</span>, <span class="nu0">480</span><span class="br0">&#93;</span>, <span class="re3">:center</span> =&gt; true&lt;/em&gt;, <span class="re3">:title</span> =&gt; <span class="st0">&quot;Postie&quot;</span>, <span class="re3">:view</span> =&gt; <span class="re3">:nolayout</span><span class="br0">&#41;</span></div>
<p>Instead of using the <code>:frame =&gt; [100, 100, 500, 500]</code> as seen in part I, I prefer to use <code>:size =&gt; [640, 480]</code><code> and </code><code>:center =&gt; true</code> to set the window with a starting size of 640&#215;480 position in the center of the screen.</p>
<p>You&#8217;ll also notice the addition of <code>:view =&gt; :nolayout</code> tacked on the end of the <code>window</code> method. This isn&#8217;t strictly necessary but saves the creation of an object we&#8217;re just going to destroy anyway. By default when a window is created a <code>LayoutView</code> will be created and appended to the window. I&#8217;m going to be creating my own layout and overriding the created one so I&#8217;m just telling the window to skip the creation of the default view.</p>
<p>Before we dig into the next chunk of code lets take a little diversion to look at <code>layout_view</code>. The <code>layout_view</code> is one of the basic building blocks for organizing your application layout. The <code>layout_view</code> method will create a <code>LayoutView</code> object which is a subclass of <code>NSView</code>. Any Cocoa methods available for an <code>NSView</code> can be called on a <code>LayoutView</code>.</p>
<p>When working with <code>layout_view</code> there are a few parameters we&#8217;re interested in. The first, similar to <code>window</code> is <code>:frame</code>. As with <code>window</code> the <code>:frame</code> parameter allows us to set the frame position and size for the view. Note, if you don&#8217;t set a <code>:frame</code> then, it appears, that the view may have 0 size. This can be changed by the children placed in the view but not always. I spent a while trying to figure out why removing the <code>:frame =&gt; [0, 0, 0, 40]</code> from the code above caused my label, text field and button to disappear.</p>
<p>A handy little trick when working with <code>layout_view</code> is to run your application in the  <code>$DEBUG</code> mode of Ruby. When <code>$DEBUG</code> is active each layout will have a red border.</p>
<p>To execute your application in <code>$DEBUG</code> you can do:</p>
<pre><code>titania:Postie dj2$ macruby -d lib/application.rb </code></pre>
<p>Other parameters we&#8217;re using for the <code>layout_view</code> calls are <code>:mode</code>, <code>:margin</code>, <code>:spacing</code> and <code>:layout</code>.</p>
<dl>
<dt><code>:mode</code></dt>
<dd>Lets us specify if this view has a <code>:vertical</code> or <code>:horizontal</code> layout. The default layout is <code>:vertical</code>.</dd>
<dt><code>:margin</code></dt>
<dd>Allows us to specify a margin size for the layout. The provided value is a single integer which will be applied to top, bottom, left and right margins of the view.</dd>
<dt><code>:spacing</code></dt>
<dd>Allows us to set the spacing for items placed into the view. The value is a single integer.</dd>
</dl>
<p>The last option we&#8217;re going to look at is <code>:layout</code>. The layout option isn&#8217;t restricted to just <code>layout_view</code> calls and is available on all of the other widgets I&#8217;ve created so far.</p>
<p>The <code>:layout</code> hash will be turned into a <code>LayoutOptions</code> object. The available keys are: <code>:start</code>, <code>:expand</code>, <code>:padding</code>, <code>:[left | right | bottom | top]_padding</code> and <code>:align</code>.</p>
<dl>
<dt><code>:start</code></dt>
<dd>Signifies if the view is packed at the start or end of the packing view. I&#8217;ll admit, I don&#8217;t really know what that means. I&#8217;m stealing it from the <code>LayoutOption docs</code>. It appears, in my limited fiddling, that setting it to <code>false</code> causes your subviews to end up at the top of the layout. The default value is <code>true</code>.</dd>
<dt><code>:expand</code></dt>
<dd>Specifies how the view will grow when the window is resized. The available options are: <code>:height</code>, <code>:width</code>, <code>[:height, :width]</code> and <code>nil</code>. The default setting is <code>nil</code>.</dd>
<dt><code>:*padding</code></dt>
<dd>Allows you to set the padding around the view. The padding values are specified as a float with a default of <code>0.0</code>.</dd>
<dt><code>:align</code></dt>
<dd>Allows us to specify the alignment of the view as long as it isn&#8217;t set to <code>:expand</code> in the other direction. The available options are: <code>:left</code>, <code>:center</code>, <code>:right</code>, <code>:top</code> and <code>:bottom</code>.</dd>
</dl>
<p>With that out of the way, back to our code. As you can see by the layout image, our layout isn&#8217;t overly complicated. All of the layout is handled by two layout views and a scroll view.</p>
<div class="dean_ch" style="white-space: wrap;">
win.<span class="me1">view</span> = layout_view<span class="br0">&#40;</span><span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:expand =&gt; <span class="br0">&#91;</span><span class="re3">:width</span>, <span class="re3">:height</span><span class="br0">&#93;</span>, <span class="re3">:padding</span> =&gt; <span class="nu0">0</span><span class="br0">&#125;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |vert|</div>
<p><a href="http://everburning.com/wp-content/uploads/2009/05/postie_layout.png"><img src="http://everburning.com/wp-content/uploads/2009/05/postie_layout-263x300.png" alt="postie_layout" title="postie_layout" width="263" height="300" class="alignright size-medium wp-image-466" /></a>We start by creating the main window view. If you remember, we created the window with <code>:view =&gt; :nolayout</code> so there is currently no view in our window. We assign the new <code>layout_view</code> to the <code>win.view</code> instead of using <code>&lt;&lt;</code> to pack it into the view. We remove the padding on the view and set the expand to <code>[:width, :height]</code> so the view will fill the entire window and resize correctly.</p>
<p>Since we didn&#8217;t specify a <code>:mode</code> the main window will layout its packed widgets in a vertical fashion.</p>
<p>As each layout view is created you can attach a block to the <code>layout_view</code> call. The block will be called with the <code>LayoutView</code> object that was just created. This makes it easy to pack subviews into the views as they&#8217;re created.</p>
<div class="dean_ch" style="white-space: wrap;">
vert &lt;&lt; layout_view<span class="br0">&#40;</span><span class="re3">:frame</span> =&gt; <span class="br0">&#91;</span><span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">40</span><span class="br0">&#93;</span>, <span class="re3">:mode</span> =&gt; <span class="re3">:horizontal</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:padding =&gt; <span class="nu0">0</span>, <span class="re3">:start</span> =&gt; <span class="kw2">false</span>, <span class="re3">:expand</span> =&gt; <span class="br0">&#91;</span><span class="re3">:width</span><span class="br0">&#93;</span><span class="br0">&#125;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |horiz|</div>
<p>Next we pack a horizontal view (<code>:mode =&gt; :horizontal</code>) to hold the label, text field and button. We set the view to <code>:expand =&gt; [:width]</code> so we&#8217;ll only get horizontal expansion and maintain the height specified in our <code>:frame</code> parameter. You&#8217;ll notice we&#8217;re setting a <code>:frame</code> on this <code>layout_view</code>. If we don&#8217;t have this parameter it appears that the view will be drawn with 0 height. Effectively making the view invisible.</p>
<div class="dean_ch" style="white-space: wrap;">
horiz &lt;&lt; label<span class="br0">&#40;</span><span class="re3">:text</span> =&gt; <span class="st0">&quot;Feed&quot;</span>, <span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:align =&gt; <span class="re3">:center</span><span class="br0">&#125;</span><span class="br0">&#41;</span><br />
horiz &lt;&lt; <span class="re1">@feed_field</span> = text_field<span class="br0">&#40;</span><span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:expand =&gt; <span class="br0">&#91;</span><span class="re3">:width</span><span class="br0">&#93;</span><span class="br0">&#125;</span><span class="br0">&#41;</span><br />
horiz &lt;&lt; button<span class="br0">&#40;</span><span class="re3">:title</span> =&gt; <span class="st0">&#8216;go&#8217;</span>, <span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:align =&gt; <span class="re3">:center</span><span class="br0">&#125;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |b|<br />
&nbsp; b.<span class="me1">on_action</span> <span class="br0">&#123;</span> load_feed <span class="br0">&#125;</span><br />
<span class="kw1">end</span></div>
<p>Into the horizontal layout we pack our <code>label</code>, <code>text_field</code> and <code>button</code>. For both the label and button we&#8217;re specifying an <code>:align =&gt; :center</code>  to line them up with the center of the text field. The only item we&#8217;re setting an expand on is the <code>text_field</code>. The other two widgets will maintain their positions and sizes when the window is resized.</p>
<p>When we create the <code>button</code> we attach a block for the <code>on_action</code> callback. This will be triggered when the button is pressed. In our case we&#8217;re just calling the <code>load_feed</code> method.</p>
<div class="dean_ch" style="white-space: wrap;">
vert &lt;&lt; scroll_view<span class="br0">&#40;</span><span class="re3">:layout</span> =&gt; <span class="br0">&#123;</span>:expand =&gt; <span class="br0">&#91;</span><span class="re3">:width</span>, <span class="re3">:height</span><span class="br0">&#93;</span><span class="br0">&#125;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |scroll|<br />
&nbsp; scroll.<span class="me1">setAutohidesScrollers</span><span class="br0">&#40;</span><span class="kw2">true</span><span class="br0">&#41;</span></div>
<p>With the horizontal view out of the way we create a <code>scroll_view</code> and pack it into our main vertical view. We want the scroll view to <code>:expand =&gt; [:width, :height]</code> so it fills the entire window on resize. There is currently no exposed HotCocoa sugar for auto hiding the scrollbars so we drop down and call <code>setAutohidesScrollers</code> to set the scrollbars to auto hide.</p>
<div class="dean_ch" style="white-space: wrap;">
scroll &lt;&lt; <span class="re1">@table</span> = table_view<span class="br0">&#40;</span><span class="re3">:columns</span> =&gt; <span class="br0">&#91;</span>column<span class="br0">&#40;</span><span class="re3">:id</span> =&gt; <span class="re3">:data</span>, <span class="re3">:title</span> =&gt; <span class="st0">&#8221;</span><span class="br0">&#41;</span><span class="br0">&#93;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re3">:data</span> =&gt; <span class="br0">&#91;</span><span class="br0">&#93;</span><span class="br0">&#41;</span> <span class="kw1">do</span> |table|<br />
&nbsp; table.<span class="me1">setUsesAlternatingRowBackgroundColors</span><span class="br0">&#40;</span><span class="kw2">true</span><span class="br0">&#41;</span><br />
&nbsp; table.<span class="me1">setGridStyleMask</span><span class="br0">&#40;</span>NSTableViewSolidHorizontalGridLineMask<span class="br0">&#41;</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
<span class="kw1">end</span></div>
<p>The last view we pack is a <code>table_view</code>.  We&#8217;re again dropping down to Cocoa for the <code>setUsesAlternatingRowBackgroundColors</code> and <code>setGridStyleMask</code>.</p>
<p>Now, the <code>table_view</code> itself. The table view is more complicated then the other widgets we&#8217;ve looked at in that it requires column and data source information.</p>
<p>The column information is provided by the <code>:columns =&gt; [column(:id =&gt; :data, :title =&gt; '')]</code> parameter. You provide an array of <code>column</code> objects which define the table columns. The <code>:title</code> will be displayed at the top of a column. If you don&#8217;t provide a <code>:title</code> the default is <em>Column</em>. We also providing an <code>:id</code> parameter to our column method. This parameter will be passed when we&#8217;re accessing our data source, and, if you&#8217;re using the default data source, used to access the specific column information. We&#8217;ll dig into columns and their relations to data sources in a moment.</p>
<p>There are two ways to provide data source information. You can either pass an array, which is what we&#8217;re doing here or provide your own data source object. </p>
<p>If you decide to define your own data source then it must respond to <code>numberOfRowsInTable(tableView) => integer</code> and <code>tableView(view, objectValueForTableColumn:column, row:i) => string</code>. </p>
<p>If you opt to use the default data source then you provide an array of hashes. The keys into the hash are the <code>:id</code> values set when we created our columns.</p>
<p><img src="http://everburning.com/wp-content/uploads/2009/05/picture-11-300x64.png" alt="picture-11" title="picture-11" width="300" height="64" class="alignright size-medium wp-image-489" />Hopefully an example will make this a bit clearer. Lets say we want to create a table with three columns, name, age and sex. We would define our table as:</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="re1">@table</span> = table_view<span class="br0">&#40;</span><span class="re3">:columns</span> =&gt; <span class="br0">&#91;</span>column<span class="br0">&#40;</span><span class="re3">:id</span> =&gt; <span class="re3">:name</span>, <span class="re3">:title</span> =&gt; <span class="st0">&#8216;Name&#8217;</span><span class="br0">&#41;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;column<span class="br0">&#40;</span><span class="re3">:id</span> =&gt; <span class="re3">:age</span>, <span class="re3">:title</span> =&gt; <span class="st0">&#8216;Age&#8217;</span><span class="br0">&#41;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;column<span class="br0">&#40;</span><span class="re3">:id</span> =&gt; <span class="re3">:sex</span>, <span class="re3">:title</span> =&gt; <span class="st0">&#8216;Sex&#8217;</span><span class="br0">&#41;</span><span class="br0">&#93;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re3">:data</span> =&gt; <span class="br0">&#91;</span><span class="br0">&#123;</span> <span class="re3">:name</span> =&gt; <span class="st0">&#8216;Betty Sue&#8217;</span>, <span class="re3">:age</span> =&gt; <span class="nu0">29</span>, <span class="re3">:sex</span> =&gt; <span class="st0">&#8216;F&#8217;</span> <span class="br0">&#125;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span> <span class="re3">:name</span> =&gt; <span class="st0">&#8216;Brandon Oberon&#8217;</span>, <span class="re3">:age</span> =&gt; <span class="nu0">0.005</span>, <span class="re3">:sex</span> =&gt; <span class="st0">&#8216;M&#8217;</span> <span class="br0">&#125;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span> <span class="re3">:name</span> =&gt; <span class="st0">&#8216;Sally Joe&#8217;</span>, <span class="re3">:age</span> =&gt; <span class="nu0">48</span>, <span class="re3">:sex</span> =&gt; <span class="st0">&#8216;F&#8217;</span><span class="br0">&#125;</span><span class="br0">&#93;</span><span class="br0">&#41;</span></div>
<p>With the table view finished all that&#8217;s left is to define the <code>load_feed</code> method. For this first iteration we&#8217;re just going to take the content of the feed text field and load it into our table view.</p>
<div class="dean_ch" style="white-space: wrap;"><span class="kw1">def</span> load_feed<br />
&nbsp; str = <span class="re1">@feed_field</span>.<span class="me1">stringValue</span><br />
&nbsp; <span class="kw1">unless</span> str.<span class="kw2">nil</span>? || str =~ /^\s*$/<br />
&nbsp; &nbsp; <span class="re1">@table</span>.<span class="me1">dataSource</span>.<span class="me1">data</span> &lt;&lt; <span class="br0">&#123;</span>:data =&gt; str<span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="re1">@table</span>.<span class="me1">reloadData</span><br />
&nbsp; <span class="kw1">end</span><br />
<span class="kw1">end</span></div>
<p><img src="http://everburning.com/wp-content/uploads/2009/05/picture-1-276x300.png" alt="Postie V1" title="Postie V1" width="276" height="300" class="alignleft size-medium wp-image-465" />We use the Cocoa <code>stringValue</code> method to retrieve the value set into the text field. As long as it isn&#8217;t blank we append a new hash (with our <code>:data</code> key as defined in our <code>column</code> setup) into the <code>@table.dataSource.data</code>. We then call <code>@table.reloadData</code> to get the table to re-display its content.</p>
<p>That&#8217;s it. You should be able to run the application, enter some text in the text field, hit go and see the text appear in the table view. As you add more table rows, scrollbars should appear and you can scroll in the table as needed.</p>
<p>The third part of this series will be, hopefully, pulling real data back from <a href="http://postrank.com">PostRank</a> and displaying it in a custom cell in our table.</p>
<p><strong>Update:</strong> <a href="http://everburning.com/news/heating-up-with-hotcocoa-part-iii/">Part III</a> is now available.</p>

<span class="slashdigglicious">
<a href="http://slashdot.org/bookmark.pl?url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-ii%2F&amp;title=Heating+up+with+HotCocoa+Part+II" title="Slashdot It!"><img src="http://slashdot.org/favicon.ico" height="16" width="16" alt="[Slashdot]" /></a>
<a href="http://digg.com/submit?phase=2&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-ii%2F&amp;title=Heating+up+with+HotCocoa+Part+II" title="Digg This Story"><img src="http://digg.com/favicon.ico" width="16" height="16" alt="[Digg]" /></a>
<a href="http://reddit.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-ii%2F&amp;title=Heating+up+with+HotCocoa+Part+II" title="Reddit"><img src="http://reddit.com/favicon.ico" width="16" height="16" alt="[Reddit]" /></a>
<a href="http://del.icio.us/post?url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-ii%2F&amp;title=Heating+up+with+HotCocoa+Part+II" title="Save to del.icio.us" onclick="window.open('http://del.icio.us/post?v=4&amp;noui&amp;jump=close&amp;url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-ii%2F&amp;title=Heating+up+with+HotCocoa+Part+II', 'delicious', 'toolbar=no,width=700,height=400'); return false;"><img src="http://images.del.icio.us/static/img/delicious.small.gif" width="16" height="16" alt="[del.icio.us]" /></a>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-ii%2F" title="Share on Facebook"><img src="http://www.facebook.com/favicon.ico" width="16" height="16" alt="[Facebook]" /></a>
<a href="http://technorati.com/faves?add=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-ii%2F" title="Add to my Technorati Favorites"><img src="http://technorati.com/favicon.ico" width="16" height="16" alt="[Technorati]" /></a>
<a href="http://www.google.com/bookmarks/mark?op=edit&amp;output=popup&amp;bkmk=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-ii%2F&amp;title=Heating+up+with+HotCocoa+Part+II" title="Save to Google Bookmarks"><img src="http://www.google.com/favicon.ico" width="16" height="16" alt="[Google]" /></a>
<a href="http://www.stumbleupon.com/submit?url=http%3A%2F%2Feverburning.com%2Fnews%2Fheating-up-with-hotcocoa-part-ii%2F&amp;title=Heating+up+with+HotCocoa+Part+II" title="Stumble it!"><img src="http://www.stumbleupon.com/favicon.ico" width="16" height="16" alt="[StumbleUpon]" /></a>
</span>]]></content:encoded>
			<wfw:commentRss>http://everburning.com/news/heating-up-with-hotcocoa-part-ii/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

<!-- Dynamic Page Served (once) in 4.117 seconds -->

