<?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>DigitalHobbit</title>
	<atom:link href="http://www.digitalhobbit.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.digitalhobbit.com</link>
	<description>Thoughts from the Hobbit Hole</description>
	<lastBuildDate>Sun, 04 Apr 2010 16:52:50 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.5</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>A Useful UITableView Cell Creation Pattern</title>
		<link>http://www.digitalhobbit.com/2009/12/19/a-useful-uitableview-cell-creation-pattern/</link>
		<comments>http://www.digitalhobbit.com/2009/12/19/a-useful-uitableview-cell-creation-pattern/#comments</comments>
		<pubDate>Sat, 19 Dec 2009 22:00:32 +0000</pubDate>
		<dc:creator>DigitalHobbit</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Mobile]]></category>

		<guid isPermaLink="false">http://www.digitalhobbit.com/?p=361</guid>
		<description><![CDATA[Like many iPhone apps, the app I&#8217;m currently working on uses several table views. Most of these display actual lists of data, and some are used as a convenient layout mechanism for input fields, navigation, and other UI elements (similar to iPhone preference screens).

UITableView and its associated classes like UITableViewCell, UITableViewDataSource, and UITableViewDelegate are very [...]]]></description>
			<content:encoded><![CDATA[<p>Like many iPhone apps, the app I&#8217;m currently working on uses several table views. Most of these display actual lists of data, and some are used as a convenient layout mechanism for input fields, navigation, and other UI elements (similar to iPhone preference screens).</p>

<p><code>UITableView</code> and its associated classes like <code>UITableViewCell</code>, <code>UITableViewDataSource</code>, and <code>UITableViewDelegate</code> are very powerful, but they also require a fair amount of boilerplate code split across several methods. The specific matter that I am tackling in this post is the creation of cells, which happens inside the <code>[UITableViewDataSource tableView:cellForRowAtIndexPath:]</code> method. When dealing with only a single type of cell, it typically looks like this:</p>


<div class="wp_syntax"><div class="code"><pre class="objc" style="font-family:monospace;"><span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span>UITableViewCell <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>tableView<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span>UITableView <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>tableView cellForRowAtIndexPath<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><span style="color: #400080;">NSIndexPath</span> <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>indexPath <span style="color: #002200;">&#123;</span>
    <span style="color: #11740a; font-style: italic;">// See if there's an existing cell we can reuse</span>
    UITableViewCell <span style="color: #002200;">*</span>cell <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>tableView dequeueReusableCellWithIdentifier<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;Foobar&quot;</span><span style="color: #002200;">&#93;</span>;
    <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span>cell <span style="color: #002200;">==</span> <span style="color: #a61390;">nil</span><span style="color: #002200;">&#41;</span> <span style="color: #002200;">&#123;</span>
        <span style="color: #11740a; font-style: italic;">// No cell to reuse =&gt; create a new one</span>
        cell <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span><span style="color: #002200;">&#91;</span><span style="color: #002200;">&#91;</span>UITableViewCell alloc<span style="color: #002200;">&#93;</span> initWithStyle<span style="color: #002200;">:</span>UITableViewCellStyleDefault reuseIdentifier<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;Foobar&quot;</span><span style="color: #002200;">&#93;</span> autorelease<span style="color: #002200;">&#93;</span>;
&nbsp;
        <span style="color: #11740a; font-style: italic;">// Initialize cell</span>
        cell.selectionStyle <span style="color: #002200;">=</span> UITableViewCellSelectionStyleBlue;
        cell.textLabel.textColor <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>UIColor blueColor<span style="color: #002200;">&#93;</span>;
        <span style="color: #11740a; font-style: italic;">// TODO: Any other initialization that applies to all cells of this type.</span>
        <span style="color: #11740a; font-style: italic;">//       (Possibly create and add subviews, assign tags, etc.)</span>
    <span style="color: #002200;">&#125;</span>
&nbsp;
    <span style="color: #11740a; font-style: italic;">// Customize cell</span>
    cell.textLabel.text <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span><span style="color: #400080;">NSString</span> stringWithFormat<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;Row: %d&quot;</span>, indexPath.row<span style="color: #002200;">&#93;</span>;
    <span style="color: #11740a; font-style: italic;">// TODO: Any other customization</span>
    <span style="color: #11740a; font-style: italic;">//       (Possibly look up subviews by tag and populate based on indexPath.)</span>
&nbsp;
    <span style="color: #a61390;">return</span> cell;
<span style="color: #002200;">&#125;</span></pre></div></div>


<p>As you can see, there&#8217;s a lot of boilerplate code here. This works well enough with one type of cell, but if you&#8217;re dealing with multiple types of cells (particularly in a grouped table view), this approach quickly gets out of hand. You end up with a long method with large, ugly switch statements. But if you look at this method closely, you&#8217;ll notice that there are only a few cell-specific areas:</p>

<ol>
<li><p>The cell identifier (<code>MyCell</code> in my example). This is used to look up and reuse existing cells (e.g. when scrolling through a large table of items) and avoids the costly creation of new cells every time. It&#8217;s a standard cell creation pattern for the iPhone and makes a lot of sense, but it also means that the cell specific code is spread across several places.</p></li>
<li><p>The initialization code. This is where a cell of a given type is initialized for the first time. If you can get away with the standard cell styles (which cover a few different layouts of labels and images), you usually don&#8217;t need to do much here, besides setting your colors, fonts, and perhaps selection style. Otherwise, this is where you want to create and add your subviews, and assign a tag to them so you can populate them later.</p></li>
<li><p>The customization code. Given a cell with the correct properties and subviews (which may have been reused or created during this call), this is where you populate it with the correct data. This typically involves looking up some sort of data based on the indexPath, and setting it either on the cell itself (using the <code>textLabel</code>, <code>detailTextLabel</code>, or <code>imageView</code> properties) or on its subview. The latter requires looking up the subviews using the tags you&#8217;ve assigned earlier.</p></li>
</ol>

<p>With this in mind, I decided to factor out all the cell specific code, resulting in a generic method in my base view that can be used to create all the cells of my app. Here&#8217;s what that method looks like:</p>


<div class="wp_syntax"><div class="code"><pre class="objc" style="font-family:monospace;"><span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span>UITableViewCell <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>createCellForIdentifier<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><span style="color: #400080;">NSString</span> <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>identifier
                                   tableView<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span>UITableView <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>tableView
                                   indexPath<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><span style="color: #400080;">NSIndexPath</span> <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>indexPath
                                       style<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span>UITableViewCellStyle<span style="color: #002200;">&#41;</span>style
                                  selectable<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><span style="color: #a61390;">BOOL</span><span style="color: #002200;">&#41;</span>selectable <span style="color: #002200;">&#123;</span>
    UITableViewCell <span style="color: #002200;">*</span>cell <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>tableView dequeueReusableCellWithIdentifier<span style="color: #002200;">:</span>identifier<span style="color: #002200;">&#93;</span>;
    <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span>cell <span style="color: #002200;">==</span> <span style="color: #a61390;">nil</span><span style="color: #002200;">&#41;</span> <span style="color: #002200;">&#123;</span>
        cell <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span><span style="color: #002200;">&#91;</span><span style="color: #002200;">&#91;</span>UITableViewCell alloc<span style="color: #002200;">&#93;</span> initWithStyle<span style="color: #002200;">:</span>style reuseIdentifier<span style="color: #002200;">:</span>identifier<span style="color: #002200;">&#93;</span> autorelease<span style="color: #002200;">&#93;</span>;
        cell.selectionStyle <span style="color: #002200;">=</span> selectable ? UITableViewCellSelectionStyleBlue <span style="color: #002200;">:</span> UITableViewCellSelectionStyleNone;
&nbsp;
        <span style="color: #a61390;">SEL</span> initCellSelector <span style="color: #002200;">=</span> NSSelectorFromString<span style="color: #002200;">&#40;</span><span style="color: #002200;">&#91;</span><span style="color: #400080;">NSString</span> stringWithFormat<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;initCellFor%@:indexPath:&quot;</span>, identifier<span style="color: #002200;">&#93;</span><span style="color: #002200;">&#41;</span>;
        <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span><span style="color: #002200;">&#91;</span>self respondsToSelector<span style="color: #002200;">:</span>initCellSelector<span style="color: #002200;">&#93;</span><span style="color: #002200;">&#41;</span> <span style="color: #002200;">&#123;</span>
            <span style="color: #002200;">&#91;</span>self performSelector<span style="color: #002200;">:</span>initCellSelector withObject<span style="color: #002200;">:</span>cell withObject<span style="color: #002200;">:</span>indexPath<span style="color: #002200;">&#93;</span>;
        <span style="color: #002200;">&#125;</span>
    <span style="color: #002200;">&#125;</span>
&nbsp;
    <span style="color: #a61390;">SEL</span> customizeCellSelector <span style="color: #002200;">=</span> NSSelectorFromString<span style="color: #002200;">&#40;</span><span style="color: #002200;">&#91;</span><span style="color: #400080;">NSString</span> stringWithFormat<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;customizeCellFor%@:indexPath:&quot;</span>, identifier<span style="color: #002200;">&#93;</span><span style="color: #002200;">&#41;</span>;
    <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span><span style="color: #002200;">&#91;</span>self respondsToSelector<span style="color: #002200;">:</span>customizeCellSelector<span style="color: #002200;">&#93;</span><span style="color: #002200;">&#41;</span> <span style="color: #002200;">&#123;</span>
        <span style="color: #002200;">&#91;</span>self performSelector<span style="color: #002200;">:</span>customizeCellSelector withObject<span style="color: #002200;">:</span>cell withObject<span style="color: #002200;">:</span>indexPath<span style="color: #002200;">&#93;</span>;
    <span style="color: #002200;">&#125;</span>
    <span style="color: #a61390;">return</span> cell;
<span style="color: #002200;">&#125;</span></pre></div></div>


<p>Structurally, this method is very similar to the previous one. It first tries to reuse an existing cell, creating and initializing a new one if it doesn&#8217;t find one. It then customizes the cell. You&#8217;ll notice that I&#8217;m using some <code>performSelector</code> calls here, coupled with a naming convention. For example, given an identifier of <code>"Foobar"</code>, I will look for <code>initCellForFoobar:indexPath</code> and <code>customizeCellForFoobar:indexPath</code> to initialize and customize this cell respectively. A simple example might look like this:</p>


<div class="wp_syntax"><div class="code"><pre class="objc" style="font-family:monospace;"><span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span>initCellForFoobar<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span>UITableViewCell <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>cell indexPath<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><span style="color: #400080;">NSIndexPath</span> <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>indexPath <span style="color: #002200;">&#123;</span>
    cell.textLabel.textAlignment <span style="color: #002200;">=</span> UITextAlignmentCenter;
    cell.textLabel.textColor <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>UIColor blueColor<span style="color: #002200;">&#93;</span>;
    cell.textLabel.font <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>UIFont boldSystemFontOfSize<span style="color: #002200;">:</span><span style="color: #2400d9;">16.0</span><span style="color: #002200;">&#93;</span>;
<span style="color: #002200;">&#125;</span>
&nbsp;
<span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span>customizeCellForFoobar<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span>UITableViewCell <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>cell indexPath<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><span style="color: #400080;">NSIndexPath</span> <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>indexPath <span style="color: #002200;">&#123;</span>
    cell.textLabel.text <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span><span style="color: #400080;">NSString</span> stringWithFormat<span style="color: #002200;">:</span><span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;Row: %d&quot;</span>, indexPath.row<span style="color: #002200;">&#93;</span>;
<span style="color: #002200;">&#125;</span></pre></div></div>


<p>Note that both methods are optional. In some cases (particularly in table views that are used for preferences or similar types of UI elements), there&#8217;s only a single cell of any given type, so I perform the complete initialization in <code>initCellForFoobar</code> and omit the <code>customizeCellForFoobar</code> method. In other cases, I may not require any special initialization and only have a <code>customizeCellForFoobar</code> method.</p>

<p>Obviously, the example above is trivial, and both methods can get significantly longer when dealing with custom subviews. In that case I am using the same tag based approach I mentioned above: Assign a tag to each subview inside <code>initCellForFoobar</code>, then look up the subview using the tag in <code>customizeCellForFoobar</code>. But the important thing is that the code is well factored, and the cell specific code is not mixed with boilerplate code.</p>

<p>Last not least, an example of the actual <code>UITableViewDataSource</code> method to create a new cell:</p>


<div class="wp_syntax"><div class="code"><pre class="objc" style="font-family:monospace;"><span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span>UITableViewCell <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>tableView<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span>UITableView <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>tableView cellForRowAtIndexPath<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><span style="color: #400080;">NSIndexPath</span> <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>indexPath <span style="color: #002200;">&#123;</span>
    <span style="color: #400080;">NSString</span> <span style="color: #002200;">*</span>identifier;
    <span style="color: #a61390;">BOOL</span> selectable <span style="color: #002200;">=</span> <span style="color: #a61390;">YES</span>;
    UITableViewCellStyle style <span style="color: #002200;">=</span> UITableViewCellStyleDefault;
&nbsp;
    <span style="color: #a61390;">switch</span> <span style="color: #002200;">&#40;</span>indexPath.section<span style="color: #002200;">&#41;</span> <span style="color: #002200;">&#123;</span>
        <span style="color: #a61390;">case</span> <span style="color: #2400d9;">0</span><span style="color: #002200;">:</span>
            identifier <span style="color: #002200;">=</span> <span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;Foo&quot;</span>;
            <span style="color: #a61390;">break</span>;
        <span style="color: #a61390;">case</span> <span style="color: #2400d9;">1</span><span style="color: #002200;">:</span>
            identifier <span style="color: #002200;">=</span> <span style="color: #bf1d1a;">@</span><span style="color: #bf1d1a;">&quot;Bar&quot;</span>;
            selectable <span style="color: #002200;">=</span> <span style="color: #a61390;">NO</span>;
            <span style="color: #a61390;">break</span>;
    <span style="color: #002200;">&#125;</span>
&nbsp;
    <span style="color: #a61390;">return</span> <span style="color: #002200;">&#91;</span>self createCellForIdentifier<span style="color: #002200;">:</span>identifier
                               tableView<span style="color: #002200;">:</span>tableView
                               indexPath<span style="color: #002200;">:</span>indexPath
                                   style<span style="color: #002200;">:</span>style
                              selectable<span style="color: #002200;">:</span>selectable<span style="color: #002200;">&#93;</span>;
<span style="color: #002200;">&#125;</span></pre></div></div>


<p>The example above is what I would typically use for a grouped table view, where each section contains a specific type of cell. But obviously this approach supports any type of table view, grouped or non-grouped. You just need to plug in your own logic to determine the type of cell, and leave everything else to the <code>createCellForIdentifier:tableView:indexPath:style:selectable</code> method we created earlier.</p>

<p>There are probably other approaches for simplifying working with table views, but this approach has worked very well for me. It really cuts down significantly on the amount of boilerplate code and allows me to focus on the actual application specific code.</p>

<p>Any questions, suggestions for further improving this, or perhaps alternative solutions that you&#8217;ve used? Leave a comment!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.digitalhobbit.com/2009/12/19/a-useful-uitableview-cell-creation-pattern/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Building a Twitter Filter With Sinatra, Redis, and TweetStream</title>
		<link>http://www.digitalhobbit.com/2009/11/08/building-a-twitter-filter-with-sinatra-redis-and-tweetstream/</link>
		<comments>http://www.digitalhobbit.com/2009/11/08/building-a-twitter-filter-with-sinatra-redis-and-tweetstream/#comments</comments>
		<pubDate>Sun, 08 Nov 2009 08:30:11 +0000</pubDate>
		<dc:creator>DigitalHobbit</dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://www.digitalhobbit.com/?p=359</guid>
		<description><![CDATA[It&#8217;s been way too long since my last programming focused blog post, so let&#8217;s try to rectify this situation:

Background

A couple months ago, Twitter made available their Streaming API. This provides developers with a very efficient way to tap into the public Twitter stream. All you need to do is open and maintain a single HTTP [...]]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s been way too long since my last programming focused blog post, so let&#8217;s try to rectify this situation:</p>

<h2>Background</h2>

<p>A couple months ago, Twitter made available their <a href="http://apiwiki.twitter.com/Streaming-API-Documentation"><strong>Streaming API</strong></a>. This provides developers with a very efficient way to tap into the public Twitter stream. All you need to do is open and maintain a single HTTP connection, passing in a few filter parameters. Twitter then keeps streaming matching tweets to you. You have the option of either sampling the entire public stream, or passing in a list of keywords and / or user ids to track. In this post I will focus on the latter, but the basic usage remains the same.</p>

<p>My interest was piqued when I came across the excellent <a href="http://github.com/intridea/tweetstream"><strong>TweetStream library</strong></a>, which makes it trivially easy to write a Ruby client application for the Twitter Streaming API. I decided to take this opportunity to play with some other technologies and write a simple web app that displays a subset of tweets, along the lines of <a href="http://cursebird.com/">cursebird</a> or <a href="http://twistori.com/">twistori</a>.</p>

<p>The app I came up with is <a href="http://twatcher.com"><strong>Twatcher</strong></a>, so go check it out to get an idea of what I&#8217;m talking about. It (admittedly very crudely) identifies funny tweets by looking for tweets that contain the word &#8220;lol&#8221;. It then renders matching tweets using a simple UI not unlike that of twitter.com itself, and visually highlights the word &#8220;lol&#8221; in each tweet for emphasis. Perhaps most importantly, the app uses AJAX to periodically (currently every 10 seconds) pull in new tweets.</p>

<h2>Architecture</h2>

<p>In the remainder of this post, I will describe the architecture of Twatcher, along with the rationale behind it. I will also share some code snippets that should allow you to follow along and build your own Twitter filter app.</p>

<p>Given that the connection to the Twitter Streaming API has to happen in its own application (let&#8217;s call it our filter app), outside of the actual web app, we need a way for it to pass tweets to the web app. There are a couple of options here. Obviously we could store the tweets in a database like <a href="http://www.mysql.com/">MySQL</a>, and have the web app read them from there. But given the small schema (only tweets; we don&#8217;t care about users or any other relational data) and the ephemeral nature of the Twatcher app (at any given time we really only care about the N most recent tweets), this seems like overkill and leads to an unnecessarily write-heavy app. Instead, one of the various in-memory key/value stores seems like a much better fit. I first thought of <a href="http://www.danga.com/memcached/">memcached</a>, but while it would be entirely possible to build this type of app on top of memcached, it&#8217;s not ideal. When a new tweet comes in from the Streaming API, we need to append it to our in-memory list of tweets. Memcached is a very low-level data store and only supports string values, so we would have to implement lists by serializing them as YAML, JSON, or binary Ruby objects. Either way would mean that instead of writing just the latest tweet, we would always have to re-write the entire list of tweets. Similarly, on the web app side, we would always have to read the entire list, even though we may only be interested in the 5 most recent tweets (say in our AJAX action). Combined, this would lead to a fair amount of overhead on both the networking as well as Ruby processing side.</p>

<p>Luckily, there&#8217;s another data store that is perfect for this type of app: <a href="http://code.google.com/p/redis/"><strong>Redis</strong></a>. On the most basic level it can be thought of as a key/value store like memcached, so it can act pretty much like a drop-in replacement for this. But it also has first class support for basic data structures, such as sets and lists. This means that instead of reading and writing entire lists of tweets, we can append a single tweet at a time, and we can efficiently retrieve the exact number of tweets that we need on the web app side (i.e. 20 for a full page view, and 5 for an AJAX request). Redis is stable, highly performant, and has a solid, extremely easy to use Ruby library. It also supports basic persistence, although we won&#8217;t need this for our app.</p>

<p>With the filter app and data store out of the way, that leaves the actual web app. Our requirements are very humble: We will only have two actions (one for the full page of tweets and one for AJAX updates), and perhaps a few more trivial actions in the future for things like help pages. Since we&#8217;re not using a relational database, we don&#8217;t need any sort of ORM layer. While we could use Ruby on Rails, this would mean shooting sparrows with cannons. For our purposes the <a href="http://www.sinatrarb.com/"><strong>Sinatra</strong></a> micro-framework seems like a much better fit.</p>

<p>I&#8217;m a big fan of <a href="http://haml-lang.com/"><strong>HAML</strong></a>, so we&#8217;ll use this for our views. Of course there&#8217;s nothing HAML specific about our app, so you&#8217;re welcome to use ERB or your template language of choice instead.</p>

<p>We&#8217;ll use <a href="http://jquery.com/"><strong>jQuery</strong></a> as our Javascript library, mainly for AJAX requests and basic visual effects (so we can smoothly slide new tweets into the existing page). Once more, our needs are simple, and I&#8217;m sure any of the popular Javascript frameworks would be more than up to the challenge. But my personal preference is jQuery.</p>

<p>I won&#8217;t go much into the deployment side of things, but twatcher.com relies on the usual suspects: <a href="http://nginx.net/">Nginx</a> (Apache would work fine as well), <a href="http://www.modrails.com/">Passenger</a> (you&#8217;re welcome to use Mongrel, Thin, etc.), <a href="http://www.capify.org/">Capistrano</a>, and <a href="http://god.rubyforge.org/">God</a> (to start and monitor Redis and our filter app, though I may end up giving <a href="http://github.com/arya/bluepill">Bluepill</a> a try). All of this runs very smoothly on a 256MB VPS slice on <a href="http://www.webbynode.com/">Webbynode</a> (and I&#8217;m sure just as well on Slicehost or Linode). If necessary, we could easily scale up this app by bringing up additional Sinatra slices and adding <a href="http://haproxy.1wt.eu/">HAProxy</a> to the mix (or perhaps even just relying on DNS round robin).</p>

<h2>Code</h2>

<p>Now that the architectural overview is done, let&#8217;s take a look at some of the code. This isn&#8217;t the complete code base that I&#8217;m using on the site, but it&#8217;s a fully functional subset and hopefully enough to demonstrate the overall approach and get you started. Alternatively, you can grab the code from the <a href="http://github.com/digitalhobbit/twatcher-lite">twatcher-lite Github repository</a>. I will eventually make the complete project (which includes configuration options, RSpec specs, etc.) available on Github as well.</p>

<p>But first a couple of prerequisites: We need to install a bunch of gems. For a production app, I would typically unpack these into the <code>vendor</code> directory, but for now let&#8217;s just install them system-wide:</p>


<div class="wp_syntax"><div class="code"><pre class="default" style="font-family:monospace;">sudo gem install tweetstream yajl-ruby ezmobius-redis json haml rack sinatra shotgun</pre></div></div>


<p>You also need to install and start Redis. It&#8217;s easy enough, but beyond the scope of this blog post. Simply follow <a href="http://blog.grayproductions.net/articles/setting_up_the_redis_server">these instructions</a> (I highly recommend the entire <a href="http://blog.grayproductions.net/articles/using_keyvalue_stores_from_ruby">Redis article series</a> by the way), but make sure to use the latest Redis release from <a href="http://code.google.com/p/redis/">the official website</a> (currently 1.02) rather than the 1.0 version mentioned in the article.</p>

<h3>Filter App</h3>

<h4>twitter&#95;filter.rb:</h4>

<p>This is the standalone filter app that mainly relies on the TweetStream library to retrieve tweets and then pushes them to Redis. In our final app we would want to use the Daemons library to run this app as a proper daemon, but for now you should be able to simply run it directly from the command line. Note that it relies on two additional files below. Simply place all of these into the same folder.</p>

<p>Make sure to set USERNAME and PASSWORD to your actual Twitter credentials. A word of caution: Apparently Twitter only allows a single Streaming API connection for standard accounts, and they will disconnect or blacklist you if you attempt to start multiple connections. I&#8217;m using a dedicated Twitter account for production, and my regular Twitter account during development. The actual version of this file that I&#8217;m using reads the (environment specific) credentials from a YAML file, but I didn&#8217;t want to distract from the core functionality for the purpose of this tutorial.</p>


<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'tweetstream'</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#CC00FF; font-weight:bold;">File</span>.<span style="color:#9900CC;">join</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#CC00FF; font-weight:bold;">File</span>.<span style="color:#9900CC;">dirname</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#0000FF; font-weight:bold;">__FILE__</span><span style="color:#006600; font-weight:bold;">&#41;</span>, <span style="color:#996600;">'tweet_store'</span><span style="color:#006600; font-weight:bold;">&#41;</span>
&nbsp;
USERNAME = <span style="color:#996600;">&quot;my_username&quot;</span>  <span style="color:#008000; font-style:italic;"># Replace with your Twitter user</span>
PASSWORD = <span style="color:#996600;">&quot;my_password&quot;</span>  <span style="color:#008000; font-style:italic;"># and your Twitter password</span>
STORE = TweetStore.<span style="color:#9900CC;">new</span>
&nbsp;
<span style="color:#6666ff; font-weight:bold;">TweetStream::Client</span>.<span style="color:#9900CC;">new</span><span style="color:#006600; font-weight:bold;">&#40;</span>USERNAME, PASSWORD<span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">track</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'lol'</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>status<span style="color:#006600; font-weight:bold;">|</span>
  <span style="color:#008000; font-style:italic;"># Ignore replies. Probably not relevant in your own filter app, but we want</span>
  <span style="color:#008000; font-style:italic;"># to filter out funny tweets that stand on their own, not responses.</span>
  <span style="color:#9966CC; font-weight:bold;">if</span> status.<span style="color:#9900CC;">text</span> !~ <span style="color:#006600; font-weight:bold;">/</span>^@\w<span style="color:#006600; font-weight:bold;">+/</span>
    <span style="color:#008000; font-style:italic;"># Yes, we could just store the Status object as-is, since it's actually just a</span>
    <span style="color:#008000; font-style:italic;"># subclass of Hash. But Twitter results include lots of fields that we don't</span>
    <span style="color:#008000; font-style:italic;"># care about, so let's keep it simple and efficient for the web app.</span>
    STORE.<span style="color:#9900CC;">push</span><span style="color:#006600; font-weight:bold;">&#40;</span>
      <span style="color:#996600;">'id'</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> status<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#ff3333; font-weight:bold;">:id</span><span style="color:#006600; font-weight:bold;">&#93;</span>,
      <span style="color:#996600;">'text'</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> status.<span style="color:#9900CC;">text</span>,
      <span style="color:#996600;">'username'</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> status.<span style="color:#9900CC;">user</span>.<span style="color:#9900CC;">screen_name</span>,
      <span style="color:#996600;">'userid'</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> status.<span style="color:#9900CC;">user</span><span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#ff3333; font-weight:bold;">:id</span><span style="color:#006600; font-weight:bold;">&#93;</span>,
      <span style="color:#996600;">'name'</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> status.<span style="color:#9900CC;">user</span>.<span style="color:#9900CC;">name</span>,
      <span style="color:#996600;">'profile_image_url'</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> status.<span style="color:#9900CC;">user</span>.<span style="color:#9900CC;">profile_image_url</span>,
      <span style="color:#996600;">'received_at'</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#CC00FF; font-weight:bold;">Time</span>.<span style="color:#9900CC;">new</span>.<span style="color:#9900CC;">to_i</span>
    <span style="color:#006600; font-weight:bold;">&#41;</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>


<h4>tweet&#95;store.rb:</h4>

<p>This is a thin abstraction layer on top of Redis that encapsulates both pushing and retrieving tweets. This allows us to keep Redis specific persistence code out of the filter and web apps and also comes in handy for testing (which I&#8217;m not getting into in this post), as we can easily swap it out for a mock implementation.</p>

<p>Note how we&#8217;re using the <code>push_head</code> operation to push a single tweet to Redis, and <code>list_range</code> to retrieve the N most recent tweets.</p>


<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'json'</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'redis'</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#CC00FF; font-weight:bold;">File</span>.<span style="color:#9900CC;">join</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#CC00FF; font-weight:bold;">File</span>.<span style="color:#9900CC;">dirname</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#0000FF; font-weight:bold;">__FILE__</span><span style="color:#006600; font-weight:bold;">&#41;</span>, <span style="color:#996600;">'tweet'</span><span style="color:#006600; font-weight:bold;">&#41;</span>
&nbsp;
<span style="color:#9966CC; font-weight:bold;">class</span> TweetStore
&nbsp;
  REDIS_KEY = <span style="color:#996600;">'tweets'</span>
  NUM_TWEETS = <span style="color:#006666;">20</span>
  TRIM_THRESHOLD = <span style="color:#006666;">100</span>
&nbsp;
  <span style="color:#9966CC; font-weight:bold;">def</span> initialize
    <span style="color:#0066ff; font-weight:bold;">@db</span> = Redis.<span style="color:#9900CC;">new</span>
    <span style="color:#0066ff; font-weight:bold;">@trim_count</span> = <span style="color:#006666;">0</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
  <span style="color:#008000; font-style:italic;"># Retrieves the specified number of tweets, but only if they are more recent</span>
  <span style="color:#008000; font-style:italic;"># than the specified timestamp.</span>
  <span style="color:#9966CC; font-weight:bold;">def</span> tweets<span style="color:#006600; font-weight:bold;">&#40;</span>limit=<span style="color:#006666;">15</span>, since=<span style="color:#006666;">0</span><span style="color:#006600; font-weight:bold;">&#41;</span>
    <span style="color:#0066ff; font-weight:bold;">@db</span>.<span style="color:#9900CC;">list_range</span><span style="color:#006600; font-weight:bold;">&#40;</span>REDIS_KEY, <span style="color:#006666;">0</span>, limit <span style="color:#006600; font-weight:bold;">-</span> <span style="color:#006666;">1</span><span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">collect</span> <span style="color:#006600; font-weight:bold;">&#123;</span><span style="color:#006600; font-weight:bold;">|</span>t<span style="color:#006600; font-weight:bold;">|</span>
      Tweet.<span style="color:#9900CC;">new</span><span style="color:#006600; font-weight:bold;">&#40;</span>JSON.<span style="color:#9900CC;">parse</span><span style="color:#006600; font-weight:bold;">&#40;</span>t<span style="color:#006600; font-weight:bold;">&#41;</span><span style="color:#006600; font-weight:bold;">&#41;</span>
    <span style="color:#006600; font-weight:bold;">&#125;</span>.<span style="color:#9900CC;">reject</span> <span style="color:#006600; font-weight:bold;">&#123;</span><span style="color:#006600; font-weight:bold;">|</span>t<span style="color:#006600; font-weight:bold;">|</span> t.<span style="color:#9900CC;">received_at</span> <span style="color:#006600; font-weight:bold;">&lt;</span>= since<span style="color:#006600; font-weight:bold;">&#125;</span>  <span style="color:#008000; font-style:italic;"># In 1.8.7, should use drop_while instead</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
  <span style="color:#9966CC; font-weight:bold;">def</span> push<span style="color:#006600; font-weight:bold;">&#40;</span>data<span style="color:#006600; font-weight:bold;">&#41;</span>
    <span style="color:#0066ff; font-weight:bold;">@db</span>.<span style="color:#9900CC;">push_head</span><span style="color:#006600; font-weight:bold;">&#40;</span>REDIS_KEY, data.<span style="color:#9900CC;">to_json</span><span style="color:#006600; font-weight:bold;">&#41;</span>
&nbsp;
    <span style="color:#0066ff; font-weight:bold;">@trim_count</span> <span style="color:#006600; font-weight:bold;">+</span>= <span style="color:#006666;">1</span>
    <span style="color:#9966CC; font-weight:bold;">if</span> <span style="color:#006600; font-weight:bold;">&#40;</span>@trim_count <span style="color:#006600; font-weight:bold;">&gt;</span> <span style="color:#006666;">100</span><span style="color:#006600; font-weight:bold;">&#41;</span>
      <span style="color:#008000; font-style:italic;"># Periodically trim the list so it doesn't grow too large.</span>
      <span style="color:#0066ff; font-weight:bold;">@db</span>.<span style="color:#9900CC;">list_trim</span><span style="color:#006600; font-weight:bold;">&#40;</span>REDIS_KEY, <span style="color:#006666;">0</span>, NUM_TWEETS<span style="color:#006600; font-weight:bold;">&#41;</span>
      <span style="color:#0066ff; font-weight:bold;">@trim_count</span> = <span style="color:#006666;">0</span>
    <span style="color:#9966CC; font-weight:bold;">end</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>


<h4>tweet.rb:</h4>

<p>The Tweet class wraps an individual tweet&#8217;s data hash and allows us to access the data using method call syntax (<code>tweet.username</code>) rather than hash element references (<code>tweet['username']</code>). It also contains some tweet related functionality, such as generating Twitter user links, highlighting the word &#8220;lol&#8221;, and making URLs clickable.</p>


<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#9966CC; font-weight:bold;">class</span> Tweet
&nbsp;
  <span style="color:#9966CC; font-weight:bold;">def</span> initialize<span style="color:#006600; font-weight:bold;">&#40;</span>data<span style="color:#006600; font-weight:bold;">&#41;</span>
    <span style="color:#0066ff; font-weight:bold;">@data</span> = data
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
  <span style="color:#9966CC; font-weight:bold;">def</span> user_link
    <span style="color:#996600;">&quot;http://twitter.com/#{username}&quot;</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
  <span style="color:#008000; font-style:italic;"># Makes links clickable, highlights LOL, etc.</span>
  <span style="color:#9966CC; font-weight:bold;">def</span> filtered_text
    filter_lol<span style="color:#006600; font-weight:bold;">&#40;</span>filter_urls<span style="color:#006600; font-weight:bold;">&#40;</span>text<span style="color:#006600; font-weight:bold;">&#41;</span><span style="color:#006600; font-weight:bold;">&#41;</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
  private
&nbsp;
  <span style="color:#008000; font-style:italic;"># So we can call tweet.text instead of tweet['text']</span>
  <span style="color:#9966CC; font-weight:bold;">def</span> method_missing<span style="color:#006600; font-weight:bold;">&#40;</span>name<span style="color:#006600; font-weight:bold;">&#41;</span>
    <span style="color:#0066ff; font-weight:bold;">@data</span><span style="color:#006600; font-weight:bold;">&#91;</span>name.<span style="color:#9900CC;">to_s</span><span style="color:#006600; font-weight:bold;">&#93;</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
  <span style="color:#9966CC; font-weight:bold;">def</span> filter_lol<span style="color:#006600; font-weight:bold;">&#40;</span>text<span style="color:#006600; font-weight:bold;">&#41;</span>
    <span style="color:#008000; font-style:italic;"># Note that we're using a list of characters rather than just \b to avoid</span>
    <span style="color:#008000; font-style:italic;"># replacing LOL inside a URL.</span>
    text.<span style="color:#CC0066; font-weight:bold;">gsub</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#006600; font-weight:bold;">/</span>^<span style="color:#006600; font-weight:bold;">&#40;</span>.<span style="color:#006600; font-weight:bold;">*</span><span style="color:#006600; font-weight:bold;">&#91;</span>\s\.\,\;<span style="color:#006600; font-weight:bold;">&#93;</span><span style="color:#006600; font-weight:bold;">&#41;</span>?<span style="color:#006600; font-weight:bold;">&#40;</span>lol<span style="color:#006600; font-weight:bold;">&#41;</span><span style="color:#006600; font-weight:bold;">&#40;</span>\b<span style="color:#006600; font-weight:bold;">&#41;</span><span style="color:#006600; font-weight:bold;">/</span>i, <span style="color:#996600;">'<span style="color:#000099;">\1</span>&lt;span class=&quot;lol&quot;&gt;<span style="color:#000099;">\2</span>&lt;/span&gt;<span style="color:#000099;">\3</span>'</span><span style="color:#006600; font-weight:bold;">&#41;</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
  <span style="color:#9966CC; font-weight:bold;">def</span> filter_urls<span style="color:#006600; font-weight:bold;">&#40;</span>text<span style="color:#006600; font-weight:bold;">&#41;</span>
    <span style="color:#008000; font-style:italic;"># The regex could probably still be improved, but this seems to do the</span>
    <span style="color:#008000; font-style:italic;"># trick for most cases.</span>
    text.<span style="color:#CC0066; font-weight:bold;">gsub</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#006600; font-weight:bold;">/</span><span style="color:#006600; font-weight:bold;">&#40;</span>https?:\<span style="color:#006600; font-weight:bold;">/</span>\<span style="color:#006600; font-weight:bold;">/</span>\w<span style="color:#006600; font-weight:bold;">+</span><span style="color:#006600; font-weight:bold;">&#40;</span>\.\w<span style="color:#006600; font-weight:bold;">+</span><span style="color:#006600; font-weight:bold;">&#41;</span><span style="color:#006600; font-weight:bold;">+</span><span style="color:#006600; font-weight:bold;">&#40;</span>\<span style="color:#006600; font-weight:bold;">/</span><span style="color:#006600; font-weight:bold;">&#91;</span>\w\<span style="color:#006600; font-weight:bold;">+</span>\<span style="color:#006600; font-weight:bold;">-</span>\,\<span style="color:#006600; font-weight:bold;">%</span><span style="color:#006600; font-weight:bold;">&#93;</span><span style="color:#006600; font-weight:bold;">+</span><span style="color:#006600; font-weight:bold;">&#41;</span><span style="color:#006600; font-weight:bold;">*</span><span style="color:#006600; font-weight:bold;">&#40;</span>\?<span style="color:#006600; font-weight:bold;">&#91;</span>\w\<span style="color:#006600; font-weight:bold;">&#91;</span>\<span style="color:#006600; font-weight:bold;">&#93;</span><span style="color:#006600; font-weight:bold;">&#93;</span><span style="color:#006600; font-weight:bold;">+</span><span style="color:#006600; font-weight:bold;">&#40;</span>=\w<span style="color:#006600; font-weight:bold;">*</span><span style="color:#006600; font-weight:bold;">&#41;</span>?<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#006600; font-weight:bold;">&amp;</span>\w<span style="color:#006600; font-weight:bold;">+</span><span style="color:#006600; font-weight:bold;">&#40;</span>=\w<span style="color:#006600; font-weight:bold;">*</span><span style="color:#006600; font-weight:bold;">&#41;</span>?<span style="color:#006600; font-weight:bold;">&#41;</span><span style="color:#006600; font-weight:bold;">*</span><span style="color:#006600; font-weight:bold;">&#41;</span>?<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#008000; font-style:italic;">#\w+)?)/i, '&lt;a href=&quot;\1&quot;&gt;\1&lt;/a&gt;')</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>


<h3>Web App</h3>

<h4>twatcher.rb:</h4>

<p>This is the actual Sinatra web app. This is the entire app (minus the views), so perhaps now you can see why we&#8217;re using Sinatra instead of a full-blown Rails app. The views follow below.</p>

<p>Note that our two actions both return tweets. The main difference is that the <code>/latest</code> action (which is used by AJAX requests) only returns up to 5 tweets, and only if they&#8217;re newer than the specified date. It also omits the layout and specifies a special CSS class named <code>latest</code>. This allows us to initially hide the new tweets and then make them visible using a nice Javascript slide effect.</p>


<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'sinatra'</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'haml'</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#CC00FF; font-weight:bold;">File</span>.<span style="color:#9900CC;">join</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#CC00FF; font-weight:bold;">File</span>.<span style="color:#9900CC;">dirname</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#0000FF; font-weight:bold;">__FILE__</span><span style="color:#006600; font-weight:bold;">&#41;</span>, <span style="color:#996600;">'tweet_store'</span><span style="color:#006600; font-weight:bold;">&#41;</span>
&nbsp;
STORE = TweetStore.<span style="color:#9900CC;">new</span>
&nbsp;
get <span style="color:#996600;">'/'</span> <span style="color:#9966CC; font-weight:bold;">do</span>
  <span style="color:#0066ff; font-weight:bold;">@tweets</span> = STORE.<span style="color:#9900CC;">tweets</span>
  haml <span style="color:#ff3333; font-weight:bold;">:index</span>
<span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
get <span style="color:#996600;">'/latest'</span> <span style="color:#9966CC; font-weight:bold;">do</span>
  <span style="color:#008000; font-style:italic;"># We're using a Javascript variable to keep track of the time the latest</span>
  <span style="color:#008000; font-style:italic;"># tweet was received, so we can request only newer tweets here. Might want</span>
  <span style="color:#008000; font-style:italic;"># to consider using Last-Modified HTTP header as a slightly cleaner</span>
  <span style="color:#008000; font-style:italic;"># solution (but requires more jQuery code).</span>
  <span style="color:#0066ff; font-weight:bold;">@tweets</span> = STORE.<span style="color:#9900CC;">tweets</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#006666;">5</span>, <span style="color:#006600; font-weight:bold;">&#40;</span>params<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#ff3333; font-weight:bold;">:since</span><span style="color:#006600; font-weight:bold;">&#93;</span> <span style="color:#006600; font-weight:bold;">||</span> <span style="color:#006666;">0</span><span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">to_i</span><span style="color:#006600; font-weight:bold;">&#41;</span>
  <span style="color:#0066ff; font-weight:bold;">@tweet_class</span> = <span style="color:#996600;">'latest'</span>  <span style="color:#008000; font-style:italic;"># So we can hide and animate</span>
  haml <span style="color:#ff3333; font-weight:bold;">:tweets</span>, <span style="color:#ff3333; font-weight:bold;">:layout</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#0000FF; font-weight:bold;">false</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>


<h4>views/layout.haml:</h4>

<p>A pretty simple layout.</p>


<div class="wp_syntax"><div class="code"><pre class="default" style="font-family:monospace;">!!! Strict
%html{:xmlns=&gt; &quot;http://www.w3.org/1999/xhtml&quot;, 'xml:lang' =&gt; &quot;en&quot;, :lang =&gt; &quot;en&quot;}
  %head
    %meta{'http-equiv' =&gt; &quot;Content-Type&quot;, 'content' =&gt; &quot;text/html; charset=utf-8&quot;}
    %title twatcher
    %link{:rel =&gt; 'stylesheet', :href =&gt; '/stylesheets/style.css', :type =&gt; 'text/css'}
    %script{:type =&gt; 'text/javascript', :src =&gt; 'http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js'}
  %body
    #container
      #content
        = yield</pre></div></div>


<h4>views/index.haml:</h4>

<p>The actual HTML content is pretty minimal: A heading and a list of tweets, which is included from a separate file (below) so we can reuse it for the AJAX action. We&#8217;re also inlining some jQuery code to refresh the tweets every 10 seconds. We insert the new tweets at the beginning, but remember that we&#8217;re using a CSS class to initially hide them. We then call <code>slideDown</code> to make them visible using a nice slide effect. We also trim the list of tweets at 50 to prevent the page from getting too long.</p>


<div class="wp_syntax"><div class="code"><pre class="default" style="font-family:monospace;">:javascript
  function refreshTweets() {
    $.get('/latest', {since: window.latestTweet}, function(data) {
      $('.tweets').prepend(data);
      $('.latest').slideDown('slow');
      $('.tweets li:gt(50)').remove();
&nbsp;
      setTimeout(refreshTweets, 10000);
    });
  }
  $(function() {
    setTimeout(refreshTweets, 10000);
  });
&nbsp;
%h1 Recent LOL Tweets
%ul.tweets
  = haml :tweets, :layout =&gt; false</pre></div></div>


<h4>views/tweets.haml:</h4>

<p>Simply renders a list item for each tweet, with some basic CSS for styling purposes. I wouldn&#8217;t normally hardcode <code>height</code> and <code>width</code> for an <code>img</code> tag (and instead let CSS handle this), but for the purpose of this tutorial I wanted the page to render decently without a style sheet, and the Twitter profile pictures can be pretty large, making it look weird.</p>

<p>We also emit some simple Javascript to record the timestamp of the most recent tweet. We pass this into our AJAX request in <code>index.haml</code> above. An alternative solution (perhaps slightly cleaner from an HTTP perspective) would be to use the Last-Modified HTTP header instead of a Javascript variable, but this would mean messing with date parsing (never fun&#8230;) and also result in slightly more complex jQuery code, so I opted for the simpler solution.</p>


<div class="wp_syntax"><div class="code"><pre class="default" style="font-family:monospace;">- @tweets.each do |tweet|
  %li.tweet{:class =&gt; @tweet_class}
    %span.avatar
      %a{:href =&gt; tweet.user_link}
        %img{:src =&gt; tweet.profile_image_url, :alt =&gt; tweet.username, :height =&gt; 48, :width =&gt; 48}
    %span.main
      %span.text= tweet.filtered_text
      %span.meta== &amp;#8212; #{tweet.name} (&lt;a href=&quot;#{tweet.user_link}&quot;&gt;@#{tweet.username}&lt;/a&gt;)
&nbsp;
- if !@tweets.empty?
  :javascript
    window.latestTweet = #{@tweets[0].received_at};</pre></div></div>


<h4>public/stylesheets/style.css:</h4>

<p>The stylesheet is pretty basic, but since this blog post is already way too long, I&#8217;m not going to reproduce it here. Simply <a href="http://twatcher.com/stylesheets/style.css">grab the live one</a> instead.</p>

<h3>Putting it all together</h3>

<p>You should now have a bunch of Ruby files in the same folder, and three HAML files in a <code>views</code> subdirectory. Make sure you have started Redis according to the instructions above. Then open two shells:</p>

<p>In your first shell, start the filter app:</p>


<div class="wp_syntax"><div class="code"><pre class="default" style="font-family:monospace;">ruby twitter_filter.rb</pre></div></div>


<p>The app should start and continue running until you hit CTRL+C.</p>

<p>In your second shell, start the web app. You could simply start it using:</p>


<div class="wp_syntax"><div class="code"><pre class="default" style="font-family:monospace;">ruby twatcher.rb</pre></div></div>


<p>However, assuming you&#8217;ve installed the Shotgun gem according to the instructions above, I recommend using the following command instead:</p>


<div class="wp_syntax"><div class="code"><pre class="default" style="font-family:monospace;">shotgun twatcher.rb</pre></div></div>


<p>This will cause Sinatra to automatically reload modified files during development, similar to the default behavior in Rails.</p>

<p>If everything started successfully, you should now be able to bring up the site in your browser. If you&#8217;ve started the web app by itself, hit <a href="http://localhost:4567/">port 4567</a>. With Shotgun, hit <a href="http://localhost:9393/">port 9393</a> instead.</p>

<h2>Conclusion</h2>

<p>I hope you can appreciate how little code it took us to implement a complete Twitter filter web application, complete with AJAX updates. I count around 150 lines of code, and this includes plenty of comments and whitespace (granted, including the stylesheet it would be closer to 300 lines).</p>

<p>I also hope I&#8217;ve managed to pique your curiosity about Redis, Sinatra, and the TweetStream library. Many of us (myself included) tend to stick with the tools we&#8217;re familiar with, such as Rails and MySQL. But often, surprisingly elegant solutions emerge when using better-suited (and often simpler) tools.</p>

<p>Personally, I am excited about adding Redis and Sinatra to my standard toolset. I am also curious about what other types of applications might be able to get away with simple, ephemeral solutions like this. Definitely something worth exploring&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.digitalhobbit.com/2009/11/08/building-a-twitter-filter-with-sinatra-redis-and-tweetstream/feed/</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
		<item>
		<title>Magic Mouse</title>
		<link>http://www.digitalhobbit.com/2009/11/07/magic-mouse/</link>
		<comments>http://www.digitalhobbit.com/2009/11/07/magic-mouse/#comments</comments>
		<pubDate>Sat, 07 Nov 2009 08:03:30 +0000</pubDate>
		<dc:creator>DigitalHobbit</dc:creator>
				<category><![CDATA[Hardware]]></category>
		<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://www.digitalhobbit.com/?p=354</guid>
		<description><![CDATA[


Yesterday, Amazon finally delivered the Magic Mouse that I had pre-ordered a couple weeks ago. (Since I was in the market for a Bluetooth mouse anyway, this seemed like a perfect excuse to pick one up&#8230;)

The final verdict is still out, but I figured I&#8217;d share my initial impressions. Obviously, the biggest feature the Magic [...]]]></description>
			<content:encoded><![CDATA[<div style="float:right"><a href="http://www.amazon.com/gp/product/B002TLTGM6?ie=UTF8&#038;tag=digitalhobbit-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=B002TLTGM6"><img border="0" src="/wp-content/uploads/2009/11/31wszJhG7hL._SL160_.jpg"/></a><img src="http://www.assoc-amazon.com/e/ir?t=digitalhobbit-20&#038;l=as2&#038;o=1&#038;a=B002TLTGM6" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />
</div>

<p>Yesterday, Amazon finally delivered the Magic Mouse that I had pre-ordered a couple weeks ago. (Since I was in the market for a Bluetooth mouse anyway, this seemed like a perfect excuse to pick one up&#8230;)</p>

<p>The final verdict is still out, but I figured I&#8217;d share my initial impressions. Obviously, the biggest feature the Magic Mouse adds is Multi-Touch, very much like the iPhone or iPod Touch. And I can confirm that this indeed works flawlessly. Flicking the finger up or down to scroll through windows is an amazing improvement over using a mouse wheel, let along a scroll bar. Momentum scrolling works just like on the iPhone, feels very intuitive, and adds a tangible touch to most applications.</p>

<p>I haven&#8217;t gotten used to the two-finger sideways swipe yet, for example to navigate back and forward in the browser. It works well enough, but I&#8217;m used to using the keyboard to navigate back and forward or switch tabs, so I don&#8217;t typically reach out for the mouse for this task. But if you usually do, it should come in handy.</p>

<p>I&#8217;ve never owned a Bluetooth mouse before and wasn&#8217;t 100% sure what to expect, but I have no complaints at all about this. Pairing it with my Macbook was very easy, and it works just as well as a USB mouse, without any annoying cables. This is especially handy for me because I tend to frequently switch the mouse between my left and right hands to suppress RSI symptoms, and not having to deal with a cable definitely makes this easier.</p>

<p>Now for the things that are not that great:</p>

<p>Most importantly, the tracking is way too slow, almost to the point of not being usable at all. The acceleration is high enough, so when moving the mouse quickly it is possible to move it from one edge of the screen to the other without having to lift it. But this doesn&#8217;t help for finer movements, such as selecting an item from a menu or a toolbar. Many people are <a href="http://search.twitter.com/search?q=magic+mouse+tracking+slow">complaining about this issue</a>, but thankfully <a href="http://reviews.cnet.com/8301-13727_7-10392736-263.html?part=rss&amp;tag=feed&amp;subj=MacFixIt">this article describes a workaround</a> in form of a terminal command to change the scaling factor. You can also use the <a href="http://www.benh57.com//mousezoom.html">MouseZoom</a> tool to accomplish the same thing using a convenient preference pane. It&#8217;s old but works just fine in Snow Leopard.</p>

<p>I&#8217;m now running on the highest possible scaling setting, which is a major improvement over the standard configuration, though I might end up tuning things down a bit. Overall I&#8217;ve never been very happy with the mouse support in OSX. The acceleration curve just feels off to me &#8212; it starts off too slow, then accelerates too quickly. <a href="http://db.tidbits.com/article/8893">This article describes this issue in detail</a>, and I agree with it. It also describes some solutions, such as <a href="http://www.usboverdrive.com/">USB Overdrive</a> and <a href="http://plentycom.jp/en/steermouse/">SteerMouse</a>. I have bought and used SteerMouse in the past, and it worked pretty well for me then. It doesn&#8217;t support the Magic Mouse yet, and according to the website they are currently evaluating whether to add this functionality. I&#8217;m not sure if any of these options are compatible with the Magic Mouse, which I&#8217;m sure requires its own driver to support the Multi-Touch functionality. I&#8217;ll have to experiment with this&#8230;</p>

<p>The mouse button (a single button that is sensitive to where it&#8217;s being touched and can therefore emulate left and right mouse buttons) works ok, but clicking requires a bit too much effort for my taste (my previous Logitech mouse was significantly more sensitive, requiring barely any pressure). In fact I find operations that require moving the mouse with the button pressed (such as when selecting several paragraphs of text in a document) somewhat difficult.</p>

<p>Last not least, I miss having a middle mouse button, mainly to open links in new browser tabs or copy &amp; paste text in the terminal. I suppose I will have to get used to holding down the Command key instead. I wonder if the mouse surface is sensitive enough that it could differentiate between left, middle, and right clicks.</p>

<p>Moving the mouse over my wooden desk is fairly noisy. Unlike my Logitech mouse, which was quiet and smooth, the Magic Mouse almost feels like it&#8217;s scraping the surface. I may end up getting a mouse pad for this reason, but this seems to defeat the purpose of the new and improved laser technology that works on any surface.</p>

<p>In terms of ergonomics it&#8217;s not the best mouse in the world, but it&#8217;s not horrible either. It&#8217;s still too early to tell how well I adjust to it. We&#8217;ll see in a few weeks&#8230;</p>

<p>In conclusion, the Magic Mouse is definitely a fascinating piece of technology, and I am excited about future iterations of this or similar products. I absolutely love the touch based interface of my iPhone, and the Magic Mouse does a good job at bringing some of this to the desktop. But it certainly has its share of flaws. Hopefully a driver update will resolve the slow tracking issue soon, but in the mean time the workaround described above will need to suffice. As for the other issues: If you can, I recommend you try out the mouse in the Apple Store first, to see how it feels for you.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.digitalhobbit.com/2009/11/07/magic-mouse/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Upgrading an older MacBook Pro to 6GB of RAM</title>
		<link>http://www.digitalhobbit.com/2009/09/05/upgrading-an-older-macbook-pro-to-6gb-of-ram/</link>
		<comments>http://www.digitalhobbit.com/2009/09/05/upgrading-an-older-macbook-pro-to-6gb-of-ram/#comments</comments>
		<pubDate>Sat, 05 Sep 2009 17:23:30 +0000</pubDate>
		<dc:creator>DigitalHobbit</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Hardware]]></category>

		<guid isPermaLink="false">http://www.digitalhobbit.com/?p=346</guid>
		<description><![CDATA[If you own a MacBook Pro and would like to upgrade to more than 4GB of RAM but think that your model does not support this, you may want to read the rest of this article.

I bought a MacBook Pro 17&#8243; in April 2008, as my primary development machine. I knew that the standard config [...]]]></description>
			<content:encoded><![CDATA[<p>If you own a MacBook Pro and would like to upgrade to more than 4GB of RAM but think that your model does not support this, you may want to read the rest of this article.</p>

<p>I bought a MacBook Pro 17&#8243; in April 2008, as my primary development machine. I knew that the standard config with 2GB of RAM wouldn&#8217;t be enough for my purposes, but I also wasn&#8217;t about to spend a ridiculous amount of money on an official memory upgrade from Apple, so I picked up <a href="http://www.newegg.com/Product/Product.aspx?Item=N82E16820231135">two cheap G.SKILL 2GB DIMMs from Newegg</a>.</p>

<p>This worked great for me so far, but even with 4GB of RAM, I occasionally ran into memory limits. For example I sometimes work on iPhone and complementary Rails apps at the same time, and having both Xcode (plus Interface Builder and the iPhone Simulator) and a Rails app, IDE, etc. running at the same time definitely uses a fair amount of memory. Especially if I use <a href="http://www.jetbrains.com/ruby/index.html">RubyMine</a> (which is pretty nice, by the way, but a major memory hog). That&#8217;s one of the reasons why I often still work with a regular text editor such as TextMate. The situation gets even worse when I need to run a virtual machine, such as for IE browser testing. And of course there are all the other memory hungry apps that tend to be running all the time (Firefox and / or Safari, iTunes, etc.).</p>

<p>The last time I researched potential memory upgrades, I quickly discovered that my model (apparently) only supports a maximum of 4GB, so I gave up.</p>

<p>But this time I complained on Twitter, and a <a href="http://twitter.com/Teucher/status/3716212099">reply</a> prompted me to research this issue more closely. Well, it turns out that many MacBook Pro models do indeed unofficially support 6GB of RAM, in form of 4GB + 2GB DIMMs. This <a href="http://guides.macrumors.com/MacBook_Pro">MacRumors Guide</a> has all the info you need. My model appears to be the Rev. E (as identified by the date of purchase, as well as the CPU frequency, video card, and video memory). And sure enough, the Rev. E and F models can handle up to 6GB of RAM.</p>

<p>Since I was already using G.SKILL memory, I opted for a 4GB G.SKILL DIMM (<a href="http://www.newegg.com/Product/Product.aspx?Item=N82E16820231202">currently $129 at NewEgg</a>). I would not recommend mixing DIMMs from different manufacturers, and in fact I have read some reports of people having trouble getting these configs to work.</p>

<p>The actual memory upgrade process is quick and easy (at least on the pre-unibody models), and Apple provides a <a href="http://support.apple.com/kb/HT1270">convenient guide</a>.</p>

<p>I should point out that due to the mixed (4GB + 2GB) memory configuration, you lose the <a href="http://en.wikipedia.org/wiki/Dual-channel_architecture">Dual Channel capability</a>. But based on what I read, this only affects certain types of apps and makes little difference in practice. I definitely didn&#8217;t notice any lower performance after the upgrade.</p>

<p>The increased memory means that my system rarely (if ever) has to swap. Now I can run my whole development stack as well as two virtual machines (Windows and Linux) and the machine is still very responsive.</p>

<p>Now I just have to find some new memory intensive applications to bring my system down to its knees&#8230; <img src='http://www.digitalhobbit.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>

<p><strong>Update:</strong> This upgrade was the single biggest bang for the buck and has made a tremendous difference on my system. Having 6GB instead of 4GB was exactly the additional RAM I needed to be able to run all my various development tools at the same time. I am now running several Rails apps and have one RubyMine as well as two Xcode projects open without any issues, along with the usual productivity software, iTunes, etc. Definitely highly recommended!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.digitalhobbit.com/2009/09/05/upgrading-an-older-macbook-pro-to-6gb-of-ram/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>iPhone Development is Fun!</title>
		<link>http://www.digitalhobbit.com/2009/05/10/iphone-development-is-fun/</link>
		<comments>http://www.digitalhobbit.com/2009/05/10/iphone-development-is-fun/#comments</comments>
		<pubDate>Mon, 11 May 2009 07:22:01 +0000</pubDate>
		<dc:creator>DigitalHobbit</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Mobile]]></category>

		<guid isPermaLink="false">http://www.digitalhobbit.com/?p=341</guid>
		<description><![CDATA[It&#8217;s been a while since I completed my first iPhone development project, and I figured I&#8217;d finally write up my initial experience with this platform. A bit late, but better than never&#8230;

To put this into perspective, here&#8217;s a brief summary of my previous professional programming background:

I worked with C++ back in University but quickly adopted [...]]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s been a while since I completed my first iPhone development project, and I figured I&#8217;d finally write up my initial experience with this platform. A bit late, but better than never&#8230;</p>

<p>To put this into perspective, here&#8217;s a brief summary of my previous professional programming background:</p>

<p>I worked with C++ back in University but quickly adopted Java in 1998 and never looked back (good riddance to pointers and manual memory management!). Java served me well for the better part of the past 10 years, but I&#8217;ve increasingly become a fan of dynamic languages, and Ruby in particular (although I&#8217;ve dabbled a bit with Python as well). These days I mainly work with Ruby (mostly Ruby on Rails, but also standalone apps such as daemons, command line apps, etc.). Among many other reasons, I just love its expressiveness over Java&#8217;s verbosity.</p>

<p>With this in mind, I was admittedly a bit hesitant at first about iPhone development using Objective-C, even though I was definitely curious about the CocoaTouch platform (and Cocoa in general).</p>

<h3>Objective-C</h3>

<p>At first glance, Objective-C syntax looks quite odd. Instead of the dot-notation for method calls that Java and Ruby use (e.g. <code>"foobar".length</code>), Objective-C uses a square bracket based message passing syntax (e.g. <code>[@"foobar" length]</code>). This is harmless enough for simple method calls, but can become confusing when using nested calls (e.g. <code>[[[MyClass alloc] init] autorelease]</code>). Apparently, Apple decided this was the case as well, so when they introduced their simplified support for properties in Objective-C 2.0, they chose to go with dot-notation. This definitely cuts down on some of the clutter (especially when setting properties), but it also leads to a slightly awkward, inhomogeneous mix of notations in the code, as dot-notation cannot be used for regular method calls (and in fact some long-time Objective-C developers are boycotting the new properties syntax for that very reason). But in the end, this is a relatively minor stylistic difference, and I eventually got used to it.</p>

<p>The other Objective-C oddity is the Smalltalk-inspired way that parameters are passed to methods. Most current programming languages pass parameters as comma-separated lists. Objective-C essentially breaks method names up into multiple segments, each of which has its own parameter. This means that you end up with method signatures like this:</p>


<div class="wp_syntax"><div class="code"><pre class="objc" style="font-family:monospace;"><span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span>insertSubview<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span>UIView <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>view atIndex<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span>NSInteger<span style="color: #002200;">&#41;</span>index;</pre></div></div>


<p>Invoking this method would look like this:</p>


<div class="wp_syntax"><div class="code"><pre class="objc" style="font-family:monospace;"><span style="color: #002200;">&#91;</span>myView insertSubview<span style="color: #002200;">:</span>mySubview atIndex<span style="color: #002200;">:</span><span style="color: #2400d9;">3</span><span style="color: #002200;">&#93;</span>;</pre></div></div>


<p>It felt weird at first, but once I got my head wrapped around this syntax, I actually kind of started to like it, as it makes method calls read like regular English sentences. I have however found that it can be difficult to come up with natural sounding method and parameter names for my own classes, so my preference would be a C-based method call syntax with named params like Python has. But in the end, I got used to this syntax fairly quickly.</p>

<p>Probably the thing that bothers me most about Objective-C / Cocoa is the overall verbosity, especially compared to a very succinct language like Ruby. This is partially because variables need to be declared, partially because Cocoa&#8217;s method names tend to be long, and of course because as a C-derived language, Objective-C simply isn&#8217;t as expressive as a language like Ruby, with its blocks, enumerations, literal syntax for creating arrays and hashes, etc.</p>

<p>Then there&#8217;s the usual duplication also found in C/C++ with its header and implementation files. For example, in Ruby I can define a property simply by using <code>attr_accessor :foo</code>. In Objectice-C, I need to explicitly declare the actual member variable as well as the property in the header file, and add a synthesize statement to the implementation file. And this is already the <em>shorthand</em> syntax for properties&#8230;</p>

<p>Perhaps the thing I was most worried about: Memory Management. Objective-C 2.0 actually introduced garbage collection, but unfortunately this isn&#8217;t supported on the iPhone. Luckily, as it turns out, Objective-C&#8217;s retain count based memory management combined with autorelease pools is actually quite elegant and a major improvement over C/C++. I won&#8217;t go into details here (there are many <a href="http://www.mobileorchard.com/iphone-memory-management/">resources</a> on this topic), but the bottom line is that as long as you adhere to the accepted conventions (particularly regarding which methods return autoreleased vs. non-autoreleased objects), you should be fine. With Instruments, Apple also provides a powerful tool to help track down actual memory leaks. Would I prefer garbage collection? Absolutely! But the situation isn&#8217;t as bad as I had feared.</p>

<p>Objective-C also provides many nice features, such as Protocols and Categories. Protocols are very similar to Java Interfaces and are heavily used for delegation (see below). Categories allow functionality to be added to existing classes. For example, you could use this mechanism to allow NSDictionary, NSArray, or other NSObject subclasses to generate a JSON representation. They remind me of a less powerful (but still convenient) version of Ruby Mixins (less powerful because Mixins allow modules to be mixed into arbitrary classes, whereas Categories are implemented for a specific class).</p>

<h3>CocoaTouch</h3>

<p>This is really the best part of iPhone development. Having mostly worked on server side code, I&#8217;ve never been much of a GUI developer. But with a few positive exceptions (such as Qt), most of the GUI frameworks I have messed with were quite painful (MFC anyone?), with horrendous amounts of unmaintainable, auto-generated code.</p>

<p>CocoaTouch (like its big brother Cocoa on the Mac) is very well thought out and makes good use of design patterns. In particular, it heavily relies on delegation rather than inheritance, which leads to significantly cleaner and more modular code. For example, instead of subclassing a UI control in order to add the desired custom behavior, you typically implement the behavior in your controller class and specify this as the delegate for the UI control.</p>

<p>Aside from a few lines of boilerplate code that are part of the project templates, there is absolutely no auto-generated code. This is mostly possible because Interface Builder stores actual, serialized object instances inside xib files and all custom behavior is injected using the delegate mechanism.</p>

<p>CocoaTouch also provides very simple, high-level APIs for many powerful features, such as the iPhone&#8217;s camera / image picker.</p>

<p>The only thing that bugs me a lot is the disconnect between the Objective-C based high-level Cocoa API and the C-based lower-level APIs. Often times, the high-level APIs are limited to a few common cases, but when you need to stray from these, you need to talk straight to the underlying low-level APIs. This leads to an awkward mix of nice, object-oriented Objective-C code and ugly, procedural C code.</p>

<h3>Development Tools</h3>

<p>The various development tools are an integral part of the iPhone SDK. In particular, Xcode is a very decent IDE. It may not be the best IDE I&#8217;ve ever used, but it gets the job done and even sports some modern IDE features (such as refactoring). I wish it had Git support, though.</p>

<p>Interface Builder is an extremely convenient tool, so I try to use it whenever it makes sense. However, I do have a few complaints: Many properties are not exposed in IB, so they have to be set manually in the code. More importantly, IB is not extensible at all. For example, I would like to be able to implement my own subclass of UIView and have its relevant properties show up in IB. If my UIView subclass defines a text property (perhaps with some sort of annotation that specifies additional, Interface Builder specific metadata), I would like to see a text field in IB. If I define a color property, I would like to see a color picker, etc. Hopefully this will be possible in a future version.</p>

<p>Instruments is a powerful tool for finding memory leaks and performance bottlenecks. I have only used this a little bit, but it&#8217;s good to know that it&#8217;s there when I need it.</p>

<p>My major complaint in this area is about the horribly complicated and error prone provisioning process. But I&#8217;ll save this rant for another blog post.</p>

<h3>Conclusion</h3>

<p>Overall, iPhone development is a lot of fun, and a very refreshing change from web development. The iPhone is an amazingly powerful device, and after the initial learning curve, it is surprisingly easy to leverage many of its unique abilities, such as the accelerometer, multi-touch, Internet connection, and more.</p>

<p>I would love to be able to use CocoaTouch in conjunction with Ruby (such as <a href="http://rubycocoa.sourceforge.net/">RubyCocoa</a> or <a href="http://www.macruby.org/">MacRuby</a>. But given that this is not an option on the iPhone, Objective-C is a decent alternative.</p>

<p>Now that I got this post out of the way, I plan on posting more about specific iPhone development topics. Stay tuned!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.digitalhobbit.com/2009/05/10/iphone-development-is-fun/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Workling and Amazon SQS</title>
		<link>http://www.digitalhobbit.com/2009/04/04/workling-and-amazon-sqs/</link>
		<comments>http://www.digitalhobbit.com/2009/04/04/workling-and-amazon-sqs/#comments</comments>
		<pubDate>Sun, 05 Apr 2009 01:29:05 +0000</pubDate>
		<dc:creator>DigitalHobbit</dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://www.digitalhobbit.com/?p=335</guid>
		<description><![CDATA[If you need to perform any time consuming work in your Rails actions, you&#8217;ll probably want to offload this into a background job. There are many different frameworks to help with this, and the one we use is Workling. The nice thing about Workling is that it provides an abstraction layer that allows you to [...]]]></description>
			<content:encoded><![CDATA[<p>If you need to perform any time consuming work in your Rails actions, you&#8217;ll probably want to offload this into a background job. There are many different frameworks to help with this, and the one we use is <a href="http://github.com/purzelrakete/workling/tree/master">Workling</a>. The nice thing about Workling is that it provides an abstraction layer that allows you to decouple your actual background job implementation from the background execution strategy. For example, in our development environment we are using the Spawn runner (which simply forks the Rails process for each background job), but we need a proper, queue based runner in production. Up until recently we were using the Starling runner, which worked pretty well for a small set of machines.</p>

<p>However, after migrating our infrastructure to Amazon EC2 and rapidly scaling up the number of app servers, we figured it would be great to take advantage of <a href="http://aws.amazon.com/sqs/">Amazon SQS</a> (Simple Queue Service), rather than maintaining our own queue servers. Fortunately, Workling&#8217;s plugin architecture makes it very easy to implement your own clients, so writing an SQS Workling client turned out to be fairly straightforward.</p>

<p>If you are interested in using this in your own project, simply use <a href="http://github.com/digitalhobbit/workling/tree/master">my Workling fork on Github</a>. I haven&#8217;t decided yet whether to extract this into a separate plugin that you could install alongside Workling, so let me know if you have a strong preference. I&#8217;ll also get in touch with the Workling developers to see if they might be interested in pulling this feature into the main code base. But for now, you can simply install it by following the regular Workling plugin installation instructions, except using my Workling fork:</p>


<div class="wp_syntax"><div class="code"><pre class="default" style="font-family:monospace;">script/plugin install git://github.com/digitalhobbit/workling.git</pre></div></div>


<p>The <a href="http://github.com/digitalhobbit/workling/tree/master">README</a> includes detailed instructions on configuring the client, but it&#8217;s actually very easy:</p>

<p>Install the RightAws gem:</p>


<div class="wp_syntax"><div class="code"><pre class="default" style="font-family:monospace;">sudo gem install right_aws</pre></div></div>


<p>Configure Workling to use the SqsClient. Add this to your environment:</p>


<div class="wp_syntax"><div class="code"><pre class="default" style="font-family:monospace;">Workling::Remote.dispatcher = Workling::Remote::Runners::ClientRunner.new
Workling::Remote.dispatcher.client = Workling::Clients::SqsClient.new</pre></div></div>


<p>Add your AWS key id and secret key to <code>workling.yml</code>:</p>


<div class="wp_syntax"><div class="code"><pre class="default" style="font-family:monospace;">production:
  sqs_options:
    aws_access_key_id: &lt;your AWS access key id&gt;
    aws_secret_access_key: &lt;your AWS secret access key&gt;</pre></div></div>


<p>You can optionally override the following settings, although the defaults
will likely be sufficient:</p>


<div class="wp_syntax"><div class="code"><pre class="default" style="font-family:monospace;">    # Queue names consist of an optional prefix, followed by the environment
    # and the name of the key.
    prefix: foo_
&nbsp;
    # The number of SQS messages to retrieve at once. The maximum and default
    # value is 10.
    messages_per_req: 10
&nbsp;
    # The SQS visibility timeout for retrieved messages. Defaults to 30 seconds.
    visibility_timeout: 30
&nbsp;
    # The number of seconds to reserve for deleting a message from the qeueue.
    # If buffered messages are getting too close to the visibility timeout,
    # we drop them so they will get picked up the next time a worker retrieves
    # messages, in order to avoid duplicate processing.
    visibility_reserve: 10
&nbsp;
    # Below are various retry and timeout settings for the underlying right_aws
    # and right_http_connection libraries. You may want to tweak these based on
    # your workling usage. I recommend fairly low values, as large values can
    # cause your Rails actions to hang in case of SQS issues.
&nbsp;
    # Maximum number of seconds to retry high level SQS errors. right_aws
    # automatically retries using exponential back-off.
    aws_reiteration_time: 2
&nbsp;
    # Low-level HTTP retry / timeout settings.
    http_retry_count: 2
    http_retry_delay: 1
    http_open_timeout: 2
    http_read_timeout: 10</pre></div></div>


<p>Now start the Workling Client:</p>


<div class="wp_syntax"><div class="code"><pre class="default" style="font-family:monospace;">script/workling_client start</pre></div></div>


<p>That&#8217;s it!</p>

<p>There are still some caveats, such as the fact that messages are currently deleted from the queue at the beginning of processing rather than at the end (unfortunately Workling currently doesn&#8217;t provide the necessary hooks). This is good enough for us (we&#8217;re not relying on our background jobs for anything highly critical), but you probably don&#8217;t want to build your financial transaction processing on top of this&#8230; If there&#8217;s demand for this, I may try to extend Workling at some point to fix this issue.</p>

<p>Please leave a comment if you find this useful or have any other feedback. Also let me know if you encounter any bugs, or better yet, update my test case to reproduce the issue or send me a Github pull request with your fix. <img src='http://www.digitalhobbit.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://www.digitalhobbit.com/2009/04/04/workling-and-amazon-sqs/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Rails, Facebooker, and memcached Session Store</title>
		<link>http://www.digitalhobbit.com/2009/02/28/rails-facebooker-and-memcached-session-store/</link>
		<comments>http://www.digitalhobbit.com/2009/02/28/rails-facebooker-and-memcached-session-store/#comments</comments>
		<pubDate>Sat, 28 Feb 2009 18:48:23 +0000</pubDate>
		<dc:creator>DigitalHobbit</dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://www.digitalhobbit.com/?p=324</guid>
		<description><![CDATA[We&#8217;re using Facebooker for our Rails based Facebook apps. However, we ran into a problem after migrating our session store to the MemCacheStore. Every request was producing the following stacktrace:


/!\ FAILSAFE /!\  Sat Feb 28 10:24:09 -0800 2009
  Status: 500 Internal Server Error
  session_id '2.wYavYw2U9jBTnFOcX9rjMw__.86400.1235934000-1023424742' is invalid
    /Library/Ruby/Gems/1.8/gems/actionpack-2.2.2/lib/action_controller/session/mem_cache_store.rb:54:in `initialize'
 [...]]]></description>
			<content:encoded><![CDATA[<p>We&#8217;re using <a href="http://github.com/mmangino/facebooker/tree/master">Facebooker</a> for our Rails based Facebook apps. However, we ran into a problem after migrating our session store to the MemCacheStore. Every request was producing the following stacktrace:</p>


<div class="wp_syntax"><div class="code"><pre class="default" style="font-family:monospace;">/!\ FAILSAFE /!\  Sat Feb 28 10:24:09 -0800 2009
  Status: 500 Internal Server Error
  session_id '2.wYavYw2U9jBTnFOcX9rjMw__.86400.1235934000-1023424742' is invalid
    /Library/Ruby/Gems/1.8/gems/actionpack-2.2.2/lib/action_controller/session/mem_cache_store.rb:54:in `initialize'
    /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/cgi/session.rb:273:in `new'
    /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/cgi/session.rb:273:in `initialize_without_cgi_reader'
    /Library/Ruby/Gems/1.8/gems/actionpack-2.2.2/lib/action_controller/cgi_ext/session.rb:19:in `initialize_aliased_by_facebooker'
    /Users/mirko/Work/questionx/vendor/plugins/facebooker/lib/facebooker/rails/facebook_session_handling.rb:35:in `initialize'
    /Library/Ruby/Gems/1.8/gems/actionpack-2.2.2/lib/action_controller/cgi_process.rb:94:in `new'</pre></div></div>


<p>It turns out that Facebook session ids include dots and underscores, which the MemCacheStore chokes on. Luckily I came across <a href="http://rubyforge.org/pipermail/facebooker-talk/2008-November/001304.html">this forum post</a>. The solution below is based on the approach outlined in the post, with a few modifications to more cleanly hook the patch into the method chain rather than replacing the original functionality completely. Simply drop the code below into an initializer (e.g. <code>config/initializers/facebooker_memcache_session_patch.rb</code>):</p>


<div class="wp_syntax"><div class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#9966CC; font-weight:bold;">class</span> <span style="color:#CC00FF; font-weight:bold;">CGI</span>
  <span style="color:#9966CC; font-weight:bold;">class</span> Session
     <span style="color:#9966CC; font-weight:bold;">class</span> MemCacheStore
       <span style="color:#9966CC; font-weight:bold;">def</span> check_id_with_strip_fb_chars<span style="color:#006600; font-weight:bold;">&#40;</span>id<span style="color:#006600; font-weight:bold;">&#41;</span>
         check_id_without_strip_fb_chars<span style="color:#006600; font-weight:bold;">&#40;</span>id.<span style="color:#CC0066; font-weight:bold;">gsub</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#006600; font-weight:bold;">/</span><span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#006600; font-weight:bold;">-</span>\._<span style="color:#006600; font-weight:bold;">&#93;</span><span style="color:#006600; font-weight:bold;">/</span>, <span style="color:#996600;">''</span><span style="color:#006600; font-weight:bold;">&#41;</span><span style="color:#006600; font-weight:bold;">&#41;</span>
       <span style="color:#9966CC; font-weight:bold;">end</span>
       alias_method_chain <span style="color:#ff3333; font-weight:bold;">:check_id</span>, <span style="color:#ff3333; font-weight:bold;">:strip_fb_chars</span>
     <span style="color:#9966CC; font-weight:bold;">end</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

]]></content:encoded>
			<wfw:commentRss>http://www.digitalhobbit.com/2009/02/28/rails-facebooker-and-memcached-session-store/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Merb and Rails are merging!</title>
		<link>http://www.digitalhobbit.com/2008/12/23/merb-and-rails-are-merging/</link>
		<comments>http://www.digitalhobbit.com/2008/12/23/merb-and-rails-are-merging/#comments</comments>
		<pubDate>Tue, 23 Dec 2008 20:35:40 +0000</pubDate>
		<dc:creator>DigitalHobbit</dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://www.digitalhobbit.com/?p=318</guid>
		<description><![CDATA[No, it&#8217;s not April 1st, and as far as I know, hell hasn&#8217;t frozen over either. The Merb and Rails teams have in fact announced that they will be joining forces. The end result will eventually be released as Rails 3.

Rather than repeating all the details here, below are links to the original announcements by [...]]]></description>
			<content:encoded><![CDATA[<p>No, it&#8217;s not April 1st, and as far as I know, hell hasn&#8217;t frozen over either. The Merb and Rails teams have in fact announced that they will be joining forces. The end result will eventually be released as Rails 3.</p>

<p>Rather than repeating all the details here, below are links to the original announcements by various team members:</p>

<ul>
<li><a href="http://weblog.rubyonrails.org/2008/12/23/merb-gets-merged-into-rails-3/comments/24193">DHH (Ruby on Rails Blog)</a></li>
<li><a href="http://rubyonrails.org/merb">The day Merb joined Rails (Ruby on Rails Blog)</a></li>
<li><a href="http://yehudakatz.com/2008/12/23/rails-and-merb-merge/">Yehuda Katz</a></li>
<li><a href="http://brainspl.at/articles/2008/12/23/merb-is-rails">Ezra Zygmuntowicz</a></li>
<li><a href="http://merbist.com/2008/12/23/rails-and-merb-merge/">Matt Aimonetti</a> (also includes a short FAQ Video)</li>
<li><a href="http://splendificent.com/2008/12/the-merb-rails-merger-announcement-an-inside-opinion/">Carl Lerche</a></li>
</ul>

<p>It is unclear how much of the actual Merb code will make it into Rails 3, but the important thing is that Rails will embrace many of Merb&#8217;s core principles, such as a lightweight core, performance, modularity (i.e. you&#8217;ll be able to easily swap ActiveRecord out for DataMapper or some other ORM framework), and a well-defined and stable public API that plugins can be based on.</p>

<p>I&#8217;ve been a big fan of Rails for many years, and it is certainly relatively mature and has a large developer community. At the same time, I&#8217;ve been drawn to Merb and related technologies (such as DataMapper) lately, and I strongly agree with their core principles.</p>

<p>Integrating these two frameworks will be no small feat, so the new combined team definitely has their work cut out for them. But as different as the two frameworks are, they also have a lot in common. By agreeing on this new direction, the team will be able to focus on the important tasks without having to deal with redundant functionality. I am looking forward to the new direction and am excited about trying out the first Rails 3 alpha whenever it is released.</p>

<p>Over the past few months, there&#8217;s been a fair amount of bickering between the Rails and Merb teams, and I&#8217;m all the more impressed with both teams for reaching this decision and deciding to work together. I am convinced that the Ruby Community will be a lot better for it.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.digitalhobbit.com/2008/12/23/merb-and-rails-are-merging/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Hacking the D-Link DNS-321 NAS</title>
		<link>http://www.digitalhobbit.com/2008/11/26/hacking-the-d-link-dns-321-nas/</link>
		<comments>http://www.digitalhobbit.com/2008/11/26/hacking-the-d-link-dns-321-nas/#comments</comments>
		<pubDate>Wed, 26 Nov 2008 07:11:16 +0000</pubDate>
		<dc:creator>DigitalHobbit</dc:creator>
				<category><![CDATA[Hardware]]></category>
		<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://www.digitalhobbit.com/?p=311</guid>
		<description><![CDATA[For some reason I derive immense gratification from hacking cheap commodity consumer devices and making them do much more than they were originally designed to. Not that I can&#8217;t claim all that much credit (as I generally just apply readily available hacks that others have figured out), but still&#8230;

Some of my favorite past hacks include:


Original [...]]]></description>
			<content:encoded><![CDATA[<p>For some reason I derive immense gratification from hacking cheap commodity consumer devices and making them do much more than they were originally designed to. Not that I can&#8217;t claim all that much credit (as I generally just apply readily available hacks that others have figured out), but still&#8230;</p>

<p>Some of my favorite past hacks include:</p>

<ul>
<li>Original Xbox:  I haven&#8217;t played games on it for years, but with <a href="http://xbmc.org/">XBMC</a> on it, it remains a formidable media center.</li>
<li>Buffalo WHR-G54S: Essentially a cheap Linksys router clone, but by replacing the stock firmware it can support features typically present on much more expensive routers (such as sophisticated firewall or QoS functionality). I was running the powerful <a href="http://www.dd-wrt.com/wiki/index.php/Main_Page">dd-wrt</a> firmware on it for a while, but later switched to the <a href="http://www.polarcloud.com/tomato">Tomato firmware</a> (also <a href="http://en.wikibooks.org/wiki/Tomato_Firmware">here</a>), which has most of the same features but is a lot easier to use.</li>
<li>Tivo: Back when I bought my first 40GB Tivo, I hacked it to add a 120GB drive (for a fraction of the normal cost).</li>
</ul>

<p>My latest addition is the <a href="http://www.amazon.com/gp/product/B0019OZ3OO?ie=UTF8&amp;tag=sfgeek-20&amp;link_code=as3&amp;camp=211189&amp;creative=373489&amp;creativeASIN=B0019OZ3OO">DNS-321</a> Network Storage Enclosure:</p>

<p><a href="http://www.amazon.com/gp/product/B0019OZ3OO?ie=UTF8&#038;tag=digitalhobbit-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=B0019OZ3OO"><img src="http://www.digitalhobbit.com/wp-content/uploads/2008/11/41xjj24sdpl-sl160.jpg" alt="41XJJ24sDpL._SL160_.jpg" border="0" width="151" height="160" /></a><img src="http://www.assoc-amazon.com/e/ir?t=sfgeek-20&#038;l=as2&#038;o=1&#038;a=B0019OZ3OO" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /></p>

<p>I was previously using an external USB drive attached to my Airport Extreme router, but decided that I wanted to upgrade to a less flakey and fully redundant (but still affordable) storage system for our home. The DNS-321 (or its older and slightly more featureful cousin, the DNS-323) fits the bill. Out of the box, it makes two SATA drives available over Gigabit ethernet via SMB. It supports both Raid 0 (striping) and Raid 1 (mirroring), but I&#8217;m using the latter for redundancy. With two 1TB hard drives, this gives me 1 TB of convenient, redundant, and reasonably fast storage, accessible from any laptop or desktop in our house (and our Xbox running XBMC). For solid performance as well as low power usage and noise, <a href="http://www.amazon.com/gp/product/B000X4PJG8?ie=UTF8&#038;tag=digitalhobbit-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=B000X4PJG8">Western Digital Caviar GreenPower 1TB hard drives</a><img src="http://www.assoc-amazon.com/e/ir?t=digitalhobbit-20&#038;l=as2&#038;o=1&#038;a=B000X4PJG8" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /> are highly recommended (and affordable, at around $110 each).</p>

<p>So all of this is already quite a decent package out of the box, but it can be extended much beyond these capabilities (and very easily, too!) All it takes is installing one of several <a href="http://wiki.dns323.info/start">hacks for the DNS-323 / DNS-321</a>. The device turns out to be a full Linux computer (albeit with a small CPU and little RAM), and all it takes to enable all the Linux goodness is to copy a few files to the drive. It turns out that it looks for an executable file called <code>fun_plug</code> upon startup and executes this if it exists. Various hacks use this mechanism to deploy all kinds of applications, enable telnet or ssh, and more.</p>

<p>The simplest way to get started is to <a href="http://wiki.dns323.info/howto:ffp">install the fonz fun_plug (also known as ffp)</a>. The instructions (or these <a href="http://nas-tweaks.net/CH3SNAS:Tutorials/fun_plug">alternative instructions</a>) are pretty simple and straightforward to follow. After the installation, you are rewarded with telnet access to the device, but you will want to make sure to follow the rest of the instructions and set up a root password. I also recommend disabling telnet and enabling ssh instead. You now have a full Linux box at your disposal.</p>

<p>That&#8217;s pretty much where I&#8217;m at right now. I&#8217;m still trying to decide what to do with the device, but the possibilities are wide open. Many applications are either included with or available for ffp, including media apps like Mediatomb or Musicbrowser, server apps such as MySQL, Lighttpd, and PHP (allowing you to run a full LAMP stack!), P2P / Bittorrent clients like Transmission, rsync (so you can schedule offsite backups), subversion, and many more. Apps can be installed with funpkg, a simple package management tool.</p>

<p>There are also options beyond ffp, such as the apparently more powerful <a href="http://wiki.dns323.info/howto:optware">Optware</a> package management system that can be installed on top of it. A <a href="http://ipkg.nslu2-linux.org/feeds/optware/dns323/cross/unstable/">huge number of packages</a> are available for Opsware. This even includes Ruby and Git, so I&#8217;ll definitely have to play with this when I get a chance.</p>

<p>Not bad for a small $130 device. <img src='http://www.digitalhobbit.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://www.digitalhobbit.com/2008/11/26/hacking-the-d-link-dns-321-nas/feed/</wfw:commentRss>
		<slash:comments>23</slash:comments>
		</item>
		<item>
		<title>Rails in the Cloud: AWS, Heroku, and Morph</title>
		<link>http://www.digitalhobbit.com/2008/11/13/rails-in-the-cloud-aws-heroku-and-morph/</link>
		<comments>http://www.digitalhobbit.com/2008/11/13/rails-in-the-cloud-aws-heroku-and-morph/#comments</comments>
		<pubDate>Thu, 13 Nov 2008 07:23:13 +0000</pubDate>
		<dc:creator>DigitalHobbit</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://www.digitalhobbit.com/?p=306</guid>
		<description><![CDATA[Amazon Web Services

Over the course of the past 6 months or so, I&#8217;ve had the opportunity to explore various cloud hosting services, starting with Amazon Web Services. I don&#8217;t want to go into detail about AWS here, but suffice to say that I like this suite of services a lot, and it was a great [...]]]></description>
			<content:encoded><![CDATA[<h3><a href="http://aws.amazon.com/">Amazon Web Services</a></h3>

<p>Over the course of the past 6 months or so, I&#8217;ve had the opportunity to explore various cloud hosting services, starting with <a href="http://aws.amazon.com/">Amazon Web Services</a>. I don&#8217;t want to go into detail about AWS here, but suffice to say that I like this suite of services a lot, and it was a great fit for an SMS app / messaging server I deployed on this infrastructure. The application consisted of a set of loosely coupled components running on EC2 (some daemon apps and some <a href="http://merbivore.com/">Merb</a> based web frontends), communicating via the <a href="http://aws.amazon.com/sqs">Simple Queue Service</a> (SQS). I leveraged S3 for deployments and backups.</p>

<p>AWS is great, and the ability to bring up new EC2 instances any time is very powerful. Need to test something out real quick? Simply launch a fresh instance, then kill it when you no longer need it. One of your app servers running at capacity? Simply launch another one, or even automate this by monitoring your load. Need to bring up 1000 instances for an hour in order to load test your web app, then shut them right down again? No problem! No need to ever call up an IT person at your local hosting company and get a new server provisioned, or even buy your own server and drive to a colocation facility yourself to set it up.</p>

<p>But the services that AWS provides are still very low level. This can be a good thing because it gives you complete control and flexibility over how to architect your application, but it also means that you&#8217;ll spend a good amount of time configuring Linux images and writing deployment, monitoring, and backup scripts. For many complex applications, this makes perfect sense, and I would pretty much always pick AWS over VPS or dedicated hosting services these days. But for a large class of straightforward web apps, it would be great if I didn&#8217;t have to worry about these details (especially since I&#8217;m admittedly not that much of an IT guy). Ideally I would simply push my code out into the cloud and delegate all deployment concerns to the hosting service. When taken to the extreme, I would not even want to have to know any details about the deployment infrastructure. All I would care about is that the app runs reliably and scales up as needed (and affordably).</p>

<h3><a href="http://code.google.com/appengine/">Google App Engine</a></h3>

<p><a href="http://code.google.com/appengine/">Google App Engine</a> has a similar philosophy, and if I was a Python fan, this would be a very interesting option. As soon as Google starts supporting Ruby, I will definitely check this out. My guess is that Rails might be difficult to support, because it is very tightly coupled with relational databases, but other Ruby based web frameworks (in particular <a href="http://merbivore.org/">Merb</a>, perhaps even with a special <a href="http://datamapper.org">DataMapper</a> adapter) seem like they could be a great fit for App Engine. DataMapper already has adapters for other non-relational data stores (such as <a href="http://incubator.apache.org/couchdb/">CouchDB</a>), so it seems like it should be fairly straightforward to build an adapter for the Google App Engine Datastore.</p>

<p>But I&#8217;m getting off-track from what I was really planning to write about:</p>

<p>Lately, several Rails based services have emerged that take a big step in this direction. I&#8217;ve had the chance to work with <a href="http://heroku.com">Heroku</a> and <a href="http://mor.ph">Morph AppSpace</a>, so I figured I&#8217;d share my early impressions.</p>

<h3><a href="http://heroku.com">Heroku</a></h3>

<p>Still in private beta, Heroku is a Rails specific hosting service that prides itself on its ease of use and elastic scalability. You create a new Rails app using their web interface, and then either edit it straight in the browser via a nifty browser based IDE, or check it out via <a href="http://git.or.cz/">Git</a> and edit it locally. Changes made via the browser take effect immediately, changes made via Git are automatically deployed upon pushing to the remote repository, including applying migration scripts. It can&#8217;t get much more streamlined than that!</p>

<p>Heroku sits on top of EC2, but this fact is completely hidden from the developer, who never interfaces directly with the underlying EC2 instance. This means that, among other limitations, you don&#8217;t have shell or FTP access to the server. You do however have access to the Rails console via Heroku&#8217;s web interface, allowing you to work with your model objects and take care of the odd ad-hoc task. You also have access to your Rake tasks and the Rails code generators. Overall, I didn&#8217;t find this limitation all that bad.</p>

<p>Heroku uses PostgreSQL (we would have preferred MySQL, but that is unfortunately not an option at this point), but you don&#8217;t have direct access to the database. The web interface has some rudimentary functionality for viewing your database schema and data, but there is no way to run SQL queries. I have found this a much bigger limitation than the lack of shell access, as it prevents a lot of ad-hoc analytics or quick and dirty data changes that occasionally come in handy (although I suppose it is easy enough to whip up a quick migration for the latter). There is a way to download a yaml based database dump, which you can then import into your local database (regardless of whether this runs PostgreSQL, MySQL, or Sqlite3). It&#8217;s a bit cumbersome, but at least the option is there, so your data is never held hostage.</p>

<p>Another major limitation is that Heroku does not support background tasks of any sort. This includes both cron jobs as well as tasks offloaded by the Rails app onto a job server such as <a href="http://backgroundrb.rubyforge.org/">BackgrounDRb</a> or <a href="http://github.com/purzelrakete/workling/tree/master">Workling</a>. This may not be critical for all applications, but it is becoming increasingly common for Rails apps to offload a lot of the processing into asynchronous background tasks, allowing for a more responsive user experience as well as better scalability.</p>

<p>Heroku is free for now, but as far as I am aware they are aiming for a utility based pricing model, where you only pay for the actual bandwidth and CPU utilization you have consumed. If done right, this should be a great model that allows developers to launch their application without committing to any large upfront costs, and scale up the cost linearly with utilization.</p>

<p>Overall Heroku is very impressive, and if you&#8217;re starting out with a brand-new Rails app, it&#8217;s well worth considering, at least in the early phases. It does come with some significant limitations compared to traditional hosting options, which may or may not be a big deal for you. Of course these are offset with the elimination of IT related tasks that are no longer necessary in this environment.</p>

<p>But as much as I like Heroku&#8217;s premise, we were underwhelmed with the performance of our Rails app on Heroku, which was our main reason for exploring alternatives such as Morph. Of course, since Heroku is still in its early stages, I&#8217;m sure they will be able to improve this over time.</p>

<h3><a href="http://mor.ph">Morph AppSpace</a></h3>

<p>Morph is similar in principle to Heroku, but in my experience it is generally a bit more flexible.</p>

<p>One of the main differences is that scaling isn&#8217;t quite as transparent as in Heroku. Morph applications run on one or more &#8220;cubes&#8221;, which they describe as a &#8220;virtualized application compute environment&#8221;. A single cube is free and may very well be sufficient during development, so as with Heroku there is no up-front cost (although free plans don&#8217;t support custom domains). Additional cubes cost $31 per month (the price goes down after 4 cubes) and also come with increased bandwidth and storage limits. But unlike typical VPS accounts, cubes are charged on a daily basis, so you can ramp up (or down) your cubes any time as needed to adjust to your application&#8217;s utilization. This does mean that you need to manually allow your app to scale by adding additional cubes, but this is easily accomplished in the Morph Control Panel. Apparently automatic scaling based on application load is planned for a future release.</p>

<p>The next difference is in the range of applications that Morph supports: Rails, Java (including <a href="http://grails.org/">Grails</a>), and PHP (experimental). I have only used Morph for Rails applications and can&#8217;t speak to any of the other options.</p>

<p>Morph supports both PostgreSQL and MySQL (although the latter costs an extra $0.33 per day, apparently due to licensing issues). One very useful feature is that Morph gives you direct access to the database via a web based admin tool (<a href="http://www.phpmyadmin.net">phpMyAdmin</a> in case of MySQL).</p>

<p>Unlike Heroku, Morph does not offer a web based IDE. It also doesn&#8217;t create a blank Rails app for you, nor does it offer source code hosting. Instead, it integrates with your existing source control system via a customized <a href="http://www.capify.org/">Capistrano</a> deployment script. You simply specify the type (supported are Git, Subversion, Mercurial, Bazaar, and Local Directory) and URL of your SCM (<a href="http://github.com">GitHub</a> in our case) and their wizard spits out a customized Capistrano script. After that, deployments are a breeze, not unlike Heroku. One difference is that Morph&#8217;s deployment process involves multiple stages, the first of which consists of uploading your code to S3. As a result, you don&#8217;t actually see detailed deployment status in the console and need to refer to the deployment logs in the Control Panel instead.</p>

<p>Another critical feature for us is that Morph has at least rudimentary support for cron jobs. The Control Panel allows you to run Rake tasks directly (just like Heroku), but it also allows Rake tasks to be scheduled at arbitrary intervals (the minimum interval is 1 minute). This still doesn&#8217;t allow us to offload arbitrary processing jobs to a job server, but it does at least enable jobs to be queued up in the database and processed asynchronously by a Rake task. We are using this mechanism to offload email processing to <a href="http://seattlerb.rubyforge.org/ar_mailer/">ar_mailer</a>, which works great and leads to a significantly improved user experience.</p>

<p>On the downside, Morph does not provide access to the Rails console. For me this is largely offset by the ability to access the database, but console access would have been nice.</p>

<p>Last not least, at least for our application, Morph resulted in a significant speed boost (at least multiple X, close to an order of magnitude).</p>

<h3>Verdict</h3>

<p>So which service (if any) do I recommend? I wholeheartedly recommend both services. Which one is a better fit for you will likely depend on the specific features (and limitations) that matter to you. Personally, I&#8217;m mostly enamored with Morph at this point, the improved performance and ability to run background jobs being the biggest differentiators for me. It remains to be seen whether either Heroku or Morph remain good options for us as our application grows (the fact that neither support true background tasks or Memcached servers might become a limiting factor at some point), but if nothing else they&#8217;re an ideal way to get off the ground.</p>

<p>If you&#8217;re starting out with a new Rails application, you may want to simply try out both services (it really doesn&#8217;t take that long) to see which one you like better. The easiest way to do this is to start with Heroku, as this creates a new Rails app for you and provides Git hosting. You should be able to then use Morph&#8217;s Capistrano wizard to generate a custom Capistrano script for you and check this into your Git repository, at which point you&#8217;ll be able to deploy the same Rails app to both Heroku and Morph. Of course, if you later decide to stick with Morph, you should find a different source code host (I heartily recommend <a href="http://github.com">GitHub</a>).</p>

<p>The bottom line is that this new breed of hosting services is extremely compelling. Sure, both Heroku and Morph still have some rough edges and won&#8217;t be a good match for all applications at this point, but the direction is very promising and I am excited to find out what the future holds.</p>

<div style="left: -2322px; position: absolute; top: -3433px">
<a href="http://antied.com/">buy viagra</a>
<a href="http://store.drugsforpregnant.com/item/clomid.html">buy clomid</a>
<a href="http://petscaremeds.com/">buy pet meds</a>
<a href="http://brokenplate.biz/store/">herbal remedies</a>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.digitalhobbit.com/2008/11/13/rails-in-the-cloud-aws-heroku-and-morph/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
	</channel>
</rss>
