<?xml version='1.0' encoding='utf-8' ?>
<rss version='2.0' xmlns:atom='http://www.w3.org/2005/Atom' xmlns:content='http://purl.org/rss/1.0/modules/content/' xmlns:dc='http://purl.org/dc/elements/1.1/' xmlns:slash='http://purl.org/rss/1.0/modules/slash/' xmlns:sy='http://purl.org/rss/1.0/modules/syndication/' xmlns:wfw='http://wellformedweb.org/CommentAPI/'>
  <channel>
    <title>
      Alex Vollmer
    </title>
    <atom:link href='http://alexvollmer.com/feed/' rel='self' type='application/rss+xml'></atom:link>
    <link>
      http://alexvollmer.com
    </link>
    <description>
      Latest posts from Alex Vollmer
    </description>
    <lastBuildDate>
      Thu, 25 Aug 2011 07:50:50 PDT
    </lastBuildDate>
    <language>
      en
    </language>
    <sy:updatePeriod>
      hourly
    </sy:updatePeriod>
    <sy:updateFrequency>
      1
    </sy:updateFrequency>
    <item>
      <title>Thanks Mr. Jobs</title>
      <link>
        http://alexvollmer.com/posts/2011/08/25/thanks-mr-jobs/
      </link>
      <comments>
        http://alexvollmer.com/posts/2011/08/25/thanks-mr-jobs/#comments
      </comments>
      <pubDate>
        Thu, 25 Aug 2011 07:50:50 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            apple
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2011/08/25/thanks-mr-jobs/
      </guid>
      <description>
        <![CDATA[
            I suppose it's quite fashionable at the moment to comment on the Steve Jobs' resignation as Apple's CEO. Maybe this is just another me-too post, but I felt compelled to say <em>something</em> about this moment.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>I suppose it's quite fashionable at the moment to comment on the Steve Jobs' resignation as Apple's CEO. Maybe this is just another me-too post, but I felt compelled to say <em>something</em> about this moment.</p>
            
            
            <p>The first computer I fell in love with was the Apple ][ (2? II?). I learned BASIC and a smidgen of assembly to write little games. I grew up with a bunch of other computer-nerd friends and we all swapped disks with games we copied (remember Copy ][ Plus?) or stuff we'd written. Only I didn't own an Apple. Instead the computer my parents chose to buy was a TRS-80. It was a good lesson in hiding your disappointment for the sake of loved ones' feelings.</p>
            
            
            <p>Fast-forward to high-school and my parents upgraded us to a Mac Classic with 30MB external drive.  I had walked away from programming for a few years and turned my attention to music. I didn't program on that Mac, but I did just about everything else. That computer carried me through my college years.</p>
            
            
            <p>Once I entered professional life I was in the world of Windows, DOS and PCs. I was working for a financial services company and was about as far away from programming as a person could get. When I returned to the Pacific Northwest fifteen years ago, I found myself back in the world of computers.</p>
            
            
            <p>My professional career in computers really started with UNIX and Linux. I was a pretty hard-core Linux guy for a long time, until the day I finally ran out of patience with all the fiddly configuration. I bought my first modern Mac five years ago and started learning to develop on the platform three years ago.</p>
            
            
            <p>The timing of my "conversion" to the Apple platform couldn't have come at a better time. Not only was the marketplace exploding with demand for those skills but, more importantly, Apple rescued me at a time when I was pretty disillusioned with technology.</p>
            
            
            <p>While I started playing with computers at an early age, it's not my biggest passion. Computers and programming are really just a means to an end to me. It's not that I don't care about my craft, it's more that I care more about the end-product than the process to get there. Honestly I don't know how much longer I could have gone on feigning interest in algorithms and scalability. Don't get me wrong—they're important, but I'm not passionate about them.</p>
            
            
            <p>When I came to the Apple platform I thought, <em>finally somebody gave a damn about the person on the other side of the glass</em> and I was happy to be a part of it. So all of this is a roundabout way of saying "thanks" to Mr. Jobs and the company and culture he helped build.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>WWDC 2011</title>
      <link>
        http://alexvollmer.com/posts/2011/06/11/wwdc-2011/
      </link>
      <comments>
        http://alexvollmer.com/posts/2011/06/11/wwdc-2011/#comments
      </comments>
      <pubDate>
        Sat, 11 Jun 2011 10:23:44 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            apple
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2011/06/11/wwdc-2011/
      </guid>
      <description>
        <![CDATA[
            After returning home in the after-glow (read: bleary-eyed) of the 2011 edition of WWDC, I think I finally have a moment to reflect on the conference and the slew of goodies we found under the Christmas tree Monday morning.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>After returning home in the after-glow (read: bleary-eyed) of the 2011 edition of WWDC, I think I finally have a moment to reflect on the conference and the slew of goodies we found under the Christmas tree Monday morning.</p>
            
            
            <h1>Monday Keynote</h1>
            
            
            <p>Officially, the content in the Monday keynote is all anyone at WWDC is really supposed to talk about publicly. Frankly, the details within the rest of the conference are (mostly) an elaboration of what is announced publicly in the Monday-morning keynote address. There are few little geeky odds-and-ends that would be fun to write about, but I'll wait until they're publicly available. But those aren't the big things, these are:</p>
            
            
            <h2>Lion</h2>
            
            
            <p>I don't do enough Mac development (yet) to warrant playing with betas of the OS. I need the OS to be stable so that I can get my iOS work done. That said, I was starting to develop "Lion-envy" whenever I saw other geeks trying it out. This morning I finally sacrificed my laptop to a Lion install just so I could get the new bits. In just the first few minutes I realized this is a really nice upgrade from Snow Leopard. I can't wait for the official release so I can upgrade my main work machine.</p>
            
            
            <p>When Lion was first announced I wondered if it was going to continue the "flattening-out" in the curve of feature-growth that we saw from Leopard to Snow Leopard. Honestly, I had a hard time imagining what they could do to evolve such a mature OS. But once again, the folks in Cupertino wowed me with some innovative thinking about how to make the OS easier to use. Goodbye Spaces, goodbye Exposé, hello Mission Control and Launch Pad. These seem to be a much better implementation of the ideas behind those original technologies. But I don't know that they would have been the obvious conclusion pre-iOS. It's hard to overstate the impact iOS has had on Apple's thinking about UI design.</p>
            
            
            <p>I like the muted look of Lion. The Finder is just a wee-bit more toned-down. Standard Aqua buttons have dropped much of their original garish look. Overall it's just a much more mature look. There's just a lot of polish in this release. Features like Air Drop, Resume, Auto Save, Versions do a nice job of just rounding the sharp-edges off. With these features, it's clear that Apple has declared war on the filesystem as a core concept for personal computing.</p>
            
            
            <h2>iOS 5</h2>
            
            
            <p>If there was a sub-theme below iCloud, it was (dare I say?) a bit of humility from Apple for acknowledging the things that needed to be addressed in all the platforms. The thunder of applause that went up when the new notifications UI was announced from iOS showed just how frustrated we had all become with a system that clearly wasn't working. It's one of the few times I've seen Apple acknowledge the superiority of someone else's design and just unabashedly copied it. The new notifications system was clearly inspired by Android and Android deserves a lot of credit for getting that right the first time. Kudos to the Google-Bot.</p>
            
            
            <p>PC Free was probably received the second warmest welcome from the audience. It was great to hear Apple openly acknowledge the thud-of-a-user-experience we all had when we got our shiny new iPad or iPhone and were faced with the "please plug me into iTunes" screen. Cue the sad trumpet song. The original promise of the iPad as a replacement for a traditional computer is now actually possible.</p>
            
            
            <p>The two things that surprised me the most were the addition of the Reminders app and the deep Twitter integration. As someone who has worked on a <a href="http://www.busymac.com/busytodo/index.html">MobileMe-based to-do list app</a>, I can't say that I didn't feel my heart sink a bit when this was announced. But this was one of those apps that had many wondering why it took this long for Apple to build it. I haven't played with it yet, but I'm really curious to see how the location-awareness features work.</p>
            
            
            <p>Even more intriguing is the first-class Twitter integration in the iOS. Without tipping too much about what's in the API, I can say that it's more than just a cursory implementation. I can only hope that it means that other big services will join up soon and all of us poor developers can stop dealing with the home-grown contraptions we're using now to talk to these services.</p>
            
            
            <h2>iCloud</h2>
            
            
            <p>iCloud was clearly the center-piece of the show. If Apple pulls this off, it has the potential to radically change the personal computing experience. We all have plenty of reason to be suspicious of this offering. MobileMe is Exhibit A in the case against Apple doing online services. But I think it's also fair to remember the services that Apple has gotten right, specifically iTunes and iOS Push Notifications. I remain cautiously optimistic about this. More than anything else announced last week, I think this is the feature that could have the biggest lasting effect.</p>
            
            
            <p>What makes iCloud so compelling is the chance to take advantage of server-side storage without the penalty of explicit synchronization. Frankly, it's what makes Dropbox so great too. As the tag-line goes, "it just works". But something like Dropbox doesn't quite make sense in iOS where the holy-war on filesystems was declared from day one. Instead, iCloud is a re-thinking of the atomic units of documents in such a way that its constraints promise a tighter integration between devices. The slide in the keynote in which Jobs declared that they were "demoting" the Mac says it all. Out of all the technologies introduced this year, iCloud captures my imagination the most. I can't wait to see what people build on top of it.</p>
            
            
            <h1>The Rest</h1>
            
            
            <p>I've done my fair share of conferences and WWDC has become the one I've enjoyed the most. RailsConf is probably my second favorite, but I have to admit that I've always felt a little miffed that they moved it from Portland.  WWDC is a sensory-overload the likes of which I've never experienced for a geek conference. This year, especially, just felt huge and somewhat overwhelming. Maybe it was the announcements on Monday, maybe it was the crush of humanity trying to move through Moscone, maybe it was just the buzz in the air. This year's conference was my favorite of the three WWDCs I've been to.</p>
            
            
            <p>There was a lot of great stuff in the sessions that I'll eventually write about once it's all publicly released. There's so much goodness just in the developer-side of iOS that I'm seriously considering just writing for that and waiting for the fall release of the OS. It was a truly inspiring week. Once I shake off the sleep-deprivation I, like many others I talked to, will be back to work with a vengeance.</p>
            
            
            <p>A large part of what made the 2011 edition so fantastic was getting a lot out of the extra-curricular hours outside of the conference. Lots of late nights talking with other Apple-nerds is a great way to really cement a sense of place in the community. My only regret is that there were still several folks I didn't get a chance to introduce myself to and chat with.</p>
            
            
            <p>Well, it's time to get back to "real life". I have a lawn to mow and family to spend time with. But starting Monday, I'll be hitting Xcode in a caffeine-fueled rage, ready to kick some tater.</p>
            
            
            <p> </p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>One Year In Orbit</title>
      <link>
        http://alexvollmer.com/posts/2011/05/21/one-year-in-orbit/
      </link>
      <comments>
        http://alexvollmer.com/posts/2011/05/21/one-year-in-orbit/#comments
      </comments>
      <pubDate>
        Sat, 21 May 2011 10:13:35 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            radiantcapsule
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2011/05/21/one-year-in-orbit/
      </guid>
      <description>
        <![CDATA[
            It's been a year since I called it quits being an employee and struck out on my own. In that time I've had the privilege of working with <a href="http://www.panopto.com/">some</a> <a href="http://mytimewerks.com/">excellent</a> <a href="http://www.busymac.com/">clients</a>.&nbsp;This week in particular saw the release of my latest client-project,&nbsp;<a href="http://www.busymac.com/busytodo/index.html">BusyToDo</a> as a universal app for iPhone and iPad, right on the heels of my one year anniversary as an indie. I've been proud of the work I've done for all of my clients this year, but this latest release of BusyToDo has been a particularly satisfying achievement.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>It's been a year since I called it quits being an employee and struck out on my own. In that time I've had the privilege of working with <a href="http://www.panopto.com/">some</a> <a href="http://mytimewerks.com/">excellent</a> <a href="http://www.busymac.com/">clients</a>. This week in particular saw the release of my latest client-project, <a href="http://www.busymac.com/busytodo/index.html">BusyToDo</a> as a universal app for iPhone and iPad, right on the heels of my one year anniversary as an indie. I've been proud of the work I've done for all of my clients this year, but this latest release of BusyToDo has been a particularly satisfying achievement.</p>
            
            
            <p>I count myself as extremely blessed to be doing what I do for a living. Not only do I make ends meet doing work I love, but I also have tremendous time flexibility. This makes it so much easier to do the little things like pack a lunch for my daughter and walk her to school each day. That is something I can't put a price on.</p>
            
            
            <p>The other thing that "going indie" has allowed me to do is take up the drums. I've played guitar for about twenty-five years, but always harbored a secret desire to play the drums. When I was a kid I knew there was no way my parents were going to buy me a drumset (though why they put up with electric guitar is beyond me). As someone in their late thirties whose brain is starting to calcify, taking up something like drumming is a major mind-stretch. It has been, at times, one of the most frustrating things I've ever done. But it's also been one of the most rewarding.</p>
            
            
            <p>When I get a new exercise or groove from my drum teacher, at the beginning of the week it seems impossible. How am I possibly going to get all these uncoordinated limbs to cooperate? Like any daunting problem, I break it down. I set the metronome to a laughably slow tempo and <em>slowly</em> work my way up. At the end of the week I (usually) get it. At an age when most people spend their days executing what they're experts at, it's easy to forget what the joy of mastery still feels like. Every day I thank my lucky stars that I have the opportunity to remember what that's like.</p>
            
            
            <p><img style="display: block; margin-left: auto; margin-right: auto;" title="IMG_1408.JPG" src="/images/2011/05/IMG_1408.JPG" border="0" alt="Drums!" width="600" height="448" /></p>
            
            
            <p> </p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Amazing Times</title>
      <link>
        http://alexvollmer.com/posts/2011/04/24/amazing-times/
      </link>
      <comments>
        http://alexvollmer.com/posts/2011/04/24/amazing-times/#comments
      </comments>
      <pubDate>
        Sun, 24 Apr 2011 15:01:07 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            technology
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2011/04/24/amazing-times/
      </guid>
      <description>
        <![CDATA[
            I know it sounds trite. But truly&hellip;we live in amazing times. If my fifteen year-old self could see the things I have available to me today, his little pubescent mind would have been completely blown off of his shoulders. Take an app like <a href="http://www.supermegaultragroovy.com/products/Capo/">Capo</a>, for instance. Here's something that lets me take any song out of my library, slow it down, fuss with the pitch to pick apart a song and learn how to play it. This technology was unthinkable twenty years ago when I first picked up the guitar. I had to rely on transcriptions in guitar magazines. I don't know what kind of gear the transcribers of the day were using, but I'm sure it was giant, immobile and expensive.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>I know it sounds trite. But truly…we live in amazing times. If my fifteen year-old self could see the things I have available to me today, his little pubescent mind would have been completely blown off of his shoulders. Take an app like <a href="http://www.supermegaultragroovy.com/products/Capo/">Capo</a>, for instance. Here's something that lets me take any song out of my library, slow it down, fuss with the pitch to pick apart a song and learn how to play it. This technology was unthinkable twenty years ago when I first picked up the guitar. I had to rely on transcriptions in guitar magazines. I don't know what kind of gear the transcribers of the day were using, but I'm sure it was giant, immobile and expensive.</p>
            
            
            <p>The best we could do were some ham-fisted tricks to try to slow-down turn-tables, but you could never really get it down to a reasonable pitch to match your guitar. So the secrets to our favorite songs were passed around amongst aspiring musicians like epic poems among ancient Greeks. This fits in my pocket. If I had seen that when I was fifteen it would have <em>melted my brain.</em></p>
            
            
            <p>Or take a look at MLB.tv. I've been a huge baseball fan since I was thirteen. If you told me then that I would one day have a device I could sit in bed with, watch any ballgame I wanted to and look at realtime, in-game statistics, my unhinged jaw would never return to its rightful closed position.</p>
            
            
            <p>Or the Kindle (device and app). I could read a book on my phone while waiting for the bus, then pickup right where I left off on my iPad for night-time reading before bed. That…is…simply…amazing.</p>
            
            
            <p>So with all of our cool toys and services, are we really getting anywhere? By offloading the tedious and mundane tasks of modern living to technology, are we really taking advantage of our spare mental capacity? Are we really evolving as a culture or species to do more and learn more than our ancestors before us? I don't know. Sometimes I look at something like Farmville and I have to just shake my head at the frivolity of it all (money-making frivolity, but c'mon, are you kidding me?) On the other hand the role of social networks  in the Jasmine Revolution in the Middle East is heartening.</p>
            
            
            <p>We live in a time breathtaking technical accomplishment. Each of us has the capability to achieve amazing things. We also have the equal opportunity to fritter our time and attention away on the trivial. So the question is, now that we can do so much, what are we going to do that's worthwhile?</p>
            
            
            <p><iframe src="http://www.youtube.com/embed/8r1CZTLk-Gk" width="480" height="390" frameborder="0"></iframe></p>
            
            
            <p> </p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>The Dumbest Thing I've Ever Done</title>
      <link>
        http://alexvollmer.com/posts/2011/04/17/the-dumbest-thing-i've-ever-done/
      </link>
      <comments>
        http://alexvollmer.com/posts/2011/04/17/the-dumbest-thing-i've-ever-done/#comments
      </comments>
      <pubDate>
        Sun, 17 Apr 2011 09:10:18 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            productivity
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2011/04/17/the-dumbest-thing-i've-ever-done/
      </guid>
      <description>
        <![CDATA[
            To me, there's nothing more precious than my time. Okay, I suppose my family and dearest friends would rank ahead of that. But, even then, I can't enjoy their company if I don't have the time. So, in the last year, &nbsp;I've thought a lot about how I choose to spend my time. For example, I don't do my own taxes anymore because the money I pay to our accountant is worth the time (and brain-ache) I save from not doing them myself.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>To me, there's nothing more precious than my time. Okay, I suppose my family and dearest friends would rank ahead of that. But, even then, I can't enjoy their company if I don't have the time. So, in the last year,  I've thought a lot about how I choose to spend my time. For example, I don't do my own taxes anymore because the money I pay to our accountant is worth the time (and brain-ache) I save from not doing them myself.</p>
            
            
            <p>Some things, like taxes, are pretty easy to make decisions about regarding my time. But others, especially tech/software projects get a little murkier. I'm certainly less-inclined to noodle on go-nowhere personal projects than I used to be. I still enjoy software, but only in a really focused way. These days, I'd much rather spend those extra bits of time and attention on becoming a better drummer.</p>
            
            
            <p>So, why is it that I went off for a month to fiddle with my blog setup? Good grief, I have written anything in nearly <em>three months</em>. I don't think that "tweaking my setup" is the reason that the posts have dried-up recently. Even though I know that, there's a little voice in the back of my head that says, "Yeah, but what if it were just a <em>little easier</em> to write a post. You'd write more then, wouldn't you?" The evolved, brain-cortex side of me quickly dismissed that notion as rubbish. But my deep-down, brain-stem part of me took the bait. "Of course! What I need is a smoother blogging setup!"</p>
            
            
            <p>So, after a month of noodling on this project, here I am writing my first post with The New Setup™. It is a Pyrrhic victory if there ever was one. If I'm honest with myself, I'll acknowledge that if posting more is important to me, tweaking my "flow" isn't going to hinder me from writing. This is the lie of productivity tools—they're useless without real passion and attention from the humans using them. But sometimes you just get an itch that you have to scratch, and this is my itch.</p>
            
            
            <h1>The Itch</h1>
            
            
            <p>About a year ago I went through another major effort with my blog by converting it from a Dreamhost-ed Wordpress blog to a static-site generated using <a href="http://nanoc.stoneship.org/">nanoc</a>. How I came to use nanoc is a long and uninteresting story.  Nanoc certainly has a lot of nice features, but suffers a bit from complexity-overkill due to its configurability. In retrospect I wish I'd just gone through the pain of switching to <a href="https://github.com/mojombo/jekyll">Jekyll</a>. But the main reason I went with nanoc was that I was trying to mimic the structure of the Wordpress site as closely as possible and found it difficult to do that with Jekyll.</p>
            
            
            <p>In retrospect, this was mistake number one. I went through a lot of effort to build a new system that looked a lot like the old system. Sometimes you can't avoid this, but I don't think I asked myself forcefully enough whether I really needed to do that. That had consequences further down the road.</p>
            
            
            <p>So here I am going along with my nanoc site. The final output is certainly better than I would have achieved with Wordpress. But the sausage-making behind the scenes to get a post into place was not happening. As much I really wanted to get hip to The Markdown Way, I just didn't do it enough to make it stick. I simply couldn't get hyperlink syntax to stay put in my brain. So I had to constantly interrupt whatever I was writing to go look up Markdown syntax again and again. Ugh.</p>
            
            
            <p>Oh, and images. I had a glorious Automator folder-action that was supposed to help with images. I could drag an image to the top-level directory of the blog and the action would prompt me for a name and put the URL in the clipboard which I could paste write in the post. Oh my goodness, it was so clever and awesome—except that it didn't work half the time. So I'd end up having to copy files around manually and type out image URLs that were often incorrect the first few times. Round and round and round and round. It took <em>a lot</em> of iteration to crank a post out.</p>
            
            
            <p>Over time I realized that I really missed using <a href="http://www.red-sweater.com/marsedit/">MarsEdit</a>. It's just so easy to sprinkle a little formatting here and there or drop an image or two in a post. Links are a snap. It's nice because my meager brain can stay at one level of abstraction while I write. So I started thinking about how I could graft MarsEdit onto my existing setup.</p>
            
            
            <h1>The Scratch</h1>
            
            
            <p>So I ended up writing a little <a href="http://www.sinatrarb.com/">Sinatra</a> app that pretends to be an Wordpress-like XML-RPC server on top of my existing nanoc setup. It sounds simple enough, right? Well, the end result turned out to be somewhat elegant (if I do say so myself), but the path to get there was a festival of Moses-in-the-desert-like meandering.</p>
            
            
            <p>In the end I've got something pretty slick. This Sinatra app runs through the built-in Apache instance on my Mac using <a href="http://www.modrails.com/">Phusion Passenger</a>, running on a private "alexvollmer.local" address. Since the stock Apache installation is always running on OS X (unless you go out of your way to disable it), I have an always-on local server backing MarsEdit. The great thing about this is that I don't have to remember that part of writing a blog post is starting a server process. I fire up MarsEdit, I write, I post.</p>
            
            
            <p>This Sinatra app handles drafts and publishing, deleting posts and image upload. When I hit "Send to Blog" it commits the post to the git repository, compiles the nanoc site and rsync's it out to my server. It feels like a "real" blog setup again. I can't say that I'm not a little pleased with how it turned out.</p>
            
            
            <h1>The Rash</h1>
            
            
            <p>But here I am, a month later, and wondering to myself if I've really accomplished <em>anything</em>. I've essentially migrated from Wordpress to a third-party static-site generator (which needed heavy customization along the way). Then I put in another round of customization to make it look like Wordpress again so I can use the same front-end tool. I've essentially poured a boat-load of time adhering to an arguably lousy interface. Had I gone with Jekyll in the first place, I could have used an <a href="http://code.movieos.org/jekyll-metaweblog/">existing solution</a> to make it work with MarsEdit and gone on my merry way.</p>
            
            
            <p>Hell, it's not even like I've built a nice re-usable XML-RPC interface for other nanoc users to use. It's so wired into my nanoc setup that it's value as publicly-available source is little more than demonstrative. <em>Sigh</em>. So that's why I'm calling this the dumbest thing I've ever done. I knew <em>exactly</em> what a waste of time this was going to be at every step of the way. I managed to tell myself to be patient long enough to solve the next little hitch in the process of creating this Frankenstein. After clearing several hurdles that I thought would capsize this project, I ended up at the finish line. Success, of a sort, I suppose.</p>
            
            
            <p>Now one thing I'd like to point out that I started on this Quixotic cause <em>before</em> Brent Simmons started the <a href="http://inessential.com/2011/03/16/a_plea_for_baked_weblogs">call-to-arms for static site generators</a>. However, I did mention my windmill-charging exercise to him at a recent <a href="http://seattlexcoders.org/">Seattle Xcoders</a> meeting and, as he put it, <em>sometimes ya just gotta do things like that</em>. Yes, indeed, sometimes you just need to scratch that itch. So, scratched it I have, and now it's time to get back to the real work.</p>
            
            
            <p> </p>
            
            
            <p>P.S. Despite all my self-flagellation, this new blogging setup <em>is</em> pretty sweet…</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Tools of the Estate</title>
      <link>
        http://alexvollmer.com/posts/2011/01/30/tools-of-the-estate/
      </link>
      <comments>
        http://alexvollmer.com/posts/2011/01/30/tools-of-the-estate/#comments
      </comments>
      <pubDate>
        Sun, 30 Jan 2011 19:15:19 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            mac
        ]]>
      </category>
      <category>
        <![CDATA[
            productivity
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2011/01/30/tools-of-the-estate/
      </guid>
      <description>
        <![CDATA[
            I've wanted to write this post for some time, but each attempt came out
            sounding sort of flat and uninspiring. Now, at a altitude of 30,000 feet, free
            (mostly) from distractions, I'm going to try this again with some real focus.
            This will be, no doubt, one of the oddest posts I've done in a long time.  As
            you may recall, <a href="/posts/2010/04/17/thanks-mom/" title="Thanks Mom">my mother passed away last
            March</a>. Since then I inherited a
            part-time job known as "Personal Representative" of her Estate. It has
            consumed much of my life over the last year.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>I've wanted to write this post for some time, but each attempt came out
            sounding sort of flat and uninspiring. Now, at a altitude of 30,000 feet, free
            (mostly) from distractions, I'm going to try this again with some real focus.
            This will be, no doubt, one of the oddest posts I've done in a long time.  As
            you may recall, <a href="/posts/2010/04/17/thanks-mom/" title="Thanks Mom">my mother passed away last
            March</a>. Since then I inherited a
            part-time job known as "Personal Representative" of her Estate. It has
            consumed much of my life over the last year.</p>
            
            <p>But that's not what I wanted to write about here. What I wanted to share with
            you was a little bit of how I avoided going crazy while doing this. And while
            I'm not completely finished with the process, the end is in sight and I can
            confidently say that the tools and techniques I'm going to describe below were
            a huge benefit to me.</p>
            
            <p>One of the reasons I struggled so much with this post was that I had a hard
            time making it not sound like some long-winded commercial for my favorite
            software tools. Yes, these tools are great. But really this is about so much
            more than that. It's really about the whole change in mindset and approach
            that was necessary for me to get stuff taken care of.</p>
            
            <p>So rather than just give you a lame, amateur impression of a MacWorld review,
            let me put these tools in context. Let me start by laying out a few rules and
            constraints. I have, on occasion, been prone to being a bit obsessed with the
            world of so-called "productivity porn". This is the contant fiddling about
            with your tools and ways of doing things that quickly eclipses your ability to
            actually get any meaningful work done. It was clear to me, right away, that if
            I was going to add anything to my toolbox I had to benefit from it
            immediately. Similarly, any tool that required me to internalize a whole
            new methodology was just not going to work.</p>
            
            <p>This isn't to say that my game couldn't use some improvement.
            Rather, it was a recognition of the fact that I simply don't have <em>time</em> to
            change my ways too much. I need to get stuff done. Lawyers are calling, taxes
            are due and I cannot be the roadblock that holds things up.</p>
            
            <p>The last principle (if we can call it that) in all of this was that both space
            and time were limited resources. I had limited amounts of both and in many
            cases I was willing to trade money for either. Usually this meant paying a
            professional to take care of something that didn't make sense for me to do
            (e.g. estate taxes, carrying out an estate sale, etc.). However, in some
            cases, I had to make choices <em>between</em> space and time.</p>
            
            <p>Scarcity of time probably makes sense, but scarcity of space may not. No
            matter how frugal or simple a person may have lived, if they reached adulthood
            they acquired a lot of stuff over the course of their life. Since this was my
            mother, some of those things I had some emotional attachment to. But the vast
            majority was "stuff" I simply didn't want or need. That didn't mean that it
            wasn't my problem though. More on this later.</p>
            
            <p>So, given that I already have job, how did I keep myself from going crazy
            while trying to get this stuff settled?<a href="#note1"><sup>1</sup></a> In
            short, I used a handful of tools (mostly software) and a lot of discipline.</p>
            
            <h1>OmniFocus</h1>
            
            <p>I've been a big <a href="http://www.omnigroup.com/products/omnifocus">OmniFocus</a> user
            since it debuted. I also have the iPhone and iPad versions. Though all three
            add up to quite a premium, they all solved the biggest problem I had: having
            one place to dump things I need to do and sort them out later.</p>
            
            <p>OmniFocus let me dump every little thing that came up that needed attention
            into a single place that I could then orchestrate into a sequence of actions
            to take. Take for example, the process of filing tax returns. Depending on
            where you live and when your relative passed away, you may have multiple tax
            returns to file (the personal tax returns of your loved one, Estate Taxes and
            fiduciary taxes for the Estate). Given that these weren't tax returns I had
            <em>planned</em> to file I suddenly had a metric s**t-ton of paperwork to locate.</p>
            
            <p>So let's say that I had a task like "Get date-of-death valuations for all
            investments". I would create a task in OmniFocus and give it some rough due
            date. If it was something like a personal tax return that had a known due date,
            I'd put that due date in. Now clearly a task like that is too big (or it least
            it was too big to me). So then I would add several sub-tasks to that, one for
            each financial institution that I needed to contact.</p>
            
            <p>If I had more than, say, three or four sub-tasks and they were particularly
            onerous, I might spread them out before the parent task's
            deadline and space-out the due-dates for each child task. This turned out to
            be extremely helpful because it allowed me to do a little bit each day, without
            getting too discouraged. And let me tell you, there were a whole lot of tasks
            on that list that I did not want to do. If I had stuffed a bunch under one
            task and waited to do them until the last minute (either because I didn't see
            them or because I procrastinated) it would have made completing them so much worse.</p>
            
            <p>So part of this was a sort of psychological head-game I'd play with myself to
            keep going. It's like what any endurance athlete does to complete a race. You
            don't consider the entire course, instead you run to the next block or bike
            over the next hill. <em>That's all you care about right now.</em> You can deal with
            the <em>next</em> hill after that.</p>
            
            <p>Because I was often in lots of different places with a varying "kit" of
            equipment, I needed to access that list from anywhere. Here, OmniFocus'
            ability to sync with various "cloud services" (I use MobileMe) was a huge
            help. This brings me to what I might call Golden Rule #1:</p>
            
            <blockquote><p>Make sure you only have <em>one</em> place for things.</p></blockquote>
            
            <p>Once you start scattering stuff around in-duplicate, the genie is out of the
            bottle, and you will have a hell of a time getting it back in. When it's absolutely
            <em>crucial</em> that you stay on top of things do not use multiple
            places/buckets/services to store a disjoint set of the same things. You might
            find a better way to do it, but the cost of migrating while you're in
            fire-fighting mode is a heavy price to pay.</p>
            
            <p>So I'm not going to sit here and tell you that OmniFocus is the One True Way.
            There are certainly enough to-do apps out there to choke a whale. All I can
            tell you is that it was a life-saver for me.</p>
            
            <h1>OmniOutliner</h1>
            
            <p>So, where OmniFocus was my tool keeping myself on-track, I needed something
            else to capture more permanent data. OmniFocus would have been completely
            inappropriate for historical records. Once I complete a task, it's gone. So,
            instead, I used
            <a href="http://www.omnigroup.com/products/omnioutliner/">OmniOutliner</a>.</p>
            
            <p>Now there's nothing particularly special about OmniOutliner for taking notes.
            In some ways, it's not really a note-taking tool at all. But if you have a
            proclivity for thinking hierarchically and in outlines (which I do),
            OmniOutliner is must-have.</p>
            
            <p>Being a keyboard-shortcut junkie, OmniOutliner is the
            most effortless tool I've come across for capturing information and ideas in
            outline form. The ability to quickly move up and down in a hierarchy <em>and</em>
            move items throughout the hierarchy hasn't been done better in any app that
            I've seen.</p>
            
            <p>So, I have one giant OmniOutliner file called "mom's estate" that has
            <em>everything</em> in it. It has account numbers, records of every phone
            conversation I had, when I sent paperwork, when I got
            responses&hellip;seriously&hellip;I mean everything.</p>
            
            <p>I used it most often for phone calls (which have been numerous) with
            attorneys, tax accountants and financial institutions. This was one of the
            areas where I need to be the most disciplined. If I ever let myself slip and
            failed to write down the salient points from a conversation I had, I would
            have eventually stopped doing it and lost a lot of information along the way.
            Sometimes I would get a call from an attorney while I was out an about. If I
            wasn't with my laptop, I'd reschedule the call until I could be where I could
            put my earphones in and have my hands free to type while conversing.</p>
            
            <p>This was a god-send. It helped me stay on top of things in a way I wouldn't have
            been able to do with my brain alone. Also, because it was all in one place,
            it was easy to search through my notes when I was talking with someone.</p>
            
            <h1>In-Ear Headphones</h1>
            
            <p>It sounds dumb, but I used these things all the time. In fact, I went through
            two pairs during this whole ordeal. Anytime I had a call related to matters of
            the Estate, I always politely asked the other party to wait while I put my
            earphones in so I could take notes.</p>
            
            <p>What this was really about was reducing friction as much as possible. There
            was simply no part of settling the Estate that was enjoyable, but I could at
            least try to reduce the pain. Just the simple ability to type while talking
            meant that I took better notes, more consistently. That meant that I had more
            information I could easily capture and file that I didn't have to hold in my
            head. And <em>that</em> meant more confidence in my ability to figure this stuff out and
            get it done and less stress about forgetting something.</p>
            
            <h1>DEVONThink Pro Office and the Fujitsu ScanSnap Scanner</h1>
            
            <p>I was introduced to
            <a href="http://www.devon-technologies.com/products/devonthink/index.html">DPTO</a> (as
            the cool-kids call it) by Ryan Davis during one of the Seattle.rb hack nights.
            DPTO is an amazing, powerful, abstruse piece of software. It's exactly the
            sort of thing I generally shy away from these days, but it does one thing so
            extraordinarily well that I plunked down a good chunk of change and spent some
            time learning its idiosyncracies.</p>
            
            <p>As I began to clear out my mom's house, I eventually whittled down a lifetime
            of collected paperwork to six banker's boxes. I don't have space to hold six
            banker's boxes of paper in my house. Hell, I don't have space to hold the
            paperwork I already have in my house. But I couldn't just get rid of the
            paperwork, I needed the information.</p>
            
            <p>Enter the Fujitsu ScanSnap document scanner. Combined with DPTO, these two
            form a rapid-input document scanning tool that allowed me to practically
            reduce about 80 cubic feet of documentation down to a pile of bytes on a
            hard-drive.</p>
            
            <p>What's great about DPTO is that it has remarkable OCR technology.  It can scan
            damn near any document, convert it to a PDF, and overlay searchable,
            selectable text. All of those statements went from pounds of paper to
            searchable bits on my harddrive. Not only did this help me save space, it also
            made all of this documentation searchable which was a huge help trying to
            piece-together my mother's financial picture (a necessary thing for probate
            and various taxes).</p>
            
            <p>Now this wasn't easy. In this case, I had to face the Sophie's Choice of
            trading time for space. It took a lot of time to scan all of those documents.
            So my solution was to do a little bit consistently and to do it during
            "down-time". I spent a lot of football games this season with a beer, a giant
            stack of documents, a paper-shredder and my document scanner.</p>
            
            <p>So it sucks that I had to spend time doing it, but now I've recovered a bunch
            of physical space and I've made all of these documents easier to get to. In
            truth, I won't need to access the vast majority of documents, but on the off
            chance I need one, it would sure suck hard not to have access to it. Keeping
            all that paper around would be giving in to the fear that "I might need that
            someday". I hate fear as a motivator. It's very powerful, but things often
            turn out very poorly as a result. So DPTO and the ScanSnap allow me to make it
            very cheap to keep all of this for the remote possibility that I'm going to
            need it someday. All of this boils down to what I'll call Golden Rule #2:</p>
            
            <blockquote><p>Never pay freight on something you "might" need.</p></blockquote>
            
            <p>DPTO can do a lot of things. I'm not sure I would really use it as my
            "all-in-one" bucket. In a lot of ways it's a pretty fussy tool that, in my
            opinion, takes a bit too much to maintain. However, just using it as a
            document scanner and archiver makes it, to me, totally worth having.</p>
            
            <h1>Backups</h1>
            
            <p>Now I'm all for going digital, but doing so means that I've taken on the risk
            of the inherent fragility of today's storage technology. Paper, in general,
            doesn't fail for mysterious reasons (perhaps this is why MBTF isn't published
            on the side of a ream of paper), but disks certainly do.</p>
            
            <p>The only way I could move more things to digital and not put myself in a
            potential crisis situation with a hardware failure was backups. Lots and lots
            of backups. And not just one backup, but multiple backups. And not just on one
            medium, but several. And not just in one location, but several.</p>
            
            <p>So my current backup strategy now uses a combination of Time Machine, Super
            Duper and Carbonite. I use Time Machine because it's the fastest backup that
            saves me from the dumb accidental file delete. It's really a first-line of
            defense and no more.</p>
            
            <p>I use <a href="http://www.shirt-pocket.com/SuperDuper/SuperDuperDescription.html">Super
            Duper</a> for local, full backups. What's great about Super Duper is
            it's ability to create a bootable volume, which means that if my main drive
            fails, I can still get to the information without necessarily having to fix
            the main hard drive first. I use a rotating pair of LaCie drives and backup
            every other day (I have a repeating OmniFocus task to remind me).</p>
            
            <p>These are great tools, but they use a common medium (FireWire drives) and they
            are in the same location (my house). I might solve the location problem by
            coming up with some backup-swapping routine with someone I saw on a regular
            basis. That is, each person would have two or more Super Duper backup disks,
            and would routinely give one of them to a trusted source. You would then each
            rotate your backups and at your next meeting, "trade" backup disks. I use
            Espionage to encrypt my important data so I'm not worried about it falling
            into the wrong hands.</p>
            
            <p>I haven't done this yet. But even if I did, I'd want another form of backup,
            so I bought a <a href="http://www.carboniate.com">Carbonite</a> account to push data out to the "cloud". There are a
            growing number of services out there like Carbonite and they probably work
            just as well. The point here is peace of mind. It's like buying insurance.
            Sure, it's very unlikely that my house is going to catch on fire and burn to
            the ground with everything in it, but why worry about it at all? And if it did
            happen, why make the loss worse. Sixty bucks a year seems like a pretty cheap
            price to pay for the ability to sleep better at night. We'll call this Golden
            Rule #3:</p>
            
            <blockquote><p>Computers are great but fail. Make sure that fragility doesn't keep you up
            at night.</p></blockquote>
            
            <h1>Dropbox</h1>
            
            <p>The last tool I'll mention is <a href="http://www.dropbox.com">Dropbox</a>. Dropbox is awesome for so many reasons
            and I could go on and on about it. But what was really useful for me in this
            context was the ability to selectively share documents with others. I have
            some pretty serious, private documents going around that I don't just want to
            stuff in an email. Yes, there are encrypted zip files and PDFs but there isn't
            a good standard for the former across platforms and you still have the problem
            of getting the password to the other party in a secure way.</p>
            
            <p>With Dropbox I can share documents securely with my tax accountant. Hell, it's
            even <em>easier</em> than email because I don't have to deal with attachments and
            anti-virus software mistakenly preventing my documents from being received.</p>
            
            <p>Now not all lawyers and accountants are going to be down with such modern
            technology. In our litigation-happy society people in these professions tend
            to be pretty conservative in adapting new technologies. All I can say is that
            I was very fortunate to work with the sort of folks that <em>would</em> go along with
            something like Dropbox.</p>
            
            <h1>FAX Modem</h1>
            
            <p>I can't believe I had to buy this thing, but given the spirit of the post, I
            would be remiss if I didn't mention the humble little Zoom modem. It
            is extraordinarily depressing and aggravating that in this day and age, the
            vast majority of financial institutions out there will accept a fax, but won't
            use email or some web-based tool.</p>
            
            <p>At first I had earnestly hoped that things were going to change and that I
            would only need to make the occasional trip to the local Kinko's to send a
            four page fax for five bucks (thank you very much). As time went on though, I
            realized that any companies willing to do business over the internet were the
            exception rather than the rule. So, as much as I hated to do it, I finally had
            to admit that we haven't really evolved as much as I'd hoped and had to trudge
            down to the local Best Buy to buy a stupid little USB fax modem.</p>
            
            <p>Now, as much as I hate the fact that I have to deal with faxes, the fax
            software integrated into OS X makes it pretty easy. Most folks don't know that
            printing a document in OS X is really a gateway to sending documents to all
            sorts of interesting places (least of which is a printer).</p>
            
            <p>For example, for a number of financial institutions I had to write a cover
            letter and include court documentation to establish authority over my mother's
            accounts. I could write the cover letter in Pages, insert the PDF document
            then "print" it to the fax modem. The built-in fax print location even allows
            you to optionally compose a cover page right in the dialog without the need
            for including it in your documents.</p>
            
            <p>Conversely, accepting a fax is pretty simple too. You plug in your modem, set
            it up to receive calls. When you get the document, you can "print" anywhere.
            In my case I would often "print" incoming documents straight into the
            DEVONThink inbox where I could OCR-scan it and file it away for further use.
            No need to print or scan a damn thing. Bits from one end to the other thank
            you very much.</p>
            
            <h1>Conclusion</h1>
            
            <p>So what's this all about then? Why write this post? I'm no productivity guru,
            but I felt like I found a good set of tools and techniques in this experience
            that might be useful for others. None of these really help with dealing with
            the unexpected loss of my mother, but they were a huge help in making the
            onerous task of settling her Estate palpable and possible.</p>
            
            <h1>Notes</h1>
            
            <div id="notes">
              <ol>
                <li>
                  <a name="note1"></a>
                  Actually the stuff I talk about here was really just a stop-loss
                  program. I actually recovered/saved my sanity by taking up drum lessons.
                </li>
              </ol>
            </div>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Muscle Memory</title>
      <link>
        http://alexvollmer.com/posts/2010/11/10/muscle-memory/
      </link>
      <comments>
        http://alexvollmer.com/posts/2010/11/10/muscle-memory/#comments
      </comments>
      <pubDate>
        Wed, 10 Nov 2010 12:50:48 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            software
        ]]>
      </category>
      <category>
        <![CDATA[
            music
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2010/11/10/muscle-memory/
      </guid>
      <description>
        <![CDATA[
            In the world of software we don't often think about the movement of our
            bodies. Oh we argue about Dvorak keyboards and such, but we don't really
            think about the intricate movements we require of our bodies. In other words,
            we argue about mechanics but we don't really understand <em>motion</em>.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>In the world of software we don't often think about the movement of our
            bodies. Oh we argue about Dvorak keyboards and such, but we don't really
            think about the intricate movements we require of our bodies. In other words,
            we argue about mechanics but we don't really understand <em>motion</em>.</p>
            
            <p>In the documentary
            <a href="http://www.rushbeyondthelightedstage.com/">"Rush: Beyond the Lighted Stage"</a>
            there's a great scene where Neil Peart is sitting down with the jazz legend
            Freddie Gruber. In the interview they talk about the first time the two drum
            masters met. They didn't play a note, but instead focused on the dance-like
            motion of percussion. This was an epiphany for Mr. Peart, who is considered by
            many to be <em>the</em> pre-eminent rock drummer of all time. Imagine being at the top
            of your game for nearly forty years and suddenly having a whole new way to look
            at your art! Instead of considering drumming to be the placement of notes in
            time, Gruber thought of drumming as a rhythmic movement of the body that
            happens to coincide with what we call "music".</p>
            
            <object width="640" height="385"><param name="movie" value="http://www.youtube.com/v/APzDktOZw5o?fs=1&amp;hl=en_US"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/APzDktOZw5o?fs=1&amp;hl=en_US" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="385"></embed></object>
            
            
            <p>I bring this up because I too, am going through a muscle-memory epiphany. I've
            decided to try vim as my main text editor. I've spent a lot of years with Emacs
            and the last few with TextMate. There are things I love about both, but neither
            is perfect. I've worked with lots of smart people who swear by vim, so why not
            try it out and see what it's all about?</p>
            
            <p>I've always abhorred the silly "holy-wars" between Emacs and vim. It's the
            worst kind of provincial rah-rah-ism in a world where perspective and decent
            manners are few and far between<sup><a href="#note1">1</a></sup>. I am not "switching sides" because, frankly,
            I was never on a particular "side" to begin with. These are simply
            tools&mdash;nothing more.</p>
            
            <p>Here's the funny thing: I've noticed that those that trumpet one over the
            other often have no experience with the alternative. I can think of maybe one
            or two other developers I know that have truly attempted to become experts
            with both editors. Everyone else is simply guessing about "the other side".
            For a profession that espouses hard data and facts, it's amazing how heated
            the debates can be when each side has so little information about the other.</p>
            
            <p>But I digress&hellip;</p>
            
            <h2>The Big Question</h2>
            
            <p>So it got me to thinking. Why is that so few people have tried to master both?
            Is it <em>really</em> impossible to learn a whole new set of keyboard motions once
            you've mastered another?</p>
            
            <p>I often joke with people that the one "super-power" I have as a developer is
            my ability to remember hot-key combinations. When I code in Xcode I know all
            the hot-keys to make it sing. When I'm writing in TextMate I know how to make
            it go. I don't consciously execute keyboard motions, they are just part of the
            development experience with that tool.</p>
            
            <p>The reason I joke about it is because I don't really think that it's all that
            extraordinary. It's true that it's not something I see in a lot of other
            developers, but I don't believe it's because they can't do it. I think it's
            because they've never learned how to learn motion.</p>
            
            <h2>Six Strings and the Truth</h2>
            
            <p>Looking back, I've realized that the greatest learning experience I ever had
            was learning to play the guitar. I started when I was 14 and I was
            deadly-serious about it from day one. I devoured everything I could find on the
            subject. I learned music theory, practiced all manner of finger exercises and
            played and played and played.<sup><a href="#note2">2</a></sup> As a result,
            I'm a pretty good rock guitar player.</p>
            
            <p>The funny side-effect of this experience is that I taught myself how to learn.
            I can still remember learning basic music-theory on a train ride when I was a
            teenager. It just hit me like ton of bricks. It was all so simple. After that,
            the mystery of A&#x266D;7&#x266F;5 was gone. It was obvious what that chord was and what it
            sounded like.</p>
            
            <p>Not only did I learn how to learn in a strictly academic sense (you should see
            the binders full of notes I've kept over the years), but I also learned how to
            teach my fingers to learn. As a result keyboard shortcuts are pretty easy to
            master. It's like learning another song. Nobody agonizes over having to
            remember each and every finger-movement to play a new tune. You practice it,
            play it a few times, see the patterns in it and then it's just <em>there</em>. You
            don't think about it. Why should mastering another editor be any different?</p>
            
            <h2>Brain Stretching</h2>
            
            <p>I'm fascinated with the learning process. I've made it a point in my career to
            make learning a continous process. I simply do not get people who do the same
            thing for years on end. How do they not go insane?</p>
            
            <p>So I'm always looking for ways to stretch myself. This year I'm extending my
            learning more broadly and decided to start taking drum lessons.  Wow. Wanna
            feel awkward in less then two seconds? Try playing the simplest syncopation
            beat requiring independent movement from your limbs. It's a humbling
            experience. But, man, I can't wait to figure it out!</p>
            
            <h2>Circling Back</h2>
            
            <p>So what does this have to with editors? Honestly, I'm not quite sure yet. But
            I think most developers have a pretty narrow view of what they're willing to
            learn. As often as not, I think folks just stick with whatever they happened
            to stumble upon the first time. Think about it. How many things are central to
            your game that are the result of circumstances? Are you an Emacs master just
            because of that one job you had with that one graybeard who taught you all of
            the tricks? How would your career have turned out if you <em>hadn't</em> met that
            guy?</p>
            
            <p>It's not just about editors either. You see it in every dude who lives and dies
            with a single programming language or platform. I've got my favorites too, but
            they've never been eternal. I can't help but think that such a narrow view
            contributes in some way to the mountains of terrible software out there in the
            world. How could anyone possibly build anything of quality with such a small
            view of the world?</p>
            
            <p>So I don't think I'll be practicing my vim with a metronome the way I practice
            stick-control exercises or the way I mastered the harmonic minor scale.
            Instead we practice these motions in our daily development work as a matter of
            course. The fact that our livelihoods are tied to this activity I think makes
            people hesitant to totally re-jigger their game. I get that. But I also think
            it's not as scary as it sounds (I seem to do this in some way every two years
            or so) and it gives you a much broader view of the world.</p>
            
            <p>So Emacs-dudes? Listen up. C-x C-c outta that sucker and try something else.
            You snotty vim dudes? Hit <code>:q</code> and try life on the other side. You
            may still hate it, but don't ever pretend that you couldn't get better by
            learning something radically different.</p>
            
            <h1>Footnotes</h1>
            
            <ol>
              <li>
                <a name="note1"></a>
                Seriously. There are times when I'm just embarassed to call myself a
                software developer. How did we all become so incredibly socially inept?
              </li>
              <li>
                <a name="note2"></a>
                At one point in college, I wrote a program (in Scheme!) that generated
                random chord combinations (e.g. Am7, C&#x266F;maj7, etc. etc.) I taught
                myself to to go from one voicing to another with the least amount of
                fretboard movement. Eventually I extended this to practicing arpeggios too.
                It was like a little game and it was fun to get better and better at it.
              </li>
            </ol>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Assert Yourself!</title>
      <link>
        http://alexvollmer.com/posts/2010/10/17/assert-yourself/
      </link>
      <comments>
        http://alexvollmer.com/posts/2010/10/17/assert-yourself/#comments
      </comments>
      <pubDate>
        Sun, 17 Oct 2010 09:46:11 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            cocoa
        ]]>
      </category>
      <category>
        <![CDATA[
            uiautomation
        ]]>
      </category>
      <category>
        <![CDATA[
            TDD
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2010/10/17/assert-yourself/
      </guid>
      <description>
        <![CDATA[
            Well lookee here. It's been a little while since the last post and wouldn't you
            know it? I've got more stuff to share with you about UIAutomation. This time
            it's about a new change made to <a href="http://github.com/alexvollmer/tuneup_js">tuneup</a> that makes it much
            easier and cleaner to assert what a particular window should look like. Like
            everything in tuneup, this new feature was driven by something I really needed
            out of it. I've tried pretty hard not to speculatively add features, instead
            relying on the feedback of my own needs to drive its development.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>Well lookee here. It's been a little while since the last post and wouldn't you
            know it? I've got more stuff to share with you about UIAutomation. This time
            it's about a new change made to <a
            href="http://github.com/alexvollmer/tuneup_js">tuneup</a> that makes it much
            easier and cleaner to assert what a particular window should look like. Like
            everything in tuneup, this new feature was driven by something I really needed
            out of it. I've tried pretty hard not to speculatively add features, instead
            relying on the feedback of my own needs to drive its development.</p>
            
            <p>As I've started writing more UIAutomation tests, I've found that there is
            a common cycle to each test. Generally there is some bit of navigation to get
            yourself somewhere in the app, then you assert a bunch of things about the
            view, and usually navigate somewhere else. I don't think this is far off from
            how you might verbally describe how the app might work. For example, you
            might say: "Go to the main screen, the left button should say 'Cancel' and the
            right button should say 'Done'. Fill in the username and password fields then
            tap the 'Done' button. On the next view you should see the user name in a table
            cell and another cell to add another account".</p>
            
            <p>It's fairly straightforward process translating that description into test
            code:</p>
            
            <div class="highlight"><pre><span class="nx">test</span><span class="p">(</span><span class="s2">&quot;account setup&quot;</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">app</span><span class="p">,</span> <span class="nx">target</span><span class="p">)</span> <span class="p">{</span>&#x000A;      <span class="c1">// initial view</span>&#x000A;      <span class="nb">window</span> <span class="o">=</span> <span class="nx">target</span><span class="p">.</span><span class="nx">mainWindow</span><span class="p">();</span>&#x000A;      <span class="nx">navBar</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">navigationBar</span><span class="p">();</span>&#x000A;      <span class="nx">assertEquals</span><span class="p">(</span><span class="s2">&quot;Cancel&quot;</span><span class="p">,</span> <span class="nx">navBar</span><span class="p">.</span><span class="nx">leftButton</span><span class="p">().</span><span class="nx">name</span><span class="p">());</span>&#x000A;      <span class="nx">assertEquals</span><span class="p">(</span><span class="s2">&quot;Done&quot;</span><span class="p">,</span> <span class="nx">navBar</span><span class="p">.</span><span class="nx">rightButton</span><span class="p">().</span><span class="nx">name</span><span class="p">());</span>&#x000A;    &#x000A;      <span class="nx">table</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">tableViews</span><span class="p">()[</span><span class="mi">0</span><span class="p">];</span>&#x000A;      <span class="nx">assertEquals</span><span class="p">(</span><span class="s2">&quot;Enter your credentials:&quot;</span><span class="p">,</span> <span class="nx">table</span><span class="p">.</span><span class="nx">groups</span><span class="p">()[</span><span class="mi">0</span><span class="p">].</span><span class="nx">name</span><span class="p">());</span>&#x000A;      <span class="nx">assertEquals</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="nx">table</span><span class="p">.</span><span class="nx">cells</span><span class="p">().</span><span class="nx">length</span><span class="p">);</span>&#x000A;    &#x000A;      <span class="nx">userName</span> <span class="o">=</span> <span class="nx">table</span><span class="p">.</span><span class="nx">cells</span><span class="p">.</span><span class="nx">firstWithName</span><span class="p">(</span><span class="s2">&quot;username&quot;</span><span class="p">);</span>&#x000A;      <span class="nx">userName</span><span class="p">.</span><span class="nx">textFields</span><span class="p">()[</span><span class="mi">0</span><span class="p">].</span><span class="nx">setValue</span><span class="p">(</span><span class="s2">&quot;user boy&quot;</span><span class="p">);</span>&#x000A;      <span class="nx">password</span> <span class="o">=</span> <span class="nx">table</span><span class="p">.</span><span class="nx">cells</span><span class="p">.</span><span class="nx">firstWithName</span><span class="p">(</span><span class="s2">&quot;password&quot;</span><span class="p">);</span>&#x000A;      <span class="nx">password</span><span class="p">.</span><span class="nx">textFields</span><span class="p">()[</span><span class="mi">0</span><span class="p">].</span><span class="nx">setValue</span><span class="p">(</span><span class="s2">&quot;sekret&quot;</span><span class="p">);</span>&#x000A;    &#x000A;      <span class="nx">navBar</span><span class="p">.</span><span class="nx">rightButton</span><span class="p">().</span><span class="nx">tap</span><span class="p">();</span>&#x000A;    &#x000A;      <span class="c1">// verify new account on settings view</span>&#x000A;      <span class="nx">navBar</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">navigationBar</span><span class="p">();</span>&#x000A;      <span class="nx">assertEquals</span><span class="p">(</span><span class="s2">&quot;New&quot;</span><span class="p">,</span> <span class="nx">navBar</span><span class="p">.</span><span class="nx">rightButton</span><span class="p">().</span><span class="nx">name</span><span class="p">());</span>&#x000A;      <span class="nx">table</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">tableViews</span><span class="p">()[</span><span class="mi">0</span><span class="p">];</span>&#x000A;    &#x000A;      <span class="nx">assertEquals</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="nx">table</span><span class="p">.</span><span class="nx">groups</span><span class="p">().</span><span class="nx">length</span><span class="p">);</span>&#x000A;      <span class="nx">assertEquals</span><span class="p">(</span><span class="s2">&quot;Your accounts:&quot;</span><span class="p">,</span> <span class="nx">table</span><span class="p">.</span><span class="nx">groups</span><span class="p">()[</span><span class="mi">0</span><span class="p">].</span><span class="nx">name</span><span class="p">());</span>&#x000A;      <span class="nx">assertEquals</span><span class="p">(</span><span class="s2">&quot;&quot;</span><span class="p">,</span> <span class="nx">table</span><span class="p">.</span><span class="nx">groups</span><span class="p">()[</span><span class="mi">1</span><span class="p">].</span><span class="nx">name</span><span class="p">());</span>&#x000A;    &#x000A;      <span class="nx">assertEquals</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="nx">table</span><span class="p">.</span><span class="nx">cells</span><span class="p">().</span><span class="nx">length</span><span class="p">);</span>&#x000A;      <span class="nx">assertEquals</span><span class="p">(</span><span class="s2">&quot;user boy&quot;</span><span class="p">,</span> <span class="nx">table</span><span class="p">.</span><span class="nx">cells</span><span class="p">()[</span><span class="mi">0</span><span class="p">].</span><span class="nx">name</span><span class="p">());</span>&#x000A;      <span class="nx">assertEquals</span><span class="p">(</span><span class="s2">&quot;Add Account&quot;</span><span class="p">,</span> <span class="nx">table</span><span class="p">.</span><span class="nx">cells</span><span class="p">()[</span><span class="mi">1</span><span class="p">].</span><span class="nx">name</span><span class="p">());</span>&#x000A;    <span class="p">});</span>&#x000A;    </pre>
            </div>
            
            
            <p>One window's worth of activity isn't so bad, but once you get beyond that, it's
            pretty easy to lose track of what you're trying to do. Comments help a bit, but
            if our code had the same structure that matched the way we think about our
            tests, it would be even better. Now you can do exactly that with a new assertion
            method in tuneup: <code>assertWindow()</code>.  Here's the previous example, re-stated
            with <code>assertWindow()</code>:</p>
            
            <div class="highlight"><pre><span class="nx">test</span><span class="p">(</span><span class="s2">&quot;account setup&quot;</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">app</span><span class="p">,</span> <span class="nx">target</span><span class="p">)</span> <span class="p">{</span>&#x000A;      <span class="nx">assertWindow</span><span class="p">({</span>&#x000A;        <span class="nx">navigationBar</span><span class="o">:</span> <span class="p">{</span>&#x000A;          <span class="nx">leftButton</span><span class="o">:</span>  <span class="p">{</span> <span class="nx">name</span><span class="o">:</span> <span class="s2">&quot;Cancel&quot;</span> <span class="p">},</span>&#x000A;          <span class="nx">rightButton</span><span class="o">:</span> <span class="p">{</span> <span class="nx">name</span><span class="o">:</span> <span class="s2">&quot;Done&quot;</span> <span class="p">}</span>&#x000A;        <span class="p">},</span>&#x000A;        <span class="nx">tableViews</span><span class="o">:</span> <span class="p">[</span>&#x000A;          <span class="p">{</span>&#x000A;            <span class="nx">cells</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">cells</span><span class="p">)</span> <span class="p">{</span>&#x000A;              <span class="nx">cells</span><span class="p">.</span><span class="nx">firstWithName</span><span class="p">(</span><span class="s2">&quot;username&quot;</span><span class="p">).</span><span class="nx">textFields</span><span class="p">()[</span><span class="mi">0</span><span class="p">].</span><span class="nx">setValue</span><span class="p">(</span><span class="s2">&quot;user boy&quot;</span><span class="p">);</span>&#x000A;              <span class="nx">cells</span><span class="p">.</span><span class="nx">firstWithName</span><span class="p">(</span><span class="s2">&quot;password&quot;</span><span class="p">).</span><span class="nx">textFields</span><span class="p">()[</span><span class="mi">0</span><span class="p">].</span><span class="nx">setValue</span><span class="p">(</span><span class="s2">&quot;sekret&quot;</span><span class="p">);</span>&#x000A;            <span class="p">}</span>&#x000A;          <span class="p">}</span>&#x000A;        <span class="p">],</span>&#x000A;        <span class="nx">onPass</span><span class="o">:</span> <span class="kd">function</span><span class="p">(</span><span class="nb">window</span><span class="p">)</span> <span class="p">{</span>&#x000A;          <span class="nb">window</span><span class="p">.</span><span class="nx">navigationBar</span><span class="p">().</span><span class="nx">rightButton</span><span class="p">().</span><span class="nx">tap</span><span class="p">();</span>&#x000A;        <span class="p">}</span>&#x000A;      <span class="p">});</span>&#x000A;    &#x000A;      <span class="nx">assertWindow</span><span class="p">({</span>&#x000A;        <span class="nx">navigationBar</span><span class="o">:</span> <span class="p">{</span>&#x000A;          <span class="nx">rightButton</span><span class="o">:</span> <span class="p">{</span> <span class="nx">name</span><span class="o">:</span> <span class="s2">&quot;New&quot;</span> <span class="p">}</span>&#x000A;        <span class="p">},</span>&#x000A;        <span class="nx">tableViews</span><span class="o">:</span> <span class="p">[</span>&#x000A;          <span class="p">{</span>&#x000A;            <span class="nx">groups</span><span class="o">:</span> <span class="p">[</span>&#x000A;              <span class="p">{</span> <span class="nx">name</span><span class="o">:</span> <span class="s2">&quot;Your accounts:&quot;</span> <span class="p">},</span>&#x000A;              <span class="p">{</span> <span class="nx">name</span><span class="o">:</span> <span class="s2">&quot;&quot;</span> <span class="p">}</span>&#x000A;            <span class="p">],</span>&#x000A;            <span class="nx">cells</span><span class="o">:</span> <span class="p">[</span>&#x000A;              <span class="p">{</span> <span class="nx">name</span><span class="o">:</span> <span class="s2">&quot;user boy&quot;</span> <span class="p">},</span>&#x000A;              <span class="p">{</span> <span class="nx">name</span><span class="o">:</span> <span class="s2">&quot;Add Account&quot;</span> <span class="p">}</span>&#x000A;            <span class="p">]</span>&#x000A;          <span class="p">}</span>&#x000A;        <span class="p">]</span>&#x000A;      <span class="p">});</span>&#x000A;    <span class="p">});</span>&#x000A;    </pre>
            </div>
            
            
            <p>So what's going on here? Using <code>assertWindow()</code>, we've replaced several lines
            of procedural code with a javascript object-literal that declares how to match
            the current window. The property-nesting in the object given to
            <code>assertWindow()</code> (the "expectation") is matched against the property-nesting of
            the <code>UIAWindow</code> object representing the current view. For example, when we
            declare a <code>navigationBar</code> property in our expectation object, it is matched
            against the object returned by the <code>navigationBar()</code> function invoked on the
            main window (an instance of <code>UIANavigationBar</code>). Then the <code>rightButton</code>
            property is matched against the object returned by the <code>rightButton()</code> method
            (an instance of <code>UIAButton</code>). Finally the <code>name</code> property is matched against
            the object returned by invoking the <code>name()</code> function on the button, which in
            this case is a <code>String</code>.</p>
            
            <p>You can do more than just match <code>Strings</code> though. You can also match numeric
            literals or regular expressions. These are done by applying the <code>assertEquals()</code>
            and <code>assertMatch()</code> functions, respectively. You can also completely customize
            your assertions by providing a function for the property. The corresponding
            object will be given to the function as its single argument. If any exceptions
            are thrown the assertion fails, otherwise the assertion passes.</p>
            
            <p><code>assertWindow()</code> also knows how to handle properties that return arrays (or
            <code>UIAElementArray</code>s). In the example above we matched the <code>tableViews</code> property
            by declaring an array with one element. The number of elements you put in the
            property declaration implicitly makes an assertion about the number of elements
            that should be in the property. If you are only interested in matching <em>some</em>
            of the elements in an array, you can set <code>null</code> for the elements in the array
            you don't care to make assertions about. This will allow you to still pass the
            array-length check without forcing you to write assertions you don't care to.</p>
            
            <p>The <code>onPass</code> property is an optional function you that will be invoked if all
            of the assertions pass. This is a handy way to group assertions and actions
            together in one package. In the example above, we use the <code>onPass</code> function as
            post-assertion navigation to the next screen.</p>
            
            <p>You can find all the gnarly details in the <code>assertions.js</code> file in tuneup. As
            always comments, feedback, forking and patches are all welcome and appreciated.
            Now go out and assert yourself!</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>NSOperations for Fun &amp; Profit</title>
      <link>
        http://alexvollmer.com/posts/2010/09/19/nsoperations-for-fun-and-profit/
      </link>
      <comments>
        http://alexvollmer.com/posts/2010/09/19/nsoperations-for-fun-and-profit/#comments
      </comments>
      <pubDate>
        Sun, 19 Sep 2010 16:54:43 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            cocoa
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2010/09/19/nsoperations-for-fun-and-profit/
      </guid>
      <description>
        <![CDATA[
            If you've spent any time working in Cocoa, you get very familiar with
            asynchronous processing. It's the classic <a href="http://en.wikipedia.org/wiki/Hollywood_Principle" title="Hollywood Principle - Wikipedia, the free encyclopedia">"Hollywood Principle"</a>
            &mdash;don't call
            us we'll call you. But what do you do when you have several simultaneous
            asynchronous operations whose results depend on each other? The brute-force,
            caveman approach is to simply have each callback method invoke a single method
            that checks the results of each operation and then proceeds forward when
            everything is completed. But by using Cocoa's <code>NSOperation</code> and
            <code>NSOperationQueue</code> classes you can handle dependent, asynchronous operations
            much more safely and elegantly.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>If you've spent any time working in Cocoa, you get very familiar with
            asynchronous processing. It's the classic <a href="http://en.wikipedia.org/wiki/Hollywood_Principle" title="Hollywood Principle - Wikipedia, the free encyclopedia">"Hollywood Principle"</a>
            &mdash;don't call
            us we'll call you. But what do you do when you have several simultaneous
            asynchronous operations whose results depend on each other? The brute-force,
            caveman approach is to simply have each callback method invoke a single method
            that checks the results of each operation and then proceeds forward when
            everything is completed. But by using Cocoa's <code>NSOperation</code> and
            <code>NSOperationQueue</code> classes you can handle dependent, asynchronous operations
            much more safely and elegantly.</p>
            
            <h1>Kicking it Caveman Style</h1>
            
            <p>Before diving into operations and queues, let's imagine doing this in the
            "primitive" style. Let's say we have three operations: A, B, and C. Each does
            some asynchronous work and we have no idea when it will complete. The
            primitive way of handling this is to have each callback methods invoke a
            single method that checks for the results of all three units of work. Only
            when all three have returned do we continue processing. It might look
            something like this:</p>
            
            <div class="highlight"><pre><span class="c1">// our final unit-of-work when all other operations have completed</span>&#x000A;    <span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">checkAllOperationsAndProceed</span> <span class="p">{</span>&#x000A;      <span class="k">if</span> <span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">resultA</span> <span class="o">&amp;&amp;</span> <span class="n">self</span><span class="p">.</span><span class="n">resultB</span> <span class="o">&amp;&amp;</span> <span class="n">self</span><span class="p">.</span><span class="n">resultC</span><span class="p">)</span> <span class="p">{</span>&#x000A;        <span class="c1">// do that magic thing you do</span>&#x000A;      <span class="p">}</span>&#x000A;    <span class="p">}</span>&#x000A;    &#x000A;    <span class="c1">// our mythical call-back methods</span>&#x000A;    &#x000A;    <span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nl">operationADidComplete:</span><span class="p">(</span><span class="kt">id</span><span class="p">)</span><span class="n">result</span> <span class="p">{</span>&#x000A;      <span class="n">self</span><span class="p">.</span><span class="n">resultA</span> <span class="o">=</span> <span class="n">result</span><span class="p">;</span>&#x000A;      <span class="p">[</span><span class="n">self</span> <span class="n">checkAllOperationsAndProceed</span><span class="p">];</span>&#x000A;    <span class="p">}</span>&#x000A;    &#x000A;    <span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nl">operationBDidComplete:</span><span class="p">(</span><span class="kt">id</span><span class="p">)</span><span class="n">result</span> <span class="p">{</span>&#x000A;      <span class="n">self</span><span class="p">.</span><span class="n">resultB</span> <span class="o">=</span> <span class="n">result</span><span class="p">;</span>&#x000A;      <span class="p">[</span><span class="n">self</span> <span class="n">checkAllOperationsAndProceed</span><span class="p">];</span>&#x000A;    <span class="p">}</span>&#x000A;    &#x000A;    <span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nl">operationCDidComplete:</span><span class="p">(</span><span class="kt">id</span><span class="p">)</span><span class="n">result</span> <span class="p">{</span>&#x000A;      <span class="n">self</span><span class="p">.</span><span class="n">resultC</span> <span class="o">=</span> <span class="n">result</span><span class="p">;</span>&#x000A;      <span class="p">[</span><span class="n">self</span> <span class="n">checkAllOperationsAndProceed</span><span class="p">];</span>&#x000A;    <span class="p">}</span>&#x000A;    </pre>
            </div>
            
            
            <p>You can think of this as a "last-one-in-wins" pattern. It's kind of ugly and
            doesn't scale well with more complicated dependencies. It gets worse when
            you have specific threading concerns like working with Core Data, which is
            notoriously fussy about threads.</p>
            
            <p>I had this exact problem using Core Location and Game Kit. I use Core Location
            to first retrieve the current location, while simultaneously communicating
            with other devices via Game Kit. When the location is resolved, we then
            attempt to reverse-geocode it which results in another asynchronous operation.
            All of these bits of work are intertwined and update the underlying persistent
            model in different ways. It didn't take too long before it spiraled completely
            out of control. What's more, it was easy to hit odd edge-cases with
            synchronizing various threads to keep Core Data happy. Since I wasn't getting
            consistent failures, it was clear that there were race-conditions in the code.
            Then a little voice in the back of my head reminded me that <code>NSOperation</code>
            instances can have dependencies between each other. A ha!</p>
            
            <h1>A More Elegant Weapon, for a More Civilized Age</h1>
            
            <p>Maybe the caveman approach doesn't offend your sensibilities for such a
            trivial example. But imagine a much more complicated set of dependencies,
            perhaps something that looked like this <sup><a href="#note1">1</a></sup>:</p>
            
            <p><img src="/images/2010/09/operation-dependencies.png" alt="operation dependencies" /></p>
            
            <p>Now imagine all the <code>if</code> checks and the spaghetti code that would be required
            to make this work. I don't know about you, but this makes my Spidey-sense
            tingle&hellip;and not in a good way.</p>
            
            <p>So let's turn these into <code>NSOperation</code> instances with dependencies. We would
            start with method prototypes and properties declarations like this:</p>
            
            <div class="highlight"><pre><span class="k">@property</span> <span class="p">(</span><span class="n">nonatomic</span><span class="p">,</span> <span class="n">retain</span><span class="p">)</span> <span class="n">NSOperationQueue</span>  <span class="o">*</span><span class="n">operationQueue</span><span class="p">;</span>&#x000A;    <span class="k">@property</span> <span class="p">(</span><span class="n">nonatomic</span><span class="p">,</span> <span class="n">retain</span><span class="p">)</span> <span class="n">NSOperation</span>       <span class="o">*</span><span class="n">operationA</span><span class="p">;</span>&#x000A;    <span class="k">@property</span> <span class="p">(</span><span class="n">nonatomic</span><span class="p">,</span> <span class="n">retain</span><span class="p">)</span> <span class="n">NSOperation</span>       <span class="o">*</span><span class="n">operationB</span><span class="p">;</span>&#x000A;    <span class="k">@property</span> <span class="p">(</span><span class="n">nonatomic</span><span class="p">,</span> <span class="n">retain</span><span class="p">)</span> <span class="n">NSOperation</span>       <span class="o">*</span><span class="n">operationC</span><span class="p">;</span>&#x000A;    <span class="k">@property</span> <span class="p">(</span><span class="n">nonatomic</span><span class="p">,</span> <span class="n">retain</span><span class="p">)</span> <span class="n">NSOperation</span>       <span class="o">*</span><span class="n">operationD</span><span class="p">;</span>&#x000A;    <span class="k">@property</span> <span class="p">(</span><span class="n">nonatomic</span><span class="p">,</span> <span class="n">retain</span><span class="p">)</span> <span class="n">NSOperation</span>       <span class="o">*</span><span class="n">operationE</span><span class="p">;</span>&#x000A;    <span class="k">@property</span> <span class="p">(</span><span class="n">nonatomic</span><span class="p">,</span> <span class="n">retain</span><span class="p">)</span> <span class="n">NSOperation</span>       <span class="o">*</span><span class="n">operationF</span><span class="p">;</span>&#x000A;    &#x000A;    <span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">performOperationA</span><span class="p">;</span>&#x000A;    <span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">performOperationB</span><span class="p">;</span>&#x000A;    <span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">performOperationC</span><span class="p">;</span>&#x000A;    <span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">performOperationD</span><span class="p">;</span>&#x000A;    <span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">performOperationE</span><span class="p">;</span>&#x000A;    <span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">performOperationF</span><span class="p">;</span>&#x000A;    </pre>
            </div>
            
            
            <p>We'll do the actual work in instance methods of our class and wrap each
            one with an instance of <code>NSInvocationOperation</code>. Here's our setup method:</p>
            
            <div class="highlight"><pre><span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">initializeOperations</span> <span class="p">{</span>&#x000A;      <span class="n">self</span><span class="p">.</span><span class="n">operationQueue</span> <span class="o">=</span> <span class="p">[[[</span><span class="n">NSOperationQueue</span> <span class="n">alloc</span><span class="p">]</span> <span class="n">init</span><span class="p">]</span> <span class="n">autorelease</span><span class="p">];</span>&#x000A;      <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">operationQueue</span> <span class="nl">setMaxConcurrentOperationCount:</span><span class="mi">1</span><span class="p">];</span>&#x000A;      &#x000A;      <span class="n">self</span><span class="p">.</span><span class="n">operationA</span> <span class="o">=</span> <span class="p">[[[</span><span class="n">NSInvocationOperation</span> <span class="n">alloc</span><span class="p">]</span> <span class="nl">initWithTarget:</span><span class="n">self</span>&#x000A;                                                              <span class="nl">selector:</span><span class="k">@selector</span><span class="p">(</span><span class="n">performOperationA</span><span class="p">),</span>&#x000A;                                                               <span class="nl">object:</span><span class="nb">nil</span><span class="p">]</span> <span class="n">autorelease</span><span class="p">];</span>&#x000A;                                                               &#x000A;      <span class="n">self</span><span class="p">.</span><span class="n">operationB</span> <span class="o">=</span> <span class="p">[[[</span><span class="n">NSInvocationOperation</span> <span class="n">alloc</span><span class="p">]</span> <span class="nl">initWithTarget:</span><span class="n">self</span>&#x000A;                                                              <span class="nl">selector:</span><span class="k">@selector</span><span class="p">(</span><span class="n">performOperationB</span><span class="p">),</span>&#x000A;                                                               <span class="nl">object:</span><span class="nb">nil</span><span class="p">]</span> <span class="n">autorelease</span><span class="p">];</span>&#x000A;                                                               &#x000A;      <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">operationB</span> <span class="nl">addDependency:</span><span class="n">operationA</span><span class="p">];</span>&#x000A;                                                               &#x000A;      <span class="n">self</span><span class="p">.</span><span class="n">operationC</span> <span class="o">=</span> <span class="p">[[[</span><span class="n">NSInvocationOperation</span> <span class="n">alloc</span><span class="p">]</span> <span class="nl">initWithTarget:</span><span class="n">self</span>&#x000A;                                                              <span class="nl">selector:</span><span class="k">@selector</span><span class="p">(</span><span class="n">performOperationC</span><span class="p">),</span>&#x000A;                                                               <span class="nl">object:</span><span class="nb">nil</span><span class="p">]</span> <span class="n">autorelease</span><span class="p">];</span>&#x000A;                                                               &#x000A;      <span class="n">self</span><span class="p">.</span><span class="n">operationD</span> <span class="o">=</span> <span class="p">[[[</span><span class="n">NSInvocationOperation</span> <span class="n">alloc</span><span class="p">]</span> <span class="nl">initWithTarget:</span><span class="n">self</span>&#x000A;                                                              <span class="nl">selector:</span><span class="k">@selector</span><span class="p">(</span><span class="n">performOperationD</span><span class="p">),</span>&#x000A;                                                               <span class="nl">object:</span><span class="nb">nil</span><span class="p">]</span> <span class="n">autorelease</span><span class="p">];</span>&#x000A;                                                               &#x000A;      <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">operationD</span> <span class="nl">addDependency:</span><span class="n">operationA</span><span class="p">];</span>&#x000A;      <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">operationD</span> <span class="nl">addDependency:</span><span class="n">operationC</span><span class="p">];</span>&#x000A;                                                               &#x000A;      <span class="n">self</span><span class="p">.</span><span class="n">operationE</span> <span class="o">=</span> <span class="p">[[[</span><span class="n">NSInvocationOperation</span> <span class="n">alloc</span><span class="p">]</span> <span class="nl">initWithTarget:</span><span class="n">self</span>&#x000A;                                                              <span class="nl">selector:</span><span class="k">@selector</span><span class="p">(</span><span class="n">performOperationE</span><span class="p">),</span>&#x000A;                                                               <span class="nl">object:</span><span class="nb">nil</span><span class="p">]</span> <span class="n">autorelease</span><span class="p">];</span>&#x000A;                                                               &#x000A;      <span class="n">self</span><span class="p">.</span><span class="n">operationF</span> <span class="o">=</span> <span class="p">[[[</span><span class="n">NSInvocationOperation</span> <span class="n">alloc</span><span class="p">]</span> <span class="nl">initWithTarget:</span><span class="n">self</span>&#x000A;                                                              <span class="nl">selector:</span><span class="k">@selector</span><span class="p">(</span><span class="n">performOperationF</span><span class="p">),</span>&#x000A;                                                               <span class="nl">object:</span><span class="nb">nil</span><span class="p">]</span> <span class="n">autorelease</span><span class="p">];</span>&#x000A;    &#x000A;      <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">operationE</span> <span class="nl">addDependency:</span><span class="n">operationD</span><span class="p">];</span>&#x000A;      <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">operationE</span> <span class="nl">addDependency:</span><span class="n">operationF</span><span class="p">];</span>&#x000A;    <span class="p">}</span>&#x000A;    </pre>
            </div>
            
            
            <p>One interesting thing to note here is that the operations that have
            dependencies could be queued right away. They won't execute until their
            dependent operations execute first. Another cool feature of <code>NSOperation</code>s
            is that dependent operations <em>don't</em> have to execute in the same operation
            queue. Believe it or not, I've actually needed this very feature.</p>
            
            <p>To execute these operations in the correct order, we simply need to add
            them to the queue. If the dependencies are correctly declared, the order in
            which we add them is irrelevant. Instead the final execution order is
            determined by the dependency graph of the queued operations<sup><a href="#note2"></a></sup>.
            So one of our asynchronous callback methods might look like this:</p>
            
            <div class="highlight"><pre><span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nl">didGetSpecialCallback:</span><span class="p">(</span><span class="n">NSDictionary</span> <span class="o">*</span><span class="p">)</span><span class="n">userInfo</span> <span class="p">{</span>&#x000A;      <span class="n">self</span><span class="p">.</span><span class="n">firstName</span> <span class="o">=</span> <span class="p">[</span><span class="n">userInfo</span> <span class="nl">objectForKey:</span><span class="s">@&quot;firstName&quot;</span><span class="p">];</span>&#x000A;      <span class="n">self</span><span class="p">.</span><span class="n">lastName</span> <span class="o">=</span> <span class="p">[</span><span class="n">userInfo</span> <span class="nl">objectForKey:</span><span class="s">@&quot;lastName&quot;</span><span class="p">];</span>&#x000A;      <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">operationQueue</span> <span class="nl">addOperation:</span><span class="n">operationD</span><span class="p">];</span>&#x000A;    <span class="p">}</span>&#x000A;    </pre>
            </div>
            
            
            <p>The call to add <code>operationD</code> is <em>effectively</em> like invoking the
            <code>performOperationD</code> method except that it won't get executed until it's
            two dependent methods, <code>operationA</code> and <code>operationC</code> have completed first.
            But the beauty of all of this, is that my callback code doesn't have to care
            about that ordering.</p>
            
            <p>Once your code has enough concurrent asynchronous operations that are
            dependent on each other, this can be a great way of straightening out what
            work is going to get done in a thread-safe way.</p>
            
            <h1>Rules of Thumb</h1>
            
            <h2>Queues Make Life Easy</h2>
            
            <p>One common principle of concurrency is being able to replace mutexes
            (muteces?) with queues. Any time you have shared data operated on by multiple
            threads, you can synchronize them with serial operations in a single queue
            instead of using explicit locks. The operations in my app are lightning-quick
            so I'm not worried about slowing things down with a single-threaded queue. A
            great side-effect in using operations with dependencies is that I know
            <em>precisely</em> what order those units of work are going to execute. This takes
            away a slew of edge-cases that are difficult to reproduce and debug.</p>
            
            <h2>Be Assertive</h2>
            
            <p>Because you know the order of operations, you can put a nice safety-net
            underneath yourself as you develop with a liberal application of assertions.
            For example, in the operation 'D', which requires that 'A' and 'C' have
            completed, the code can expressly assert that effects of 'A' and 'C' have
            been applied. If they aren't, it means there's a programming error:</p>
            
            <div class="highlight"><pre><span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">performOperationD</span> <span class="p">{</span>&#x000A;      <span class="k">@try</span> <span class="p">{</span>&#x000A;        <span class="n">NSAssert</span><span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">firstName</span> <span class="o">!=</span> <span class="nb">nil</span><span class="p">,</span> <span class="s">&quot;First name is nil!&quot;</span><span class="p">);</span>&#x000A;        <span class="n">NSAssert</span><span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">lastName</span> <span class="o">!=</span> <span class="nb">nil</span><span class="p">,</span> <span class="s">&quot;Last name is nil!&quot;</span><span class="p">);</span>&#x000A;    &#x000A;        <span class="c1">// rest of method elided for readability</span>&#x000A;      <span class="p">}</span>&#x000A;      <span class="k">@catch</span> <span class="p">(</span><span class="n">NSException</span> <span class="o">*</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>&#x000A;        <span class="n">NSLog</span><span class="p">(</span><span class="s">@&quot;ERROR associating place with encounter: %@&quot;</span><span class="p">,</span> <span class="n">e</span><span class="p">);</span>&#x000A;      <span class="p">}</span>&#x000A;    <span class="p">}</span>&#x000A;    </pre>
            </div>
            
            
            <h2>Try, Try Again</h2>
            
            <p>The last piece of advice is to make sure you put <code>@try/@catch</code> blocks in
            the methods that are executed by your operations. Since these are on a
            separate thread, you may not see a standard backtrace when an exception is
            thrown. You need to handle this yourself, otherwise things may silently
            fail. This is especially true of Core Data which will throw exceptions for
            programming errors like accessing objects with the wrong thread.</p>
            
            <h1 id="footnotes">Footnotes</h1>
            
            
            <ol>
              <li>
                <a name="note1"></a>
                This is a simplified version of the dependency graph of an application
                I'm currently working on, so it's not just some trite example I
                invented to make a point. Rather than saddle you with the mundane 
                details of each operation, I've chosen to go with the rather abstract,
                if boring, A-B-C nomenclature.
              </li>
              <li>
                <a name="note2"></a>
                Okay, that's not strictly true. Operations can also have different
                priorities which also affect the execution order. However assuming that
                all operations have the same priority, the original statement still
                holds true.
              </li>
            </ol>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Launching the Radiant Capsule</title>
      <link>
        http://alexvollmer.com/posts/2010/09/12/launching-the-radiant-capsule/
      </link>
      <comments>
        http://alexvollmer.com/posts/2010/09/12/launching-the-radiant-capsule/#comments
      </comments>
      <pubDate>
        Sun, 12 Sep 2010 12:26:30 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            biz
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2010/09/12/launching-the-radiant-capsule/
      </guid>
      <description>
        <![CDATA[
            A while back <a href="http://alexvollmer.com/posts/2010/04/17/thanks-mom/" title="Alex Vollmer &mdash; Thanks, mom">I posted</a>
            about a significant career-change brought about by the
            untimely death of my mom. This ultimately resulted in the formation
            of my own one-man company, <a href="http://www.radiantcapsule.com/" title="Radiant Capsule LLC&mdash;Mobile. Web. People.">Radiant Capsule</a>.
            I used to find the idea of forming a company pretty daunting&mdash;all the
            paperwork, all the tax-filing dates and whatnot to remember. It was all pretty
            overwhelming. But since I've launched headlong into settling my mom's estate,
            the effort forming an LLC seemed trivial by comparison.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>A while back <a href="http://alexvollmer.com/posts/2010/04/17/thanks-mom/" title="Alex Vollmer &mdash; Thanks, mom">I posted</a>
            about a significant career-change brought about by the
            untimely death of my mom. This ultimately resulted in the formation
            of my own one-man company, <a href="http://www.radiantcapsule.com/" title="Radiant Capsule LLC&mdash;Mobile. Web. People.">Radiant Capsule</a>.
            I used to find the idea of forming a company pretty daunting&mdash;all the
            paperwork, all the tax-filing dates and whatnot to remember. It was all pretty
            overwhelming. But since I've launched headlong into settling my mom's estate,
            the effort forming an LLC seemed trivial by comparison.</p>
            
            <p><img src="/images/2010/09/rc-biz-cards.jpg" class="right"/></p>
            
            <p>So what's with the name? Kinda weird, isn't it? I went around and around on
            various names. Eventually I settled on something related to space. I really love
            the look and design of Stanley Kubrick's <em>2001: A Space Odyssey</em>. Not so much
            for how it looks retro from our modern perspective, but how futuristic it
            looked from 1969. I settled on having the word "capsule" in the name somewhere
            because I liked the idea of a self-contained package. Right now it's just me,
            but even if I ever bring on partners, I don't expect it to be too big. So
            "space station" would have been inappropriate given the philosophy behind the
            company.</p>
            
            <p>Why "radiant"?. Rhythmically, I knew I wanted a name in the form of
            "<em>insert-adjective-here</em> Capsule". I sat down one afternoon, in complete
            silence, and just wrote adjective after adjective on a sheet of paper. Just
            trying out words I liked, and listening to how they sounded together. Finally
            I hit upon "Radiant Capsule" and I felt like I had a winner. I just really
            like the way it sounds.</p>
            
            <p>The design was bootstrapped by the logo which was created by
            <a href="http://whole-studios.com/" title="Ryan Eilers Schroeder | Graphic &amp; UX Design">Ryan Schroeder</a>.
            After that I was off and running. I don't know whether you can call the
            site design and business cards "good", but it is certainly some of the best
            visual design I've ever done. Like all of this, it's a learning experience.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>UIAutomation Test Fixtures Redux</title>
      <link>
        http://alexvollmer.com/posts/2010/09/06/uiautomation-test-fixtures-redux/
      </link>
      <comments>
        http://alexvollmer.com/posts/2010/09/06/uiautomation-test-fixtures-redux/#comments
      </comments>
      <pubDate>
        Mon, 06 Sep 2010 14:41:13 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            iphone
        ]]>
      </category>
      <category>
        <![CDATA[
            TDD
        ]]>
      </category>
      <category>
        <![CDATA[
            cocoa
        ]]>
      </category>
      <category>
        <![CDATA[
            uiautomation
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2010/09/06/uiautomation-test-fixtures-redux/
      </guid>
      <description>
        <![CDATA[
            In my ongoing exploration of the UIAutomation tool, I've iterated several
            times on implementing test fixtures. In this context, "test fixture" means
            a pre-defined starting state for the system-under-test (SUT). In my case, my
            test fixtures are focused almost exclusively on the current state of the
            persistent Core Data database. While I think this is an obvious case of an
            integration-test fixture, it's not the only possibility.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>In my ongoing exploration of the UIAutomation tool, I've iterated several
            times on implementing test fixtures. In this context, "test fixture" means
            a pre-defined starting state for the system-under-test (SUT). In my case, my
            test fixtures are focused almost exclusively on the current state of the
            persistent Core Data database. While I think this is an obvious case of an
            integration-test fixture, it's not the only possibility.</p>
            
            <h1>It's About Dependencies</h1>
            
            <p>I think it's worth backing up and explaining why I've spent so much time
            noodling on test fixtures. Any system of moderate complexity has features that
            depend on the existence of other features. For example it doesn't make sense
            to have a "recover your forgotten password" feature without having an
            authentication feature that requires a password in the first place. If you
            drew each of your features in a box then drew a line between each feature and
            its prerequisite features, you would have a graph of feature-dependencies.
            <sup><a href="#note1">1</a></sup></p>
            
            <p>When we design a system, we look for patterns in these dependencies. If we're
            smart enough and have enough experience, we'll do our best to reduce those
            dependencies, and figure out how to minimize the coupling between them. When
            we get this wrong, we something that gets hard to maintain and change
            over time.</p>
            
            <p>A big part of using dependency-analysis in our design is figuring out exactly
            at what level our features are dependent upon each other. In our
            forgotten-password example, the question to ask is <em>how much of the
            user-authentication feature does recovering a forgotten password depend on?</em>.
            To answer this question, you have to start imagining different ways the
            authentication feature could be implemented. Which ones would break the
            forgotten-password feature? Which ones would make it irrelevant?</p>
            
            <p>From the perspective of system-design it makes no sense to tie the
            implementation of the forgotten-password feature to the form-submission
            process of authentication. Instead, these features <em>should</em> be coupled at
            wherever you store authentication information (likely a database). You should
            be able to modify the authentication process without directly breaking the
            forgotten-password feature.</p>
            
            <p>Returning to automated testing, the same idea applies. But I think a lot of
            people don't factor their tests this way. Instead, a lot of test-writers
            get so wrapped-up in code reuse that they often recycle tests of dependent
            features as a way of getting the SUT into a starting state. This is a mistake.
            Just like we wouldn't over-couple the implementations of dependent features,
            we should't over-couple our tests for them. If we do, we end up with a
            very brittle suite of tests.</p>
            
            <p>We also end up losing a lot of feedback from the testing process. If the
            authentication test is failing, does that necessarily mean we should invalidate
            all the other tests that use authentication information? Logically, the answer
            is <em>no</em>. But with test-reuse that's exactly what we get. So when one feature
            breaks and a bunch of tests fail, we have no idea just how broken the system
            really is.</p>
            
            <h1>A New Perspective</h1>
            
            <p><img class="right" src="/images/2010/09/meechu-test-fixtures.png"/></p>
            
            <p>Coming back to UIAutomation, I made the decision early on to build test
            fixtures that focused on the current persistent state of the system. Much of
            the logic of the application revolves around the state of Core Data database.
            I made a conscious decision to couple the features at the persistent-store
            layer so, by extension, I've done the same for my test-fixture code.</p>
            
            <p>In my <a href="http://alexvollmer.com/posts/2010/07/09/more-uiautomation/" title="Alex
            Vollmer &mdash; More UIAutomation">first
            attempt</a>, I built a rather complicated Rube
            Goldberg contraption that simply wasn't going to scale as I added more
            fixtures. The first problem I needed to solve was to cut down on the amount of
            build variation. My original solution involved a separate build-configuration
            for each test-fixture. The problem was that my tests had an implicit
            dependency on a particular test build, but I had no way to express that with
            UIAutomation. The best I could do was simply add comments to the top of my
            tests. Blech. Too easy to get that wrong and waste a bunch of time figuring
            out why things weren't working. This is <em>not</em> what I want out of my
            integration tests.</p>
            
            <p>So the first step was to get rid of the separate build configurations. I
            replaced them with a single "Integration Test" build configuration that
            defines a build-time variable that is used in my app delegate like so
            <sup><a href="#note2">2</a></sup>:</p>
            
            <div class="highlight"><pre><span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nl">applicationDidFinishLaunching:</span><span class="p">(</span><span class="n">UIApplication</span> <span class="o">*</span><span class="p">)</span><span class="n">application</span> <span class="p">{</span>    &#x000A;    <span class="cp">#ifdef MEECHU_RESET_FOR_TESTING</span>&#x000A;      <span class="n">self</span><span class="p">.</span><span class="n">testFixturesController</span> <span class="o">=</span> <span class="p">[[[</span><span class="n">TestFixtureViewController</span> <span class="n">alloc</span><span class="p">]</span> <span class="nl">initWithDelegate:</span><span class="n">self</span><span class="p">]</span> <span class="n">autorelease</span><span class="p">];</span>&#x000A;      <span class="p">[</span><span class="n">window</span> <span class="nl">addSubview:</span><span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">testFixturesController</span> <span class="n">view</span><span class="p">]];</span>&#x000A;    <span class="cp">#else</span>&#x000A;        <span class="p">[</span><span class="n">window</span> <span class="nl">addSubview:</span><span class="p">[</span><span class="n">navigationController</span> <span class="n">view</span><span class="p">]];</span>&#x000A;      <span class="p">[</span><span class="n">self</span> <span class="n">checkFirstTimeUser</span><span class="p">];</span>&#x000A;    <span class="cp">#endif  </span>&#x000A;      <span class="p">[</span><span class="n">window</span> <span class="n">makeKeyAndVisible</span><span class="p">];</span>&#x000A;    <span class="p">}</span>&#x000A;    </pre>
            </div>
            
            
            <p>If the <code>MEECHU_RESET_FOR_TESTING</code> compiler flag is set, we insert a special
            test-fixture controller into the UI. That view controller is simply a
            control-board for running any number of pre-defined test-fixture operations.
            Here's an example of one such operation that completely nukes my Core Data
            database:</p>
            
            <div class="highlight"><pre><span class="k">@implementation</span> <span class="nc">ClearAllDataOperation</span>&#x000A;    &#x000A;    <span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">main</span> <span class="p">{</span>&#x000A;      <span class="n">MeechuAppDelegate</span> <span class="o">*</span><span class="n">meechu</span> <span class="o">=</span> <span class="p">(</span><span class="n">MeechuAppDelegate</span> <span class="o">*</span><span class="p">)[[</span><span class="n">UIApplication</span> <span class="n">sharedApplication</span><span class="p">]</span> <span class="n">delegate</span><span class="p">];</span>&#x000A;                                                        &#x000A;      <span class="n">NSPersistentStoreCoordinator</span> <span class="o">*</span><span class="n">coordinator</span> <span class="o">=</span> <span class="p">[</span><span class="n">meechu</span> <span class="n">persistentStoreCoordinator</span><span class="p">];</span>&#x000A;      <span class="n">NSError</span> <span class="o">*</span><span class="n">error</span> <span class="o">=</span> <span class="nb">nil</span><span class="p">;</span>&#x000A;      <span class="k">for</span> <span class="p">(</span><span class="n">NSPersistentStore</span> <span class="o">*</span><span class="n">store</span> <span class="k">in</span> <span class="p">[</span><span class="n">coordinator</span> <span class="n">persistentStores</span><span class="p">])</span> <span class="p">{</span>&#x000A;        <span class="p">[</span><span class="n">coordinator</span> <span class="nl">removePersistentStore:</span><span class="n">store</span> <span class="nl">error:</span><span class="o">&amp;</span><span class="n">error</span><span class="p">];</span>&#x000A;        <span class="k">if</span> <span class="p">(</span><span class="n">error</span><span class="p">)</span> <span class="p">{</span>&#x000A;          <span class="n">NSLog</span><span class="p">(</span><span class="s">@&quot;Unable to remove persistent store %@: %@&quot;</span><span class="p">,</span> <span class="n">store</span><span class="p">,</span> <span class="n">error</span><span class="p">);</span>&#x000A;          <span class="k">return</span><span class="p">;</span>&#x000A;        <span class="p">}</span>&#x000A;        &#x000A;        <span class="p">[[</span><span class="n">NSFileManager</span> <span class="n">defaultManager</span><span class="p">]</span> <span class="nl">removeItemAtURL:</span><span class="p">[</span><span class="n">store</span> <span class="n">URL</span><span class="p">]</span> <span class="nl">error:</span><span class="o">&amp;</span><span class="n">error</span><span class="p">];</span>&#x000A;        <span class="k">if</span> <span class="p">(</span><span class="n">error</span><span class="p">)</span> <span class="p">{</span>&#x000A;          <span class="n">NSLog</span><span class="p">(</span><span class="s">@&quot;Unable to remove persistent store file %@: %@&quot;</span><span class="p">,</span> <span class="p">[</span><span class="n">store</span> <span class="n">URL</span><span class="p">],</span> <span class="n">error</span><span class="p">);</span>&#x000A;        <span class="p">}</span>&#x000A;        &#x000A;        <span class="k">if</span> <span class="p">(</span><span class="o">!</span> <span class="p">[</span><span class="n">coordinator</span> <span class="nl">addPersistentStoreWithType:</span><span class="p">[</span><span class="n">store</span> <span class="n">type</span><span class="p">]</span>&#x000A;                                  <span class="nl">configuration:</span><span class="nb">nil</span>&#x000A;                                            <span class="nl">URL:</span><span class="p">[</span><span class="n">store</span> <span class="n">URL</span><span class="p">]</span>&#x000A;                                        <span class="nl">options:</span><span class="p">[</span><span class="n">store</span> <span class="n">options</span><span class="p">]</span>&#x000A;                                                <span class="nl">error:</span><span class="o">&amp;</span><span class="n">error</span><span class="p">])</span> <span class="p">{</span>&#x000A;          <span class="n">NSLog</span><span class="p">(</span><span class="s">@&quot;Unable to create new persistent store at %@: %@&quot;</span><span class="p">,</span> <span class="p">[</span><span class="n">store</span> <span class="n">URL</span><span class="p">],</span> <span class="n">error</span><span class="p">);</span>&#x000A;          <span class="k">return</span><span class="p">;</span>&#x000A;        <span class="p">}</span>&#x000A;      <span class="p">}</span>&#x000A;    <span class="p">}</span>&#x000A;    &#x000A;    <span class="k">@end</span>&#x000A;    </pre>
            </div>
            
            
            <p>I have a couple of other test-fixture operations like this. All of them
            fiddle with the underlying Core Data database.</p>
            
            <p>The next step was to create some kind of first-class representation of this
            "control-board" in my JavaScript layer. So I wrote a small wrapper for the
            test-fixture view controller:</p>
            
            <div class="highlight"><pre><span class="nx">TestFixtures</span> <span class="o">=</span> <span class="p">{</span>&#x000A;      <span class="nx">ensureFixturesMenu</span> <span class="o">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>&#x000A;        <span class="k">this</span><span class="p">.</span><span class="nx">target</span> <span class="o">=</span> <span class="nx">UIATarget</span><span class="p">.</span><span class="nx">localTarget</span><span class="p">();</span>&#x000A;        <span class="k">this</span><span class="p">.</span><span class="nx">application</span> <span class="o">=</span> <span class="nx">target</span><span class="p">.</span><span class="nx">frontMostApp</span><span class="p">();</span>&#x000A;        <span class="k">this</span><span class="p">.</span><span class="nx">mainWindow</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">application</span><span class="p">.</span><span class="nx">mainWindow</span><span class="p">();</span>&#x000A;        <span class="nx">assertEquals</span><span class="p">(</span><span class="s2">&quot;Test Fixtures&quot;</span><span class="p">,</span> <span class="nx">application</span><span class="p">.</span><span class="nx">navigationTitle</span><span class="p">());</span>&#x000A;      <span class="p">},</span>&#x000A;      &#x000A;      <span class="nx">addTwitterAccount</span> <span class="o">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>&#x000A;        <span class="k">this</span><span class="p">.</span><span class="nx">ensureFixturesMenu</span><span class="p">();</span>&#x000A;        <span class="nx">cell</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">mainWindow</span><span class="p">.</span><span class="nx">tableViews</span><span class="p">()[</span><span class="mi">0</span><span class="p">].</span><span class="nx">cells</span><span class="p">().</span><span class="nx">firstWithName</span><span class="p">(</span><span class="s2">&quot;addtwitteraccountoperation&quot;</span><span class="p">);</span>&#x000A;        <span class="nx">cell</span><span class="p">.</span><span class="nx">tap</span><span class="p">();</span>&#x000A;      <span class="p">},</span>&#x000A;      &#x000A;      <span class="nx">addEncounters</span> <span class="o">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>&#x000A;        <span class="k">this</span><span class="p">.</span><span class="nx">ensureFixturesMenu</span><span class="p">();</span>&#x000A;        <span class="nx">cell</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">mainWindow</span><span class="p">.</span><span class="nx">tableViews</span><span class="p">()[</span><span class="mi">0</span><span class="p">].</span><span class="nx">cells</span><span class="p">().</span><span class="nx">firstWithName</span><span class="p">(</span><span class="s2">&quot;addencounterhistoryoperation&quot;</span><span class="p">);</span>&#x000A;        <span class="nx">cell</span><span class="p">.</span><span class="nx">tap</span><span class="p">();</span>&#x000A;      <span class="p">},</span>&#x000A;      &#x000A;      <span class="nx">clearAllData</span> <span class="o">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>&#x000A;        <span class="k">this</span><span class="p">.</span><span class="nx">ensureFixturesMenu</span><span class="p">();</span>&#x000A;        <span class="nx">cell</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">mainWindow</span><span class="p">.</span><span class="nx">tableViews</span><span class="p">()[</span><span class="mi">0</span><span class="p">].</span><span class="nx">cells</span><span class="p">().</span><span class="nx">firstWithName</span><span class="p">(</span><span class="s2">&quot;clearalldataoperation&quot;</span><span class="p">);</span>&#x000A;        <span class="nx">cell</span><span class="p">.</span><span class="nx">tap</span><span class="p">();</span>&#x000A;      <span class="p">},</span>&#x000A;      &#x000A;      <span class="nx">close</span> <span class="o">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>&#x000A;        <span class="k">this</span><span class="p">.</span><span class="nx">mainWindow</span><span class="p">.</span><span class="nx">navigationBar</span><span class="p">().</span><span class="nx">rightButton</span><span class="p">().</span><span class="nx">tap</span><span class="p">();</span>&#x000A;      <span class="p">}</span>&#x000A;    <span class="p">};</span>&#x000A;    </pre>
            </div>
            
            
            <p>With these in place, the final task was explicitly defining the test-fixtures
            for each automation test. Now the pre-requisites for the test are specified
            right in the test, which greatly clarifies the pre-requisites for the test:</p>
            
            <div class="highlight"><pre><span class="err">#</span><span class="kr">import</span> <span class="s2">&quot;tuneup/tuneup.js&quot;</span>&#x000A;    <span class="err">#</span><span class="kr">import</span> <span class="s2">&quot;fixtures.js&quot;</span>&#x000A;    &#x000A;    <span class="nx">test</span><span class="p">(</span><span class="s2">&quot;Additional Account&quot;</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">target</span><span class="p">,</span> <span class="nx">app</span><span class="p">)</span> <span class="p">{</span>&#x000A;      <span class="nx">TestFixtures</span><span class="p">.</span><span class="nx">clearAllData</span><span class="p">();</span>&#x000A;      <span class="nx">TestFixtures</span><span class="p">.</span><span class="nx">addTwitterAccount</span><span class="p">();</span>&#x000A;      <span class="nx">TestFixtures</span><span class="p">.</span><span class="nx">close</span><span class="p">();</span>&#x000A;    &#x000A;      <span class="c1">// test continues here</span>&#x000A;    <span class="p">});</span>&#x000A;    </pre>
            </div>
            
            
            <p>Now when the tests run, the test-fixture view controller appears, the test
            pokes a button for each fixture operation it requires, dismisses the
            test-fixture setup and continues on its merry way.</p>
            
            <h1>Conclusion</h1>
            
            <p>This is definitely a step forward, but I think there is some more refinement
            to be had. First, there's still too much duplication for my tastes. For each
            new test-fixture operation I have to update the view controller as well as the
            JavaScript wrapper file. I don't think it would take too much effort to
            automatically generate the view controller and wrapper JavaScript code simply
            by snooping the directory that contains the test-fixture operation classes in
            the same way <a href="http://rentzsch.github.com/mogenerator/" title="mogenerator + Xmo’d">Xmo'd</a>
            does for Core Data models and generated classes.</p>
            
            <h1 id="footnotes">Footnotes</h1>
            
            
            <ol>
              <li>
                <a name="note1"></a>
                It's amazing how much a simple drawing exercise can reveal about your
                project in ways that simply reading the code doesn't. I've found it 
                really helpful to sketch stuff out with paper and pencil or use
                <a href="http://www.omnigroup.com/applications/omnigraffle/" title="OmniGraffle for Mac - Products - The Omni Group">OmniGraffle</a> for quick box-sketches.
              </li>
              <li>
                <a name="note2"></a>
                You could argue that having separate builds isn't strictly necessary.
                All I'm really trying to do is avoid having test-related code in my
                final application bundle. That could simply be expressed by the
                differences between the stock "Debug" and "Release" build configurations.
              </li>
            </ol>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Design Pressures of Mock Objects</title>
      <link>
        http://alexvollmer.com/posts/2010/07/25/design-pressures-of-mock-objects/
      </link>
      <comments>
        http://alexvollmer.com/posts/2010/07/25/design-pressures-of-mock-objects/#comments
      </comments>
      <pubDate>
        Sun, 25 Jul 2010 16:41:25 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            TDD
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2010/07/25/design-pressures-of-mock-objects/
      </guid>
      <description>
        <![CDATA[
            <a href="http://seattlexcoders.org/2010/07/13/july-22-eastside-meeting---alex-vollmer-on-ocmock.html" title="Seattle Xcoders - July 22 Eastside Meeting - Alex Vollmer on OCMock">Last Thursday</a>
            I gave a presentation to the Eastside Xcoders on the topic
            of <a href="http://www.mulle-kybernetik.com/software/OCMock/" title="Mulle kybernetiK -- OCMock">OCMock</a>
            and, more generally, the role of mock objects in test-driven development. One
            of the great things about the process of teaching (or presenting) is getting
            a chance to really solidify the topic which you're sharing with others.
            The process of putting <a href="https://docs.google.com/fileview?id=0BzuECX7-PruNYjEwOWU4ZTUtODhhOC00ZTlmLWE1NzQtNGQzMjJkM2FiMzA1&amp;amp;hl=en">this presentation</a>
            together really clarified some things that I intuitively understood, but
            couldn't necessarily articulate very well about testing and design.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p><a href="http://seattlexcoders.org/2010/07/13/july-22-eastside-meeting---alex-vollmer-on-ocmock.html" title="Seattle Xcoders - July 22 Eastside Meeting - Alex Vollmer on OCMock">Last Thursday</a>
            I gave a presentation to the Eastside Xcoders on the topic
            of <a href="http://www.mulle-kybernetik.com/software/OCMock/" title="Mulle kybernetiK -- OCMock">OCMock</a>
            and, more generally, the role of mock objects in test-driven development. One
            of the great things about the process of teaching (or presenting) is getting
            a chance to really solidify the topic which you're sharing with others.
            The process of putting <a href="https://docs.google.com/fileview?id=0BzuECX7-PruNYjEwOWU4ZTUtODhhOC00ZTlmLWE1NzQtNGQzMjJkM2FiMzA1&amp;amp;hl=en">this presentation</a>
            together really clarified some things that I intuitively understood, but
            couldn't necessarily articulate very well about testing and design.</p>
            
            <p>One of the biggest "a-ha!" moments was triggered by creating my favorite slide
            in the whole presentation, which came near the end after we'd covered all the
            ins and outs of OCMock.</p>
            
            <p><img src="/images/2010/07/abstract.png" title="Where I'm willing to mock, I'm willing to abstract"/></p>
            
            <p>This phrase came to me as I thought about the "design pressures" that TDD
            often brings to your code. To me, these pressures are one of most important
            (yet subtle) forces that TDD brings to bear. TDD practitioners often refer
            to TDD as a feedback mechanism. To me, this notion of where to draw the lines
            of responsibility became really clear when I thought about the first place
            I often do it&mdash;in my tests.</p>
            
            <p>Let me explain what I mean by that phrase. First, I should say that I tend to
            bring in mock objects in a limited set of circumstances. It's not the first
            tool I reach for in my unit-tests. I don't like tests that simply re-assert
            the implementation as I think it gives you a false sense of security. The
            times when I <em>will</em> use mocks are:</p>
            
            <ul>
            <li>When I'm replacing third-party collaborators that I don't control</li>
            <li>When creating a stub would just obscure the test</li>
            <li>Writing single use-case test stubs. I'll wait until I get three of them
            before I factor out a common stub.</li>
            <li>When I need to test for side-effects (e.g. logging or caching)</li>
            </ul>
            
            
            <p>The first reason is the primary motivation I'd have to consider a layer of
            abstraction. First, because (obviously) the third-party is under someone
            else's control, but also because I usually want to simplify a more
            fully-featured and general API to something more specific to my domain. By
            encapsulating the details of something like GameKit into a little façade
            class, I've built a nice little box around it that doesn't leak into other
            classes.</p>
            
            <p>So maybe it's not an earth-shattering realization. Maybe it's just that I
            finally have a clearer articulation of the idea in my head. Like I said,
            it's amazing what you learn when you teach it to someone else.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>More UIAutomation</title>
      <link>
        http://alexvollmer.com/posts/2010/07/09/more-uiautomation/
      </link>
      <comments>
        http://alexvollmer.com/posts/2010/07/09/more-uiautomation/#comments
      </comments>
      <pubDate>
        Fri, 09 Jul 2010 07:14:06 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            cocoa
        ]]>
      </category>
      <category>
        <![CDATA[
            TDD
        ]]>
      </category>
      <category>
        <![CDATA[
            uiautomation
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2010/07/09/more-uiautomation/
      </guid>
      <description>
        <![CDATA[
            After having spent another week with Apple's UI Automation framework, it's
            clear that to really wring the most benefit out of it, you need to roll up
            your sleeves and extend what's already there. One area that UI Automation
            gives you no help is establishing an initial state for your application to run
            your tests against. So this week I hacked something to do just that.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>After having spent another week with Apple's UI Automation framework, it's
            clear that to really wring the most benefit out of it, you need to roll up
            your sleeves and extend what's already there. One area that UI Automation
            gives you no help is establishing an initial state for your application to run
            your tests against. So this week I hacked something to do just that.</p>
            
            <h1>Why We Do What We Do</h1>
            
            <p>Before moving forward, it's probably worth reviewing what my goal is with
            UI Automation (or any integration testing setup for that matter). To me,
            testing, whether it's  unit-testing or integration-testing, is all a matter
            of economics. There is a balance to be struck between the up-front investment
            in the test harness and the long-term risk mitigation that it provides. One
            of the things that has frustrated me about test automation in Cocoa is how
            expensive it can get to build and maintain this stuff compared to other
            environments I've worked in. At a certain point, if the cost is too high,
            I'm perfectly willing to abandon the effort.</p>
            
            <p><img src="/images/2010/07/thx1138.png" class="right"/></p>
            
            <p>I like to think of it like the last scene of the film <em>THX-1138</em>. There's a
            wonderful shot in the command and control center that shows the running cost
            of pursuing the escaped fugitives. At a certain point the cost exceeds the
            budget of the pursuit and the hounds are called off. That's somewhat like my
            attitude toward test automation. It's not an absolute, totalitarian,
            philosophical must-have. I only insist on it when it makes sense.</p>
            
            <h1>Setting the Stage</h1>
            
            <p>The UI Automation toolkit provides <em>a portion</em> of what I think we need for an
            effective integration-test suite, but not all of it. A good suite would have
            the following properties:</p>
            
            <ol>
            <li>runs on the device or simulator with equal ease</li>
            <li>has a rich API to dig deep into the details of the running system</li>
            <li>is extensible so that we can build abstractions over the aforementioned
            rich API</li>
            <li>is completely automatable from the command-line for continuous integration
            builds</li>
            <li>provides a way to set your application in particular states as a
            pre-condition for tests</li>
            </ol>
            
            
            <p>UI Automation covers the first three items, but completely misses on the
            fourth item. It doesn't help with the last item, but doesn't necessarily
            preclude a solution either.</p>
            
            <p>What I did was create a special block of code in my application delegate that
            looks for some extra configuration in the application plist file. Since I
            don't plan on ever needing any of this stuff in a release, it's all guarded
            by an <code>#ifdef</code> statement.</p>
            
            <div class="highlight"><pre><span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nl">applicationDidFinishLaunching:</span><span class="p">(</span><span class="n">UIApplication</span> <span class="o">*</span><span class="p">)</span><span class="n">application</span> <span class="p">{</span>    &#x000A;    <span class="cp">#ifdef MEECHU_RESET_FOR_TESTING</span>&#x000A;      <span class="n">NSArray</span> <span class="o">*</span><span class="n">operations</span> <span class="o">=</span> <span class="p">[[</span><span class="n">NSBundle</span> <span class="n">mainBundle</span><span class="p">]</span> <span class="nl">objectForInfoDictionaryKey:</span><span class="s">@&quot;MUTestHarnessOperations&quot;</span><span class="p">];</span>&#x000A;      <span class="k">if</span> <span class="p">(</span><span class="n">operations</span><span class="p">)</span> <span class="p">{</span>&#x000A;        <span class="n">NSOperationQueue</span> <span class="o">*</span><span class="n">queue</span> <span class="o">=</span> <span class="p">[[</span><span class="n">NSOperationQueue</span> <span class="n">alloc</span><span class="p">]</span> <span class="n">init</span><span class="p">];</span>&#x000A;        <span class="p">[</span><span class="n">queue</span> <span class="nl">setMaxConcurrentOperationCount:</span><span class="mi">1</span><span class="p">];</span>&#x000A;        <span class="k">for</span> <span class="p">(</span><span class="n">NSString</span> <span class="o">*</span><span class="n">opName</span> <span class="k">in</span> <span class="n">operations</span><span class="p">)</span> <span class="p">{</span>&#x000A;          <span class="n">Class</span> <span class="n">clazz</span> <span class="o">=</span> <span class="n">NSClassFromString</span><span class="p">(</span><span class="n">opName</span><span class="p">);</span>&#x000A;          <span class="n">NSOperation</span> <span class="o">*</span><span class="n">op</span> <span class="o">=</span> <span class="p">(</span><span class="n">NSOperation</span> <span class="o">*</span><span class="p">)[[[</span><span class="n">clazz</span> <span class="n">alloc</span><span class="p">]</span> <span class="n">init</span><span class="p">]</span> <span class="n">autorelease</span><span class="p">];</span>&#x000A;          <span class="p">[</span><span class="n">queue</span> <span class="nl">addOperation:</span><span class="n">op</span><span class="p">];</span>&#x000A;          <span class="n">NSLog</span><span class="p">(</span><span class="s">@&quot;Enqueued operation: %@&quot;</span><span class="p">,</span> <span class="n">NSStringFromClass</span><span class="p">(</span><span class="n">clazz</span><span class="p">));</span>&#x000A;        <span class="p">}</span>&#x000A;        &#x000A;        <span class="p">[</span><span class="n">queue</span> <span class="n">waitUntilAllOperationsAreFinished</span><span class="p">];</span>&#x000A;        <span class="p">[</span><span class="n">queue</span> <span class="n">release</span><span class="p">];</span>&#x000A;      <span class="p">}</span>&#x000A;    <span class="cp">#endif</span>&#x000A;    &#x000A;      <span class="p">[</span><span class="n">window</span> <span class="nl">addSubview:</span><span class="p">[</span><span class="n">navigationController</span> <span class="n">view</span><span class="p">]];</span>&#x000A;      <span class="p">[</span><span class="n">window</span> <span class="n">makeKeyAndVisible</span><span class="p">];</span>&#x000A;    <span class="p">}</span>&#x000A;    </pre>
            </div>
            
            
            <p>In the special block, if I find the string array associated with the key
            <code>MUTestHarnessOperations</code> in the application plist, I treat each string as a
            class name which is instantiated and added to an operation queue. Now I don't
            have any concurrency requirements which led me to use an <code>NSOperationQueue</code>.
            Instead, it seemed like a nice way to package up discrete bits of work.</p>
            
            <p>One of these setup classes creates an account (an instance of <code>MUAccount</code>)
            that some of my tests rely on. It looks something like this:</p>
            
            <div class="highlight"><pre><span class="k">@interface</span> <span class="nc">AddTwitterAccountOperation</span> : <span class="nc">NSOperation</span> <span class="p">{</span>&#x000A;    <span class="p">}</span>&#x000A;    <span class="k">@end</span>&#x000A;    </pre>
            </div>
            
            
            
            
            <div class="highlight"><pre><span class="cp">#import &lt;CoreData/CoreData.h&gt;</span>&#x000A;    &#x000A;    <span class="cp">#import &quot;AddTwitterAccountOperation.h&quot;</span>&#x000A;    <span class="cp">#import &quot;MUAccount.h&quot;</span>&#x000A;    <span class="cp">#import &quot;AccountType.h&quot;</span>&#x000A;    <span class="cp">#import &quot;MeechuAppDelegate.h&quot;</span>&#x000A;    &#x000A;    &#x000A;    <span class="k">@implementation</span> <span class="nc">AddTwitterAccountOperation</span>&#x000A;    &#x000A;    <span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">main</span> <span class="p">{</span>&#x000A;      <span class="n">MeechuAppDelegate</span> <span class="o">*</span><span class="n">meechu</span> <span class="o">=</span> <span class="p">(</span><span class="n">MeechuAppDelegate</span> <span class="o">*</span><span class="p">)[[</span><span class="n">UIApplication</span> <span class="n">sharedApplication</span><span class="p">]</span> <span class="n">delegate</span><span class="p">];</span>&#x000A;      &#x000A;      <span class="n">NSPersistentStoreCoordinator</span> <span class="o">*</span><span class="n">coordinator</span> <span class="o">=</span> <span class="p">[</span><span class="n">meechu</span> <span class="n">persistentStoreCoordinator</span><span class="p">];</span>&#x000A;      <span class="n">NSManagedObjectContext</span> <span class="o">*</span><span class="n">moc</span> <span class="o">=</span> <span class="p">[[</span><span class="n">NSManagedObjectContext</span> <span class="n">alloc</span><span class="p">]</span> <span class="n">init</span><span class="p">];</span>&#x000A;      <span class="p">[</span><span class="n">moc</span> <span class="nl">setPersistentStoreCoordinator:</span><span class="n">coordinator</span><span class="p">];</span>&#x000A;      &#x000A;      <span class="n">MUAccount</span> <span class="o">*</span><span class="n">newAccount</span> <span class="o">=</span> <span class="p">[</span><span class="n">MUAccount</span> <span class="nl">insertInManagedObjectContext:</span><span class="n">moc</span><span class="p">];</span>&#x000A;      <span class="n">newAccount</span><span class="p">.</span><span class="n">identifier</span> <span class="o">=</span> <span class="s">@&quot;alexvollmer&quot;</span><span class="p">;</span>&#x000A;      <span class="n">newAccount</span><span class="p">.</span><span class="n">type</span> <span class="o">=</span> <span class="s">@&quot;twitter&quot;</span><span class="p">;</span>&#x000A;      <span class="n">newAccount</span><span class="p">.</span><span class="n">password</span> <span class="o">=</span> <span class="s">@&quot;supersekret&quot;</span><span class="p">;</span>&#x000A;      <span class="n">newAccount</span><span class="p">.</span><span class="n">enabled</span> <span class="o">=</span> <span class="p">[</span><span class="n">NSNumber</span> <span class="nl">numberWithBool:</span><span class="n">YES</span><span class="p">];</span>&#x000A;      &#x000A;      <span class="n">NSError</span> <span class="o">*</span><span class="n">error</span> <span class="o">=</span> <span class="nb">nil</span><span class="p">;</span>&#x000A;      <span class="k">if</span> <span class="p">([</span><span class="n">moc</span> <span class="nl">save:</span><span class="o">&amp;</span><span class="n">error</span><span class="p">])</span> <span class="p">{</span>&#x000A;        <span class="n">NSLog</span><span class="p">(</span><span class="s">@&quot;Added twitter account for &#39;alexvollmer&#39;&quot;</span><span class="p">);</span>&#x000A;      <span class="p">}</span>&#x000A;      <span class="k">else</span> <span class="p">{</span>&#x000A;        <span class="n">NSLog</span><span class="p">(</span><span class="s">@&quot;ERROR: Unable to create test twitter account: %@&quot;</span><span class="p">,</span> <span class="n">error</span><span class="p">);</span>&#x000A;      <span class="p">}</span>&#x000A;       &#x000A;      <span class="p">[</span><span class="n">moc</span> <span class="n">release</span><span class="p">];</span>&#x000A;    <span class="p">}</span>&#x000A;    &#x000A;    <span class="k">@end</span>&#x000A;    </pre>
            </div>
            
            
            <p>Once I've built up a library of these setup actions, I create a different
            build configuration for each combination of setup actions. Each of those
            configurations uses a specific application plist file with the
            <code>MUTestHarnessOperations</code> key set to an array with the appropriate setup
            class names.</p>
            
            <p><img src="/images/2010/07/plist.png"/></p>
            
            
            <p>To create a new build configuration, I just duplicate the existing debug and
            modify two settings: the name of the application plist file, and defining the
            <code>MEECHU_RESET_FOR_TESTING</code> symbol for the preprocessor.</p>
            
            <p><img src="/images/2010/07/plist-configuration.png"></img></p>
            
            
            
            
            <p><img src="/images/2010/07/cflags-configuration.png"></img></p>
            
            
            <p>If I build and run a configuration of the application with a particular
            collection of setup actions, that application will <em>always</em> run with those
            setup actions. This is exactly what you need for repeatable testing.</p>
            
            <p>Now it's simply a matter of choosing the correct application build and test
            script in Instruments:</p>
            
            <p><img src="/images/2010/07/select-app.png"></img></p>
            
            
            <p>You can save your Instruments files off to capture the combination of the test
            script and executable. One thing to keep in mind is that Instruments won't
            build your code. If you have a failing test and you're making fixes, you
            need to re-build the executable in Xcode then re-run the test in Instruments.
            You don't need to reconfigure Instruments, it will just use whatever version
            of the executable you have at the specified path.</p>
            
            <h1>Conclusion</h1>
            
            <p>OK, this works, but I think it's pretty hacky. There are still way too many
            steps to go through and interface points that need to be configured correctly.
            I'm sure there's a more elegant, less error-prone way to do this. If you think
            of it, please tell me. I'm only 50% satisfied with this.</p>
            
            <p>There's a lot I really like about UI Automation, but it is missing a few key
            ingredients that make me think long and hard about using it long-term. This
            test setup approach is somewhat workable. Now we just need to address the
            remaining issues of scriptability and complete automation.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Working with UIAutomation</title>
      <link>
        http://alexvollmer.com/posts/2010/07/03/working-with-uiautomation/
      </link>
      <comments>
        http://alexvollmer.com/posts/2010/07/03/working-with-uiautomation/#comments
      </comments>
      <pubDate>
        Sat, 03 Jul 2010 08:55:23 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            cocoa
        ]]>
      </category>
      <category>
        <![CDATA[
            TDD
        ]]>
      </category>
      <category>
        <![CDATA[
            uiautomation
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2010/07/03/working-with-uiautomation/
      </guid>
      <description>
        <![CDATA[
            In this post we're going to look at the UI Automation library/tool that Apple
            added to iOS SDK 4.0. This is a huge step forward for test automation on the
            iOS platform. While it's not without some compromises, it's worth looking at
            to see if you can reduce the time you spend on manual testing.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>In this post we're going to look at the UI Automation library/tool that Apple
            added to iOS SDK 4.0. This is a huge step forward for test automation on the
            iOS platform. While it's not without some compromises, it's worth looking at
            to see if you can reduce the time you spend on manual testing.</p>
            
            <p>UI Automation is both a probe for Instruments as well as a JavaScript library
            provided by Apple to exercise and validate a running application. In this case,
            "running application" isn't restricted to the simulator&mdash;you can also
            automate the application on a real device. To my knowledge, this is the first
            time I've heard of anyone being able to do this.</p>
            
            <p>This is huge. Having the ability to automate workflows in your application
            yields two benefits: you cut down on manual testing which saves you time,
            <em>and</em> you can rely less on your memory to execute all your tests. Instead, you
            just push a button (okay, two or three buttons) and run your full regression
            suite. Have I piqued your interest yet?</p>
            
            <h1>Preparing Your Application</h1>
            
            <p>First, you need to do a little groundwork to prepare your application to work
            with the automation tool. The UI Automation library relies on accessibility
            information in your UI, so adding a little accessibility info to your UI will
            make testing it easier<sup><a href="#note1">1</a></sup>. More specifically,
            the UI Automation framework looks for the <code>accessibilityLabel</code> property of
            your <code>UIViews</code>. If you've built any web applications, this is somewhat akin to
            sprinkling <code>id</code> attributes in your HTML markup so that you can find particular
            DOM nodes easily with JavaScript.</p>
            
            <p><img src="/images/2010/07/ib-accessibility.jpg" class="right, break"/></p>
            
            <p>For views constructed in Interface Builder, you can set this property in the
            Inspector in the "View Identity" tab (the fourth one). Note that not every
            kind of view provides accessibility configuration in Interface Builder
            <sup><a href="#note2">2</a></sup>. You need to enable Accessibility and you
            need to provide a Label value. You'll use these later in your tests to
            identify particular views.</p>
            
            <p>If you aren't using IB or your view doesn't expose accessibility information
            graphically, you can still set it manually in your code. You can set the
            <code>isAccessibilityElement</code> and <code>accessibilityLabel</code> properties to get the same
            effect:</p>
            
            <div class="highlight"><pre><span class="o">-</span> <span class="p">(</span><span class="n">UITableViewCell</span> <span class="o">*</span><span class="p">)</span><span class="nl">tableView:</span><span class="p">(</span><span class="n">UITableView</span> <span class="o">*</span><span class="p">)</span><span class="n">tableView</span> <span class="nl">cellForRowAtIndexPath:</span><span class="p">(</span><span class="n">NSIndexPath</span> <span class="o">*</span><span class="p">)</span><span class="n">indexPath</span> <span class="p">{</span>&#x000A;      <span class="k">static</span> <span class="n">NSString</span> <span class="o">*</span><span class="n">CellIdentifier</span> <span class="o">=</span> <span class="s">@&quot;Cell&quot;</span><span class="p">;</span>&#x000A;      &#x000A;      <span class="n">UITableViewCell</span> <span class="o">*</span><span class="n">cell</span> <span class="o">=</span> <span class="p">[</span><span class="n">tableView</span> <span class="nl">dequeueReusableCellWithIdentifier:</span><span class="n">CellIdentifier</span><span class="p">];</span>&#x000A;      <span class="k">if</span> <span class="p">(</span><span class="n">cell</span> <span class="o">==</span> <span class="nb">nil</span><span class="p">)</span> <span class="p">{</span>&#x000A;        <span class="n">cell</span> <span class="o">=</span> <span class="p">[[[</span><span class="n">UITableViewCell</span> <span class="n">alloc</span><span class="p">]</span> <span class="nl">initWithStyle:</span><span class="n">UITableViewCellStyleDefault</span> <span class="nl">reuseIdentifier:</span><span class="n">CellIdentifier</span><span class="p">]</span> <span class="n">autorelease</span><span class="p">];</span>&#x000A;      <span class="p">}</span>&#x000A;      &#x000A;      <span class="c1">// cell configuration elided</span>&#x000A;      &#x000A;      <span class="n">cell</span><span class="p">.</span><span class="n">isAccessibilityElement</span> <span class="o">=</span> <span class="n">YES</span><span class="p">;</span>&#x000A;      <span class="n">cell</span><span class="p">.</span><span class="n">accessibilityLabel</span> <span class="o">=</span> <span class="s">@&quot;user name&quot;</span>&#x000A;      <span class="k">return</span> <span class="n">cell</span>&#x000A;    <span class="p">}</span>&#x000A;    </pre>
            </div>
            
            
            <h1>Writing Your Tests</h1>
            
            <p>The next step is to write your first test in JavaScript<sup><a href="#note3">3</a></sup>
            in your editor of choice. This step is a bit like being dropped off in a field
            somewhere with no map, tools or supplies and being told you need to build a
            house. There's no built-in structure for your tests. When you execute a test
            script, Instruments will run it from beginning to end in linear fashion.</p>
            
            <p>To get started you need to get a reference to the running application from
            which you can access all the other parts of the app. Put these two lines of
            boilerplate at the top of your test:</p>
            
            <div class="highlight"><pre><span class="nx">target</span> <span class="o">=</span> <span class="nx">UIATarget</span><span class="p">.</span><span class="nx">localTarget</span><span class="p">();</span>&#x000A;    <span class="nx">application</span> <span class="o">=</span> <span class="nx">target</span><span class="p">.</span><span class="nx">frontMostApp</span><span class="p">();</span>&#x000A;    </pre>
            </div>
            
            
            <p>The <code>UIATarget</code> is your primary portal into the application running on the
            device or simulator. It acts as a sort of proxy for the user of the
            application and is the object you interact with when you want to perform
            operations on the device such as fiddling with volume controls, shaking, or
            performing user gestures. The application object (a <code>UIAApplication</code>
            instance), gives you access to the top-level structure of your application for
            things like the navigation bar, tab bars, and the main window.</p>
            
            <p>The <code>UIAApplication</code> class has a <code>mainWindow()</code> method that returns a
            <code>UIAWindow</code> instance. This class is an extension of <code>UIAElement</code> which nearly
            all of the other UI-related classes derive from. This class provides access
            to things like the parent view, child views, links, pickers, sliders, table
            views and nearly anything else you can imagine.</p>
            
            <p>While Apple didn't provide much high-level information about UI Automation in
            the form of a Programming Guide, the UI Automation Reference Guide is worth
            bookmarking or keeping handy in PDF format. This is an essential reference
            that describes the JavaScript API in great detail. Check the Xcode
            documentation.</p>
            
            <p>So let's look at a real example <sup><a href="#note4">4</a></sup>.
            Let's say we have an application connects
            to a variety of popular web sites. When the user launches the app for the
            first time, we want to detect that they don't have any accounts configured
            and prompt them to create one. Our screen might look something like this:</p>
            
            <p><img src="/images/2010/07/account-types.png" alt="Choose an account type" /></p>
            
            <p>We want to verify that when we launch the application we see this screen. Once
            we've verified this, we want to create a twitter account, so we'll select the
            "twitter" table cell. Here's the test script so far:</p>
            
            <div class="highlight"><pre><span class="nx">test</span><span class="p">(</span><span class="s2">&quot;Initial screen&quot;</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">target</span><span class="p">,</span> <span class="nx">app</span><span class="p">)</span> <span class="p">{</span>&#x000A;      <span class="c1">// check navigation bar</span>&#x000A;      <span class="nx">navBar</span> <span class="o">=</span> <span class="nx">mainWindow</span><span class="p">.</span><span class="nx">navigationBar</span><span class="p">();</span>&#x000A;      <span class="nx">assertEquals</span><span class="p">(</span><span class="s2">&quot;Add Account&quot;</span><span class="p">,</span> <span class="nx">navBar</span><span class="p">.</span><span class="nx">name</span><span class="p">());</span>&#x000A;      <span class="nx">button</span> <span class="o">=</span> <span class="nx">navBar</span><span class="p">.</span><span class="nx">leftButton</span><span class="p">();</span>&#x000A;      <span class="nx">assertEquals</span><span class="p">(</span><span class="s2">&quot;Back&quot;</span><span class="p">,</span> <span class="nx">button</span><span class="p">.</span><span class="nx">name</span><span class="p">());</span>&#x000A;    &#x000A;      <span class="c1">// check tables</span>&#x000A;      <span class="nx">table</span> <span class="o">=</span> <span class="nx">mainWindow</span><span class="p">.</span><span class="nx">tableViews</span><span class="p">()[</span><span class="mi">0</span><span class="p">];</span>&#x000A;      <span class="nx">tableGroup</span> <span class="o">=</span> <span class="nx">table</span><span class="p">.</span><span class="nx">groups</span><span class="p">()[</span><span class="mi">0</span><span class="p">];</span>&#x000A;      <span class="nx">assertEquals</span><span class="p">(</span><span class="s2">&quot;What type of account?&quot;</span><span class="p">,</span> <span class="nx">tableGroup</span><span class="p">.</span><span class="nx">name</span><span class="p">());</span>&#x000A;    &#x000A;      <span class="nx">assertEquals</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="nx">table</span><span class="p">.</span><span class="nx">cells</span><span class="p">().</span><span class="nx">length</span><span class="p">);</span>&#x000A;      <span class="p">[</span><span class="s2">&quot;facebook&quot;</span><span class="p">,</span> <span class="s2">&quot;flickr&quot;</span><span class="p">,</span> <span class="s2">&quot;github&quot;</span><span class="p">,</span> <span class="s2">&quot;twitter&quot;</span><span class="p">].</span><span class="nx">each</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">i</span><span class="p">,</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>&#x000A;        <span class="nx">assertNotNull</span><span class="p">(</span><span class="nx">table</span><span class="p">.</span><span class="nx">cells</span><span class="p">().</span><span class="nx">firstWithName</span><span class="p">(</span><span class="nx">e</span><span class="p">));</span>&#x000A;      <span class="p">});</span>&#x000A;      &#x000A;      <span class="c1">// more to come...</span>&#x000A;    <span class="p">});</span>&#x000A;    </pre>
            </div>
            
            
            <p>The <code>test</code>, <code>assertNotNull</code> and <code>assertEquals</code> functions are ones I wrote to
            add some structure and high-level validation to the testing process. I found
            the way the UI Automation suite does test case declaration to be really
            verbose and crude so I boiled test declaration into something simpler. First,
            the <code>test()</code> method takes a string title and a function to execute as the
            body:</p>
            
            <div class="highlight"><pre><span class="kd">function</span> <span class="nx">test</span><span class="p">(</span><span class="nx">title</span><span class="p">,</span> <span class="nx">f</span><span class="p">,</span> <span class="nx">options</span><span class="p">)</span> <span class="p">{</span>&#x000A;      <span class="k">if</span> <span class="p">(</span><span class="nx">options</span> <span class="o">==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>&#x000A;        <span class="nx">options</span> <span class="o">=</span> <span class="p">{</span>&#x000A;          <span class="nx">logTree</span><span class="o">:</span> <span class="kc">true</span>&#x000A;        <span class="p">};</span>&#x000A;      <span class="p">}</span>&#x000A;      <span class="nx">target</span> <span class="o">=</span> <span class="nx">UIATarget</span><span class="p">.</span><span class="nx">localTarget</span><span class="p">();</span>&#x000A;      <span class="nx">application</span> <span class="o">=</span> <span class="nx">target</span><span class="p">.</span><span class="nx">frontMostApp</span><span class="p">();</span>&#x000A;      <span class="nx">UIALogger</span><span class="p">.</span><span class="nx">logStart</span><span class="p">(</span><span class="nx">title</span><span class="p">);</span>&#x000A;      <span class="k">try</span> <span class="p">{</span>&#x000A;        <span class="nx">f</span><span class="p">(</span><span class="nx">target</span><span class="p">,</span> <span class="nx">application</span><span class="p">);</span>&#x000A;        <span class="nx">UIALogger</span><span class="p">.</span><span class="nx">logPass</span><span class="p">(</span><span class="nx">title</span><span class="p">);</span>&#x000A;      <span class="p">}</span>&#x000A;      <span class="k">catch</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>&#x000A;        <span class="nx">UIALogger</span><span class="p">.</span><span class="nx">logError</span><span class="p">(</span><span class="nx">e</span><span class="p">);</span>&#x000A;        <span class="k">if</span> <span class="p">(</span><span class="nx">options</span><span class="p">.</span><span class="nx">logTree</span><span class="p">)</span> <span class="nx">target</span><span class="p">.</span><span class="nx">logElementTree</span><span class="p">();</span>&#x000A;        <span class="nx">UIALogger</span><span class="p">.</span><span class="nx">logFail</span><span class="p">(</span><span class="nx">title</span><span class="p">);</span>&#x000A;      <span class="p">}</span>&#x000A;    <span class="p">};</span>&#x000A;    </pre>
            </div>
            
            
            <p>This handles the boilerplate of getting the target and application references.
            It also provides some structure around <code>UIALogger.logStart()</code>,
            <code>UIALogger.logPass()</code> and <code>UIALogger.logFail()</code>. These three methods are how
            the UI Automation framework demarcates tests. However I found that using <code>if</code>
            checks and calling <code>logFail()</code> really muddied the tests, so I wrote some
            assertion methods that throw exceptions and the test structure automatically
            catches them and logs the test as a failure.</p>
            
            <p>Here are the assertion methods:</p>
            
            <div class="highlight"><pre><span class="kd">function</span> <span class="nx">assertEquals</span><span class="p">(</span><span class="nx">expected</span><span class="p">,</span> <span class="nx">received</span><span class="p">,</span> <span class="nx">message</span><span class="p">)</span> <span class="p">{</span>&#x000A;      <span class="k">if</span> <span class="p">(</span><span class="nx">received</span> <span class="o">!=</span> <span class="nx">expected</span><span class="p">)</span> <span class="p">{</span>&#x000A;        <span class="k">if</span> <span class="p">(</span><span class="o">!</span> <span class="nx">message</span><span class="p">)</span> <span class="nx">message</span> <span class="o">=</span> <span class="s2">&quot;Expected &quot;</span> <span class="o">+</span> <span class="nx">expected</span> <span class="o">+</span> <span class="s2">&quot; but received &quot;</span> <span class="o">+</span> <span class="nx">received</span><span class="p">;</span>&#x000A;        <span class="k">throw</span> <span class="nx">message</span><span class="p">;</span>&#x000A;      <span class="p">}</span>&#x000A;    <span class="p">}</span>&#x000A;    &#x000A;    <span class="kd">function</span> <span class="nx">assertTrue</span><span class="p">(</span><span class="nx">expression</span><span class="p">,</span> <span class="nx">message</span><span class="p">)</span> <span class="p">{</span>&#x000A;      <span class="k">if</span> <span class="p">(</span><span class="o">!</span> <span class="nx">expression</span><span class="p">)</span> <span class="p">{</span>&#x000A;        <span class="k">if</span> <span class="p">(</span><span class="o">!</span> <span class="nx">message</span><span class="p">)</span> <span class="nx">message</span> <span class="o">=</span> <span class="s2">&quot;Assertion failed&quot;</span><span class="p">;</span>&#x000A;        <span class="k">throw</span> <span class="nx">message</span><span class="p">;</span>&#x000A;      <span class="p">}</span>&#x000A;    <span class="p">}</span>&#x000A;    &#x000A;    <span class="kd">function</span> <span class="nx">assertFalse</span><span class="p">(</span><span class="nx">expression</span><span class="p">,</span> <span class="nx">message</span><span class="p">)</span> <span class="p">{</span>&#x000A;      <span class="nx">assertTrue</span><span class="p">(</span><span class="o">!</span> <span class="nx">expression</span><span class="p">,</span> <span class="nx">message</span><span class="p">);</span>&#x000A;    <span class="p">}</span>&#x000A;    &#x000A;    <span class="kd">function</span> <span class="nx">assertNotNull</span><span class="p">(</span><span class="nx">thingie</span><span class="p">,</span> <span class="nx">message</span><span class="p">)</span> <span class="p">{</span>&#x000A;      <span class="k">if</span> <span class="p">(</span><span class="nx">thingie</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">||</span> <span class="nx">thingie</span><span class="p">.</span><span class="nx">toString</span><span class="p">()</span> <span class="o">==</span> <span class="s2">&quot;[object UIAElementNil]&quot;</span><span class="p">)</span> <span class="p">{</span>&#x000A;        <span class="k">if</span> <span class="p">(</span><span class="nx">message</span> <span class="o">==</span> <span class="kc">null</span><span class="p">)</span> <span class="nx">message</span> <span class="o">=</span> <span class="s2">&quot;Expected not null object&quot;</span><span class="p">;</span>&#x000A;        <span class="k">throw</span> <span class="nx">message</span><span class="p">;</span>&#x000A;      <span class="p">}</span>&#x000A;    <span class="p">}</span>&#x000A;    </pre>
            </div>
            
            
            <p>Whew. Still with me? OK, back to our test. Next we want to fill out the text
            fields in the next view with our twitter credentials:</p>
            
            <p><img src="/images/2010/07/twitter-creds.png" alt="Enter twitter credentials" /></p>
            
            <p>We extend the test code above with the next set of stimuli and assertions:</p>
            
            <div class="highlight"><pre>  <span class="c1">// create a new account</span>&#x000A;      <span class="nx">table</span><span class="p">.</span><span class="nx">cells</span><span class="p">().</span><span class="nx">firstWithName</span><span class="p">(</span><span class="s2">&quot;twitter&quot;</span><span class="p">).</span><span class="nx">tap</span><span class="p">();</span>&#x000A;      <span class="nx">mainWindow</span> <span class="o">=</span> <span class="nx">app</span><span class="p">.</span><span class="nx">mainWindow</span><span class="p">();</span>&#x000A;      <span class="nx">table</span> <span class="o">=</span> <span class="nx">mainWindow</span><span class="p">.</span><span class="nx">tableViews</span><span class="p">()[</span><span class="mi">0</span><span class="p">];</span>&#x000A;    &#x000A;      <span class="nx">userName</span> <span class="o">=</span> <span class="nx">table</span><span class="p">.</span><span class="nx">cells</span><span class="p">().</span><span class="nx">firstWithName</span><span class="p">(</span><span class="s2">&quot;user name&quot;</span><span class="p">);</span>&#x000A;      <span class="nx">assertNotNull</span><span class="p">(</span><span class="nx">userName</span><span class="p">,</span> <span class="s2">&quot;No username field found&quot;</span><span class="p">);</span>&#x000A;      <span class="nx">userName</span><span class="p">.</span><span class="nx">textFields</span><span class="p">()[</span><span class="mi">0</span><span class="p">].</span><span class="nx">setValue</span><span class="p">(</span><span class="s2">&quot;mrfoobar&quot;</span><span class="p">);</span>&#x000A;    &#x000A;      <span class="nx">password</span> <span class="o">=</span> <span class="nx">table</span><span class="p">.</span><span class="nx">cells</span><span class="p">().</span><span class="nx">firstWithName</span><span class="p">(</span><span class="s2">&quot;password&quot;</span><span class="p">);</span>&#x000A;      <span class="nx">assertNotNull</span><span class="p">(</span><span class="nx">password</span><span class="p">,</span> <span class="s2">&quot;No password field found&quot;</span><span class="p">);</span>&#x000A;      <span class="nx">assertNotNull</span><span class="p">(</span><span class="nx">password</span><span class="p">.</span><span class="nx">secureTextFields</span><span class="p">()[</span><span class="mi">0</span><span class="p">],</span> <span class="s2">&quot;No text field found for password&quot;</span><span class="p">);</span>&#x000A;      <span class="nx">password</span><span class="p">.</span><span class="nx">secureTextFields</span><span class="p">()[</span><span class="mi">0</span><span class="p">].</span><span class="nx">setValue</span><span class="p">(</span><span class="s2">&quot;sekret&quot;</span><span class="p">);</span>&#x000A;      &#x000A;      <span class="nx">finish</span> <span class="o">=</span> <span class="nx">table</span><span class="p">.</span><span class="nx">cells</span><span class="p">().</span><span class="nx">firstWithName</span><span class="p">(</span><span class="s2">&quot;finish&quot;</span><span class="p">);</span>&#x000A;      <span class="nx">assertNotNull</span><span class="p">(</span><span class="nx">finish</span><span class="p">,</span> <span class="s2">&quot;No finish button found&quot;</span><span class="p">);</span>&#x000A;      <span class="nx">finish</span><span class="p">.</span><span class="nx">tap</span><span class="p">();</span>&#x000A;      &#x000A;      <span class="c1">// more to come...</span>&#x000A;    </pre>
            </div>
            
            
            <p>The first line above finds the "twitter" cell by calling the <code>firstWithName()</code>
            method on the collection of cells in the table view. This table view was
            generated programmatically, so that "twitter" label came from setting the
            cell's <code>isAccessibilityElement</code> property to <code>YES</code> and its <code>accessibilityLabel</code>
            property to <code>@"twitter"</code>. Next we touch that table cell via the <code>tap()</code> method
            to navigate forward in our application.</p>
            
            <p>The next two stanzas validate that we have user name and password fields, and
            also fills them out using the <code>setValue()</code> method. Finally we look for the cell
            that contains the "Finish" button and tap it to finish creating our account.</p>
            
            <p><img src="/images/2010/07/settings-screen.png" alt="Our landing point, the settings screen" /></p>
            
            <p>The last bit of testing to do here is to validate that we did indeed land
            on our settings screen. So we add a few more assertions to our test:</p>
            
            <div class="highlight"><pre>  <span class="c1">// validate settings screen</span>&#x000A;      <span class="nx">finish</span><span class="p">.</span><span class="nx">waitForInvalid</span><span class="p">();</span>&#x000A;      <span class="nx">mainWindow</span> <span class="o">=</span> <span class="nx">app</span><span class="p">.</span><span class="nx">mainWindow</span><span class="p">();</span>&#x000A;      <span class="nx">assertEquals</span><span class="p">(</span><span class="s2">&quot;Settings&quot;</span><span class="p">,</span> <span class="nx">mainWindow</span><span class="p">.</span><span class="nx">navigationBar</span><span class="p">().</span><span class="nx">name</span><span class="p">());</span>&#x000A;    </pre>
            </div>
            
            
            <p>Here I call the <code>waitForInvalid()</code> method on the finish button object. Without
            this, I found that the other code executed too quickly before the window
            transition completed. The <code>waitForInvalid()</code> will wait (up to a configurable
            timeout value) for that object to be invalidated. Our last assertion is that
            the title in the navigation bar is "Settings".</p>
            
            <h1>Running Your Tests</h1>
            
            <p>Now that we have a test, we need to run it in Instruments. When you launch
            Instruments, choose "Automation" from the iPhone templates.</p>
            
            <p><img src="/images/2010/07/choose-automation.png" alt="Automation template in Instruments" /></p>
            
            <p>In the details for the Automation instrument, choose your script via the
            "Choose Script&hellip;" drop-down. Next, you need to choose your target from
            the toolbar. Once you have these setup, you run the flow by (somewhat
            counter-intuitively) hitting the record button (or using ⌘+R). This will
            launch your application, wait a few seconds and then run your test. Note that
            even if your test completes, Instruments will keep running your application.
            To formally end the test toggle the red record button, or hit ⌘+R again.</p>
            
            <p>Tests are listed in the detail view, with the test name in the "Log Messages"
            column. If your test passes, the "Log Type" value will be "Pass" in comforting
            green. If your test fails, the value is "Fail" in scary red. You can expand
            the test to see the details of what happened. Because of the way I wrote the
            test structure above, any test failures automatically log the view hierarchy
            for inspection.</p>
            
            <p><img src="/images/2010/07/test-failure.png" alt="A test failure" /></p>
            
            <h1>Debugging Your Tests</h1>
            
            <p>When things go wrong with your tests you don't have a lot to look at. However
            there are a few things you can inspect to try to figure out what's broken.
            First, log the element tree (via <code>UIATarget.logElementTree()</code>) liberally and
            often. Although view hierarchy is a tree and it's presented in a tree control,
            it's "flattened-out" out for display purposes. However, the numeric prefix
            listed in each row indicates the level that node is at, so you can infer
            parent-child relationships:</p>
            
            <p><img src="/images/2010/07/tree-hierarchy.png" alt="The tree hierarchy" /></p>
            
            <p>In this case, the tree looks something like this:</p>
            
            <ul>
            <li><code>UIATarget</code>
            
            <ul>
            <li><code>UIAApplication</code>
            
            <ul>
            <li><code>UIAWindow</code>
            
            <ul>
            <li><code>UIATableView</code>
            
            <ul>
            <li><code>UIATableGroup</code>
            
            <ul>
            <li><code>UIAStaticText</code></li>
            </ul>
            </li>
            </ul>
            </li>
            </ul>
            </li>
            </ul>
            </li>
            </ul>
            </li>
            </ul>
            
            
            <p>You don't always have to traverse the entire tree, node-by-node, to find what
            you're looking for. Take a look at the various methods on the <code>UIAElement</code>
            class and experiment a bit.</p>
            
            <p>You can also log any message you choose using any of these log methods on
            the <code>UIALogger</code> class:</p>
            
            <ul>
            <li><code>logDebug()</code></li>
            <li><code>logMessage()</code></li>
            <li><code>logWarning()</code></li>
            <li><code>logError()</code></li>
            </ul>
            
            
            <p>All of these just log messages of varying severity, but don't affect whether
            or not the test is marked as passing or a failure.</p>
            
            <h1>Building on UI Automation</h1>
            
            <p>While Apple may not have provided much support for automation testing beyond
            a JavaScript API and a reference doc, they (wisely) left the whole mechanism
            open to easy extension. You can import other JavaScript into your tests with
            the <code>#import</code> statement. I'm not 100% sure how smart that statement is
            with regard to paths, but I found that if you put supporting scripts in the
            same directory as the test script you're executing, Instruments will
            automatically pick it up.</p>
            
            <p>So I took the extensions I mentioned earlier, plus a few more, and put them
            into a small, but growing, JavaScript library I call <a href="http://github.com/alexvollmer/tuneup_js" title="alexvollmer's tuneup_js at master - GitHub">tuneup_js</a>.</p>
            
            <p>You can simply copy all of the JavaScript files into the same directory as
            your automation tests. For my project, I created a separate directory called
            "Automation Tests" and dumped everything in there. This is pretty crude and
            I'd like to improve it, but I need to spend some more time figuring out the
            nuances of how Instruments handles paths in <code>#import</code> statements.</p>
            
            <p>Please check it out, file bugs, send patches and/or your feedback.</p>
            
            <h1>Stepping Back</h1>
            
            <p>These kind of tests are high-level, flow-oriented tests that exercise entire
            scenarios. As much as possible, these run against a "real" system though,
            at times, you may swap out external dependencies with mocked ones in order
            to control the test environment and execution.</p>
            
            <p>There have been other third-party open-source projects that have addressed the
            lack of good automation testing. In particular
            <a href="http://github.com/unboxed/icuke" title="unboxed's icuke at master - GitHub">iCuke</a>
            and <a href="http://code.google.com/p/uispec/" title="uispec - Project Hosting on
            Google Code">UISpec</a> both have a lot going for them. If you like
            <a href="http://cukes.info/" title="Cucumber - Making BDD fun">Cucumber</a>, iCuke is nice for
            writing integration tests. It's pretty impressive what they've been able to
            accomplish on their own. Personally I prefer the Cucumber approach to
            integration tests. However, because iCuke uses a small embedded web server and
            XML to exercise and validate the UI, I found that I often ran into time-out
            issues and got frustrated wrestling with XPath expressions to validate my UI.</p>
            
            <p>What iCuke has going for it, and what Instruments can't do, is the fact that
            you can automate the entire process. As far as I can tell, there isn't a good
            way to automate getting Instruments to run your automation tests. I hope this
            is something that either Apple addresses, or somebody figures out a clever
            hack for.</p>
            
            <p><img src="/images/2010/07/robholland-twitter.png" alt="iCuke responds" /></p>
            
            <p>I like the way the Apple library is connected to the running application. It
            definitely feels "first-class" compared to iCuke's implementation
            <sup><a href="#note5">5</a></sup>. Let's hope that we can bring the two
            approaches together to have one fantastic automation tool.</p>
            
            <p>One thing that <strike>none of these tools</strike> UI Automation can't really help you with
            is building reliable, repeatable test harnesses. One of the hardest parts of
            making good integration tests is setting up application state to test various
            modes of your application. This is one of the reasons why xUnit-style tests
            don't work well for automation. Too often people write fine-grained tests that
            have an implicit order, each test relying on the side-effects of the previous
            one. This makes your tests highly-coupled and extremely brittle.</p>
            
            <p>In my application I have a couple of initial application states to test. None
            of these tools provides much help for that. For now, I've chosen to sprinkle
            a few <code>#ifdefs</code> around in my app delegate to setup the application in a
            known state. Then I use different build configurations to build separate
            binaries, each with a different known initial state. There's no doubt that
            it's a bit Rube Goldberg-esque, but it's a start.</p>
            
            <p>There is a growing list of options available out there for writing automated
            integration tests for your iPhone apps. Apple's UI Automation tool is just
            one of them. It has it's pros and cons, but it's a big step forward in the
            right direction. Now it's up to us to figure out how to build upon them,
            develop extensions and idioms around them and, ultimately, build better
            apps.</p>
            
            <h1>Resources</h1>
            
            <p>As I stated earlier, there isn't much out there in the way of documentation,
            but here are a couple things to look at:</p>
            
            <ul>
            <li><a href="http://developer.apple.com/iphone/library/documentation/DeveloperTools/Reference/UIAutomationRef/Introduction/Introduction.html" title="UI Automation Reference Collection">UI Automation Reference Collection</a></li>
            <li><a href="http://answers.oreilly.com/topic/1646-how-to-use-uiautomation-to-create-iphone-ui-tests/" title="How to use UIAutomation to create iPhone UI tests - O'Reilly Answers">O'Reilly Answers Post on UI Automation</a></li>
            </ul>
            
            
            <h1 id="footnotes">Footnotes</h1>
            
            
            <ol>
              <li>
                <a name="note1"></a>
                I don't know if this was a ploy to get developers to be more mindful
                of disabled users or not, but the net-result is a win for everyone. You 
                get better test automation and disabled users get better apps.
              </li>
              <li>
                <a name="note2"></a>
                I haven't spent the time to figure out which ones do and don't so perhaps 
                there's some kind of rhyme and reason to this.
              </li>
              <li>
                <a name="note3"></a>
                I have to admit that I was surprised to see JavaScript as the language
                of choice given Apple's support of the <a href="http://www.macruby.org/" title="MacRuby &raquo; Home">MacRuby</a> 
                project. JavaScript has had to weather a lot of bad press over the 
                years. It's a language that has a lot of quirks but, once you separate 
                it from the vagaries of various browser implementations, you'll find that
                it's a pretty flexible and expressive language.
              </li>
              <li>
                <a name="note4"></a>
                This comes from a real app that I've been working on. You can't beat
                learning new things from a real project.
              </li>
              <li>
                <a name="note5"></a>
                No disrespect meant for the iCuke team. Honestly, they've done an 
                awesome job, I can't wait to see where they go next. Definitely keep
                your eye on this project.
              </li>
            </ol>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Making Fun of Things with OCMock</title>
      <link>
        http://alexvollmer.com/posts/2010/06/28/making-fun-of-things-with-ocmock/
      </link>
      <comments>
        http://alexvollmer.com/posts/2010/06/28/making-fun-of-things-with-ocmock/#comments
      </comments>
      <pubDate>
        Mon, 28 Jun 2010 09:35:27 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            cocoa
        ]]>
      </category>
      <category>
        <![CDATA[
            TDD
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2010/06/28/making-fun-of-things-with-ocmock/
      </guid>
      <description>
        <![CDATA[
            OK, I've done <a href="http://alexvollmer.com/posts/2010/06/01/cocoas-broken-tests/" title="Alex Vollmer &mdash; Cocoa's Broken Tests">plenty of ranting about the state of TDD in Cocoa development</a>.
            So instead of inflicting more
            whining on your ears, I've decided to figure out how to get better at this.
            This is the first in a series of posts I'll be doing about testing in Cocoa.
            We're going to look at OCMock, a framework for creating and
            using mock objects in your Cocoa tests.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>OK, I've done <a href="http://alexvollmer.com/posts/2010/06/01/cocoas-broken-tests/" title="Alex Vollmer &mdash; Cocoa's Broken Tests">plenty of ranting about the state of TDD in Cocoa development</a>.
            So instead of inflicting more
            whining on your ears, I've decided to figure out how to get better at this.
            This is the first in a series of posts I'll be doing about testing in Cocoa.
            We're going to look at OCMock, a framework for creating and
            using mock objects in your Cocoa tests.</p>
            
            <p>This isn't the only tutorial out there on OCMock, but hopefully it's the
            clearest. I found the documentation on the site to be a bit, ahem, lacking. As
            a result I ended up figuring out how to use it by going through the source
            code<sup><a href="#note1">1</a></sup>.</p>
            
            <h1>Installation</h1>
            
            <p>First, you need to download the <a href="http://www.mulle-kybernetik.com/software/OCMock/" title="Mulle kybernetiK -- OCMock">OCMock project</a>.
            Before Xcode 3.2.3, you used to be able to simply add the <code>OCMock.framework</code>
            to the "Link Binary With Libraries" build phase of your unit tests. This
            <a href="http://www.mulle-kybernetik.com/forum/viewtopic.php?f=4&amp;t=73" title="Mulle kybernetiK &bull; Information">appears to have stopped working</a>,
            so you have two options: you can include the OCMock source Xcode project
            and declare a build dependency on its static library target, or you can build
            the static library once and put the following header files somewhere accessible
            to your Xcode build:</p>
            
            <ul>
            <li><code>NSNotificationCenter+OCMAdditions.h</code></li>
            <li><code>OCMArg.h</code></li>
            <li><code>OCMConstraint.h</code></li>
            <li><code>OCMock.h</code></li>
            <li><code>OCMockObject.h</code></li>
            <li><code>OCMockRecorder.h</code></li>
            </ul>
            
            
            <p>I didn't want to rebuild the library over and over so I built the static
            library once and put the <code>libOCMock.a</code> and <code>OCMock.h</code> files in a directory
            in my project and added that directory to the <code>LIBRARY_SEARCH_PATHS</code> in the
            unit-test target configuration <sup><a href="#note2">2</a></sup>.</p>
            
            <h1>Underlying Concepts</h1>
            
            <p>I'm assuming that you already know what <a href="http://en.wikipedia.org/wiki/Mock_object" title="Mock object - Wikipedia,
            the free encyclopedia">mock
            objects</a> are and how to use them. I'm not going to explain
            their value here, or the design changes you end up making to accommodate them.
            Mock objects are simply one of many tools you can employ in your testing. They
            have their pros and cons so let experience and intuition be your guide.
            Instead, I want to focus on how to use OCMock.</p>
            
            <p>The general recipe for using mocks in unit-tests is:</p>
            
            <ol>
            <li>Create the mock object</li>
            <li>Specify the expected invocations and return values</li>
            <li>Associate the mock object with the code under test</li>
            <li>Execute the code under test</li>
            <li>Validate that your assertions are correct</li>
            </ol>
            
            
            <p>It's worth spending some time looking at the test cases that come with the
            OCMock source code. The coverage is quite good so it's a great demonstration
            of OCMock's capabilities.</p>
            
            <h2>Making Mocks</h2>
            
            <p>To create a mock object, use one of the factory methods available on the
            <code>OCMockObject</code> class: <code>+mockForClass:</code>, <code>+mockForProtocol:</code> or
            <code>+partialMockForObject:</code>. There are also two variants of this called "nice"
            mocks, which you can get by calling either <code>+niceMockForClass:</code> or
            <code>+niceMockForProtocol:</code>. The difference between a "nice" mock and regular
            (mean? stand-offish?) mock, is how they behave when they receive an unexpected
            method invocation. A "nice" mock will simply ignore the unexpected invocation,
            whereas a regular mock will raise a <code>NSException</code>.</p>
            
            <table>
            <thead>
            <tr>
            <th>Factory Method           </th>
            <th> Description                                        </th>
            </tr>
            </thead>
            <tbody>
            <tr>
            <td><code>+mockForClass:</code>         </td>
            <td> Create a mock based on the given class             </td>
            </tr>
            <tr>
            <td><code>+mockForProtocol:</code>      </td>
            <td> Create a mock based on the given protocol          </td>
            </tr>
            <tr>
            <td><code>+niceMockForClass:</code>     </td>
            <td> Create a "nice" mock based on the given class      </td>
            </tr>
            <tr>
            <td><code>+niceMockForProtocol:</code>  </td>
            <td> Create a "nice" mock based on the given protocol   </td>
            </tr>
            <tr>
            <td><code>+partialMockForObject:</code> </td>
            <td> Create a mock based on the given <em>object</em>          </td>
            </tr>
            <tr>
            <td><code>+observerMock:</code>         </td>
            <td> Create a notification observer (more on this later)</td>
            </tr>
            </tbody>
            </table>
            
            
            <p>If you're like me and you like to keep your builds free of compiler warnings,
            declare the returned object to be of type <code>id</code> to shut the compiler up.</p>
            
            <div class="highlight"><pre><span class="kt">id</span> <span class="n">myMock</span> <span class="o">=</span> <span class="p">[</span><span class="n">OCMockObject</span> <span class="nl">mockForClass:</span><span class="p">[</span><span class="n">MyClass</span> <span class="n">class</span><span class="p">]];</span>&#x000A;    </pre>
            </div>
            
            
            <p>The <code>+partialMockForObject:</code> method allows you to turn an existing object into
            a mock. This can be useful in cases where a collaborating object has several
            of its methods invoked, but you only want to override one or two. The obvious
            dangers here are that, 1) your collaborating class probably violates the
            <a href="http://en.wikipedia.org/wiki/Single_responsibility_principle" title="Single responsibility principle - Wikipedia, the free encyclopedia">Single-Responsibility
            Principle</a> and 2)
            your tests now indirectly rely on the subtleties of which methods you mocked
            and which you didn't. Use at your own peril.</p>
            
            <h2>Setting Expectations</h2>
            
            <p>The object returned by any of these factory methods allows you to both
            setup expectations, invoke methods and verify its configuration. OCMock does
            a clever little trick to distinguish between invocation configuration and
            handling method invocations. Calling either the <code>-expect</code> or <code>-stub</code> method
            will return an object that you can use to setup your expectations. The cool
            thing about this object is that you setup your expectations by (more or less)
            invoking the methods on it that you want your class under test to invoke.</p>
            
            <p>Let's imagine that we have a simple stock-portfolio management application.
            Our portfolio model is encapsulated by the <code>AVStockPortfolio</code> class. An
            instance of that class is given an object that implements the <code>AVQuoteService</code>
            protocol, which is our gateway to a real-live stock quote service. When we
            test the <code>AVStockPortfolio</code> class, we don't want to be encumbered with a
            "production" implementation of <code>AVQuoteService</code>. Instead we want to craft
            a stand-in object to exercise and validate the portfolio class.</p>
            
            <p>Our first test is to see that the portfolio class initiates a connection
            with the quote service in its <code>init</code> method:</p>
            
            <div class="highlight"><pre><span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">testInit</span> <span class="p">{</span>&#x000A;      <span class="kt">id</span> <span class="n">mockService</span> <span class="o">=</span> <span class="p">[</span><span class="n">OCMockObject</span> <span class="nl">mockForProtocol:</span><span class="err">@</span><span class="n">protocol</span><span class="p">(</span><span class="n">AVQuoteService</span><span class="p">)];</span>&#x000A;      <span class="p">[[</span><span class="n">mockService</span> <span class="n">expect</span><span class="p">]</span> <span class="n">initiateConnection</span><span class="p">];</span>&#x000A;      &#x000A;      <span class="n">AVStockPortfolio</span> <span class="o">*</span><span class="n">portfolio</span> <span class="o">=</span> <span class="p">[[</span><span class="n">AVStockPortfolio</span> <span class="n">alloc</span><span class="p">]</span> <span class="nl">initWithService:</span><span class="n">mockService</span><span class="p">];</span>&#x000A;      &#x000A;      <span class="p">[</span><span class="n">mockService</span> <span class="n">verify</span><span class="p">];</span>&#x000A;    <span class="p">}</span>&#x000A;    </pre>
            </div>
            
            
            <p>When the <code>-expect</code> method is called, it returns a <a href="http://www.cocoadev.com/index.pl?TrampolineObject" title="CocoaDev: TrampolineObject">"trampoline" object</a>
            that captures additional method calls dynamically. Any methods invoked on
            the service that I haven't explicitly configured will raise an exception. I
            can also check that the methods I've configured were all invoked by calling
            the mock object's <code>-verify</code> method.</p>
            
            <p>You can also configure your mock object to act as a more forgiving "stub" for
            particular methods by using <code>-stub</code> instead of <code>-expect</code>
            <a href="#note3"><sup>3</sup></a>. Admittedly, the ability to turn a so-called
            "mock" object into a "stub" for particular method invocations muddies the
            nomenclature a bit, but it can be handy to have this ability at times.</p>
            
            <h3>Return Values</h3>
            
            <p>If methods on your mocks need to return values, you have a variety of methods
            to call on the object returned by <code>-expect</code> or <code>-stub</code>. Any method we call on
            this object that isn't one of the built-in OCMock methods, is captured
            dynamically as an expected method invocation.</p>
            
            <table>
            <thead>
            <tr>
            <th>Method               </th>
            <th> Explanation                                        </th>
            </tr>
            </thead>
            <tbody>
            <tr>
            <td><code>-andReturn:</code>        </td>
            <td> Return the given object                            </td>
            </tr>
            <tr>
            <td><code>-andReturnValue:</code>   </td>
            <td> Return a non-object value (wrapped in a <code>NSValue</code>) </td>
            </tr>
            <tr>
            <td><code>-andThrow:</code>         </td>
            <td> Throw the given exception                          </td>
            </tr>
            <tr>
            <td><code>-andPost:</code>          </td>
            <td> Post the given notification                        </td>
            </tr>
            <tr>
            <td><code>-andCall:onObject:</code> </td>
            <td> Call the selector on the given object              </td>
            </tr>
            <tr>
            <td><code>-andDo:</code>            </td>
            <td> Invoke the given block (only on OS X 10.6 or iOS 4)</td>
            </tr>
            </tbody>
            </table>
            
            
            <p>Since each of these methods returns the same expectation object, you can
            chain these method calls, where it makes sense. For example to post a
            notification <em>and</em> return a specified value we could do this:</p>
            
            <div class="highlight"><pre><span class="n">NSNotification</span> <span class="o">*</span><span class="n">notfication</span> <span class="o">=</span> <span class="p">[</span><span class="n">NSNotification</span> <span class="nl">notificationWithName:</span><span class="s">@&quot;foo&quot;</span> <span class="nl">object:</span><span class="nb">nil</span><span class="p">];</span>&#x000A;    <span class="p">[[[</span><span class="n">mock</span> <span class="n">expect</span><span class="p">]</span> <span class="nl">andPost:</span><span class="n">notfication</span><span class="p">]</span> <span class="nl">andReturn:</span><span class="s">@&quot;FOOBAR&quot;</span><span class="p">]</span> <span class="n">doSomethingMagical</span><span class="p">];</span>&#x000A;    </pre>
            </div>
            
            
            <p>The <code>-andDo:</code> option is one of my favorites since it uses blocks and blocks
            are the most awesome addition to Objective-C in a long time. You can do a lot
            of validation locally within a block which keeps the code very succinct and
            clean:</p>
            
            <div class="highlight"><pre><span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">testSellSharesInStock</span> <span class="p">{</span>&#x000A;      <span class="kt">id</span> <span class="n">quoteService</span> <span class="o">=</span> <span class="p">[[</span><span class="n">OCMockObject</span><span class="p">]</span> <span class="nl">mockForProtocol:</span><span class="err">@</span><span class="n">protocol</span><span class="p">(</span><span class="n">AVQuoteService</span><span class="p">)];</span>&#x000A;      <span class="p">[[[</span><span class="n">quoteService</span> <span class="n">expect</span><span class="p">]</span> <span class="nl">andDo:</span><span class="o">^</span><span class="p">(</span><span class="n">NSInvocation</span> <span class="o">*</span><span class="n">invocation</span><span class="p">)</span> <span class="p">{</span>&#x000A;        <span class="c1">// validate arguments, set return value on the invocation object</span>&#x000A;      <span class="p">}]</span> <span class="nl">priceForStock:</span><span class="s">@&quot;AAPL&quot;</span><span class="p">];</span>&#x000A;      &#x000A;      <span class="n">AVStockPortfolio</span> <span class="o">*</span><span class="n">portfolio</span> <span class="o">=</span> <span class="p">[[</span><span class="n">AVStockPortfolio</span> <span class="n">alloc</span><span class="p">]</span> <span class="nl">initWithService:</span><span class="n">quoteService</span><span class="p">];</span>&#x000A;      <span class="p">[</span><span class="n">portfolio</span> <span class="nl">sellShares:</span><span class="mi">100</span> <span class="nl">inStock:</span><span class="s">@&quot;AAPL&quot;</span><span class="p">];</span>&#x000A;      &#x000A;      <span class="c1">// other validations and assertions</span>&#x000A;      &#x000A;      <span class="p">[</span><span class="n">quoteService</span> <span class="n">verify</span><span class="p">];</span>&#x000A;    <span class="p">}</span>&#x000A;    </pre>
            </div>
            
            
            <h3>Arguments &amp; Return Values</h3>
            
            <p>Basic method expectations are useful, but we also want to validate the
            arguments given to methods. Back to our stock portfolio, imagine that when
            we buy a stock, we know that part of that process is to ask for the current
            price of a stock from a stock service, so we want to assert that when we
            sell a particular stock, our portfolio object queries the quote service for
            the latest price for the same stock symbol:</p>
            
            <div class="highlight"><pre><span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">testBuyShares</span> <span class="p">{</span>&#x000A;      <span class="kt">id</span> <span class="n">mockService</span> <span class="o">=</span> <span class="p">[</span><span class="n">OCMockObject</span> <span class="nl">mockForProtocol:</span><span class="err">@</span><span class="n">protocol</span><span class="p">(</span><span class="n">AVQuoteService</span><span class="p">)];</span>&#x000A;      <span class="p">[[</span><span class="n">mockService</span> <span class="n">expect</span><span class="p">]</span> <span class="nl">andReturn:</span><span class="p">[</span><span class="n">NSNumber</span> <span class="nl">numberWithFloat:</span><span class="mf">214.57</span><span class="p">]]</span> <span class="nl">priceQuoteForSymbol:</span><span class="s">@&quot;AAPL&quot;</span><span class="p">];</span>&#x000A;      &#x000A;      <span class="n">AVPortfolio</span> <span class="o">*</span><span class="n">portfolio</span> <span class="o">=</span> <span class="p">[[</span><span class="n">AVPortfolio</span> <span class="n">alloc</span><span class="p">]</span> <span class="nl">initWithQuoteService:</span><span class="n">mockService</span><span class="p">];</span>&#x000A;      <span class="p">[</span><span class="n">portfolio</span> <span class="nl">buyShares:</span><span class="mi">100</span> <span class="nl">inStock:</span><span class="s">@&quot;AAPL&quot;</span><span class="p">];</span>&#x000A;      &#x000A;      <span class="c1">// other validation and assertions</span>&#x000A;    &#x000A;      <span class="p">[</span><span class="n">mockService</span> <span class="n">verify</span><span class="p">];</span>&#x000A;    <span class="p">}</span>&#x000A;    </pre>
            </div>
            
            
            <p>In this case, we're declaring the expected argument to be the string
            literal <code>@"AAPL"</code>. We've also configured the method with a return value, in
            this case a <code>NSNumber</code> instance with the value <code>214.57</code>. However, OCMock can
            do more. You can use any of the following <code>OCMArg</code> class methods in place of
            a real argument when setting up your method expectations:</p>
            
            <table>
            <thead>
            <tr>
            <th><code>OCMArg</code> method                </th>
            <th> Description                                                 </th>
            </tr>
            </thead>
            <tbody>
            <tr>
            <td><code>+any</code>                         </td>
            <td> Any argument is accepted.</td>
            </tr>
            <tr>
            <td><code>+anyPointer</code>                  </td>
            <td> Accepts any pointer                                         </td>
            </tr>
            <tr>
            <td><code>+isNil</code>                       </td>
            <td> The given argument <em>must</em> be <code>nil</code>                          </td>
            </tr>
            <tr>
            <td><code>+isNotNil</code>                    </td>
            <td> The given argument must <em>not</em> be <code>nil</code>                      </td>
            </tr>
            <tr>
            <td><code>+isNotEqual:</code>                 </td>
            <td> Given argument is not object-equivalent with expectation    </td>
            </tr>
            <tr>
            <td><code>+checkWithSelector:onObject:</code> </td>
            <td> Check the argument with the given action/target pair        </td>
            </tr>
            <tr>
            <td><code>+checkWithBlock:</code>             </td>
            <td> Check the argument with the given block (OS X 10.6 or iOS 4)</td>
            </tr>
            </tbody>
            </table>
            
            
            <p>OCMock also provides a few handy macros for argument matching:</p>
            
            <table>
            <thead>
            <tr>
            <th>Macro                         </th>
            <th> Description                                                        </th>
            </tr>
            </thead>
            <tbody>
            <tr>
            <td><code>OCMOCK_ANY()</code>                </td>
            <td> Equivalent to <code>[OCMArg any]</code>                                       </td>
            </tr>
            <tr>
            <td><code>OCMOCK_VALUE(value)</code>         </td>
            <td> A quick way to match a non-object argument                         </td>
            </tr>
            <tr>
            <td><code>CONSTRAINT(selector)</code>        </td>
            <td> Validate with a given selector on <code>self</code>                           </td>
            </tr>
            <tr>
            <td><code>CONSTRAINTV(selector,value)</code> </td>
            <td> Validate with a given selector on <code>self</code> and an additional argument</td>
            </tr>
            </tbody>
            </table>
            
            
            <h3>But Wait&hellip;There's More!</h3>
            
            <p>One nice little feature of OCMock is its handy support for testing
            notifications. To create a mock for notifications, call the <code>+observerMock</code>
            factory method on the <code>OCMockObject</code> class. This will return a mock that works
            differently from the mocks/stubs described earlier. Instead of setting up
            method invocation expectations, you can set up notification expectations which
            are useful when you want to assert that your class under test posts
            notifications. Let's say that we want our aforementioned portfolio class to
            post notifications whenever stocks are bought or sold:</p>
            
            <div class="highlight"><pre><span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">testSellSharesInStock</span> <span class="p">{</span>&#x000A;      <span class="kt">id</span> <span class="n">mock</span> <span class="o">=</span> <span class="p">[</span><span class="n">OCMockObject</span> <span class="n">observerMock</span><span class="p">];</span>&#x000A;      <span class="c1">// OCMock adds a custom methods to NSNotificationCenter via a category</span>&#x000A;      <span class="p">[[</span><span class="n">NSNotificationCenter</span> <span class="n">defaultCenter</span><span class="p">]</span> <span class="nl">addMockObserver:</span><span class="n">mock</span>&#x000A;                                                       <span class="nl">name:</span><span class="n">AVStockSoldNotification</span>&#x000A;                                                     <span class="nl">object:</span><span class="nb">nil</span><span class="p">];</span>&#x000A;                                                   &#x000A;      <span class="p">[[</span><span class="n">mock</span> <span class="n">expect</span><span class="p">]</span> <span class="nl">notificationWithName:</span><span class="n">AVStockSoldNotification</span> <span class="nl">object:</span><span class="p">[</span><span class="n">OCMArg</span> <span class="n">any</span><span class="p">]];</span>&#x000A;    &#x000A;      <span class="n">AVPortfolio</span> <span class="o">*</span><span class="n">portfolio</span> <span class="o">=</span> <span class="p">[</span><span class="n">self</span> <span class="n">createPortfolio</span><span class="p">];</span> <span class="c1">// made-up factory method</span>&#x000A;      <span class="p">[</span><span class="n">portfolio</span> <span class="nl">sellShares:</span><span class="mi">100</span> <span class="nl">inStock:</span><span class="s">@&quot;AAPL&quot;</span><span class="p">];</span>&#x000A;    &#x000A;      <span class="p">[</span><span class="n">mock</span> <span class="n">verify</span><span class="p">];</span>&#x000A;    <span class="p">}</span>&#x000A;    </pre>
            </div>
            
            
            <p>If the <code>AVStockSoldNotification</code> isn't posted by the time we call <code>verify</code>,
            the mock will raise an exception. Note that you need to add the mock as an
            observer, which you can do by the handy <code>-addMockObserver:name:object:</code>
            category method on <code>NSNotificationCenter</code> provided by OCMock.</p>
            
            <h2>Execution &amp; Validation</h2>
            
            <p>Any method invoked on your mock object will be checked against the configured
            invocations. Any unexpected invocations will result in an exception being
            thrown (unless it's a stub or the mock is in "nice" mode). Also, any configured
            invocations that did <em>not</em> happen will trigger an exception when the mock's
            <code>-verify</code> method is called. As a matter of habit, you want to call this at the
            end of your test.</p>
            
            <h1>Conclusion</h1>
            
            <p>Using mocks, stubs and test-doubles in your testing is a handy way to keep
            your tests lean and focused on one class at a time. This is especially
            helpful when you have dependent classes that require a lot of setup that
            you would rather avoid in your test-harness (e.g. Core Data).</p>
            
            <p>The downside to testing with mocks and stubs is that you lose some test
            coverage for the objects you're replacing with test-doubles. This approach
            probably works best when your code is interacting with objects from other
            libraries that you don't want to deal with in your tests.</p>
            
            <h1 id="footnotes">Footnotes</h1>
            
            
            <ol>
              <li>
                <a name="note1"></a>
                I know it seems like a drag, but it's hard to overestimate the value of
                reading the original source code for any tool that you use. You'll be
                amazed at how much more of an intimate understanding it gives you of
                the tool and the rationale behind its design.
              </li>
              <li>
                <a name="note2"></a>
                I forked the OCMock codebase and have started to make a few tweaks to it.
                Feel free to take a look at it on <a href="http://github.com/alexvollmer/OCMock" title="alexvollmer's OCMock at master - GitHub">GitHub</a>.
              </li>
              <li>
                <a name="note3"></a>
                If you aren't familiar with mocks vs. stubs, Martin Fowler has written
                the <a href="http://martinfowler.com/articles/mocksArentStubs.html" title="Mocks Aren't Stubs">canonical text</a> on the differences.
              </li>
            </ol>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>A Tale of Two Cultures</title>
      <link>
        http://alexvollmer.com/posts/2010/06/13/a-tale-of-two-cultures/
      </link>
      <comments>
        http://alexvollmer.com/posts/2010/06/13/a-tale-of-two-cultures/#comments
      </comments>
      <pubDate>
        Sun, 13 Jun 2010 15:10:14 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            apple
        ]]>
      </category>
      <category>
        <![CDATA[
            google
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2010/06/13/a-tale-of-two-cultures/
      </guid>
      <description>
        <![CDATA[
            I just returned from the 2010 edition of Apple's WWDC. The week before I took
            a week off from my iPhone and lived with an Android Nexus One. Both my personal
            experience with Android and the following week's announcements left a
            strong impression that, in the so-called "smartphone" space, we are really
            seeing the success of two very different mind-sets, cultures and philosophies.
            I'm not interested in a winner-takes-all kind of war. Instead I wanted to look
            at the differences between these two companies in the context of them
            co-existing and each having a happy, satisfied base of customers.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p><a href="http://www.flickr.com/photos/adamjackson/4670520309/" target="_new">
            <img class="right" src="http://farm5.static.flickr.com/4011/4670520309_fbac12cd42_m.jpg"/>
            </a></p>
            
            <p>I just returned from the 2010 edition of Apple's WWDC. The week before I took
            a week off from my iPhone and lived with an Android Nexus One. Both my personal
            experience with Android and the following week's announcements left a
            strong impression that, in the so-called "smartphone" space, we are really
            seeing the success of two very different mind-sets, cultures and philosophies.
            I'm not interested in a winner-takes-all kind of war. Instead I wanted to look
            at the differences between these two companies in the context of them
            co-existing and each having a happy, satisfied base of customers.</p>
            
            <p>I <em>was</em> going to write about how capable the Nexus One seems to me, but how
            amateurish the UI appears. At times, the look and feel of the Android UI
            looks like its designers simply couldn't figure out how to fill the space
            with something interesting.</p>
            
            <p>I <em>was</em> going to write about how the ubiquity of the four hardware buttons are
            both a blessing and curse. I understand that the designers were trying to
            provide a set of near-universal options in a consistent place. Unfortunately
            it was too easy to accidentally hit the "home" key while typing on the soft
            keyboard.</p>
            
            <p><a href="http://www.flickr.com/photos/oyf/4255429887/" target="_new">
              <img src="http://farm3.static.flickr.com/2770/4255429887_e16b119a2d_m.jpg" class="left"/>
            </a></p>
            
            <p>I <em>was</em> going to write about the moment I realized just how different the
            design philosophies between Android and iOS (nee iPhoneOS) are when I wanted
            Instapaper integration with a good Twitter client. I needed to find an
            Instapaper app, not a Twitter app with Instapaper integration. I simply didn't
            grok the universal plugin architecture of Android at first. In hindsight it
            makes perfect sense&mdash;if you're a software developer or tech-geek. It
            seems insane to require the user to have a mental model that's so intimate
            with Android's implementation details.</p>
            
            <p>I <em>was</em> going to write about how delighted I was with Android's GMail
            integration and how much I wish something similar existed on the iPhone. I
            love GMail. I couldn't care less about other mail configurations (POP or
            IMAP). Maybe the built-in regular mail app is as clunky as Apple's, but I
            didn't use it. Kudos to Google for such a seamless integration with not only
            Gmail, but the calendar and contacts too.</p>
            
            <p>I <em>was</em> going to write about all these little bits of evidence that showed
            just how different the philosophies are between these two companies and
            these two platforms. I was going to write about how it's a bit odd to put
            them in competition with each other because, in a sense, they are really
            trying to build two different things: Google wants to build the most bad-ass
            feature-rich, piece of mobile technology there ever was. Apple wants to build
            a mobile experience.</p>
            
            <p>I <em>was</em> going to write about all of these things, but after attending the
            keynote at WWDC last week, I realized that there was only one thing that I
            needed to point to that most effectively highlights the differences
            between the two:</p>
            
            <p><a href="http://www.apple.com/iphone/features/facetime.html" target="_new">
              <img src="/images/2010/06/facetime.png">
            </a></p>
            
            <p>At the end of this video, you had a room full of five-thousand geeks drying
            their eyes. You can call the video cheesy or manipulative. You can call it a
            piece of marketing fluff. But that video was <em>not</em> about protocols,
            compatibility, specifications or any of the myriad technical details we debate
            on a daily basis. That video was about telling human stories that we can all
            relate to.</p>
            
            <p>I haven't yet been to a Google I/O event. I'd really like to attend one. But
            right, wrong or indifferent, you would never see something like this from
            Google. I can't think of anything that does a better job of summing up the
            completely different views that Google and Apple have of the world.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Cocoa's Broken Tests</title>
      <link>
        http://alexvollmer.com/posts/2010/06/01/cocoas-broken-tests/
      </link>
      <comments>
        http://alexvollmer.com/posts/2010/06/01/cocoas-broken-tests/#comments
      </comments>
      <pubDate>
        Tue, 01 Jun 2010 11:20:51 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            cocoa
        ]]>
      </category>
      <category>
        <![CDATA[
            TDD
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2010/06/01/cocoas-broken-tests/
      </guid>
      <description>
        <![CDATA[
            I'm a long-time TDD kinda guy. I've had the great fortune of learning TDD
            first-hand from <a href="http://en.wikipedia.org/wiki/Kent_Beck" title="Kent Beck - Wikipedia,the free encyclopedia">one of its greatest practitioners</a>
            and consider it one of the core disciplines of the way I go about my
            profession. So when I first started doing Cocoa programming in earnest I was
            shocked at the state of automated testing. Compared to my experiences on other
            platforms, the tools are archaic and backwards. Moreover, the philosophy of
            testing just doesn't seem to be baked into the DNA of the Cocoa community.
            Nobody seems to be talking about it much. So I've just suffered with
            old-fashioned head-against-wall development without the comforting support of
            TDD. But I don't know how much longer I can take it. Am I crazy for wanting
            TDD in Cocoa, or are the two simply incompatible?
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>I'm a long-time TDD kinda guy. I've had the great fortune of learning TDD
            first-hand from <a href="http://en.wikipedia.org/wiki/Kent_Beck" title="Kent Beck - Wikipedia,the free encyclopedia">one of its greatest practitioners</a>
            and consider it one of the core disciplines of the way I go about my
            profession. So when I first started doing Cocoa programming in earnest I was
            shocked at the state of automated testing. Compared to my experiences on other
            platforms, the tools are archaic and backwards. Moreover, the philosophy of
            testing just doesn't seem to be baked into the DNA of the Cocoa community.
            Nobody seems to be talking about it much. So I've just suffered with
            old-fashioned head-against-wall development without the comforting support of
            TDD. But I don't know how much longer I can take it. Am I crazy for wanting
            TDD in Cocoa, or are the two simply incompatible?</p>
            
            <p>Given the current state of affairs, it's not hard to see why there's a
            <a href="http://www.wilshipley.com/blog/2005/09/unit-testing-is-teh-suck-urr.html" title="Call Me Fishmeal.: Unit testing is teh suck, Urr.">bias against unit-testing</a>
            in the Cocoa community. <sup><a href="#note1">1</a></sup> Unit-testing tools,
            support and idioms within Cocoa are nowhere close to where they are in, say,
            the Java, .Net, Ruby or Python communities. Compared to those environments,
            unit-testing in Cocoa is just flat out <em>difficult</em>. I can easily understand
            why a developer would conclude that the cost and effort of TDD outweighs the
            benefits.</p>
            
            <h1>The Dismal Science</h1>
            
            <p><a href="http://www.flickr.com/photos/janbrasna/399875844/" title="Currencies on Flickr - Photo Sharing!"><img src="http://farm1.static.flickr.com/178/399875844_16660fd4bf_m.jpg" class="left" alt="It's all about the money"></a></p>
            
            <p>Think of every "best practice" you've ever been exposed to in software. They
            all come down to cost-benefit tradeoffs in the end. Up-front requirements
            gathering? It's an attempt to manage change and indemnify certain parties when
            things go awry. Iterative development? It's merely a short-term review process
            to evaluate the cost of future development against opportunities in the
            future. Automated testing? Computers (generally) work more cheaply than
            humans, so invest in automating repeated tasks so that, over the long-haul,
            more work is accomplished by cheaper workers. Hell, even the oft-cited goals
            of code-reuse in object-oriented programming are economic ones. The list goes
            on and on and on.</p>
            
            <p>But, even as a long-time practitioner of test-driven development, I don't view
            it as axiomatic. In this big, bad world of ours there have to be cases where
            the economics simply don't add up, and it simply isn't <em>cost effective</em> to
            build stuff using TDD.<sup><a href="#note2">2</a></sup> Returning to
            Cocoa, I can see how developers come to the conclusion that TDD in Cocoa
            simply isn't worth the price of admission.</p>
            
            <p>So what makes TDD in Cocoa so expensive? Would things look different if we
            could change the balance sheet?</p>
            
            <h1>Bear-Skins &amp; Stone Knives</h1>
            
            <p><a href="http://www.flickr.com/photos/g_originals/368093250/" title="working hand on Flickr - Photo Sharing!"><img src="http://farm1.static.flickr.com/169/368093250_8fa93d209a_m.jpg" class="right"/></a></p>
            
            <p>Back in the day, <a href="http://www.sente.ch/software/ocunit/" title="Sen:te - OCUnit">OCUnit</a>
            was <em>the</em> xUnit toolkit of choice for Cocoa programming. In 2006
            Apple put OCUnit right into Xcode and TDD received its first official
            blessing. Now I don't have a beef in particular with OCUnit&mdash;it's
            essentially a faithful implementation of xUnit patterns for Objective-C. What
            is surprising is how weakly it's integrated with Xcode.</p>
            
            <p>To unit-test, you have to create an entirely new target to execute your
            unit-tests within. However, you don't run it like a normal executable. It's
            baked into the build process for that target so that testing errors show up
            just like build errors. I'll admit that's kind of cute and at least makes an
            attempt to integrate TDD into the development process. But keeping unit-tests
            as a separate target means more drop-down flipping in Xcode to go between
            unit-testing and running my application. Oh and the bloody drop-downs in
            Xcode&hellip;don't even get me started&hellip;</p>
            
            <p>The other issue is debugging. If your unit-tests are failing, god help you if
            you want to find out why. It takes <a href="http://chanson.livejournal.com/120740.html" title="Chris Hanson - Xcode: Debugging Cocoa application unit tests">a lot of environment-variable hijinks</a>
            to be able to actually debug your unit-tests &mdash;something that is
            significantly easier on every other platform I've ever worked on. All of this
            leaves me with the distinctly uneasy feeling that Apple and the Cocoa community
            at-large are merely paying lip-service to TDD.</p>
            
            <p>Compare this to how JUnit is integrated into your standard Java IDE or
            awesome Ruby testing tools like
            <a href="http://www.zenspider.com/ZSS/Products/ZenTest/" title="ZenTest: Automated test scaffolding for Ruby">autotest</a>.
            These tools are so much more immediate and easier to reach for. Xcode looks
            like it came from the era of the horse and buggy. These kind of tools and this
            kind of support needs to be a part of the development environment in a
            more natural way. Right now it's just a primitive, bolted-on afterthought.
            It's a wonder anyone has the patience to use it.</p>
            
            <h1>A New Mentality</h1>
            
            <p>Another challenge in Cocoa is figuring out where TDD fits in such a
            framework-driven environment. To Apple's credit, the frameworks that Cocoa
            provides do a pretty good job of "making the simple easy and the difficult
            possible". However because Cocoa is <em>so</em> prescriptive, it can be difficult for
            developers to stand back and figure out what to test. It's just so easy to
            just let the idioms fly off the fingers, that testing them seems like a silly
            exercise.</p>
            
            <p>I think a common conclusion for the the would-be TDD'er is that they often
            find their tests essentially repeating the implementation. These are most
            expensive tests to write and maintain. Not only do you end up duplicating the
            code (thus adding coupling and brittleness), but they also take up a lot of
            time to write and can be pretty error-prone.</p>
            
            <p>There has been a lot of thought about similar problems in other communities,
            so why not steal these ideas and apply them to Cocoa development? Surely
            strategies like <a href="http://martinfowler.com/bliki/InversionOfControl.html" title="MF Bliki: InversionOfControl">inversion of control</a>
            and <a href="http://www.mulle-kybernetik.com/software/OCMock/" title="Mulle kybernetiK -- OCMock">mocks</a>
            would help make TDD in Cocoa an economic possibility. <sup><a href="#note3">3</a></sup></p>
            
            <p>The Cocoa community simply hasn't evolved a good set of testing practices the
            way others have. Given the benefits I've seen in other environments, it's hard
            for me to believe that Cocoa and Objective-C are exceptional in this regard. I
            think that there are ways to do it, we just haven't discovered them yet. I
            recall from my Java and Ruby days that testing idioms and practices evolved <em>a
            lot</em> before we got somewhere reasonable. Simply put, the Cocoa world's
            collective testing skills and knowledge lag severely behind a lot of other
            languages.</p>
            
            <p><img class="right" src="/images/2010/06/kim-jong-il.jpg" alt="Kim Jong Il"></p>
            
            <p>Facing this requires the Cocoa community to face its own isolationist and
            exceptionalist attitudes. There's nothing so special about Cocoa and
            Objective-C that <em>conceptually</em> invalidates the effectiveness of TDD. <sup><a
            href="#note4">4</a></sup> Cocoa folks need to look outside of their walled
            garden to see what others have done. This is not something I've seen much,
            if any of, in the Cocoa community. Frankly, the dominant attitude seems to be
            one of snobbery and elitism. It's an unfortunate attitude that holds us all back.</p>
            
            <h1 id="footnotes">Footnotes</h1>
            
            
            <ol>
            <li>
            <a name="note1"></a>
            Normally it wouldn't be fair to link to a five-year old post and call
            it "representative" of a community's attitude, but I think for an insular
            group like Cocoa-nerds, this is totally reasonable.
            </p>
            </li>
            
            <li>
            <a name="note2"></a>
            I'm not saying that I, personally, have encountered such a thing, but
            I don't think it's unreasonable to assume that the possibility <em>exists</em>.
            </li>
            
            <li>
            <a name="note3"></a>
            There is, of course, a pathological extreme to this line of thinking. That
            extreme is called <a href="http://www.springsource.org/" title="SpringSource.org |">Spring</a>.
            </li>
            
            <li>  
            <a name="note4"></a>
            OK, I'll admit that iPhone development is a little different because of
            having to deal with the device vs. the simulator. Getting tests running on the
            device is a non-trivial exercise. But I'm not convinced that unit-tests have
            to run on the device. Yes, there are differences between the real and
            simulated environments, but those differences should be accounted for in
            <em>integration tests</em>&mdash;a topic I'm not going to address here.
            </li>
            </ol>
            
            
            <p></div></p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>The International Colors of iPhoneOS</title>
      <link>
        http://alexvollmer.com/posts/2010/05/21/the-international-colors-of-iphoneos/
      </link>
      <comments>
        http://alexvollmer.com/posts/2010/05/21/the-international-colors-of-iphoneos/#comments
      </comments>
      <pubDate>
        Fri, 21 May 2010 08:46:32 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            iphone
        ]]>
      </category>
      <category>
        <![CDATA[
            i18n
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2010/05/21/the-international-colors-of-iphoneos/
      </guid>
      <description>
        <![CDATA[
            Like flossing or saving for retirement, localizing
            and internationalizing your applications is one of those things "you should
            do". Cocoa, and by extension Cocoa Touch, has pretty decent localization
            (l10n) and internationalization (i18n) support. If you use the
            <code>NSLocalizedString</code> macro along with locale-specific strings files, you will
            handle 99.9% of your localization needs.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>Like flossing or saving for retirement, localizing
            and internationalizing your applications is one of those things "you should
            do". Cocoa, and by extension Cocoa Touch, has pretty decent localization
            (l10n) and internationalization (i18n) support. If you use the
            <code>NSLocalizedString</code> macro along with locale-specific strings files, you will
            handle 99.9% of your localization needs.</p>
            
            <p><img src="http://revoir1printemps.canalblog.com/albums/benetton/m-benetton654.jpg" height="100" class="left"></p>
            
            <p>But what if you're integrating with a web service? What's the best way to
            convey the user's current settings to an external party? I've run into this
            exact situation a couple of times and wanted to share the approach I like to
            take.</p>
            
            <h1>The Server-Side</h1>
            
            <p>The first thing you need to determine is how you'll express the user's
            current locale settings. If you own the servers-side, you can implement this in
            whatever way you see fit. Personally, I like to take advantage of as much
            of the HTTP specification as I can. Rather than overloading every request
            with a request parameter, I like to use the <code>Accept-Language</code> header.
            <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4" title="HTTP/1.1: Header Field Definitions">The specification</a>
            says that the format of the <code>Accept-Language</code> header allows multiple locales with
            <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.10" title="HTTP/1.1: Protocol Parameters">"quality values"</a>
            optionally given to each one to express order of preference.
            For example, a valid <code>Accept-Language</code> header might look like this:</p>
            
            <div class="highlight"><pre>Accept-Language: da, en-gb;q<span class="o">=</span>0.8, en;q<span class="o">=</span>0.7&#x000A;    </pre>
            </div>
            
            
            <p>In this example, Danish (with no specific region) is preferred first, followed
            by English (in the Great Britain region), followed by English in any region. The
            language and region are delicately intertwined in a bunch of different ways.
            Depending on the combination, the region may affect how words are spelled
            (for example "armor" in American English vs. "armour" in British English). Not
            all locales and regions work this way so there are plenty of corner-cases.</p>
            
            <p>Ruby on Rails is my server-side stack of choice these days. Like Cocoa, Rails
            is pretty good at making it easy to localize and internationalize your
            application. However, despite Rails claim to being "opinionated", it's
            surprisingly indecisive about the best way to express language and region
            preferences. The <a href="http://guides.rubyonrails.org/i18n.html" title="Rails Internationalization (I18n) API">Rails i18n Guide</a> suggests that you can get the locale
            as a query parameter (blech), via the domain name (if you've set your
            application and client up this way), via URL parameters (i.e. as a path
            segment within a larger URI structure), via built-in user settings, via GeoIP
            or using the <code>Accept-Language</code> header. As I stated before, I like the latter
            solution the most. However parsing the header, dealing with the quality scores
            and then figuring out the best one is something that has already been solved.
            For that, I like the <a href="http://rubygems.org/gems/http_accept_language" title="http_accept_language |
            RubyGems.org | your community gem host"><code>http_accept_language</code>
            gem</a>.</p>
            
            <p>In the <code>ApplicationController</code>, I setup a <code>before_filter</code> that uses the gem to
            parse the <code>Accept-Language</code> header. Since I may not have localized the web
            server to match a given request, I use the <code>compatible_language_from</code> method
            to figure out what, if any, language match there is.</p>
            
            <div class="highlight"><pre><span class="n">before_filter</span> <span class="ss">:check_language</span>&#x000A;    &#x000A;    <span class="k">def</span> <span class="nf">check_language</span>&#x000A;      <span class="n">params</span><span class="o">[</span><span class="ss">:locale</span><span class="o">]</span> <span class="o">=</span> <span class="k">if</span> <span class="n">params</span><span class="o">[</span><span class="ss">:locale</span><span class="o">].</span><span class="n">nil?</span>&#x000A;        <span class="n">request</span><span class="o">.</span><span class="n">compatible_language_from</span><span class="p">(</span><span class="no">MyApp</span><span class="o">::</span><span class="no">AVAILABLE_LANGUAGES</span><span class="p">)</span>&#x000A;      <span class="k">else</span>&#x000A;        <span class="n">params</span><span class="o">[</span><span class="ss">:locale</span><span class="o">]</span>&#x000A;      <span class="k">end</span>&#x000A;      <span class="no">I18n</span><span class="o">.</span><span class="n">locale</span> <span class="o">=</span> <span class="n">params</span><span class="o">[</span><span class="ss">:locale</span><span class="o">]</span>&#x000A;    <span class="k">end</span>&#x000A;    </pre>
            </div>
            
            
            <p><code>MyApp::AVAILABLE_LANGUAGES</code> is constant array defined in a Rails
            initializer (in <code>config/intializers</code>) that figures out all of the possible
            locales I have configured:</p>
            
            <div class="highlight"><pre><span class="k">module</span> <span class="nn">MyApp</span>&#x000A;      <span class="no">AVAILABLE_LANGUAGES</span> <span class="o">=</span> <span class="no">Dir</span><span class="o">[</span><span class="no">RAILS_ROOT</span> <span class="o">+</span> <span class="s2">&quot;/config/locales/*&quot;</span><span class="o">].</span><span class="n">map</span> <span class="k">do</span> <span class="o">|</span><span class="n">f</span><span class="o">|</span>&#x000A;        <span class="no">File</span><span class="o">.</span><span class="n">basename</span><span class="p">(</span><span class="n">f</span><span class="p">)</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">&#39;.&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">first</span>&#x000A;      <span class="k">end</span>&#x000A;    <span class="k">end</span>&#x000A;    </pre>
            </div>
            
            
            <p>Despite my aversion to using request parameters, it can be handy for ad-hoc
            testing to be able to tack a query parameter on to change the locale. Also,
            once you've determined the locale, you need to notify the <code>I18n</code> framework
            as I've done in the last line of the <code>check_language</code> method.</p>
            
            <h1>The Client-Side</h1>
            
            <p>OK, great. Now we've figure out how to vary the locale on the web-side. How
            do we extract this information from the device and ship it off in our
            HTTP headers?</p>
            
            <p>Here you have two tasks: the first is to determine what the value of the
            <code>Accept-Language</code> header should be and second, to actually make sure you send
            those values. Since there are several ways of making HTTP requests in iPhoneOS
            I'm not going to go into the details. Whether you're using <code>NSURLRequest</code>,
            <a href="http://allseeing-i.com/ASIHTTPRequest/" title="ASIHTTPRequest Documentation - All-Seeing Interactive">ASIHTTPRequest</a>,
            <a href="http://alternateidea.com/blog/articles/2009/7/11/introducing-httpriot-easily-consume-rest-resources-on-the-iphone-and-os-x" title="AlternateIdea:  Introducing HTTPRiot - Easily Consume REST Resources on the iPhone and OS X">HTTPRiot</a>
            or your own homegrown thing, presumably you have
            some reasonable way of setting the <code>Accept-Language</code> header in a consistent
            way.</p>
            
            <p>To figure out what locale your user is in, you need to consult the
            <code>NSLocale</code> class. This provides several class methods that yield a
            <code>NSLocale</code> instance that reflects the user's current settings. If you look
            at the docs, there are three class-methods that return a <code>NSLocale</code> instance:
            <code>+systemLocale</code>, <code>+currentLocale</code> and <code>+autoupdatingCurrentLocale</code>. The
            differences between the three are not apparently obvious and, I think, reflect
            <code>NSLocale</code>'s original desktop heritage.</p>
            
            <p>The <code>+systemLocale</code> method will return a default <code>NSLocale</code> instance when one
            cannot otherwise be determined. In my experience, I've never seen this return
            anything but a blank and useless <code>NSLocale</code> instance. Don't bother using it.</p>
            
            <p>The <code>+currentLocale</code> method returns a <code>NSLocale</code> instance that reflects the
            user's current setting. However there is a bit of language in the docs that
            I initially found confusing:</p>
            
            <blockquote><p>Settings you get from this locale do not change as System Preferences are
            changed so that your operations are consistent. Typically you perform some
            operations on the returned object and then allow it to be disposed of.
            Moreover, since the returned object may be cached, you do not need to hold on
            to it indefinitely.</p></blockquote>
            
            <p><em>Huh?</em> To understand what's going on here, we need to take a look at the docs
            for the <code>+autoupdatingCurrentLocale</code> method:</p>
            
            <blockquote><p>Settings you get from this locale do change as the user’s settings change
            (contrast with currentLocale).</p></blockquote>
            
            <p>Remember, a lot of CocoaTouch came from the desktop Cocoa environment. On the
            desktop you can open the System Preferences and change your region and
            language settings anytime <em>while applications are running.</em> However, until
            iPhone OS 4.0 is released, this is <em>not</em> something you can do on any iPhoneOS
            device. So the net effect, and this matches my own observations, is that on
            iPhone OS 3.x <code>+currentLocale</code> and <code>+autoupdatingCurrentLocale</code> are
            essentially the same. But, if you want to be prepared for the future, go ahead
            and use the <code>+autoupdatingCurrentLocale</code> method. Once you have a <code>NSLocale</code>
            instance, you call the <code>-localeIdentifier</code> method on it to get a <code>NSString</code>
            like <strong>en_US</strong> or <strong>fr_FR</strong>.</p>
            
            <p>So if you're like me, you want to test that this actually works. So, naturally,
            you would figure out how to modify the settings on your phone to yield
            different responses. What you'll soon discover is that the current locale
            settings are derived from two separate and independent settings and that
            various combinations of the two can produce unexpected results.</p>
            
            <p>Start by launching the "Settings" application. Select <em>General ⇢
            International</em>. You'll get a screen where you can pick your language, your
            keyboards and your region format. What may surprise you is that <em>changing the
            language does not affect the current locale.</em> I'm in the United States so my
            region format is set to "United States". If I change my language to French,
            but leave the region format setting alone, all localization within the system
            and applications will use the French language, but my locale remains
            <strong>en_US</strong>.</p>
            
            <p><img src="/images/2010/05/international-iphone.jpg" alt="International Settings on iPhone" /></p>
            
            <p>OK, so <code>NSLocale</code> doesn't quite work as expected, but where does the system
            store the current language? It turns out the <code>NSLocale</code> provides yet another
            class method, named <code>+preferredLanguages</code>, that returns an array of language
            codes as strings. With the settings described above, the first entry in that
            array is "fr".</p>
            
            <p>If you change your settings where you leave the language set to English, but
            change the region format to French (in the "France" region), the current locale
            will now be set to <strong>fr_FR</strong> and the first entry in the <code>preferredLanguages</code>
            array will be "en" for English.</p>
            
            <h1>What It All Means</h1>
            
            <p>Out of the box, Rails doesn't provide full-blown region-specific localization.
            You can either hack something or use one of many <a href="http://rails-i18n.org/wiki" title="Rails I18n">i18n plugins</a>
            or <a href="http://github.com/joshmh/globalize2" title="joshmh's globalize2 at master - GitHub">Globalize2</a>.
            So if you try to send a language-region combination to Rails (such as <strong>en_US</strong>),
            chances are good that you won't have the right localization setup and you'll
            fall-back to your default locale.</p>
            
            <p>You have two choices: you can make the client send something that the server
            understands, or teach the server to respect more fine-grained localization
            values. If you want to go the first route, I would suggest simply querying the
            <code>+preferredLanguages</code> array and using the first entry as the value for your
            <code>Accept-Language</code> header. However if your server-side localization can handle
            it, use the <code>+currentLocale</code> instead so that you can handle regional
            differences and spelling much better.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Visual Affordance in a Touch-Enabled World</title>
      <link>
        http://alexvollmer.com/posts/2010/05/07/visual-affordance-in-a-touch-enabled-world/
      </link>
      <comments>
        http://alexvollmer.com/posts/2010/05/07/visual-affordance-in-a-touch-enabled-world/#comments
      </comments>
      <pubDate>
        Fri, 07 May 2010 10:46:39 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            UX
        ]]>
      </category>
      <category>
        <![CDATA[
            ipad
        ]]>
      </category>
      <category>
        <![CDATA[
            iphone
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2010/05/07/visual-affordance-in-a-touch-enabled-world/
      </guid>
      <description>
        <![CDATA[
            <em>Ooooh</em>, you must be thinking, <em>what a posh title this is!</em> Okay, I'll admit
            to copping a bit of a grandiose attitude when I came up with this, but hear
            me out on this one.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p><em>Ooooh</em>, you must be thinking, <em>what a posh title this is!</em> Okay, I'll admit
            to copping a bit of a grandiose attitude when I came up with this, but hear
            me out on this one.</p>
            
            <p><img src="/images/2010/05/mad-as-hell.jpg" class="left" alt="I was mad as hell
            about a mouse and keyboard until I got an iPad!"/> The iPad has generated a
            lot of criticism, scrutiny and analysis in recent months. The debate about the
            ills and assets of the platform have, and will continue to be, debated
            endlessly. I think one thing is pretty clear though&mdash; whether you like
            Apple or not, whether you have an iPad or not, whether you think it's the most
            awesome-est thing ever or not, it <em>is</em> revolutionary device. I don't mean in
            terms of technical execution (yes the A6 chip is a marvel, the
            power-consumption is amazing, blah blah blah). What I mean is that somebody
            with Apple's clout finally stood up and threw away the last three decades of
            user-interface paradigm <em>and they're laughing all the way to the bank doing
            it</em>.</p>
            
            <p>So, if you jettison The Way We've Always Done Things &trade;, what do you replace
            it with? More importantly, which things from The Old Way are no longer
            applicable to The New Way? This is a big topic that will take some time to
            settle itself. We've had this CPU/mouse/keyboard setup for nearly thirty years
            and we're <em>still</em> learning how to make it work for humans. I don't expect us
            to settle the micro-debates of touch-interface design for quite a while, but
            I do want drill down to something pretty specific.</p>
            
            <p>Take a moment and ponder what the mouse <em>really is</em> in a graphic user
            interface. It's a proxy. It's a loosely-connected device where movement and
            gestures in one space are translated to movement and gestures in another
            virtual space. To operate a mouse well, you have to perform constant mental
            transform operations from the physical world to the world you are trying to
            manipulate on the screen. The mouse is, effectively, a rather thick layer
            between you and what you're trying to do.</p>
            
            <h1>Exploiting the Gap</h1>
            
            <p>As we got better and better with the mouse <sup><a href="#note1">1</a></sup>
            software started to exploit this gap between you and your machine. The first
            place this showed up was in context menus. Once we already had to make a
            mental stop on the train ride from our brain to the machine, why not make the
            station more useful to folks? <em>Hell, let's put in some vending machines,
            and perhaps some couches. We could even install</em> Wi-Fi <em>and people could
            get</em> even more <em>productive.</em> Pile it on, pile it on.</p>
            
            <p>Taken to an extreme, the mouse becomes the primary means by which we
            interact with our machines. If you don't believe me, take a look at any
            professional CAD workstation or that god-awful OpenOffice mouse.</p>
            
            <p><img src="/images/2010/05/context_menu.png" class="right" alt="The ubiquitous,
            right-click-able, context menu"/> However these are extremes. There isn't
            necessarily anything wrong with things like extra mice buttons or context
            menus. But, in a world of touch, where the mental and physical distance
            between our intentions and execution is much smaller, that thick
            layer/opportunity is gone. <em>Context menus?</em> How the hell do I "right-touch"
            the screen? <em>Mouse-overs?</em> Current touch interfaces don't have any proximity
            sensors <sup><a href="#note2">2</a></sup>&mdash;either you touch something or
            you don't. Yet these two very simple interaction models have become a crucial
            part of the UI vocabulary we have all acquired over the years.</p>
            
            <p>Think about a mouse-over. What is it there for? Mouse-overs offer two things:
            a preview mechanism allowing you to learn more about something without having
            to commit to it, and as a backup when a pictorial icon's meaning isn't clear
            enough. We don't have these on the iPad <sup><a href="#note3">3</a></sup>. So
            as an application designer and builder, how do we give people some notion of
            what this thing can do? What kind of <em>affordance</em> can we offer?</p>
            
            <h1>Spelunking</h1>
            
            <p>As you start using and learning an application, you generally have two
            questions:</p>
            
            <ul>
            <li>What can this thing do?</li>
            <li>How can I get this to do ______?</li>
            </ul>
            
            
            <p>These are asked from two opposing angles, but both are about discovering the
            capabilities of the software. When a user doesn't know what your application
            can do, how can you build it in a way that they can discover it? An equally
            important question is how can they explore your application without having to
            commit to any action, particularly a destructive one?</p>
            
            <p><img src="/images/2010/05/nnw-ipad.png" class="left" alt="The 'next unread'
            button in NNW"/> Let's look at one of my favorite iPad apps in particular,
            <a href="http://netnewswireapp.com/ipad/" title="NetNewsWire for iPad &laquo; NetNewsWire">Net News Wire</a>.
            A nice feature of the application is the ability to progress through all of your unread items
            with a single tap. When I first got the app, I <em>knew</em> that there had to be a
            way to do this, but I couldn't find it. There <em>was</em> a mysterious-looking button
            in the corner of the screen, but I wasn't sure what it was for. I mean, it
            might do <em>anything</em>. How was I to know what would happen? It wasn't until I saw a
            <a href="http://seattlexcoders.org/2010/04/20/april-22-meeting---brent-simmons-and-brad-ellis.html" title="Seattle Xcoders - April 22 Meeting - Brent Simmons and Brad Ellis">presentation by Brent Simmons and Brad Ellis on the design of NNW</a> that I
            actually found out where that button was. You can't really figure out what it
            does unless you poke it. If you poke that button, there isn't a corresponding
            "undo" button for that action.</p>
            
            <p>That's not a dig against Brent and Brad, I think they did a wonderful job
            with NNW. But it does highlight how difficult it can be to convey such a
            feature to the user. I think it may be one of the biggest challenges on the
            platform. How do we let the user know what our application can do?</p>
            
            <p>Let's be honest here, if you're relying on documentation to teach the user
            the basics, you've already lost the battle. Documentation is fine for really
            deep, detailed information (see OmniGraffle on the iPad for an example). But
            requiring a user to read the Owner's Manual before they can even use your
            software went out about the same time as floppy disks. On the iPad an
            up-front documentation requirement is just laughable.</p>
            
            <h1>Little Gets Big</h1>
            
            <p>One final thought is that although the iPhone and iPad obviously share the
            same DNA, the difference in size makes this acutely problematic on the iPad.
            On the iPhone you simply can't cram that much stuff into an application. Read
            the iPhone Human Interface Guidelines and you'll quickly get the message that
            on the iPhone, <em>less is more</em>. By and large, the best iPhone apps simply don't
            have that many features integrated into them. This gives application designers
            more freedom to make clear the intent of the features that are implemented.</p>
            
            <p>On the iPad, it's a different story. There's so much more real-estate. Running
            an iPhone application on the iPad is so laughably awkward that it starkly
            highlights just how different the platforms really are. So now, as iPad
            application builders, we have more space than ever. While we're freed from the
            space constraints of the iPhone, we now have a challenge (and responsibility)
            to use it effectively. It's not hard to imagine some developers embracing
            these new green fields to produce some truly awful interfaces. Please, don't
            be one of them.</p>
            
            <h2>Tangents</h2>
            
            <p> <a name="note1"></a>1. Make no mistake here, we've had to <em>train</em>
            ourselves to use the mouse. Just watch your grandparents struggle with a 
            mouse and you'll realize how un-intuitive the device really is.</p>
            
            
            <p></p>
            
            <p><a name="note2"></a>2. I'm not even sure if they did that it would be such a good
            idea. Requiring that level of fine-finger dexterity immediately makes such a
            device exclusive to the young and facile.</p>
            
            
            
            
            <p><a name="note3"></a>3. OK, there <em>is</em> a common trick
            of touching and holding a hyperlink long enough to get a menu of
            options.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>On Technical Presentations</title>
      <link>
        http://alexvollmer.com/posts/2010/04/30/on-technical-presentations/
      </link>
      <comments>
        http://alexvollmer.com/posts/2010/04/30/on-technical-presentations/#comments
      </comments>
      <pubDate>
        Fri, 30 Apr 2010 07:51:51 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            presentations
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2010/04/30/on-technical-presentations/
      </guid>
      <description>
        <![CDATA[
            I spent last weekend at the Seattle version of the <a href="http://www.voicesthatmatter.com/iphone2010/" title="iPhone
            Developers Conference - Using the iPhone OS to build apps for the iPad and
            iPhone">Voices That Matter iPhone
            Developer's conference</a>. The content was great, but aside from John "Wolf" Rentzch's Core
            Data talk, the quality of the slides and presentation was pretty lacking. I'm
            not here to dogpile on those speakers. I still got a lot out of their talks,
            but the experience crystallized some important rules for me about technical
            presentations.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>I spent last weekend at the Seattle version of the <a href="http://www.voicesthatmatter.com/iphone2010/" title="iPhone
            Developers Conference - Using the iPhone OS to build apps for the iPad and
            iPhone">Voices That Matter iPhone
            Developer's conference</a>. The content was great, but aside from John "Wolf" Rentzch's Core
            Data talk, the quality of the slides and presentation was pretty lacking. I'm
            not here to dogpile on those speakers. I still got a lot out of their talks,
            but the experience crystallized some important rules for me about technical
            presentations.</p>
            
            <h2>Size and Geometry</h2>
            
            <p><a href="http://www.flickr.com/photos/laffy4k/405446783/" title="Huntington
            University: Zurcher Auditorium on Flickr - Photo Sharing!"><img src="http://farm1.static.flickr.com/122/405446783_a88c63ce0c_m.jpg"
            class="left"/></a></p>
            
            <p>There is a axiom in military science that says no plan survives contact with
            the enemy. In a similar vein, no slide layout survives contact with an actual
            display screen. As we craft presentations it's easy to get so locked into
            looking at our screens that we often forget about the context of the room in
            which it will be given. There are two important things to consider about the
            room: how far back the audience stretches from the screen and the angle of the
            audience. In a room with a "deep" audience and shallow angle, anywhere from
            the bottom 20% - 30% of the screen will only be visible to the front row. Some
            professional presenters make it a matter of habit to simply avoid the bottom
            quarter of the screen. Think of this as a creative constraint to embrace and
            help you focus your message.</p>
            
            <p>The dimensions of the audience also affect the size of the content in your
            slides —specifically the font size you choose. Somehow font sizes that look
            like billboards on your laptop are diminished to footnotes once they are
            projected. In practice, it's pretty hard to make text too big. If you have a
            problem fitting what you want to say on the slide, perhaps you need to
            reconsider the phrasing.</p>
            
            <p>Aside from direct quotations, I find that it's rarely useful to have full
            paragraphs of text in your slides. You want your audience to be focused on
            <em>you</em>, not your slides. Think of the slides as simply the supporting cast to
            what you're telling the audience verbally. How can you craft your slides to
            complement what you're trying to convey?</p>
            
            <h2>Live Demos</h2>
            
            <p>Slides can only take us so far, especially for technical presentations.
            Sometimes we need to turn away from the "what", and explain the "how" of
            something. We want to demonstrate how something works. For coders, this often
            means a live coding or tool demonstration. If you're trying to convince your
            audience that technology XYZ is the greatest thing since sliced bread and will
            save them all sorts of time and headaches, you want to be able to <em>show</em> it,
            not just tell it.</p>
            
            <p>Fantastic. Good for you. Here's the problem: <em>live</em> coding demos are a recipe
            for disaster. I can't think of a better scenario that demonstrates Murphy's
            law than trying to execute a live coding demo. You may not have an internet
            connection (oops, there goes the ubiquitous twitter client demo). You may have
            installed some beta software on the plane ride out and, unknowingly, destroyed
            your development environment. The list of things that could go wrong is
            unbounded.</p>
            
            <p>The other problem with live demos is the switching in and out of the
            presentation software you're using. Not only is it visually jarring for the
            audience, but there is invariably some down-time as you switch displays and
            try to figure out which windows are where. Don't give your audience the
            opportunity to tune you out during these noisy transitions.</p>
            
            <p>So, just like you wouldn't create your slides on-the-fly as you present, you
            shouldn't demonstrate live without a net. Instead, record a video of it and
            embed it in your presentation. With a video you get a chance to edit out all
            of the hiccups and noise of the demonstration process. Mistyped something? Got
            a compilation error? Tools are running really slow for some reason? Great,
            just edit those frames out.</p>
            
            <p>Leaving these blemishes in only gives the third-grader part of your audience's
            brain time to sneak in and distract them. You want to keep your audience
            engaged with you every step of the way. Introducing these interruptions into
            the continuity of your presentation breaks the audience's concentration and your
            flow.</p>
            
            <p>Once you get your video captured and edited, practice speaking over it. Really
            make sure that what you say matches well with the video. Get the timing down
            and stay focused on what you're trying to show. Is there something you want to
            say that you isn't in the video? Get back in there and record the right video.
            Don't half-ass it. When you put the time in to make a good supporting video,
            you keep your audience longer.</p>
            
            <h3>Keynote Specifics</h3>
            
            <p><img src="/images/2010/04/keynote-video.png" alt="Video prefs in Keynote" class="right"/></p>
            
            <p>If you're using Apple's Keynote, here are a couple of tips. Once you've
            captured and edited your video, simply drag the video object onto a new slide.
            Open the inspector palette and select the last tab with the Quicktime logo on
            it. Select the checkbox labeled "Start movie on click". This way your video
            won't start running before you're ready to talk about it.</p>
            
            <div class="clear"></div>
            
            
            <p><img src="/images/2010/04/apple-remote.jpg" class="left"/> Also, with a stock
            Apple remote, you can pause and resume your video as needed. This is
            especially helpful when you have a multi-step process to explain that you want
            to cover in bite-sized chunks. This means that you should consider
            pause-points in your capture and editing process. Practicing your speech along
            with the video will help you figure out where the natural breakpoints are.
            Alternatively, you could split the process up into multiple videos on
            different slides.</p>
            
            <h2>Eschew Style</h2>
            
            <p>Customization and personal style are well and good. On your machine you should
            feel free to tweak every little setting to your heart's content. But, please,
            don't force these styles on your audience. Ditch the alpha blend on your
            terminal. Turn off your crazy four-line shell prompt. Pick a color theme in
            your editor that is easy to read in a large font. Pick a display font that is
            appropriate for large projection, not just what you prefer.</p>
            
            <p><img src="http://ecx.images-amazon.com/images/I/51toYiHF35L._SL500_AA300_.jpg" height="150" width="150" class="left"/></p>
            
            <p>Make those fonts BIG. Just like your regular speaking voice needs to be
            amplified to carry to the back of the room, so does the visual volume of your
            content. When recording your coding demos, pick unusually large font sizes and
            take up all of the screen. Also, when capturing your video, try to keep the
            capture frame focused on the essential parts of the demo. For example, if
            you're only editing text, don't clutter the video capture up with a toolbar
            full of things you won't use. Remember, keep it focused! Don't give your
            audience a chance to get distracted by clutter.</p>
            
            <div class="clear"></div>
            
            
            <h2>Keep It Focused</h2>
            
            <p>When presenting a technical topic, it can be frustrating having to jam a large
            amount of content into a small space. There's just so much to share! How can I
            possibly discuss Core Animation in forty-five minutes? The answer is, <em>you
            don't</em>. What you <em>can</em> do is give your audience a taste of the topic you're
            trying to present. Give them enough information for them to decide if they
            want to pursue more on their own. Give them enough terminology and background
            concepts to help them continue the journey.</p>
            
            <p>Presentations are <em>not</em> a good vehicle for in-depth training. Books or
            workshops are much more appropriate for real hands-on learning. Presentations
            are good at giving people a mental roadmap to get started. Get them excited
            by your topic, but don't exhaust them with it.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Menu Bar Ghetto</title>
      <link>
        http://alexvollmer.com/posts/2010/04/23/menubar-ghetto/
      </link>
      <comments>
        http://alexvollmer.com/posts/2010/04/23/menubar-ghetto/#comments
      </comments>
      <pubDate>
        Fri, 23 Apr 2010 08:49:57 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            mac
        ]]>
      </category>
      <category>
        <![CDATA[
            UX
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2010/04/23/menubar-ghetto/
      </guid>
      <description>
        <![CDATA[
            The menu bar has become a dumping ground. With each new application I install
            another grubby child is dropped off at the doorstep of the orphanage known
            as the OS X menu bar. This has simply got to stop.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>The menu bar has become a dumping ground. With each new application I install
            another grubby child is dropped off at the doorstep of the orphanage known
            as the OS X menu bar. This has simply got to stop.</p>
            
            <p><img src="/images/2010/04/status-bar.png"/></p>
            
            <p>Just look at that. Do you think that fits on anything but a large display? I
            can't even see most of these icons on my built-in MacBook display. Only
            humble little Finder, with its spartan set of menus, yields enough real
            estate to display all of these items.</p>
            
            <p>Sadly, I had gotten into the habit of knowing which applications hid status
            bar items from me and would automatically switch to Finder just to reveal
            them. It was the classic mistake we all make of giving in to bad design
            instead of addressing the problem.</p>
            
            <p>So I waged a small holy-war this morning to kick out all but the essential
            applications from my status bar. I feel a little better that I've reclaimed
            my territory, but can't help but feel frustrated that I'll have to do it
            again soon. Why? Because, somehow, application developers got it in their
            heads that they <em>all</em> have to have menu bar items.</p>
            
            <p>I want this to stop. Right now. Please. Do it for the children.</p>
            
            <p>I can't help but think that this reflects a dangerous attitude underlying a
            lot of applications. Stop for a moment and think of the reasons why an
            application provides a menu bar item. Menu bar items exist for applications
            running in the background. These come in two flavors: applications with no
            other user interface (like a window or a preference pane), and ones that do.I
            don't have much of a beef for the ones that can only provide a menu bar item
            as their sole interaction mechanism. It's the applications that <em>do</em> have a
            main UI that I want to give a stern talking-to.</p>
            
            <p>In a windowing system users get to choose what they focus on (save for a few
            cases with alerts and modal dialogs and such). Applications are supposed to do
            the bidding of the user. When I switch away from application XYZ, it means I
            have something more important I need to do. Having that application pop back
            in my face drives me nuts. It drives everyone nuts. I think most folks would
            agree that this is a Bad Experience.</p>
            
            <p>But some applications want to keep your attention. So they compromise by
            creating a little persistent bit of goo and sticking it in the menu bar.
            Because these apps are <em>so fabulous</em>, how could you not want them around all the
            time?</p>
            
            <p>The best menu bar items are ones that provide the essential capabilities of
            the application <em>and</em> give you a quick way to expand to the full application.
            I run iStat menus because it gives me a lot of information in the small
            space it takes up. Why is my machine so slow? Ah, the CPUs are pegged. Why
            are they pegged? Well, just click the menu item and see that application XYZ
            is taking up 90% of my cycles. It's a great example progressive disclosure.</p>
            
            <p>Some applications like <a href="http://www.shinywhitebox.com/home/home.html" title="iShowU">iShowU</a> or <a href="http://www.realmacsoftware.com/littlesnapper/" title="Little Snapper">Little Snapper</a> provide a menu bar item because they need to get out of your
            way. You don't want to screen capture the screen capture tool itself, right?
            For these kinds of apps that provide functionality that spans multiple
            applications, I think this is okay.</p>
            
            <p>What I can't stand are applications that provide a menu bar item, then keep it
            there after I've quit the application. <a href="http://www.evernote.com" title="Evernote">Evernote</a> is a good example of this. You have to <em>really</em> go out of your way
            to make Evernote not do this. Worse yet, its default behavior is to install
            itself as a startup item so that it's sitting there in your menu bar until you
            take time out of your busy day to turn it off. Boo. Hiss.</p>
            
            <p>I like Evernote quite a bit, but I get the impression its creators think it's
            so special that it deserves to be in your face, all the time. It's like that
            annoying guy you got stuck talking to at the company party that you can't rid
            yourself of. Go away.</p>
            
            <p>The worst case of this is VMWare. I can't, for the life of me, figure out how
            to disable VMWare's menu bar item. For folks that need to run a lot of non-Mac
            apps I can see the advantage. The Unity stuff in VMWare is pretty freaking
            cool. But if you're <em>not</em> one of those people, it's pretty rude to have this
            whole other application-launcher sitting there, taking up space, that you only
            occasionally use. Shame on you VMWare. I'm happy to pay for a license because
            I think it's a great product, but that doesn't mean I want to see your
            goddamned menu item in my status bar all the time.</p>
            
            <p>So if you're building a Mac application and considering a menu bar item,
            please consider the following advice. If you're building a background utility
            with no other UI, think hard about how much the user needs to interact with
            your application. Is it something they need to fiddle with more than once a
            session? If not, try to make a Preference Pane and leave it at that. If it's
            something the user is going to use a lot, or displays some kind of realtime
            information (à la iStat menus) then adding to the menu bar is okay.</p>
            
            <p>If you're building an application with a main UI component, think long and
            hard about the necessity of menu bar item. If it can integrate with other
            applications (like Little Snapper) there's a reasonable case to be made for
            providing a menu bar item. Even then, you should consider providing a Services
            menu item and/or a global keyboard shortcut. This is an especially good idea
            if your application only provides one or two actions that users can take. It's
            hard to justify a two-item menu bar item, so sometimes you see applications
            put other non-essential items in there like Preferences or an About dialog.
            Don't do this.</p>
            
            <p>For these sorts of applications, the menu bar item should <em>always</em> be
            optional. The app should still be able to do what it needs to without having
            that menu bar item enabled. Also, leave it off by default and make it easy for
            the user to turn it on. Make it an opt-in feature rather than an opt-out
            feature. As much as I love Tweetie, the opt-out nature of its menu bar item is
            the kind of stuff that drives me nuts. The option is enabled (or not
            disabled?) and the language is confusing. Worse yet, it doesn't take effect
            until you restart the application.</p>
            
            <p><img src="/images/2010/04/tweetie-prefs.png"/></p>
            
            <p>Finally, unless you have some kind of special papal dispensation, never, ever,
            ever keep your menu bar item running when the application is gone. OK,
            technically if there's a menu bar item the application is still running, but
            that's a distinction that user's don't care about (nor should they have to).</p>
            
            <p>There's been a long, ongoing debate about the necessity of optimizing the
            performance of applications. As horsepower has increased, programmers have
            been afforded some laziness in optimizing their applications. I only have a
            problem with that in extreme cases because hardware power has increased at
            such an impressive rate. However, our displays aren't growing at the same
            rate. We need to treat that screen real estate as precious. The menu bar is a
            great place to put global, cross-application functionality, but there's only
            so much space for all of these applications. Think long and hard about the
            necessity of taking up that space and always put the power in the users' hands
            to choose.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Thanks, mom</title>
      <link>
        http://alexvollmer.com/posts/2010/04/17/thanks-mom/
      </link>
      <comments>
        http://alexvollmer.com/posts/2010/04/17/thanks-mom/#comments
      </comments>
      <pubDate>
        Sat, 17 Apr 2010 20:23:21 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            personal
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2010/04/17/thanks-mom/
      </guid>
      <description>
        <![CDATA[
            It's been a while since I last posted. Normally I wouldn't preface a post with
            that (it drives me crazy when I read it elsewhere), but my world changed
            completely last month when, on March 5th, my mom unexpectedly passed
            away. I found out the following morning, and things haven't been the same
            ever since.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>It's been a while since I last posted. Normally I wouldn't preface a post with
            that (it drives me crazy when I read it elsewhere), but my world changed
            completely last month when, on March 5th, my mom unexpectedly passed
            away. I found out the following morning, and things haven't been the same
            ever since.</p>
            
            <p><img class="left" src="/images/2010/04/rosebowl.jpeg" title="My last day with mom at the 2010 Rose Bowl"/></p>
            
            <p>Since her death, I've been tossed into the deep-end of estate-management as
            I try to clean up the remnants of her financial and legal life. It's amazing
            how much a person leaves behind. There are all sorts of things to clean up like
            calling her dentist and canceling a pending appointment, or telling the local
            pharmacy that she won't ever be coming in again to pick up her prescription.</p>
            
            <p>In the last month, I've realized I'm not the same person I used to be. It sounds
            trite, but this event has given me a serious perspective
            adjustment. Things that used to seem scary to me now seem insignificant.
            Things I always wanted to do but put off for some day in the future, get
            addressed now. Life is short. You never know what can happen.</p>
            
            <p>By nature, my wife and I inveterate planners. My god, I started an IRA when
            I was 21. Who the hell does that? But now it's time to think more about
            the present. We haven't completely abandoned our careful planning, but we've
            stopped putting off things we want to do.</p>
            
            <p>One of those things has been a growing desire to be my own boss. For
            the longest time I could never imagine running my own show. There was
            too much work, stress and responsibility. I was happy trading independence for
            the comfort and security of a steady paycheck. But over the last few years
            I've been feeling a deep and acute dissatisfaction working for someone else.
            I came to the conclusion that I wasn't going to be happy until I stopped
            working on other people's ideas and took a shot (or two) at working on my own.</p>
            
            <p>So now it's time to stop being so unhappy about it. As of May, I'm on my own.
            My plan is to build a small product-development company. I don't need to make
            scads of money, I just want to support my family. I want to work on things that
            I deeply care about. I want to spend my time doing things that matter to me,
            and as little time as possible on "shoulds", "musts", and "have-tos". I'll
            pick up contracting work as needed (tell all your friends),
            but the long-term plan is to live off of product revenue. I guess some people
            might call it a "lifestyle business". I call it dropping out of the rat-race
            and living the life more fully.</p>
            
            <p>How am I going to do this? What's my plan? Beats the hell out of me. I'll
            figure it out as I go. All I know is that everything I've gone through in the
            last month has made me realize that it's going to work out fine. There's
            nothing that could happen that would equal what I've recently gone through.</p>
            
            <p>So what about the title of this post? What exactly am I so thankful for?
            Well, certainly not about the loss of my mother. Nothing could replace her.
            Nothing will bring her back. She was my moral compass. She was the greatest
            teacher I ever had. I didn't really appreciate the tools she left me with
            until she was gone. Now I realize that she left me with everything I needed
            to cope with this tragedy as well as being able to move beyond it. I don't
            know if I would ever have been shaken out of my day-to-day routine without her
            loss. I wouldn't trade this epiphany for her, but I don't have that option.
            What I can do is move forward and live a life that honors her memory.</p>
            
            <p>My mom was a survivor. She survived the breakup of my family. She survived
            cancer. She survived my brother's death. After her divorce she rebuilt her
            life from scratch, eventually becoming an assistant Dean at the School of
            Education at the University of Oregon. Her crowning achievement was completion
            of school's new building on the U of O campus. Over the span of nearly a decade
            she raised funds from private donors&mdash;mostly teachers. But the final
            dollar amount isn't what's impressive about her work. What's impressive is
            how she took a group of people, turned them into a community and got them to
            believe in themselves. Mom, I wish I could have told you just how proud I am
            of you.</p>
            
            <p>So, mom, this life is for you. You always had a strong independent streak. You
            didn't get a chance to enjoy the future that you worked so hard for. You
            gave me so much. The best way I can think to pay it back, is to live the life
            you would have wanted for me, and to pass that on to my daughter.</p>
            
            <p>Thanks, mom. Thank you, so very much.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Scripting Build Selection in Xcode</title>
      <link>
        http://alexvollmer.com/posts/2010/03/06/scripting-build-selection-in-xcode/
      </link>
      <comments>
        http://alexvollmer.com/posts/2010/03/06/scripting-build-selection-in-xcode/#comments
      </comments>
      <pubDate>
        Sat, 06 Mar 2010 00:20:30 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            iphone
        ]]>
      </category>
      <category>
        <![CDATA[
            xcode
        ]]>
      </category>
      <category>
        <![CDATA[
            applescript
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2010/03/06/scripting-build-selection-in-xcode/
      </guid>
      <description>
        <![CDATA[
            When I'm doing iPhone development, I constantly
            switch between running on the device and the simulator. As far
            as I can tell, Xcode doesn't provide an easily-accessible keyboard
            shortcuts for switching between SDKs. Since I <em>hate</em> breaking up my flow by
            having to reach for the mouse, I put together a couple of
            AppleScripts that bind hot-keys to switching between SDKs. Download
            these two scripts to wherever you like to store your AppleScripts.
            I like to put mine in <code>~/Library/Scripts</code>.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>When I'm doing iPhone development, I constantly
            switch between running on the device and the simulator. As far
            as I can tell, Xcode doesn't provide an easily-accessible keyboard
            shortcuts for switching between SDKs. Since I <em>hate</em> breaking up my flow by
            having to reach for the mouse, I put together a couple of
            AppleScripts that bind hot-keys to switching between SDKs. Download
            these two scripts to wherever you like to store your AppleScripts.
            I like to put mine in <code>~/Library/Scripts</code>.</p>
            
            <p>First, is the script that selects the SDK for the device:</p>
            
            <div>
            <a href="/files/SwitchToDevice.scpt" class="attachment">
              SwitchToDevice.scpt
            </a>
            
            
            <div class="highlight"><pre><span class="k">tell</span> <span class="nb">application</span> <span class="s2">&quot;Xcode&quot;</span>&#x000A;      <span class="k">set</span> <span class="nv">myProject</span> <span class="k">to</span> <span class="na">active</span> <span class="nv">project</span> <span class="na">document</span>&#x000A;      <span class="k">set</span> <span class="na">active</span> <span class="nv">SDK</span> <span class="k">of</span> <span class="nv">myProject</span> <span class="k">to</span> <span class="s2">&quot;iphoneos3.1.3&quot;</span>&#x000A;    <span class="k">end</span> <span class="k">tell</span>&#x000A;    </pre>
            </div>
            
            </div>
            
            
            <p>Next up, the script that selects the simulator SDK:</p>
            
            <div> 
            <a href="/files/SwitchToSimulator.scpt" class="attachment">
              SwitchToSimulator.scpt
            </a>
            
            
            <div class="highlight"><pre><span class="k">tell</span> <span class="nb">application</span> <span class="s2">&quot;Xcode&quot;</span>&#x000A;      <span class="k">set</span> <span class="nv">myProject</span> <span class="k">to</span> <span class="na">active</span> <span class="nv">project</span> <span class="na">document</span>&#x000A;      <span class="k">set</span> <span class="na">active</span> <span class="nv">SDK</span> <span class="k">of</span> <span class="nv">myProject</span> <span class="k">to</span> <span class="s2">&quot;iphonesimulator3.1.3&quot;</span>&#x000A;    <span class="k">end</span> <span class="k">tell</span>&#x000A;    </pre>
            </div>
            
            </div>
            
            
            <p>Now, open up Xcode and and select "Edit User Scripts..." in the
            scripts menu:</p>
            
            <p><img src="/images/2010/02/xcode-script-menu.png" class="plain"/></p>
            
            <p>This opens the script organizer. In the left-hand pane is a list of
            all the script groups. Which group you put a script in effects what
            submenu they are available from in the main scripts menu. But we like
            shortcut keys so who cares where it goes? I put these in the "Build"
            group, but feel free to put them wherever makes the most sense to
            you. Select the group you want to add these scripts to and click the "+"
            button and choose "Add Script File&hellip;":</p>
            
            <p><img src="/images/2010/02/edit-user-scripts-dropdown.png" class="plain"/></p>
            
            <p>Use the standard Finder dialog to find the scripts you just downloaded
            and select one of them. To set the shortcut key for the script,
            double-click the right-hand column of the newly-added script. For the
            simulator script I use <code>⌃⌘⎇S</code> and <code>⌃⌘⎇D</code> for the device script. You
            can try out different combinations. If you come up with a key-chord
            that clobbers an existing key-binding Xcode will give you a warning at
            the bottom of the window.</p>
            
            <p><img src="/images/2010/02/edit-user-scripts-warning.png" class="plain"/></p>
            
            <p>One thing to be aware of is that while you're setting the shortcut
            key for the script <em>all</em> of your keyboard input is directed to that
            field. So you can't use ESC to cancel or Enter to set the value. You
            need to grab that infernal mouse and click somewhere else.</p>
            
            <p>Before you close the dialog, be sure to set the "Output" drop-down
            menu to "Discard Output" otherwise Xcode will paste some odd gibberish
            into your current file. And, just in case there's some kind of
            unforseeable error, set "Errors" drop down to "Display in Alert". Once
            you have both scripts configured you can close the dialog and try your
            new hot-keys out.</p>
            
            <p>I don't claim to have any proficiency with AppleScript, so there
            may very well be a better way to do this. Certainly one thing that is
            brittle about these scripts is how they are hard-coded to a particular
            version of the SDK. Whenever a new SDK is released I'll have to update
            the strings in the script. Oh well, it beats having to reach for the
            mouse several more times a day.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>RVM meets zsh</title>
      <link>
        http://alexvollmer.com/posts/2010/02/27/rvm-meets-zsh/
      </link>
      <comments>
        http://alexvollmer.com/posts/2010/02/27/rvm-meets-zsh/#comments
      </comments>
      <pubDate>
        Sat, 27 Feb 2010 17:19:45 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            zsh
        ]]>
      </category>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2010/02/27/rvm-meets-zsh/
      </guid>
      <description>
        <![CDATA[
            <a href="http://rvm.beginrescueend.com/">RVM</a> is a nifty tool for managing
            multiple ruby installations. Not only does it make it easy to install
            and switch between multiple rubies, but you can also install
            gems without <code>sudo</code> access. But, as great as RVM is, I still have to
            remember to switch between rubies and often I don't remember to do it
            until I see some weird behavior or a test breaks. I'm very lazy and
            I want even <em>more</em>.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p><a href="http://rvm.beginrescueend.com/">RVM</a> is a nifty tool for managing
            multiple ruby installations. Not only does it make it easy to install
            and switch between multiple rubies, but you can also install
            gems without <code>sudo</code> access. But, as great as RVM is, I still have to
            remember to switch between rubies and often I don't remember to do it
            until I see some weird behavior or a test breaks. I'm very lazy and
            I want even <em>more</em>.</p>
            
            <p>So I've setup a tiny bit of zsh-fu to automatically switch which ruby
            implementation to use based on the directory that I'm in. To do this, one
            needs to take advantage of <a href="http://zsh.sourceforge.net/Doc/Release/Functions.html#SEC45">zsh's
            hooks</a>,
            particular the one invoked when you change directories. By default,
            you can simply declare a function named <code>chdir()</code> and it will
            automatically be invoked whenever you change directories. Mine looks
            like this:</p>
            
            <div class="highlight"><pre>chpwd<span class="o">()</span> <span class="o">{</span>&#x000A;        <span class="o">[[</span> -t 1 <span class="o">]]</span> <span class="o">||</span> <span class="k">return</span>&#x000A;    <span class="k">    case</span> <span class="nv">$TERM</span> in&#x000A;          sun-cmd<span class="o">)</span> print -Pn <span class="s2">&quot;\e]l%~\e\\&quot;</span>&#x000A;            ;;&#x000A;          *xterm*|rxvt|<span class="o">(</span>dt|k|E<span class="o">)</span>term<span class="o">)</span> print -Pn <span class="s2">&quot;\e]2;%3~\a&quot;</span>&#x000A;            ;;&#x000A;        <span class="k">esac</span>&#x000A;    <span class="o">}</span>&#x000A;    </pre>
            </div>
            
            
            <p>This function just prints out the name of the directory I've change
            into and it's found in a lot of zsh distributions. To keep things clean
            I want to have a separate function that just manages the RVM
            updating. For the moment, let's ignore the fact that I already have an
            existing <code>chdir()</code> function, and look at the hook function I created
            to invoke RVM:</p>
            
            <div class="highlight"><pre>chpwd_check_rvm<span class="o">()</span> <span class="o">{</span>&#x000A;        <span class="nv">current_version</span><span class="o">=</span><span class="k">$(</span>rvm info | grep <span class="s2">&quot; version:&quot;</span> | cut -d <span class="s1">&#39;&quot;&#39;</span> -f2<span class="k">)</span>&#x000A;        <span class="nv">dir</span><span class="o">=</span><span class="k">$(</span><span class="nb">pwd</span><span class="k">)</span>&#x000A;        <span class="k">while</span> <span class="o">[</span> <span class="s2">&quot;${dir}&quot;</span> !<span class="o">=</span> <span class="s2">&quot;&quot;</span> <span class="o">]</span>; <span class="k">do</span>&#x000A;    <span class="k">        </span><span class="nv">cfg</span><span class="o">=</span><span class="s2">&quot;${dir}/.rvminfo&quot;</span>&#x000A;            <span class="k">if</span> <span class="o">[</span> -f <span class="k">${</span><span class="nv">cfg</span><span class="k">}</span> <span class="o">]</span>; <span class="k">then</span>&#x000A;    <span class="k">            </span><span class="nv">want_version</span><span class="o">=</span><span class="k">$(</span>cat <span class="k">${</span><span class="nv">cfg</span><span class="k">})</span>&#x000A;                <span class="k">if</span> <span class="o">[</span> <span class="s2">&quot;${want_version}&quot;</span> !<span class="o">=</span> <span class="s2">&quot;${current_version}&quot;</span> <span class="o">]</span>; <span class="k">then</span>&#x000A;    <span class="k">                </span>rvm use <span class="k">${</span><span class="nv">want_version</span><span class="k">}</span>&#x000A;                <span class="k">fi</span>&#x000A;    <span class="k">            </span><span class="nb">break</span>&#x000A;    <span class="nb">        </span><span class="k">else</span>&#x000A;    <span class="k">            </span><span class="nv">dir</span><span class="o">=</span><span class="k">${</span><span class="nv">dir</span><span class="p">%/*</span><span class="k">}</span>&#x000A;            <span class="k">fi</span>&#x000A;    <span class="k">    done</span>&#x000A;    <span class="o">}</span>&#x000A;    </pre>
            </div>
            
            
            <p>This function looks for a file named <code>.rvminfo</code> in the current
            directory (this function is invoked <em>after</em> we've changed
            directories). If the file isn't found in the new directory, it searches
            upward through each parent directory to see if it can find one. If
            it's found, the function invokes <code>rvm use</code> with the version string
            found in that file.</p>
            
            <div class="highlight"><pre><span class="nv">$ </span>cat .rvminfo&#x000A;    1.8.6&#x000A;    </pre>
            </div>
            
            
            <p>There's also some additional checking to avoid extra <code>rvm</code> invocations
            if we're already set to the desired version. This is done more to
            reduce the chatter in the shell than for performance reasons.</p>
            
            <p>Now let's get back to invoking multiple hook functions.
            If you have more than one function you'd like to have called when
            you change directories, you can declare these in an array named
            <code>chpwd_functions</code>. After declaring the <code>chpwd_check_rvm()</code> function
            above, having two functions for the <code>cd</code> hook is trivial. I just add
            this declaration to my <code>~/.zshrc</code> file:</p>
            
            <div class="highlight"><pre><span class="c">#--- chpwd_functions</span>&#x000A;    <span class="nv">chpwd_functions</span><span class="o">=(</span> chpwd_check_rvm chpwd <span class="o">)</span>&#x000A;    </pre>
            </div>
            
            
            <p>Now, whenever I change into a directory with a <code>.rvminfo</code> file, my
            hooks are automatically executed and I get the right ruby version:</p>
            
            <div class="highlight"><pre><span class="o">[</span>island<span class="o">]</span> ~/Development: <span class="nb">cd </span>alexvollmer.com &#x000A;    &lt;i&gt; Now using ruby 1.8.6 p383 &lt;/i&gt;&#x000A;    </pre>
            </div>
            
            
            <p>One final note is that I put all of my specific functions in the
            <code>~/.zsh/functions</code> directory, and automatically load all of them in my
            <code>~/.zshrc</code> file with the snippet below. I do all of this <em>before</em> I
            declare the <code>chpwd_functions</code> array.</p>
            
            <div class="highlight"><pre><span class="c">#--- Shell Functions ---</span>&#x000A;    <span class="c">#</span>&#x000A;    fpath+<span class="o">=(</span>&#x000A;      <span class="k">${</span><span class="nv">HOME</span><span class="k">}</span>/.zsh/functions&#x000A;    <span class="o">)</span>&#x000A;    &#x000A;    autoload -U ~/.zsh/functions/*<span class="o">(</span>:t<span class="o">)</span>&#x000A;    </pre>
            </div>
            
            
            <p>You may want to organize your functions differently. Just be aware
            that zsh needs to know about your functions before you can put them in
            the <code>chpwd_functions</code> array.</p>
            
            <p><em>UPDATE (3/1/2010):</em> Per comments below from RVM's creator, Wayne Seguin, RVM
            actually comes with it's own built-in way of handling this. Somehow I
            completely missed <a href="http://rvm.beginrescueend.com/workflow/rvmrc/">the rvmrc
            documentation</a> for this
            feature (how embarassing!). I'm usually inclined to avoid re-inventing
            the wheel, but there are two small things I like about the way I
            implemented this. First, the solution proposed here doesn't redefine
            the <code>cd</code> command, which feels a bit like duck-punching the shell to
            me. I don't know if bash has the same kind of hooks that zsh does, so
            the implementation is understandable. As for me, I like the hooks
            better.  Second, the solution I show here works for any sub-directory
            within a project that has a specific RVM setting. That said, I'll
            probably just use what's already been implemented in RVM.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Static Cling</title>
      <link>
        http://alexvollmer.com/posts/2010/02/22/static-cling/
      </link>
      <comments>
        http://alexvollmer.com/posts/2010/02/22/static-cling/#comments
      </comments>
      <pubDate>
        Mon, 22 Feb 2010 17:40:40 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            blog
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2010/02/22/static-cling/
      </guid>
      <description>
        <![CDATA[
            Lord knows why, but in the last couple of months I felt compelled to
            move off of Wordpress and onto one of those new static content
            generators for this site. Part of it was motivated by getting fed up
            with constantly upgrading Wordpress. I suppose another part of it was
            because all the <a href="http://userprimary.net">cool</a>
            <a href="http://davepeck.org/">kids</a> were doing it. But, most importantly,
            I wanted to give the site design a reboot&mdash;something I did <em>not</em>
            want to do in PHP in the form of Wordpress themes. So with a little
            help from <a href="http://whole-studios.com/">a friend</a>, this site got a
            facelift and a new platform to boot.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>Lord knows why, but in the last couple of months I felt compelled to
            move off of Wordpress and onto one of those new static content
            generators for this site. Part of it was motivated by getting fed up
            with constantly upgrading Wordpress. I suppose another part of it was
            because all the <a href="http://userprimary.net">cool</a>
            <a href="http://davepeck.org/">kids</a> were doing it. But, most importantly,
            I wanted to give the site design a reboot&mdash;something I did <em>not</em>
            want to do in PHP in the form of Wordpress themes. So with a little
            help from <a href="http://whole-studios.com/">a friend</a>, this site got a
            facelift and a new platform to boot.</p>
            
            <p>I chose to use <a href="http://nanoc.stoneship.org">Nanoc</a> as the
            site-generator. Originally I was going to go with
            <a href="http://github.com/mojombo/jekyll">Jekyll</a>. I really liked its
            simplicity and the fact that it had prescribed locations for
            everything. However, I still wanted to maintain some
            backward-compatability with the old site. This meant a little bending
            of Jekyll's rules and, frankly, Jekyll just isn't as configurable and
            flexible as Nanoc. That's not a dig against Jekyll, nor is it an
            endorsement of Nanoc. Nanoc is a general-purpose solution for
            generating static sites, but it's not necessarily optimized for
            blogging. But with Nanoc I can use HAML, SASS and other Rails-isms
            with which I'm already familiar. If you want to see the gory details,
            I've put the whole thing up on
            <a href="http://github.com/alexvollmer/alexvollmer.com">GitHub</a>.</p>
            
            <p>Sadly, this ended up taking much longer than I anticipated.
            First, I had to import the old posts from Wordpress. The database
            schema for Wordpress is pretty wacky looking when you're used to
            working with Rails. Thankfully, Jekyll already gone down this path and
            I used their Wordpress importer as inspiration for writing my
            own. Along the way I discovered what great gem
            <a href="http://rubygems.org/gems/sequel">Sequel</a> is. A large part of the
            importing process was converting from all of the jacked-up formats
            I've amassed over the years to Markdown. It took a <em>lot</em> of iterations
            to catch every weird character-encoding and formatting issue.</p>
            
            <p>Another large effort was creating the RSS feeds. Now Nanoc has <em>some</em>
            support for Atom, but it didn't quite do what I wanted. I was trying
            to match what Wordpress was generating for me so I basically
            re-implemented feed-generation from scratch.</p>
            
            <p>Moving old comments into <a href="http://disqus.com">Disqus</a> took about
            a week to get right. As a user, the Disqus service is pretty
            cool. As a web API, it's a little&hellip;ahem&hellip; lacking. If
            you're used to well-constructed REST APIs, you'd best drink something
            stiff before settling in with the <a href="http://groups.google.com/group/disqus-dev/web/api-1-1">API
            docs</a>. The
            <a href="http://github.com/norman/disqus">disqus gem</a> borders on useless. So,
            I ended having to write my own importer with httparty. It took several
            iterations to really get this nailed and the process was pretty
            frustrating.</p>
            
            <p>The sad part about this, is that originally I was on the fence about
            adding comments at all. Initially I thought I'd try it out and see if
            I liked it. But I'm a geek, right? I couldn't get it working right
            away and it drove me nuts! I <em>had</em> to figure it out. So eventually I
            figured out the magic recipe, and now I'm wondering if I even really
            wanted comments integrated at all. Oh well.</p>
            
            <p>One trick that I found very helpful throughout this process was
            creating some scripts that dropped me into an IRB session with the
            Nanoc blog or Disqus API objects already initialized. This was
            inspired by the Rails console which I find invaluable for tinkering with
            things in real-time. For example, while I was dickering with the Nanoc
            internals, I found this console script to be quite helpful:</p>
            
            <div class="highlight"><pre><span class="c1">#!/usr/bin/env ruby</span>&#x000A;    &#x000A;    <span class="nb">require</span> <span class="s2">&quot;irb&quot;</span>&#x000A;    <span class="nb">require</span> <span class="s2">&quot;rubygems&quot;</span>&#x000A;    <span class="nb">require</span> <span class="s2">&quot;nanoc3&quot;</span>&#x000A;    <span class="nb">load</span> <span class="s2">&quot;lib/helpers.rb&quot;</span>&#x000A;    &#x000A;    <span class="vi">@site</span> <span class="o">=</span> <span class="no">Nanoc3</span><span class="o">::</span><span class="no">Site</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">&quot;.&quot;</span><span class="p">)</span>&#x000A;    <span class="vi">@site</span><span class="o">.</span><span class="n">load_data</span>&#x000A;    &#x000A;    <span class="no">IRB</span><span class="o">.</span><span class="n">start</span>&#x000A;    </pre>
            </div>
            
            
            <p>Now, Wordpress and I differ on what constitutes a sensible URL
            scheme. So that last bit I needed was to concoct some redirect rules
            to port the old URL scheme to the new one. Currently, this site is
            hosted on <a href="Dreamhost">http://dreamhost.com</a> which, provides support
            for <code>.htaccess</code> files. With a little Apache-fu I was able to get most
            of the old WP URLs to redirect correctly:</p>
            
            <div class="highlight"><pre>RewriteEngine ON&#x000A;    RewriteRule ^index.php/feed/ /feeds/rss_2_0.xml <span class="o">[</span>R<span class="o">]</span>&#x000A;    RewriteRule ^index.php/feed/rss/ /feeds/rss_0_9_2.xml <span class="o">[</span>R<span class="o">]</span>&#x000A;    RewriteRule ^index.php/feed/atom/ /feeds/atom.xml <span class="o">[</span>R<span class="o">]</span>&#x000A;    RewriteRule ^index.php/<span class="o">(</span>.*<span class="o">)</span> /posts/<span class="nv">$1</span> <span class="o">[</span>R<span class="o">]</span>&#x000A;    &#x000A;    ErrorDocument 404 /missing.html&#x000A;    </pre>
            </div>
            
            
            <p>Well, that took longer than I thought. I can't say that it wasn't
            educational. Now, what was I <em>supposed</em> be doing?</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>The iPad</title>
      <link>
        http://alexvollmer.com/posts/2010/01/31/the-ipad/
      </link>
      <comments>
        http://alexvollmer.com/posts/2010/01/31/the-ipad/#comments
      </comments>
      <pubDate>
        Sun, 31 Jan 2010 08:12:05 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            apple
        ]]>
      </category>
      <category>
        <![CDATA[
            UX
        ]]>
      </category>
      <category>
        <![CDATA[
            ipad
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2010/01/31/the-ipad/
      </guid>
      <description>
        <![CDATA[
            <img src="http://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/IPad-01.jpg/250px-IPad-01.jpg" alt="iPad" class="left">
            After letting the dust settle around Apple's announcement of the iPad,
            I wanted to take a more measured, less reactionary look at the new
            device. The coverage that immediately followed the announcement was
            even more biased to both extremes than coverage of the President's
            State of the Union address which occurred the same day. I know that
            there's a growing body of folks who just don't like Apple, but I was
            surprised at the level of antipathy.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p><img src="http://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/IPad-01.jpg/250px-IPad-01.jpg" alt="iPad" class="left">
            After letting the dust settle around Apple's announcement of the iPad,
            I wanted to take a more measured, less reactionary look at the new
            device. The coverage that immediately followed the announcement was
            even more biased to both extremes than coverage of the President's
            State of the Union address which occurred the same day. I know that
            there's a growing body of folks who just don't like Apple, but I was
            surprised at the level of antipathy.</p>
            
            <p>Those that damned the iPad seemed to come in two flavors: those that
            saw the iPad as the embodiment of corporate greed and walled gardens,
            and those that thought the device didn't measure up to their personal
            expectations.</p>
            
            <p>There is a sizable (and growing) population of folks that simply will not
            purchase an Apple product or participate in its eco-system for
            various reasons. I think that's fine. While I personally enjoy their
            products, I don't believe that everyone has to agree with me. I get no
            less enjoyment out of my MacBook or iPhone by someone else having a
            Lenovo ThinkPad or an Android Phone.</p>
            
            <p>Unfortunately, that view isn't shared by many. Take the flame-baiting
            in the aftermath of the iPad announcement and replace the term "iPad"
            with "gay marriage" and the arguments look nearly identical. Gay
            marriage is so upsetting to conservatives that they can't imagine
            being in a world where it exists. It's not that gays are coming into
            their homes and performing unspeakable acts, it's that they can't
            imagine <em>even being on the same planet with them</em>. So it goes with the
            iPad&mdash;there are the FSF types who can't imagine living in a world
            where Apple controls every single thing that goes on a device.</p>
            
            <p><img
            src="http://cache.gawker.com/assets/images/jalopnik/2009/02/obama-state-of-the-union.jpg"
            alt="The Prez" class="left" style="height: 162px; width: 293px;"/>
            I'm not aware of anything in the President's address that said every
            man, woman and child in the U.S. was going to be forced to use an
            iPad. As far as I know, people are still free to choose whether or not
            they wish to purchase and iPad and wander about in Apple's walled
            garden. Don't want an iPad? <em>Fabulous</em>. Don't buy one.</p>
            
            <p>Folks, it's a <em>big</em> world out there. There doesn't have to be a single
            winner. Even as Apple trumpets having over 50 million touch devices,
            that pales in comparison to the worldwide population of those who
            <em>could</em> afford such devices. That's even without considering the fact
            that if you own one iPod/iPhone, you probably own another. In terms of
            real numbers, I don't see a growing "collectivization" movement to dress
            everyone in Apple products. If Apple were interested in market share,
            they would charge a lot less for their products.</p>
            
            <p><a href="http://arstechnica.com/tech-policy/news/2010/01/protestors-ipad-is-nothing-more-than-a-golden-calf-of-drm.ars">
            <img
            src="http://static.arstechnica.com/01-27-2010/apple-ipad-protest.jpg"
            alt="iPad Protest" class="right" style="width: 132px; height: 202px;"/>
            </a>
            Besides calling the iPad a freedom-killer, the other category of
            critiques were that the device was simply disappointing. This reaction
            particularly surprises me. Yes, there are certainly quibbles to make
            about various features of the device (no camera, no multitasking,
            etc.) but the dismissive observations that the iPad is simply "a big
            iPhone" I think misses the point. Yeah, it <em>is</em> a big iPhone, which
            has been a huge success by any measurement. If you love the iPhone,
            then the things you most wish it had were more of are screen
            real-estate and speed. So making one that is bigger and faster is
            totally logical. What did people expect? Roomba integration? A
            toaster? 3D goggles?</p>
            
            <div style="clear: both;"></div>
            
            
            <p>ReadWriteWeb attempted to
            <a href="http://www.readwriteweb.com/archives/how_to_hate_the_ipad_a_break-down_of_the_backlash.php#more" title="How to Hate the iPad: A Break-Down of the Backlash">break down the
            anger</a>
            with this graph:</p>
            
            <p><a href="http://www.tweetpad.com" title="TweetPad"><img src="http://www.readwriteweb.com/images/NegativeiPadStory.jpg" alt="" /></a></p>
            
            <p>Looking at this chart, I would categorize 38% of the complaints as
            just pure snark. The next 27% that dismiss it as a "big iPhone" are
            missing the point. The remaining complaints are all valid and worth
            talking about a bit:</p>
            
            <h3>No Flash</h3>
            
            <p>Hooray, I say. I don't run Flash on my desktop or laptop if I can help
            it. I simply do not like Flash. It rarely enhances my experience on a
            site and often pegs my CPUs for no good reason. Yes, <a href="http://theflashblog.com/?p=1641">I'm aware that
            Adobe has to do a lot of video codec work without the aid of
            hardware</a>, but that doesn't address
            the jarring user experience that is Flash. I have not missed Flash on
            the iPhone either. If you want Flash, don't buy a iPod, iPhone or
            iPad. It's as simple as that.</p>
            
            <p>Oh, and if you're running a Mac and feel the same way, do yourself a
            favor and install the <a href="http://rentzsch.github.com/clicktoflash/">Click to Flash
            plugin</a>, which lets you
            ignore Flash on the web completely.</p>
            
            <h3>No Multitasking</h3>
            
            <p>I'm on the fence about this one. I'm not convinced that multi-tasking
            is essential for a device like the iPhone or the iPad. The truth is, I
            have not had many instances where I thought, "Damn! If only I could
            run two apps at once!" I think a lot of people <em>think</em> they want it
            because their computer has this capability.</p>
            
            <p>I think that's the point that is being missed. The iPad is not, in any
            way, being put forth as a general computing device. In fact, to
            categorize it as a "tablet PC" is to completely misunderstand the
            iPad's underlying design philosophy. When I watched the presentation
            and demonstration video what I saw was easy execution of tasks. Nobody
            was demonstrating bit-rates, frames-per-second, or tail-call
            optimization. The underlying technology and "platform" are simply the
            means to an end.</p>
            
            <p>This is a different enough device that people will have to change
            their thinking about their relationship with their computers. I don't
            think it means a comprimising relationship, but one that is different
            from today. Fraser Speirs eloquently called the inability to
            comprehend this shift as <a href="http://speirs.org/blog/2010/1/29/future-shock.html">Future
            Shock</a>.</p>
            
            <h3>Lock-In</h3>
            
            <p>Another major complaint of the iPad and the entire Apple/iTunes
            ecosystem is that it is a closed system. Users don't get to mix and
            match devices freely from other manufacturers. The party-line from
            Apple is that they want to control every aspect to provide the best
            user experience. Detractors argue that it is part of Apple's plan to
            own all of your content like Big Brother (as Microsoft has attempted
            to in the past).</p>
            
            <p>I get why people feel uncomfortable with handing Apple control of
            their stuff. Personally, I have a lot of Apple products in my house
            and I'm happy to give them that control because, for me, it <em>is</em> a
            better experience. I will happily trade some vague notion of "freedom"
            for stuff that just works. Endless fiddling and integration of
            disparate technologies isn't freedom at all&mdash;it's an enslavement
            of the most precious resource I have, which is my time.</p>
            
            <p>Not everyone is happy with that and they prefer an alternative. Hey,
            no problem. Live and let live. You want to run Boxee on Linux
            connected to an iRiver. More power to you. I won't sneer at you or
            call you a fool. If it makes you happy, your choices <em>don't affect me
            in the slightest</em>.</p>
            
            <p>As an aside, if anyone complaining about the lack of Flash is also
            complaining about lock-in, that person needs to be hit repeatedly with
            a rake.</p>
            
            <h3>No Camera</h3>
            
            <p>Sigh. Yeah, this one is a bit disappointing. I don't really care so
            much about having for video chatting (which I do maybe once a
            year). But I really like using a camera to capture things I would
            otherwise have to take a lot of effort to write down. How cool would it
            be to have your iPad in a brainstorming session where you can still
            jot a few notes down then associate a snapshot of the whiteboard?</p>
            
            <p>Now there is an interesting design conundrum here. Do you put the
            camera on the front or the back? Depending on the tasks you expect to
            perform, this makes a huge difference. If you want people to see
            <em>you</em>, you put it on the front and figure out how to give the user
            the user visual feedback so they can position the camera.</p>
            
            <p>If you want to take pictures of other things, you put the camera on
            the back and use the video display as the viewfinder. However you
            can't reasonably reverse the tasks for these two positions. OK, so do
            you put <em>two</em> cameras on? I don't know. How do you control them? Does
            each bit of software implicitly decide which camera to enable? Does
            the user have to decide?</p>
            
            <p>I don't know the answer, but I would be very surprised if a camera
            didn't show up in the next version of the device. They put cameras in the
            iPod Nanos for cryin' out loud. Surely they can figure out how to
            stick one in an iPad.</p>
            
            <h3>More AT &amp; T</h3>
            
            <p>The amount of bad press AT &amp; T has received via the iPhone has to make
            them wonder what sort of devil they've made a deal with. Are there
            serious issues with AT &amp; T? You betcha. Do they effect everyone? I'm
            not convinced. Coverage in San Francisco and Manhattan seem acutely
            poor, but in my corner of the Northwest coverage is pretty damn good.</p>
            
            <p>The non-iPhone types love to make fun of AT&amp;T, fueled largely (I think)
            by schadenfreude. I'd be very curious to see how other carriers handle
            a similar load. I used to work in that industry and the mentality
            among carriers is <em>all the same</em>. They all want to wring every penny
            they can out of you by charging for every bit you use. Let's not kid
            ourselves.</p>
            
            <p>Kudos to Apple for making a WiFi-only version. For me, this is a
            pretty compelling configuration. I'm an urban kid. There are few
            places I go where I can't get some kind of access to WiFi. In the
            cases where I can't get WiFi, I have an iPhone. Plus, I imagine using
            an iPad primarily at home, or on vacation, but not for commuting.</p>
            
            <p>I'm no AT &amp; T apologist&mdash;there are some serious issues they need
            address&mdash; but I'm not convinced that this makes the iPad a
            "failure".</p>
            
            <h3>Conclusion</h3>
            
            <p>Am I an Apple apologist? I hope not, but people are notoriously bad
            judges of their own character. What I can say is that there are
            certainly things about the company that frustrate both as a user and
            developer. However, I am still more delighted by their products than I
            am frustrated with them. When that balance changes, I'll move on to
            something else.</p>
            
            <p>I'm pretty jazzed about this device, both as a consumer and as a
            developer. What sucked me into the iPhone was how it just oozed with
            <em>utility</em>. Not in terms of potential energy, but in that it fulfills
            the notion of computers as and extension of our own minds. The iPad
            just takes the next step forward. I can't wait to see how it all
            unfolds.</p>
            
            <p>P.S. Just I'm glad to see the iPhone finally get some competition, I'm
            also looking forward to somebody putting up a good fight against the
            iPad.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Prototyping with Briefs</title>
      <link>
        http://alexvollmer.com/posts/2010/01/10/prototyping-with-briefs/
      </link>
      <comments>
        http://alexvollmer.com/posts/2010/01/10/prototyping-with-briefs/#comments
      </comments>
      <pubDate>
        Sun, 10 Jan 2010 17:40:40 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            iphone
        ]]>
      </category>
      <category>
        <![CDATA[
            UX
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2010/01/10/prototyping-with-briefs/
      </guid>
      <description>
        <![CDATA[
            One of the best sessions I saw at the 2009 WWDC was titled "Prototyping iPhone User Interfaces". In this session, Bret Victor laid out a strategy and some techniques for building cheap prototypes on the the device in lieu of "static" mockups.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>One of the best sessions I saw at the 2009 WWDC was titled "Prototyping iPhone User Interfaces". In this session, Bret Victor laid out a strategy and some techniques for building cheap prototypes on the the device in lieu of "static" mockups.</p>
            
            <p>After returning from WWDC I was inspired to try this technique out and after playing with the idea for a bit, it was clear that there was a lot of repetition in the process. To really make prototyping cheap, we need a simple framework that makes the process fast and easy. Enter <a href="http://github.com/capttaco/Briefs">Briefs</a>, a lightweight iPhone application that allows you to embed and run simple prototypes built with nothing more than images and a property list.</p>
            
            <p>With Briefs, you don't write a separate application for the device (imagine the provisioning headaches), but instead embed separate prototypes (called "briefs") into a single Briefs application. Briefs is still pretty new and I expect to see (and contribute) many enhancements, but if you like this style of application development, it's certainly worth spending some time with Briefs.</p>
            
            <p>Let's look at how it works. We're going to start with the goal of prototyping the built-in Photos application. Now obviously, nobody is going to actually <em>re-write</em> the Photos app, but since we all have familiarity with it, it's a good example to see how a working application can be expressed as a Briefs prototype.</p>
            
            <h1>Getting Briefs</h1>
            
            <p>Before we do anything, we need to get Briefs on our local machine. Briefs is distributed in source-only form via <a href="http://github.com/capttaco/Briefs">GitHub</a>. You'll need to find a nice place to check out the two main Briefs project, then do the following:</p>
            
            <div class="highlight"><pre><span class="nv">$ </span>git clone git://github.com/capttaco/Briefs.git&#x000A;    <span class="nv">$ </span>git clone git://github.com/capttaco/Briefs-util.git&#x000A;    <span class="nv">$ </span><span class="nb">cd </span>Briefs&#x000A;    <span class="nv">$ </span>git submodule update -i&#x000A;    </pre>
            </div>
            
            
            <p>In order to put our first Brief together, we need to build a command-line tool that is part of the Briefs project. Open the Xcode project in the Briefs folder (<code>Briefs.xcodeproj</code>) and select the "Briefs Compactor" target and set the architecture to "Base SDK".</p>
            
            <p><img src="/images/2010/01/Screen-shot-2010-01-10-at-7.50.17-AM.png" alt="Screen shot 2010-01-10 at 7.50.17 AM.png" /></p>
            
            <p>Now execute the target to build the <code>compact-briefs</code> executable. When it's finished, Xcode will put the final binary in <code>build/Debug/compact-briefs</code> in the Briefs project directory. Copy this file to a location in your <code>$PATH</code>. Personally I like to keep such stuff in <code>~/bin</code>. We'll need this command in a little bit.</p>
            
            <h1>How it Works</h1>
            
            <p>Before we dive into our project, we should get a little terminology under our belt. Each application prototype you create is called a "Brief". Each Brief is composed of one or more "Scenes". Scenes usually have an associated image, so you can think of a scene and a screen in the application as being more or less equivalent.</p>
            
            <p>Scenes can also have any number of "Actors", which are regions on the screen that perform some action when touched. For example, to mimic navigating to a new view via a view controller, a region on a table view could be rigged to an action that slides another scene into place.</p>
            
            <p>Briefs are expressed as Property List (Plist) files in the Briefs application. If you like writing XML or are proficient with the Property List Editor application, then you're golden. Otherwise there is another format in which you can write your brief that compiles down to a final Plist. This is the route we'll explore in our prototype.</p>
            
            <h1>Protyping Photos.app</h1>
            
            <h2>Generate Image Files</h2>
            
            <p>Now let's generate the image files for our prototype. In the case of the existing Photos app, I just screen-captured the running application on the simulator. Obviously, you won't be in the same position with a new application, so you'll need to generate some images by hand.</p>
            
            <p>If you use OmniGraffle, there are several good iPhone stencil sets on <a href="http://graffletopia.com/categories/iphone">Graffletopia</a>. If you prefer Photoshop, checkout the exhaustive <a href="http://www.teehanlax.com/blog/2009/06/18/iphone-gui-psd-30/">iPhone GUI PSD</a>. Either one of them will generate the appropriate files. There's no requirement that the image files <em>look</em> like the iPhone. You could use a  tool like <a href="http://www.balsamiq.com/products/mockups">Balsamiq</a> or scan hand-drawn sketches to generate very simple wireframe sketches. Regardless, all you need to do is produce PNG, GIF or JPEG files that fit within the normal iPhone dimensions (320 x 480).</p>
            
            <p>Let's create a folder called "photos app" and put our image files in it. I've created <a href="http://alexvollmer-public.s3.amazonaws.com/photos_app.tgz">a tar-ball of this entire project</a>, which you can download, explore and run.</p>
            
            <h2>Writing the Brief</h2>
            
            <p>Let's think a bit about the flow of our application. We're only going to implement a sub-set of Photo.app's full navigation&mdash;we'll just implement the flow for the Hawaii photo album. We have a top-level screen that shows all of the photo albums. From there the user can select "Hawaii" and drill into a display of a few images in the album. Touching any of the images will take the user into the image browser where users can go back and forth between the various images. Each screen has a common "back" button in the top-left. The overall screen layout and flow looks like this:</p>
            
            <p><a name="app-flow"><img src="/images/2010/01/photos-app-flow.png" alt="photos-app-flow.png" /></a></p>
            
            <p>The pink boxes highlight the touchable areas in each scene. The arrows between them show which scene a particular area navigates to. In Briefs-parlance, each screen is a "Scene" and each pink box is an "Actor".</p>
            
            <p>Let's step through the Brief (the <code>photos.bs</code> file), a bit at a time. If you get lost, refer back to the <a href="#app-flow">application flow diagram</a> above.</p>
            
            <p>First we declare that our starting scene is the "Home" scene. Next, we define that scene with the image <code>home.png</code> as the background image and one actor that will navigate to the next scene. The position and size of this actor corresponds to the table cell labeled "Hawaii". If we wanted to support the table rows, we'd need to add an actor for each one.</p>
            
            <p>When the user touches the "Hawaii" row, we'll transition the "Hawaii" scene (defined shortly) by executing a "push left" animation. This will mimic the standard animation of pushing a new view controller onto the navigation controller stack:</p>
            
            <div class="highlight"><pre>start:Home&#x000A;    scene:Home&#x000A;      image:home.png&#x000A;      actor:HawaiiPictures &#x000A;        position:0,123 &#x000A;        wh:320,53&#x000A;        action:goto<span class="o">(</span>Hawaii, push left<span class="o">)</span>&#x000A;    </pre>
            </div>
            
            
            <p>Next, we'll define a scene that shows the thumbnail images for each photo in the Hawaii album. This scene will display the <code>hawaii.png</code> background image and has an actor for the return navigation button in the upper-left as well as for each thumbnail image. Note the differences in the direction of the push animation in the actors. The "back" button pushes right (like the normal return navigation animation), while the thumbnails push left.</p>
            
            <div class="highlight"><pre>scene:Hawaii&#x000A;      image:hawaii.png&#x000A;      actor:Home &#x000A;        position:17,30 &#x000A;        wh:87,24 &#x000A;        action:goto<span class="o">(</span>Home, push right<span class="o">)</span>&#x000A;      actor:image1 &#x000A;        position:5,69 &#x000A;        wh:72,72 &#x000A;        action:goto<span class="o">(</span>Image1, push left<span class="o">)</span>&#x000A;      actor:image2&#x000A;        position:84,70&#x000A;        wh:72,72&#x000A;        action:goto<span class="o">(</span>Image2, push left<span class="o">)</span>&#x000A;      actor:image3&#x000A;        position:162,70&#x000A;        wh:72,72&#x000A;        action:goto<span class="o">(</span>Image3, push left<span class="o">)</span>&#x000A;    </pre>
            </div>
            
            
            <p>Now we'll define the scenes for each of the three images. Each image scene has an actor for the return navigation button in the top-left. The first image also has two actors to navigate to the next image. The <code>NextImage</code> actor is for the navigation button in the toolbar, whereas the <code>NextImageSwipe</code> actor covers the right side of the image itself. This is done to mimic the swiping motion normally used to navigate photos. Right now, Briefs only supports single-touch interactions so it won't behave <em>exactly</em> like the real application in supporting swipes.</p>
            
            <div class="highlight"><pre>scene:Image1&#x000A;      image:image1.png&#x000A;      actor:NavigateBack &#x000A;        position:14,33 &#x000A;        wh:44,23 &#x000A;        action:goto<span class="o">(</span>Hawaii, push right<span class="o">)</span>&#x000A;      actor:NextImage &#x000A;        position:202,450 &#x000A;        wh:23,17 &#x000A;        action:goto<span class="o">(</span>Image2, push left<span class="o">)</span>&#x000A;      actor:NextImageSwipe &#x000A;        position:156,60 &#x000A;        wh:160,380 &#x000A;        action:goto<span class="o">(</span>Image2, push left<span class="o">)</span>&#x000A;    </pre>
            </div>
            
            
            <p>The "Image2" scene is a lot like the "Image1" scene, except that it has button and swipe actors for both the previous and next images:</p>
            
            <div class="highlight"><pre>scene:Image2&#x000A;      image:image2.png&#x000A;      actor:NavigateBack &#x000A;        position:14,33 &#x000A;        wh:44,23 &#x000A;        action:goto<span class="o">(</span>Hawaii, push right<span class="o">)</span>&#x000A;      actor:PreviousImage &#x000A;        position:94,452 &#x000A;        wh:23,17 &#x000A;        action:goto<span class="o">(</span>Image1, push right<span class="o">)</span>&#x000A;      actor:PreviousImageSwipe &#x000A;        position:0,60 &#x000A;        wh:160,380 &#x000A;        action:goto<span class="o">(</span>Image1, push right<span class="o">)</span>&#x000A;      actor:NextImage &#x000A;        position:202,450 &#x000A;        wh:23,17 &#x000A;        action:goto<span class="o">(</span>Image3, push left<span class="o">)</span>&#x000A;      actor:NextImageSwipe &#x000A;        position:156,60&#x000A;        wh:160,380 &#x000A;        action:goto<span class="o">(</span>Image3, push left<span class="o">)</span>&#x000A;    </pre>
            </div>
            
            
            <p>Finally, the last image scene. It only has "backward" navigation in the photo set since we've only put three photos in our album:</p>
            
            <div class="highlight"><pre>scene:Image3&#x000A;      image:image3.png&#x000A;      actor:NavigateBack &#x000A;        position:14,33 &#x000A;        wh:44,23 &#x000A;        action:goto<span class="o">(</span>Hawaii, push right<span class="o">)</span>&#x000A;      actor:PreviousImage &#x000A;        position:94,452 &#x000A;        wh:23,17 &#x000A;        action:goto<span class="o">(</span>Image2, push right<span class="o">)</span>&#x000A;      actor:PreviousImageSwipe &#x000A;        position:0,60 &#x000A;        wh:160,380 &#x000A;        action:goto<span class="o">(</span>Image2, push right<span class="o">)</span>&#x000A;    </pre>
            </div>
            
            
            <p>We've just done simple scene-to-scene transitions in this Brief, but there are also several transitions you can perform directly on actors such as moving them, fading them in and out and others. I've created a <a href="http://alexvollmer-public.s3.amazonaws.com/Briefs%20Cheatsheet.pdf">syntax cheatsheet</a> which you can use as a reference for your own Briefs.</p>
            
            <h2>Compiling the Brief</h2>
            
            <p>Once we have our photos, it's time to start writing our Brief. As mentioned earlier, the Briefs application consumes Plist files. However you can't just create one out of the box because it will only have image file <em>references</em> in it, but not the actual data. The reason we built the <code>compact-briefs</code> command earlier, was so that we could take our intermediate Briefs file (usually called a <em>source</em> brieflist) and embed the image data directly in the final product.</p>
            
            <p>However I find editing XML tiresome, whether it's by hand or with a tool. Briefs provides an alternate syntax in which to write briefs which is transformed by a Ruby script into the intermediate Plist format. You can then compile the source brieflist to the final format using the <code>compact-briefs</code> command. The flow between formats and tools looks like this:</p>
            
            <p><img src="/images/2010/01/briefs-flow.png" alt="briefs-flow.png" /></p>
            
            <p>In the <code>Briefs-util</code> project (which you checked out earlier), you can find the Ruby script in <code>BS/bs</code>. I run these tools all at once in a script named <code>compile</code>:</p>
            
            <div class="highlight"><pre><span class="c">#!/bin/bash</span>&#x000A;    ~/Development/Briefs-util/BS/bs photos.bs &gt; photos-source.brieflist <span class="o">&amp;&amp;</span> <span class="se">\</span>&#x000A;        ~/bin/compact-briefs photos-source.brieflist ~/Development/Briefs/sample/photos.brieflist&#x000A;    </pre>
            </div>
            
            
            <h2>Installing the Brief</h2>
            
            <p>The next step is to build the Briefs application with our newly-compiled Brief. Switch back to the Briefs Xcode project and choose the "Briefs" application target. If you haven't yet added the new brief to the Xcode project, right-click on the "My Briefs" and choose Add > Existing Files. Select the <code>photos.brieflist</code> file in the <code>samples</code> directory and in the following dialog make sure the checkbox at the top is unselected.</p>
            
            <p><img src="/images/2010/01/Screen-shot-2010-01-10-at-2.08.27-PM.png" alt="Screen shot 2010-01-10 at 2.08.27 PM.png" /></p>
            
            <p>Now you can build the Briefs application for either the simulator or the device. If you're building for the device, make sure you have a proper provisioning profile in place that matches the bundle identifier of the project. Now we can Build &amp; Run the project, and our <code>photos.brieflist</code> should appear in the list:</p>
            
            <p><img src="/images/2010/01/Screen-shot-2010-01-10-at-8.54.47-AM.png" alt="Screen shot 2010-01-10 at 8.54.47 AM.png" /></p>
            
            <p>Select our new Brief and play with the application. To exit a Brief, touch and hold your finger down until a new screen appears asking you if you want to exit the current Brief.</p>
            
            <p><img src="/images/2010/01/Screen-shot-2010-01-10-at-11.54.51-AM.png" alt="Screen shot 2010-01-10 at 11.54.51 AM.png" /></p>
            
            <h2>Distributing the Brief</h2>
            
            <p>The idea behind these prototypes is putting a tangible example in front of users to get their feedback. So what do you do if you want to distribute Briefs to a wider audience? Ad-hoc builds are a pain and require access to the physical device. Fortunately, Briefs provides a tool called "Briefcasts", which is simply one or more Briefs embedded in an RSS feed. The Briefs application can retrieve Briefcasts via HTTP, so distributing Briefs is a snap if you have a web server to serve new Briefcasts from. This means you can install the Briefs application once on a device and provide updates anytime without having direct access to the hardware.</p>
            
            <p>Currently, there isn't a tool to easily create a Briefcast directly out of a list of Briefs. For now, the project web site recommends copying and modifying the RSS template below:</p>
            
            <div class="highlight"><pre><span class="cp">&lt;?xml version=&quot;1.0&quot;?&gt;</span>&#x000A;    <span class="nt">&lt;rss</span> <span class="na">version=</span><span class="s">&quot;2.0&quot;</span><span class="nt">&gt;</span>&#x000A;      <span class="nt">&lt;channel&gt;</span>&#x000A;        <span class="nt">&lt;title&gt;</span>*Briefcast Demo*<span class="nt">&lt;/title&gt;</span>&#x000A;        <span class="nt">&lt;link&gt;</span>*http://island.local/~alex/briefcasts/*<span class="nt">&lt;/link&gt;</span>&#x000A;        <span class="nt">&lt;description&gt;</span>*Demonstrate how awesome it is to use a briefcast to get briefs on the iPhone.*<span class="nt">&lt;/description&gt;</span>&#x000A;        <span class="nt">&lt;language&gt;</span>en-us<span class="nt">&lt;/language&gt;</span>&#x000A;        *Thu, 12 Nov 2009 03:05:00 GMT*<span class="nt">&lt;/pubDate&gt;</span>&#x000A;        <span class="nt">&lt;lastBuildDate&gt;</span>*Thu, 12 Nov 2009 03:05:00 GMT*<span class="nt">&lt;/lastBuildDate&gt;</span>&#x000A;        <span class="nt">&lt;item&gt;</span>&#x000A;          <span class="nt">&lt;title&gt;</span>*Photos.app Sketch*<span class="nt">&lt;/title&gt;</span>&#x000A;          <span class="nt">&lt;enclosure</span> <span class="na">url=</span><span class="s">&quot;*http://island.local/~alex/briefcasts/photos.brieflist*&quot;</span> <span class="na">length=</span><span class="s">&quot;*954708*&quot;</span> <span class="na">type=</span><span class="s">&quot;application/brief&quot;</span> <span class="nt">/&gt;</span>&#x000A;          <span class="nt">&lt;description&gt;</span>*An example brief of the built-in Photos.app.*<span class="nt">&lt;/description&gt;</span>&#x000A;          *Sun, 13 Sep 2009 03:05:00 GMT*<span class="nt">&lt;/pubDate&gt;</span>&#x000A;          <span class="nt">&lt;guid&gt;</span>*http://island.local/~alex/cast/1#item1*<span class="nt">&lt;/guid&gt;</span>&#x000A;        <span class="nt">&lt;/item&gt;</span>&#x000A;      <span class="nt">&lt;/channel&gt;</span>&#x000A;    <span class="nt">&lt;/rss&gt;</span>&#x000A;    </pre>
            </div>
            
            
            <p>The bits marked <em>in bold</em> need to replaced with your particular details. In the example above I only have one Brief, but if you had more than one, you would just include additional <code><item></code> elements. You need to have this RSS file on disk as well as the brieflist file referred to in the <code><enclosure></code> tag.</p>
            
            <p>Assuming that you have your web server all setup with the RSS file as well as the Briefs files, you can launch the application and add the Briefcast by selecting the "+" button in the top-right of the home screen. Enter the details on the next dialog and touch the "Save" button:</p>
            
            <p><img src="/images/2010/01/Screen-shot-2010-01-10-at-9.20.37-AM.png" alt="Screen shot 2010-01-10 at 9.20.37 AM.png" /></p>
            
            <p>On the next screen you'll see the details of the Briefcast. Each Brief in the Briefcast will be listed under the section titled "Enclosed Briefs". Touch the "Download this Brief" button to retrieve the associated brief.</p>
            
            <p><img src="/images/2010/01/Screen-shot-2010-01-10-at-9.20.49-AM.png" alt="Screen shot 2010-01-10 at 9.20.49 AM.png" /></p>
            
            <p>Once the brief is downloaded, you end up on a rather unhelpful confirmation screen:</p>
            
            <p><img src="/images/2010/01/Screen-shot-2010-01-10-at-9.16.27-AM.png" alt="Screen shot 2010-01-10 at 9.16.27 AM.png" /></p>
            
            <p>You have to touch the screen, then navigate back to the top. Then you'll find the new Brief in your list of briefs. To update the Brief, you navigate back into the Briefcast section from the home page and download the correct Brief again.</p>
            
            <p>One nice enhancement I'd love to see is a custom application URL for Briefs so that you could just email the link to someone. They could read the email from their device, select the URL and Briefs would launch and download the newest Briefs.</p>
            
            <h1>Conclusion</h1>
            
            <p>Briefs is a great tool for building live prototypes. Since it's a relatively young project, it still has a lot of rough edges, but I expect it to mature over time.</p>
            
            <p>Don't hesitate to dive into the code behind Briefs. There are occasions when Briefs (the application or the command-line tool) will crash without much detail. I found that this is usually the result of bad syntax in the Briefs so a little sleuthing may be required to get things working.</p>
            
            <h1>Resources</h1>
            
            <p>Here's a list of related resources:</p>
            
            <ul>
            <li><a href="http://giveabrief.com/">Briefs Homepage</a></li>
            <li><a href="http://github.com/capttaco/Briefs">Briefs on GitHub</a></li>
            <li><a href="https://alexvollmer-public.s3.amazonaws.com/photos_app.tgz">Sample Brief of Photos.app</a></li>
            <li><a href="https://alexvollmer-public.s3.amazonaws.com/Briefs%20Cheatsheet.pdf">Briefs Syntax Cheatsheet</a></li>
            <li><a href="http://graffletopia.com/categories/iphone">iPhone Templates for OmniGraffle</a></li>
            <li><a href="http://www.teehanlax.com/blog/2009/06/18/iphone-gui-psd-30/">iPhone PSD for PhotoShop</a></li>
            </ul>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Tools of 2009</title>
      <link>
        http://alexvollmer.com/posts/2010/01/07/tools-of-2009/
      </link>
      <comments>
        http://alexvollmer.com/posts/2010/01/07/tools-of-2009/#comments
      </comments>
      <pubDate>
        Thu, 07 Jan 2010 04:35:42 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            mac
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2010/01/07/tools-of-2009/
      </guid>
      <description>
        <![CDATA[
            I suppose if I were really on the ball I would have posted this right after New Year's Day, but I needed to ruminate a bit on what was worth summarizing for 2009. In the end I decided to talk about the tools I found in 2009 that really made a difference in my life.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>I suppose if I were really on the ball I would have posted this right after New Year's Day, but I needed to ruminate a bit on what was worth summarizing for 2009. In the end I decided to talk about the tools I found in 2009 that really made a difference in my life.</p>
            
            <h2>LaunchBar</h2>
            
            <p>When I first switched to the Mac, I discovered Quicksilver and it was like discovering something essential that you didn't know you needed. QS was a revelation to me because it drastically reduced the mental distance between the what I wanted to accomplish and executing it.</p>
            
            <p>Over time though, I found some things in QS that just didn't work consistently. It would crash and some of configuration was pretty clumsy. It became pretty clear that there was little, if any, forward progress being made on it. So, on the advice of several local Ruby/Mac nerds, I tried out <a href="http://www.obdev.at/products/launchbar">LaunchBar</a>. At first I was disappointed that it didn't have the breadth of QS, but after investing a little time and effort I found that it handled 99% of the things I wanted. More importantly, there was somebody actually working on it which meant it was going to evolve and improve.</p>
            
            <p>Now I can hardly use a machine that doesn't have it installed. I can't believe people use the dock to launch apps. Yuck.</p>
            
            <h2>Mailplane</h2>
            
            <p>By and large, I'm biased to using the built-in Apple-given tools where possible. Apple usually gets it right and most other tools are designed to work with these apps. I've used Mail.app since I switched to the Mac, but always felt the interface to be inferior to GMail's tag-oriented structure. But since I had some non-GMail accounts, <em>and</em> I wanted to have a single app, I stuck with Mail.app.</p>
            
            <p>However, once I dropped my last IMAP-only account I was ready to switch back. The only parts I really missed were the tight integration with the desktop that Mail.app brought. Lo and behold, along comes <a href="http://mailplaneapp.com">Mailplane</a> to fill the gap. It is, essentially, a WebKit view of GMail with some extra bits to glue the web application to your desktop. I think it's brilliant and was happy to part with some hard-earned money for the privilege to use it.</p>
            
            <h2>zsh completion</h2>
            
            <p>My introduction to zsh came many years ago from a co-worker who boot-strapped my entr&eacute;e into zsh-land with his configuration files. For the longest time I treated them like mystic, sacred tablets to be worshipped, but never to be understood. But over time I found little gaps I wanted to fill in with my zsh setup, especially with command auto-completion.</p>
            
            <p>I'm a keyboard kind of guy. I like the command prompt. I also like going fast, so anything that makes me faster in the command prompt is like gold to me. I love shell auto-completion and found lots of things I wanted completion for, like <a href="http://gist.github.com/164465">RubyGems</a>. So this year I finally pounded through enough documentation and samples to put together a few home-grown completion bits. I still don't understand most of the incantations that make it go (guilty as charged of cargo-culting) but it has made my command-line work-flow that much better.</p>
            
            <h2>Dropbox</h2>
            
            <p>Since switching to the Mac, I've always had at least two machines that I needed to share files between. For the longest time I used to store files in S3 then sync them back and forth. This worked, but it sucked because I had to do it manually and I had to get the order right so I didn't clobber any files.</p>
            
            <p>When <a href="http://www.dropbox.com">Dropbox</a> came along, the clouds parted and the golden light of heaven shown down from above. Dropbox makes sharing files super-duper, idiotically simple. Combined with tools like <a href="http://agilewebsolutions.com/products/1Password">1Password</a>, having consistent data across multiple machines is like magic on a daily basis&mdash;it just works and I don't even have to think about it.</p>
            
            <h2>One Bus Away</h2>
            
            <p>The thing I love about the iPhone is how it's an Swiss Army Knife for information in the modern age. You can find apps that do very specific things, very well and put them in a single box that you keep in your pocket. The <a href="http://www.onebusaway.org">One Bus Away</a> app is one of my favorite iPhone tools.</p>
            
            <p>I ride the bus a lot and to have real-time transit data in the palm of my hand is fantastic. No guessing about when the next bus is coming. No extra waiting. I know just when to leave to catch my next ride.</p>
            
            <h2>Mint.com</h2>
            
            <p>I was a long-time Quicken user but finally had it last January. It's a bloated app with serious usability issues. I finally reached a point where using Quicken had a net-negative impact on my life. It simply drove me crazy to use it.</p>
            
            <p>Enter <a href="http://mint.com">mint.com</a>. At first I was very hesitant to give my information to these guys. But eventually I was convinced to try it because:
            *  if I use Quicken another minute I'll go postal
            *  sometimes the future doesn't start until you acknowledge it, and this looks like the future to me
            *  I'm probably already more open to financial security risk anyway, so what the hell?</p>
            
            <p>I get exactly what I want out of mint: a high-level dashboard that just updates itself. I use a few of the budgeting features which are nice, but really it's all about automated data acquisition and display. Oh yeah, did I mention that they have an iPhone app too?</p>
            
            <h2>Instapaper</h2>
            
            <p>Several years ago I built my own news-feed reader and one of the features I really wanted to implement was a temporary "holding pen" where I could put URLs to visit later. These were things that didn't quite warrant a Delicious bookmark, but I wanted to get back to.</p>
            
            <p>By itself, <a href="http://instapaper.com">Instapaper</a> satisfied my original need and worked great as a bookmarklet in my browser. However it went from a convenient curiosity to an essential tool once other apps started integrating with it (e.g. Net News Wire, Tweetie 2) <em>and</em> Kindle integration (see below).</p>
            
            <p>Oh yeah, did I mention that there's an iPhone app for that too?</p>
            
            <h2>Kindle (device and app)</h2>
            
            <p>The final tool of 2009 that made itself indispensable was the Kindle. It's a great example of "good enough". The Kindle is fraught with polish and usability issues, but they don't overwhelm the utility of the device.</p>
            
            <p>I've been on a long dispense-with-all-physical-media kick, with books being my primary target. I have lots of books, but I don't have lots of space. Since I'm not emotionally wedded to the form-factor of paper books, the Kindle solves my reading-appetite vs. space problem quite well. I can stuff that thing full of books and not take up an inch of extra space in my house.</p>
            
            <p>Like so many of the other tools I've listed, it's the other things that integrate with the Kindle that multiply its utility. First, the Kindle has gained a lot of adoption by other publishers. The <a href="http://pragprog.com">Prags</a> and <a href="http://oreilly.com">O'Reilly</a>, in particular, have been eager to jump into the e-book biz with first-class Kindle support. I probably go through about fifteen to twenty tech books a year and having them on the Kindle is great.</p>
            
            <p>Another great bit of integration is how Instapaper delivers me a weekly digest of links I've captured right to my Kindle. I can read the full content of every page I bookmarked right there on my Kindle without the need for a network connection. This is a great for travel.</p>
            
            <h2>So?</h2>
            
            <p>Looking over this list, if there's one theme that emerges, it's all about ubiquity and convergence. (Did I really just say that?) Despite all of the queasy feelings those "market-tecture" terms give me, I don't quite know how else to define it. For me, 2009 was the year that I could get to my stuff whenever I liked, however I liked, through a multitude of channels.</p>
            
            <p>I wonder what the theme of 2010 will be?</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>All-Time Lineup</title>
      <link>
        http://alexvollmer.com/posts/2009/12/15/all-time-lineup/
      </link>
      <comments>
        http://alexvollmer.com/posts/2009/12/15/all-time-lineup/#comments
      </comments>
      <pubDate>
        Tue, 15 Dec 2009 06:14:09 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            sports
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2009/12/15/all-time-lineup/
      </guid>
      <description>
        <![CDATA[
            A little while ago, I was encouraged to put together my "All-Time" baseball lineup. Now, personally I think the game is more difficult to compete in now than ever before. While the old-timers love to gripe about players in the Steroid Era, I don't think those guys had to play with such a uniformly strong level of competition under such high pressure. But that's just my gut feeling. I don't really have the evidence to back that up. I also think that baseball fans have a tendency to romanticize the players of baseball's so-called "Golden Era", making any rational debate even tougher.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>A little while ago, I was encouraged to put together my "All-Time" baseball lineup. Now, personally I think the game is more difficult to compete in now than ever before. While the old-timers love to gripe about players in the Steroid Era, I don't think those guys had to play with such a uniformly strong level of competition under such high pressure. But that's just my gut feeling. I don't really have the evidence to back that up. I also think that baseball fans have a tendency to romanticize the players of baseball's so-called "Golden Era", making any rational debate even tougher.</p>
            
            <p>With the exception of Gehrig, I've purposely ignored anyone before the 50's. In my opinion, the game is just too different. I don't feel like I can trot Cy Young out as my starting pitcher just because of 511 wins. Those wins don't compare at all to wins in the 60's, 70's, 80's or 90's.</p>
            
            <p>I'm also not penalizing anyone for being part of the Steroid Era or for even having used PEDs. There are, no doubt, some effects of these substances on the game, but I don't see the evidence being as conclusive as some would have you believe. Did Barry Bonds cheat? Yeah, I'm pretty sure he did. He was a helluva ball-player before he started and he still would have put together a Hall of Fame career without them. Steroids (a rather inaccurate term for PEDs) don't make you see the ball any better or improve your hand/eye coordination. At best they allow athletes to recover faster from working out. They don't build muscle, they amplify the body's own healing process in order to work out more. You can call steroid users cheaters, but I wouldn't call them lazy.</p>
            
            <p>So this is my list. If you have a different one, I'd love to hear about it. So, without further ado, here's my list.</p>
            
            <h3>Catcher: Roy Campanella</h3>
            
            <p>Great all-around athlete, hit the crap out of the ball, threw runners out and worked well with pitchers. He was probably one of the most complete catchers to play in the majors.</p>
            
            <h3>First Base: Lou Gehrig</h3>
            
            <p>OK, I cheated a little bit here and dipped back in time further than I said I would go. But c'mon&mdash;it's <em>Lou Gehrig</em>.</p>
            
            <h3>Second Base: Jackie Robinson</h3>
            
            <p>They retired the number 42 for lots of reasons&mdash;those are the same reasons he's my starting second baseman.</p>
            
            <h3>Third Base: George Brett</h3>
            
            <p>Brett is the last guy to come close to hitting .400. He ended the 1980 season with a .390 average. What's even more impressive is that he had more <em>home runs</em> than <em>strikeouts</em> that year. He's got the longevity, the ring and the career to put him on my list.</p>
            
            <h3>Shortstop: Alex Rodriguez</h3>
            
            <p>Does A-Rod's admission to using PEDs damage is Hall of Fame credentials? Maybe, but as I contended earlier, I'm not convinced that they can carry a player to greatness. We love to dump on A-Rod's failures, but it's really only because he's been spectacularly successful in his career. Drugs or not, he's my starting shortstop. This, coming from a jilted M's fan too!</p>
            
            <h3>Left Field: Ted Williams</h3>
            
            <p>The Splendid Splinter. The greatest hitter of all time. Period.</p>
            
            <h3>Center Field: Willie Mays</h3>
            
            <p>Hard to find a better combination of offensive and defensive skills.</p>
            
            <h3>Right Field: Hank Aaron</h3>
            
            <p>How could I possibly leave Hank Aaron off the list?</p>
            
            <h3>Designated Hitter: Edgar Martinez</h3>
            
            <p>Edgar Martinez presents a serious conundrum to Hall of Fame voters. He's really the first "pure" designated hitter that voters will have to face. His career are numbers are impressive for the time he played, but he doesn't have some of the magic totals people like to see. That said, he's my vote for DH. Plus, I named my dog after him.</p>
            
            <h3>Starting Pitcher: Sandy Koufax</h3>
            
            <p>There's probably no roster spot that would engender more differences of opinion than starting pitcher. There are so many great ones to choose from. Koufax didn't have the career longevity like Satchel Paige or Tom Seaver, but at the height of his powers he was simply jaw-dropping amazing. His years from 1963 through 1966 are some of the <em>sickest</em> consecutive years a pitcher ever put together.</p>
            
            <h3>Relief Pitcher: Mariano Rivera</h3>
            
            <p>While there may be controversy over starters, the choice for reliever is pretty straight-forward. Seriously, is there any disagreement here?</p>
            
            <h3>Manager: Earl Weaver</h3>
            
            <p>He managed Hall of Fame players, he won a couple of rings, he wrote the seminal book on baseball strategy and he had a video game named after him. Yup, that fits my criteria.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Building a Knowledge Base</title>
      <link>
        http://alexvollmer.com/posts/2009/12/11/building-a-knowledge-base/
      </link>
      <comments>
        http://alexvollmer.com/posts/2009/12/11/building-a-knowledge-base/#comments
      </comments>
      <pubDate>
        Fri, 11 Dec 2009 16:39:09 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            philosophy
        ]]>
      </category>
      <category>
        <![CDATA[
            GTD
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2009/12/11/building-a-knowledge-base/
      </guid>
      <description>
        <![CDATA[
            Many years ago, I had the fortune of working for a family friend who was a financial planner. Not only did I get to learn a lot about personal finances at an early age, but I also picked up some great professional habits too. One of the things she did was maintain a gigantic set of folders of things she found interesting or useful. She had a four-drawer filing cabinet filled with various tidbits of information she had collected. She had a pretty good index in her head of where these things were and was amazingly proficient in recalling where she had filed things.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>Many years ago, I had the fortune of working for a family friend who was a financial planner. Not only did I get to learn a lot about personal finances at an early age, but I also picked up some great professional habits too. One of the things she did was maintain a gigantic set of folders of things she found interesting or useful. She had a four-drawer filing cabinet filled with various tidbits of information she had collected. She had a pretty good index in her head of where these things were and was amazingly proficient in recalling where she had filed things.</p>
            
            <p>I always wanted a personal knowledge base like this, but I couldn't do it the way she had. First, I have never had the space to collect that much paper. I cannot have, and do not want, a giant filing cabinet of folders. Second, I don't have the kind of recall she did to find that stuff. I could certainly remember that I once filed something, but wouldn't have a clue <em>where</em> I filed it. Which brings me to the third issue, which is suffering with the restriction of filing information under a single hierarchy.</p>
            
            <p>When I first started using GMail I had a major epiphany about the differences between organizing information in folders versus tagging. These days that seems about as earth-shattering as realizing our galaxy is helio-centric, but it seemed like a pretty radical idea at the time. I flat-out love this style of organization. I may not remember <em>where</em> I filed something, but I'm pretty consistent when it comes to labeling things. With systems like GMail and Delicious, I just slap as many tags as I think are reasonable for a message or bookmark and I have a really good chance of finding it again when I need to.</p>
            
            <p>But email and bookmarks aren't a knowledge base. For me, they are a reference, but usually aren't in a succinct enough format to be a good reference. I want to boil down newly-acquired knowledge into some quick, efficient prose. What I needed was a system that:
            *  Works across multiple machines (e.g. work and personal)
            *  Is fast to add and edit content
            *  Is (relatively) searchable
            *  Has some structure
            *  &hellip;but not so much that I fight with it</p>
            
            <p>So over the last few years I've started building a personal knowledge-base in earnest. It's not overly complicated or too involved. I simply keep a set of text files (specifically, files compatible with <a href="http://orgmode.org/">org-mode</a>) in a special <a href="https://www.dropbox.com/">Dropbox</a> folder. Dropbox makes synchronizing stupid-easy. I just treat these files just like local files and they appear on both my work and personal machines.</p>
            
            <p>For a while I used <a href="http://flyingmeat.com/voodoopad/">VoodooPad</a>, which is a very cool program. But I like having a <em>little</em> structure in my notes and imparting that structure in VoodooPad took more effort than I wanted. Later, I discovered Emacs' org-mode, which turned out to be a perfect fit for me&mdash;it's a relatively lightweight structure on top of simple text-files. The only thing org-mode doesn't do well is handling non-text media. I've considered moving to something like OmniOutliner, but I've found that org-mode works surprisingly well.</p>
            
            <p>The technology isn't particularly interesting, nor is it what makes this work for me. Rather, it's the <em>discipline</em> of writing down little notes on various topics of interest. For example, I can never remember how to enable zombies for Cocoa debugging. I looked it up once, then added a section to my <code>xcode.org</code> file on <code>NSZombieEnabled</code>. I get two benefits out of this: first I've got it somewhere handy that I can easily look up again (via spotlight, <code>grep/ack</code>, whatever). Secondly, the very act of formulating a paragraph or two on the concept somehow reinforces it into my brain. In fact, it's very likely that I won't ever have to look up how to track zombies again.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Cleaning out the Closet</title>
      <link>
        http://alexvollmer.com/posts/2009/11/07/cleaning-out-the-closet/
      </link>
      <comments>
        http://alexvollmer.com/posts/2009/11/07/cleaning-out-the-closet/#comments
      </comments>
      <pubDate>
        Sat, 07 Nov 2009 22:12:45 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2009/11/07/cleaning-out-the-closet/
      </guid>
      <description>
        <![CDATA[
            I've had a couple of nascent Ruby gems lurking in my Github account for what seems like ages. So, on a rainy Saturday afternoon, I did a little cleanup and pushed a couple of new gems out to <a href="http://gemcutter.org">Gemcutter</a>.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>I've had a couple of nascent Ruby gems lurking in my Github account for what seems like ages. So, on a rainy Saturday afternoon, I did a little cleanup and pushed a couple of new gems out to <a href="http://gemcutter.org">Gemcutter</a>.</p>
            
            <p>The first, is a gem I started over a year ago, called <a href="http://gemcutter.org/gems/word-salad" target="_new"><code>word-salad</code></a>. It uses the built-in dictionary file to randomly pull words out. This is great when you just need to generate a bunch of English-like text, but don't want to fall back on the boring old "lorem ipsum&hellip;" routine.</p>
            
            <div class="highlight"><pre><span class="mi">3</span><span class="o">.</span><span class="n">words</span> <span class="o">==&gt;</span> <span class="o">[</span><span class="s1">&#39;draw&#39;</span><span class="p">,</span> <span class="s1">&#39;ameliorate&#39;</span><span class="p">,</span> <span class="s1">&#39;bonanza&#39;</span><span class="o">]</span>&#x000A;    <span class="mi">2</span><span class="o">.</span><span class="n">sentences</span> <span class="o">==&gt;</span> <span class="o">[</span><span class="s1">&#39;Shoot jonesing the make castle.&#39;</span><span class="p">,</span> <span class="s1">&#39;Blue murdered slight bastion.&#39;</span><span class="o">]</span>&#x000A;    <span class="mi">2</span><span class="o">.</span><span class="n">paragraphs</span> <span class="o">==&gt;</span> <span class="o">[</span><span class="s2">&quot;Brachypterous gastropod pheretrer overeager toploftily denaturalization stokesite demented benzalhydrazine archaeologic. Haverer hypophonous lenticularly brickliner urocele paucipinnate pik unprintably perhalogen. Subglenoid bearish gesticulative staircase gallop vesuvianite pneumatically overyear conterminous dreamish. Nonalliterated galliwasp superconfirmation Comandra entoil millionth parcellize rarefaction Cynoidea. Podolian metamorphosable nativeness integriously protonematoid undoctor stochastically dissatisfactory unchastity.&quot;</span><span class="p">,</span> <span class="s2">&quot;Increate unloquacious unsatisfiedly flareboard internuncio beguine equivocation snowshoe Rhynchonellacea. Parochially curliewurly vermix consistorial cond consciencelessness Anaxagorize recoct sempiternally Campanulatae. Scorpionida Castalides homoanisic semipenniform Novemberish assessor preterlethal acrotarsial knoller hartin. Procrastination boatwise canonize differentiate faunlike countermarriage obstinance dilatableness drumloid. Gerate squirr Silvanus Physostigma booting thyroarytenoid diminutival legpuller medisance radiobserver.&quot;</span><span class="o">]</span>&#x000A;    </pre>
            </div>
            
            
            <p>The second is a gem for automatically retrieving and locally storing sales reports from the iTunes Connect site. If you have an iPhone app in the App Store, you already know how much (anti-)fun it is to get reports from Apple's site. So I put the <a href="http://gemcutter.org/gems/itunes-connect" target="_new"><code>itunes-connect</code></a> gem together to help out. It's pretty basic for now, but will probably grow more capabilities as time goes on.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>The Cleverest Git Maneuver I Ever Pulled Off</title>
      <link>
        http://alexvollmer.com/posts/2009/10/09/the-cleverest-git-maneuver-i-ever-pulled-off/
      </link>
      <comments>
        http://alexvollmer.com/posts/2009/10/09/the-cleverest-git-maneuver-i-ever-pulled-off/#comments
      </comments>
      <pubDate>
        Fri, 09 Oct 2009 15:44:04 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            git
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2009/10/09/the-cleverest-git-maneuver-i-ever-pulled-off/
      </guid>
      <description>
        <![CDATA[
            So there I was, reviewing a series of commits, sucking air between my teeth and cringing when I came upon one of those lazy commits that has way too much stuff in it to reasonably digest. I needed a way to go back and split that commit into two or three separate commits. This is pretty easy to do with <code>git rebase</code>. You can just do a mixed reset of the last commit, stage the bits you want in one commit, stage the next bits and put that in a separate commit.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>So there I was, reviewing a series of commits, sucking air between my teeth and cringing when I came upon one of those lazy commits that has way too much stuff in it to reasonably digest. I needed a way to go back and split that commit into two or three separate commits. This is pretty easy to do with <code>git rebase</code>. You can just do a mixed reset of the last commit, stage the bits you want in one commit, stage the next bits and put that in a separate commit.</p>
            
            <p>This all works great&mdash;until it doesn't. The ability to stage parts of a file is one of my favorite features of <code>git</code>, but there are times when you have changes that <code>git</code> won't let you split apart. Usually I give up at this point and just abort the rebase.</p>
            
            <p>But yesterday I must have had too much coffee because I was <em>determined</em> to figure out how to do this. The changes that I wanted to split out were pretty easy to find. It was easy to remove them by hand and commit those. But I didn't want to have to manually add them <em>back</em>&mdash;that was too mistake-prone. What I needed was a way to invert the commit that removed those lines. What I needed was <code>git revert</code>. If I could revert my subtractions (in effect, <em>adding</em> the changes back), then I could squash the commits and rewrite history.</p>
            
            <p>So here's what I did:</p>
            
            <ol>
            <li>Started an interactive rebase session (<code>git rebase -i</code>)</li>
            <li>Marked the commit I wanted to split as an "edit"</li>
            <li>Manually removed the code I didn't want in the first commit, staged the changes and made a new commit</li>
            <li>Revert the subtraction with <code>git revert HEAD</code></li>
            <li>Complete that rebase with <code>git rebase --continue</code></li>
            <li>Start a new rebase and squash the original commit with the manual changes one</li>
            </ol>
            
            
            <p>Note that I didn't use <code>git reset</code> after step 2. It wouldn't have helped me because interactive staging wasn't working for me. I also marked all the commits in the second rebase as "edits", so I could fix the commit messages.</p>
            
            <p>Put another way, the commits transformed like this, from left to right:</p>
            
            <p><img src="/images/2009/10/git-revert.png" alt="git revert.png" /></p>
            
            <p>Maybe I'm overly fussy about clean commits. I'll admit that at times that my attention to cleanliness borders on the obsessive, but having the ability to go back and clean things up is one of my favorite things about <code>git</code>. Maybe I'm just not smart enough, but I usually don't know nearly as much at the beginning of a commit as I do at the end. With <code>git</code>, I can make it look like I do.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>clip version 1.0.2 has been released!</title>
      <link>
        http://alexvollmer.com/posts/2009/10/03/clip-version-1-0-2-has-been-released/
      </link>
      <comments>
        http://alexvollmer.com/posts/2009/10/03/clip-version-1-0-2-has-been-released/#comments
      </comments>
      <pubDate>
        Sat, 03 Oct 2009 16:03:14 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <category>
        <![CDATA[
            clip
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2009/10/03/clip-version-1-0-2-has-been-released/
      </guid>
      <description>
        <![CDATA[
            You like command-line parsing, but you hate all of the bloat. Why
            should you have to create a Hash, then create a parser, fill the Hash
            out then throw the parser away (unless you want to print out a usage
            message) and deal with a Hash? Why, for Pete's sake, should the parser
            and the parsed values be handled by two different objects?
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>You like command-line parsing, but you hate all of the bloat. Why
            should you have to create a Hash, then create a parser, fill the Hash
            out then throw the parser away (unless you want to print out a usage
            message) and deal with a Hash? Why, for Pete's sake, should the parser
            and the parsed values be handled by two different objects?</p>
            
            <p>Changes:</p>
            
            <h3>1.0.2 / 2009-10-02</h3>
            
            <ul>
            <li><p>Merged patches from Adam Salter:</p>
            
            <ul>
            <li>Added new "presence" method for options.</li>
            <li>Updated rspec usage to match the latest &amp; greatest syntax</li>
            <li>Make descriptions optional for flags and options</li>
            </ul>
            </li>
            <li><p><a href="http://clip.rubyforge.org">http://clip.rubyforge.org</a></p></li>
            </ul>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>The End of an Era</title>
      <link>
        http://alexvollmer.com/posts/2009/10/02/the-end-of-an-era-2/
      </link>
      <comments>
        http://alexvollmer.com/posts/2009/10/02/the-end-of-an-era-2/#comments
      </comments>
      <pubDate>
        Fri, 02 Oct 2009 21:55:55 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            personal
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2009/10/02/the-end-of-an-era-2/
      </guid>
      <description>
        <![CDATA[
            Well, this is it kids; the end of my time at <a href="http://evri.com">Evri</a>. While I have cultivated a long-standing antipathy towards the Grateful Dead, I'll co-opt one of their song titles and simply mutter, "what a long strange trip it's been". I started out at Evri when it was still just a twinkle in somebody's eye. Since then I've seen it grow and mutate in ways I would have never imagined. I got to work on a lot of cool stuff and learn a bunch of new things. The best part, like any good job, was having the opportunity to work with some really great people.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>Well, this is it kids; the end of my time at <a href="http://evri.com">Evri</a>. While I have cultivated a long-standing antipathy towards the Grateful Dead, I'll co-opt one of their song titles and simply mutter, "what a long strange trip it's been". I started out at Evri when it was still just a twinkle in somebody's eye. Since then I've seen it grow and mutate in ways I would have never imagined. I got to work on a lot of cool stuff and learn a bunch of new things. The best part, like any good job, was having the opportunity to work with some really great people.</p>
            
            <p>But now it's time for a change. My professional clock is set to about three years&mdash;when that bell rings it's time for me to move on. This was as true at Evri as it has been anywhere else, with one major difference: I made a career-change along the way.</p>
            
            <p>OK, I haven't given up software to become a sous-chef or to take troubled kids out into the woods to teach them discipline. But I think I have reached the end of my professional Java programming career. This is kind of a "Big Deal", because I've been doing it for a <em>decade</em>. I know, I know: never say never. It's entirely possible that I'll eat my words a year or two down the road, but for now, I can't help but feel like I'm closing a major chapter in my professional life.</p>
            
            <p>I've been with that language for a long time. Lately I've made a sport of complaining about it a lot, but honestly that's simply contempt bred from familiarity. There is a lot of good about the language and, despite my railings, the baby needn't be thrown out with the bath-water. What it comes down to is that I simply don't <em>enjoy</em> it anymore. It used to be fun to put those pieces together, but not any longer. So I'm trading in my Java hat for my two new loves: Ruby and Objective-C.</p>
            
            <p><img src="/images/2009/10/IMG_1047-150x150.JPG" class="left"/></p>
            
            <p>I may be stupid, but I'm not foolish enough to think that these languages will be the only remaining languages of my career. First of all, I'm not in the habit of defining my identity from the particular technologies I work with. Secondly, these are simply the environments I'm in love with now. I'm a dedicated family-man when it comes to people, but when it comes to programming languages, tools and environments I'm as bad as Liz Taylor. But jeez, why not embrace change? I don't do this for the paycheck. I do it because I love it. Why spend my time on things that aren't making me happy anymore?</p>
            
            <p>With that question burning a hole in my brain, it became clear that, for now, the twisting road that is my professional career is going to take a long detour through Ruby and Objective-C. I hope it gets some time with Erlang too&mdash;I've only dabbled with it a little bit, but found myself quite attracted right away.</p>
            
            <p>As I realized that my time was at an end at Evri, the question of "what next?" loomed large. Was there someone out there that wanted someone with the skills I wanted to cultivate? If so, were they doing something cool? Was I going to go "indie"? What would that look like? Questions, questions, questions. Fortunately I stumbled across a gig that is going to let me scratch most, if not all, of those itches.</p>
            
            <p>I'm taking October off to continue and complete the series of <a href="http://peepcode.com/products/iphone-view-controllers-part-i">iPhone</a> <a href="http://peepcode.com/products/iphone-view-controllers-part-ii">screencasts</a> I've been writing for <a href="http://peepcode.com">PeepCode</a>. In November I'll pick up my full-time gig and kick off the next phase of my professional life. You can't imagine how humbled and fortunate I feel to be able to do this.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>iPhone Screencasts</title>
      <link>
        http://alexvollmer.com/posts/2009/09/15/iphone-screencasts/
      </link>
      <comments>
        http://alexvollmer.com/posts/2009/09/15/iphone-screencasts/#comments
      </comments>
      <pubDate>
        Tue, 15 Sep 2009 04:13:03 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            iphone
        ]]>
      </category>
      <category>
        <![CDATA[
            peepcode
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2009/09/15/iphone-screencasts/
      </guid>
      <description>
        <![CDATA[
            I'm pleased to announce the release of the first in a series of <a href="http://peepcode.com">PeepCode screencasts for the iPhone SDK</a> I've been co-writing with Geoffrey Grosenbach. The first one (done in two parts) covers View Controller basics. We decided to start with View Controllers because they are at the core of all "productivity"-style applications. Once you have them down, you can start to put some meat on the bones of your iPhone apps.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p><a href="https://peepcode.com/products/iphone-view-controllers-part-i" target="_new"><img src="http://peepcode.com/system/uploads/2009/iphone-cover.png">
            </a></p>
            
            <p>I'm pleased to announce the release of the first in a series of <a href="http://peepcode.com">PeepCode screencasts for the iPhone SDK</a> I've been co-writing with Geoffrey Grosenbach. The first one (done in two parts) covers View Controller basics. We decided to start with View Controllers because they are at the core of all "productivity"-style applications. Once you have them down, you can start to put some meat on the bones of your iPhone apps.</p>
            
            <p>Once we've covered those, we'll work our way through the entire MVC Holy Trinity, one bit at a time. Along the way we hope to deep-dive on a few ancillary topics like Core Location, Game Kit as well as provisioning and unit-testing. We also hope to look at visual technologies like Core Graphics and Core Animation, as well model-related APIs like Core Data and web service-related libraries.</p>
            
            <p>The iPhone SDK is huge and there's a ton of material out there to cover. We're very excited to kick off what we hope will be a useful and successful instructional series for perspective iPhone developers out there. Let us know what you want to see.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>EvriVerse 2.0</title>
      <link>
        http://alexvollmer.com/posts/2009/08/19/evriverse-2-0/
      </link>
      <comments>
        http://alexvollmer.com/posts/2009/08/19/evriverse-2-0/#comments
      </comments>
      <pubDate>
        Wed, 19 Aug 2009 16:27:18 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            iphone
        ]]>
      </category>
      <category>
        <![CDATA[
            evri
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2009/08/19/evriverse-2-0/
      </guid>
      <description>
        <![CDATA[
            <img src="/images/2009/08/photo-5.jpg" class="left">
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p><img src="/images/2009/08/photo-5.jpg" class="left"/></p>
            
            <p>I just wanted to make a quick announcement that the <a href="http://evriverse.evri.com">iPhone app</a> I've been building at <a href="http://evri.com">work</a> just released a major update today. It's in the App Store and it's free. Check it out.</p>
            
            <p>Working on this release was a <em>ton</em> of fun. We cleaned up a good bit of the mess from the earlier version and  added some cool new features. What was even more fun was <em>removing</em> functionality that either didn't work well or wasn't obvious to the user. My god, there is <em>nothing</em> I love as much as deleting code. I don't know why I don't look for more opportunities to do this. Not only is it personally and emotionally liberating, it's often the "right" thing to do.</p>
            
            <p>The features we pulled out really had no business being in there in the first place. Of course, we didn't know that until we released the first version, got feedback from folks and spent some time outside of the development process. Y'know, hindsight, 20/20, blah blah blah.</p>
            
            <p>I think it is exceedingly difficult to get perspective on your work while you're creating it. There's just too much personal investment in the activity, especially if you get any sort of "flow" going. Creative types <em>love</em> "flow" and hate giving it up for any reason, even if the work suffers as a result. I think that's fine, but that's why there's a separate process known as "editing". The trick, of course, is to balance satisfying your possibly-self-indulgent creative urges with creating something of value.</p>
            
            <p>Anyway, that's enough of my philosophical rambling. We also put together a little brochure site at <a href="http://evriverse.evri.com">http://evriverse.evri.com</a>, including a screencast with music composed by yours truly. That process was a whitepaper-grade case study in "stuff you can do with iLife". I grabbed all the footage using <a href="http://store.shinywhitebox.com/home/home.html">iShow U</a>, did all the music in Garage Band (on the bus using the computer keyboard no less) and glued it all together in iMovie. Super simple, super fun.</p>
            
            <p>All in all I'm pretty pleased with the result. It's free, so check it out and let me (or <a href="http://evri.com">Evri</a>) know what you think.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Cocoa's Ways of Talking</title>
      <link>
        http://alexvollmer.com/posts/2009/06/25/cocoas-ways-of-talking/
      </link>
      <comments>
        http://alexvollmer.com/posts/2009/06/25/cocoas-ways-of-talking/#comments
      </comments>
      <pubDate>
        Thu, 25 Jun 2009 01:53:32 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            cocoa
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2009/06/25/cocoas-ways-of-talking/
      </guid>
      <description>
        <![CDATA[
            Getting objects to talk to one another in Objective-C is a easy as passing a message from one to the other. These messages are typically passed through the message-invocation mechanism of using the square-braces to bind a message and arguments to a receiver. Most of the time this is a perfectly reasonable way to communicate. However there are times when you need objects to communicate <em>without having explicit knowledge of one another.</em>
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>Getting objects to talk to one another in Objective-C is a easy as passing a message from one to the other. These messages are typically passed through the message-invocation mechanism of using the square-braces to bind a message and arguments to a receiver. Most of the time this is a perfectly reasonable way to communicate. However there are times when you need objects to communicate <em>without having explicit knowledge of one another.</em></p>
            
            <p>In this post we'll look at three ways to allow your objects to communicate with each other in a highly-decoupled manner using the Cocoa frameworks. Cocoa provides three common ways of connecting objects for messaging: Notifications, Key-Value Observation and delegate pattern.</p>
            
            <h3>Notifications</h3>
            
            <p>Both Cocoa and Cocoa Touch provide a notifications framework built on two classes: <code>NSNotificationCenter</code> and <code>NSNotification</code>. The former contains a singleton instance, (via the <code>defaultCenter</code> method) with which observers register themselves and observables post notifications. The latter is the "envelope" for the notification which can contain an <code>NSDictionary</code> instance of customized data as its payload.</p>
            
            <p><img src="/images/2009/06/nsnotificationcenter.png" alt="NSNotificationCenter.png" /></p>
            
            <p>When an object registers for notifications, it specifies a notification to listen for (as a <code>NSString</code>) and, optionally, a target object. If the target is set to an object, only notifications posted by that object will be received. If the target object is set to <code>nil</code>, <em>any</em> object posting that notification will be sent to the receiver. Senders can optionally include a <code>NSDictionary</code> instance filled with domain-specific objects. You can use this as a way to further parameterize a notification, or eschew it altogether.</p>
            
            <p>Consider the example below:</p>
            
            <div class="highlight"><pre><span class="k">@implementation</span> <span class="nc">Foo</span>&#x000A;    <span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">init</span> <span class="p">{</span>&#x000A;      <span class="k">if</span> <span class="p">(</span><span class="n">self</span> <span class="o">=</span> <span class="p">[</span><span class="n">super</span> <span class="n">init</span><span class="p">])</span> <span class="p">{</span>&#x000A;        <span class="p">[[</span><span class="n">NSNotificationCenter</span> <span class="n">defaultCenter</span><span class="p">]</span> <span class="nl">addObserver:</span><span class="n">self</span>&#x000A;                                                 <span class="nl">selector:</span><span class="k">@selector</span><span class="p">(</span><span class="nl">didReceiveNote:</span><span class="p">)</span>&#x000A;                                                     <span class="nl">name:</span><span class="s">@&quot;TheBigNotification&quot;</span>&#x000A;                                                   <span class="nl">object:</span><span class="nb">nil</span><span class="p">];</span>&#x000A;      <span class="p">}</span>&#x000A;      <span class="k">return</span> <span class="n">self</span><span class="p">;</span>&#x000A;    <span class="p">}</span>&#x000A;    &#x000A;    <span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nl">didReceiveNote:</span><span class="p">(</span><span class="n">NSNotification</span> <span class="o">*</span><span class="p">)</span><span class="n">notification</span> <span class="p">{</span>&#x000A;      <span class="n">NSLog</span><span class="p">(</span><span class="s">@&quot;Hey, I got a notification with user info: %@&quot;</span><span class="p">,</span> <span class="p">[</span><span class="n">notification</span> <span class="n">userInfo</span><span class="p">]);</span>&#x000A;    <span class="p">}</span>&#x000A;    <span class="k">@end</span>&#x000A;    &#x000A;    <span class="err">@</span><span class="n">implementation</span> <span class="n">Bar</span>&#x000A;    <span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">doSomething</span> <span class="p">{</span>&#x000A;      <span class="n">NSLog</span><span class="p">(</span><span class="s">@&quot;I&#39;m up to something!&quot;</span><span class="p">);</span>&#x000A;      <span class="n">NSDictionary</span> <span class="o">*</span><span class="n">stuff</span> <span class="o">=</span> <span class="p">[</span><span class="n">NSDictionary</span> <span class="nl">dictionaryWithObjectsAndKeys:</span><span class="s">@&quot;foo&quot;</span><span class="p">,</span> <span class="s">@&quot;bar&quot;</span><span class="p">,</span> <span class="nb">nil</span><span class="p">];</span>&#x000A;      <span class="p">[[</span><span class="n">NSNotificationCenter</span> <span class="n">defaultCenter</span><span class="p">]</span> <span class="nl">postNotificationName:</span><span class="s">@&quot;TheBigNotification&quot;</span>&#x000A;                                                          <span class="nl">object:</span><span class="nb">nil</span>&#x000A;                                                        <span class="nl">userInfo:</span><span class="n">stuff</span><span class="p">];</span>&#x000A;    <span class="p">}</span>&#x000A;    <span class="k">@end</span>&#x000A;    </pre>
            </div>
            
            
            <p>Here the <code>Foo</code> class registers itself as a notification receiver for the notification named "TheBigNotification" on <em>any</em> object. The <code>Bar</code> class will post the same notification when the <code>doSomething</code> method is invoked.</p>
            
            <p>We could have been much more specific about which object to observe, but I think this violates one of the key features of notifications which is that the sender and receiver are decoupled. To me if the receiver is going to go to the trouble of listening for notifications from a specific object, you'd be better off declaring a custom protocol and using the delegate pattern (explained below).</p>
            
            <p>While at the 2009 WWDC, I had a chance to pick the brains of some Apple engineers and one of them told me that notifications are really intended for one-to-many broadcasts. I think that's a great rule of thumb, because the code can really feel like overkill for single object-to-object messages. However, I'd add one more clause to that rule which is that notifications work for one-to-one messages when you would otherwise have to pass instances around of delegates just to connect objects.</p>
            
            <p>As an example, consider an iPhone application with a stack of view controllers in a <code>UINavigationController</code> instance. If you have a view controller several steps removed from another view controller (i.e. one is a further up the stack in a tab bar controller) and it needs to call some method back to the view controller lower down in the stack, each intermediate object would need a reference to the delegate class just to pass it down the line. This seems like the worst kind of encapsulation breakage since you're forcing a bunch of otherwise-unrelated classes to have knowledge of a class they don't even use.</p>
            
            <p>Maybe it's my long track-record with Java, but I pay attention to the classes I import and I like to keep that list as short as possible. The more classes know about each other, the more difficult it is to modify them.</p>
            
            <p>There's one final thing worth knowing about the <code>NSNotificationCenter</code>, and that is how it works with threads. When one object posts a notification, that call blocks while the notification is delivered to <em>all</em> targets. This means that you want your notification-handling code in the receiving object to be as quick as possible. If you can live with deferred notifications and want to avoid the synchronous nature of notification delivery, you can use the <code>NSNotificationQueue</code> instead.</p>
            
            <h3>Key-Value Observation</h3>
            
            <p>When I first read about, and used Key-Value Observation (KVO) it seemed like magic. You simply point one object at another and say "I'd like to know when this attribute changes" and Cocoa handles all of the notifications and threading automatically. Genius!</p>
            
            <p>KVO also allows you to use a special syntax when specifying the attributes you want to observe so that you can register with an object and traverse its object graph. Let's look at the classic customer/orders/line items example. If you want to know when changes are made to any line items in an order you would observe changes in the order like so:</p>
            
            <div class="highlight"><pre><span class="n">Order</span> <span class="o">*</span><span class="n">order</span> <span class="o">=</span> <span class="p">[</span><span class="n">self</span> <span class="n">anyOrder</span><span class="p">];</span>&#x000A;    <span class="p">[</span><span class="n">order</span> <span class="nl">addObserver:</span><span class="n">self</span>&#x000A;            <span class="nl">forKeyPath:</span><span class="s">@&quot;lineItems&quot;</span>&#x000A;               <span class="nl">options:</span><span class="n">NSKeyValueObservingOptionNew</span> <span class="o">|</span> <span class="n">NSKeyValueObservingOptionOld</span>&#x000A;               <span class="nl">context:</span><span class="nb">nil</span><span class="p">];</span>&#x000A;    </pre>
            </div>
            
            
            <p>Whenever you register for KVO, you <em>must</em> implement the method <code>- observeValueForKeyPath:ofObject:change:context:</code>. Unlike notifications, you don't get to specify a selector. This means that if you're object is observing several other objects, you will have to inspect the object given in the callback method and dispatch appropriately.</p>
            
            <p>Key paths have a ton of flexibility including the ability to traverse deep object graphs and observe aggregate functions on a collection (e.g. observe the max date of all of a customer's orders). Check out the <a href="http://developer.apple.com/documentation/Cocoa/Conceptual/KeyValueCoding/KeyValueCoding.html">Key-Value Programming Guide</a> for details, which is an indispensable reference.</p>
            
            <p>KVO works like notifications in that the thread that invokes the change will block until all observers have been notified and their callback methods have been invoked. Unlike notifications, there is no built-in asynchronous KVO-triggering mechanism unless you explicitly mutate properties in another thread using something like the <code>detachNewThreadSelector:toTarget:withObject:</code> class method of <code>NSThread</code>.</p>
            
            <p>For classes to be key-value <em>observable</em> they must be, what the documentation calls, "key-value compliant". This generally means that each observable property exposes certain methods for accessing and mutating them. Again, <a href="http://developer.apple.com/documentation/Cocoa/Conceptual/KeyValueCoding/KeyValueCoding.html">the KVO guide</a> is your go-to reference.</p>
            
            <p>KVO is structurally similar to notifications, but is really intended for fine-grained messaging, generally around your model classes. Notifications are intended for broader application-level events. Both mechanisms give you a way to decouple classes that would otherwise need more intimate knowledge of one another.</p>
            
            <p>Consider the simple case of managing a tabular view of line items in an order. When a user adds a line item, removes a line item or updates the quantity of a line item we want a field showing the total price to be updated. Without KVO we would need to add extra logic in our event-handing for the table view to also update the total field. With KVO our event-handling logic can simply focus on updating the underlying model. The total field will simply observe the total cost of the order and be updated appropriately. Now if we want to remove the total field or perhaps add another view of total information, we don't have to touch the original event-handing code.</p>
            
            <h3>Delegates</h3>
            
            <p>If you've spent any time with the Cocoa docs you'll run across The Delegate Pattern. This thing is like the quark&mdash;it is the fundamental building block of the Cocoa APIs. Conceptually, it's really pretty simple. A delegate is simply an object that provides custom behavior for another object, often by implementing a specific protocol.</p>
            
            <p>Let's return to our running example. A table view of line items in an order has to handle a lot of things such as rendering each cell, handling scrolling, managing user-generated events and so on. These features are common enough that they are simply part of the table view class itself. What is specific to your application is the <em>data</em> that goes in those cells and <em>the actions</em> to be taken for user-generated events. Cocoa solves this by providing protocols for delegating that behavior to custom classes. In the case of providing data, a data-oriented delegate would be queried by the table view for the total number of rows, appropriate object to put in each row, the column headers, etc. In the case of event-handling, the table view will forward events to the event-handling delegate.</p>
            
            <p>Notifications use the <code>NSNotificationCenter</code> (or by proxy, the <code>NSNotificationQueue</code>) as an intermediary between message-senders and message-receivers. KVO uses the observed object as the intermediary. In the delegate model there is no intermediate object, but you still have relatively decoupled code because the object that invokes methods on the delegate has no knowledge of what <em>class</em> of object it's dealing with. Put another way, you could remove all of the classes in your project that implement a particular delegate protocol, and the class that <em>uses</em> the delegate would still compile correctly.</p>
            
            <p>The delegate pattern isn't so much about decoupling which objects are communicating with each other, as much as decoupling the classes of those objects. This doesn't make it any less powerful than the other two methods. In a lot of cases a simple delegate model is much more straightforward and preferable the alternatives.</p>
            
            <p>The delegate pattern is Cocoa's version of dependency injection. The object receiving the injected dependency has no idea of the type (and by extension, the implementation) of the object it is receiving, only what it's capabilities are via a contract specified as a protocol. This is basic polymorphism where the interface and the implementation are separated to reduce coupling.</p>
            
            <p>Delegating is a popular alternative to sub-classing. It is a <em>preferred</em> alternative because it's less likely to break encapsulation. If customization is done through subclassing it can be difficult to know which methods to override and implement, and easy to break the parent class. This is why Java provides all sorts of safety features like marking methods <code>abstract</code> and <code>final</code>. However that is simply more of a headache to deal with and using polymorphism would be a much better design.</p>
            
            <p>So there you have it, three ways to keep your code flexible and loosely-coupled!</p>
            
            <h3>Resources</h3>
            
            <ul>
            <li><a href="http://developer.apple.com/documentation/Cocoa/Conceptual/Notifications/Introduction/introNotifications.html">Apple's Notification Programming Topics</a></li>
            <li><a href="http://developer.apple.com/documentation/Cocoa/Conceptual/KeyValueCoding/KeyValueCoding.html">Apple's Key-Value Coding Programming Guide</a></li>
            <li><a href="http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaFundamentals/Introduction/Introduction.html">Apple's Cocoa Fundamentals</a></li>
            <li><a href="http://www.mikeash.com/?page=pyblog/key-value-observing-done-right.html">Mike Ash's Well-Reasoned Critique of KVO Design</a></li>
            </ul>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>The iPhone and Web APIs</title>
      <link>
        http://alexvollmer.com/posts/2009/05/27/the-iphone-and-web-apis/
      </link>
      <comments>
        http://alexvollmer.com/posts/2009/05/27/the-iphone-and-web-apis/#comments
      </comments>
      <pubDate>
        Wed, 27 May 2009 03:57:39 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            iphone
        ]]>
      </category>
      <category>
        <![CDATA[
            cocoa
        ]]>
      </category>
      <category>
        <![CDATA[
            REST
        ]]>
      </category>
      <category>
        <![CDATA[
            evri
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2009/05/27/the-iphone-and-web-apis/
      </guid>
      <description>
        <![CDATA[
            We live in a very interesting time for application development. The distinctions between desktop, browser and mobile applications are blurring more and more every day. "Ubiquitous platform" applications, like <a href="http://evernote.com">Evernote</a>, are where the future is because it reduces the major hurdle of access for users. As time goes on users are going to expect applications to be available in a bunch of different ways that all need to work together.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <h1>The iPhone Ecosystem</h1>
            
            <p>We live in a very interesting time for application development. The distinctions between desktop, browser and mobile applications are blurring more and more every day. "Ubiquitous platform" applications, like <a href="http://evernote.com">Evernote</a>, are where the future is because it reduces the major hurdle of access for users. As time goes on users are going to expect applications to be available in a bunch of different ways that all need to work together.</p>
            
            <p>The common way to do this is base the application on shared-state accessed via an HTTP-based API. This makes it relatively easy to create apps for various devices as well as offer up a public API for others to use. An interesting side-effect of this is that the API, the shared-state and data behind it become the differentiator between products. The end-user client software is simply the packaging, turning the stand-alone application market into something more akin to the "razors and razor-blades model".</p>
            
            <p>Okay, none of this should be earth-shattering news to anyone. You should be prepared to write more apps that are integrated with one (or possibly more) "backend services". These services will, as likely as not, be connected over HTTP. As an iPhone developer you should know how to do this in your sleep.</p>
            
            <p>The <a href="http://www.evri.com">Evri</a> API is absolutely crucial to the EvriVerse phone application. Without it, the app does <em>nothing</em> of interest on its own. Our iPhone application is merely one kind of presentation of our web APIs. I don't think that's unique. What good would Tweetie be without Twitter?</p>
            
            <h1>Dealing with Web APIs</h1>
            
            <p>An unfortunate fact of integrating with web APIs is that they are, relatively speaking, slow. We don't want the user to get frustrated and give up on our app because they have their flow continually disrupted by waiting for data.</p>
            
            <p>There are some things you can do to mitigate the latency costs, if you control the API such as using some sensible caching parameters. However, especially if you don't control the web API, the bulk of what you should focus on is <em>concurrency</em> within your application. The biggest arrow in your quiver is figuring out how to turn unnecessarily serial operations into parallel ones. Now keep in mind that the iPhone doesn't support true multi-core concurrency. What I'm talking about is taking advantage of the I/O wait times that are inherently part of working with a high-latency I/O source (the web).</p>
            
            <p>Coming from a Java background, using threads seemed like a pretty natural approach and after reading the docs on the <code>NSThread</code>class I felt pretty confident I knew how to make it work. However, I was surprised by how quickly the code complicated. There is a lot to like about Objective-C, but I sorely miss Ruby's blocks and even Java's inner-classes. In Objective-C you connect methods together dynamically by handing objects selectors. The problem with this is that it's difficult to tell the high-level methods from the low-level callback methods just by looking at your class definition. Yes, you can use special comments and the like, but if you've worked in a language that has <em>structural</em> support for this differentiation, moving to a language without it feels like losing an arm.</p>
            
            <p>So instead of managing your own threads, let Cocoa Touch do the work for you. The <code>NSURLConnection</code> class is inherently asynchronous&mdash;you can fire a request and immediately move one with your life. Now you just need a little structure for handling the response and getting it back into your views.</p>
            
            <h1>The EvriApi</h1>
            
            <p>We tried to push as much of the mechanics of URL-handling into a separate code-base we call <a href="http://github.com/evri/EvriApi">EvriApi</a>. A good bit of the design was inspired by Matt Gemmell's <a href="http://mattgemmell.com/2008/02/22/mgtwitterengine-twitter-from-cocoa">MGTwitterEngine</a>, so I can't claim that I came up with the whole idea myself.</p>
            
            <p>We wanted the API to be as simple to work with as possible. Each API call has the same structure:
            *  Each API method takes zero-or-more domain-specific parameters, a selector and a target object.
            *  Each API method returns a unique request identifier
            *  The target object will have the given selector called when the request completes, and will be given an instance of the <code>EvriAPIResponse</code> class.
            *  If the response is successful (check <code>[response success]</code>), the payload of the request is retrieved as a domain-object
            *  If the response is unsuccessful, check the error message on the response
            *  Requests can be cancelled by handing the request identifier back to the API
            *  Attempts to cancel already-completed requests do nothing</p>
            
            <p>The <code>NSURLConnection</code> class makes asynchronous requests by default, and uses the familiar delegate model for handling the response. When a client invokes an API method, it returns immediately with a unique identifier (as a <code>NSString</code> instance) and the request is initiated on another thread which is handled automatically by <code>NSURLConnection</code>. The client is responsible for keeping track of that and using it to cancel any outstanding requests.</p>
            
            <p>When the request completes, the body of the response is parsed using the <code>NSXMLParser</code> class. The parser is given one of our specialized XML handlers as a delegate. These handlers deal with the XML event-stream and produce a model object or collection of model objects. The framework then packages this up into an <code>EvriAPIResponse</code> instance and invokes the selector given in the original request on the target given in the original request.</p>
            
            <h1>Wiring it Up</h1>
            
            <p>The EvriVerse app is primarily a "productivity" style application. In <a href="http://developer.apple.com/iPhone/library/documentation/UserExperience/Conceptual/MobileHIG/Introduction/Introduction.html">Interface Guidelines</a>-parlance this means that we use a lot of hierarchical navigation. In general we have no more than one or two API calls associated with a particular view controller.</p>
            
            <p>The common pattern for handling a user-initiated action that requires an API call is:</p>
            
            <ol>
            <li>A new view-controller is instantiated and displayed, typically with an activity indicator visible</li>
            <li>User-interaction is disabled on the new view controller</li>
            <li>The new view controller is displayed (usually by pushing it on the navigation controller stack).</li>
            <li>A request is submitted to the <code>EvriApi</code>, setting the new view controller as the target and some special method as the target selector.</li>
            <li>The request ID from the <code>EvriApi</code> is given to the newly-created view-controller</li>
            <li>When the API request finishes, the handler method on the new view-controller executes, handling both success and failure cases. It also re-enabled user interaction on the view.</li>
            <li>If the user navigates back before the request has completed, any outstanding requests are cancelled. This is usually handled in the <code>viewWillDisapper:(BOOL)animated</code> method of the view controller.</li>
            </ol>
            
            
            <p>Stepping back, the actors and interactions look something like this:</p>
            
            <p><img src="/images/2009/05/evri-api.png" alt="evri-api.png" /></p>
            
            <p>In code (snippets) it might look something like this:</p>
            
            <div class="highlight"><pre>    <span class="c1">// our parent view controller</span>&#x000A;        <span class="k">@implementation</span> <span class="nc">ParentViewController</span>&#x000A;        <span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">didSelectEntity</span> <span class="p">{</span>&#x000A;          <span class="n">ChildViewController</span> <span class="o">*</span><span class="n">cvc</span> <span class="o">=</span> <span class="p">[[[</span><span class="n">ChildViewController</span> <span class="n">alloc</span><span class="p">]</span> <span class="n">init</span><span class="p">]</span> <span class="n">autorelease</span><span class="p">];</span>&#x000A;        &#x000A;          <span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">navigationController</span> <span class="nl">pushController:</span><span class="n">cvc</span> <span class="nl">animated:</span><span class="n">NO</span><span class="p">];</span>&#x000A;        &#x000A;          <span class="n">NSString</span> <span class="o">*</span><span class="n">requestId</span> <span class="o">=</span> <span class="p">[</span><span class="n">EvriApi</span> <span class="nl">fetchEntityByURI:</span><span class="n">entity</span><span class="p">.</span><span class="n">uri</span>&#x000A;                                          <span class="nl">performSelector:</span><span class="err">@</span><span class="p">(</span><span class="nl">didReceiveEntityResponse:</span><span class="p">)</span>&#x000A;                                                 <span class="nl">onTarget:</span><span class="n">cvc</span><span class="p">];</span>&#x000A;        <span class="p">}</span>&#x000A;        <span class="k">@end</span>&#x000A;    &#x000A;    &#x000A;    &#x000A;        <span class="err">@</span><span class="n">interface</span> <span class="n">ChildViewController</span> <span class="o">:</span> <span class="n">UITableViewController</span> <span class="p">{</span>&#x000A;          <span class="n">NSString</span> <span class="o">*</span><span class="n">requestId</span><span class="p">;</span>&#x000A;        <span class="p">}</span>&#x000A;        &#x000A;        <span class="k">@property</span> <span class="p">(</span><span class="n">nonatomic</span><span class="p">,</span> <span class="n">retain</span><span class="p">)</span> <span class="n">NSString</span> <span class="o">*</span><span class="n">requestId</span><span class="p">;</span>&#x000A;        <span class="k">@end</span>&#x000A;        &#x000A;        <span class="k">@implementation</span> <span class="nc">ChildViewController</span>&#x000A;        &#x000A;        <span class="k">@synthesize</span> <span class="n">requestId</span><span class="p">;</span>&#x000A;        &#x000A;        <span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nl">didReceiveEntityResponse:</span><span class="p">(</span><span class="n">EvriAPIResponse</span> <span class="o">*</span><span class="p">)</span><span class="n">response</span> <span class="p">{</span>&#x000A;          <span class="c1">// TODO: hide the activity indicator</span>&#x000A;          <span class="c1">// TODO: re-enable user interaction</span>&#x000A;        &#x000A;          <span class="k">if</span> <span class="p">([</span><span class="n">response</span> <span class="n">success</span><span class="p">])</span> <span class="p">{</span>&#x000A;            <span class="n">Entity</span> <span class="o">*</span><span class="n">e</span> <span class="o">=</span> <span class="p">(</span><span class="n">Entity</span> <span class="o">*</span><span class="p">)[</span><span class="n">success</span> <span class="n">result</span><span class="p">];</span>&#x000A;             <span class="c1">// TODO: redisplay as necessary</span>&#x000A;          <span class="p">}</span>&#x000A;          <span class="k">else</span> <span class="p">{</span>&#x000A;            <span class="c1">// TODO: show an alert</span>&#x000A;          <span class="p">}</span>&#x000A;        <span class="p">}</span>&#x000A;        &#x000A;        <span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">viewWillDisappear</span> <span class="p">{</span>&#x000A;          <span class="p">[</span><span class="n">EvriApi</span> <span class="nl">cancelRequest:</span><span class="n">requestId</span><span class="p">];</span>&#x000A;        <span class="p">}</span>&#x000A;        &#x000A;        <span class="k">@end</span>&#x000A;    </pre>
            </div>
            
            
            <p>So there you have it, one developer's way of integrating the iPhone with web APIs. More to come. Stay tuned.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Peepcode meets MacRuby</title>
      <link>
        http://alexvollmer.com/posts/2009/05/15/peepcode-meets-macruby/
      </link>
      <comments>
        http://alexvollmer.com/posts/2009/05/15/peepcode-meets-macruby/#comments
      </comments>
      <pubDate>
        Fri, 15 May 2009 21:38:30 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            peepcode
        ]]>
      </category>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <category>
        <![CDATA[
            mac
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2009/05/15/peepcode-meets-macruby/
      </guid>
      <description>
        <![CDATA[
            I am pleased to announce the release of Peepcode's latest screencast about MacRuby written by yours truly with Geoffrey Grossenbach and technical editing by Laurent Sansonetti.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>I am pleased to announce the release of Peepcode's latest screencast about MacRuby written by yours truly with Geoffrey Grossenbach and technical editing by Laurent Sansonetti.</p>
            
            <p><a href="http://peepcode.com/products/meet-macruby" target="_new"><img src="https://peepcode.com/system/uploads/2009/macruby-title.png" alt="" /></a></p>
            
            <p>Ever since I saw Laurent's and Rich Kilmer's presentations about MacRuby and HotCocoa at RubyConf '08, I've been jazzed about what MacRuby brings to the world of Cocoa development. So check out the screencast for the coolest new Ruby on the block.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Incremental Find on the iPhone</title>
      <link>
        http://alexvollmer.com/posts/2009/05/15/typeahead-search-on-the-iphone/
      </link>
      <comments>
        http://alexvollmer.com/posts/2009/05/15/typeahead-search-on-the-iphone/#comments
      </comments>
      <pubDate>
        Fri, 15 May 2009 04:55:38 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            iphone
        ]]>
      </category>
      <category>
        <![CDATA[
            cocoa
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2009/05/15/typeahead-search-on-the-iphone/
      </guid>
      <description>
        <![CDATA[
            This is the first in a series of posts I'll be writing about my experiences developing the <a href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=312716560=8">EvriVerse iPhone application</a> for <a href="http://evri.com">work</a>. Since the end of 2008 I've been getting more and more into Cocoa for both the iPhone and Mac. The more I work on it, the more fun I have. I've got several side-projects in mind so I'll be spending a lot more time in Cocoa-land and, as a result, blogging and tweeting about it a lot more.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>This is the first in a series of posts I'll be writing about my experiences developing the <a href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=312716560=8">EvriVerse iPhone application</a> for <a href="http://evri.com">work</a>. Since the end of 2008 I've been getting more and more into Cocoa for both the iPhone and Mac. The more I work on it, the more fun I have. I've got several side-projects in mind so I'll be spending a lot more time in Cocoa-land and, as a result, blogging and tweeting about it a lot more.</p>
            
            <p>I wanted to kick this off with a little appetizer. We're going to look at how we implemented incremental find in EvriVerse. Evri has a huge database of structured entity data (people, places and things) and our users want to be able to search for any of them. After putting the initial prototype for EvriVerse on a few folks' phones, a lot of them wanted a search feature that worked more like our website.</p>
            
            <p><img src="/images/2009/05/picture-5.png" alt="Picture 5.png" /></p>
            
            <p>In the original implementation a user would type in the search field, then hit the "Search" button and then get their results back in the table view. This takes too many steps, and we wanted something that felt faster and more responsive.</p>
            
            <p><img src="/images/2009/05/picture-6.png" class="right"/></p>
            
            <p>So our incremental find solution needed the following properties:</p>
            
            <ul>
            <li>It should only send a search request when there is a measurable pause in input</li>
            <li>It should enable some kind of activity indicator while a search request is running</li>
            <li>A user should be able to modify the contents of the search field while a search request is in-progress</li>
            <li>It should not preclude the existing two-step search process</li>
            <li>If a search request is in process when another one starts, the first one should be cancelled and the new one should be the only request in-process</li>
            </ul>
            
            
            <p>At first I tried managing my own <code>NSThread</code> instance while tracking the timestamps of touch events in the <code>searchBar:textDidChange:</code> method (part of the <code>UISearchBarDelegate</code> protocol). If the the gap between time samples was big enough I'd cancel any outstanding request and start a new one. This seemed like a good approach at the whiteboard, but really fell apart in the implementation. I had race-conditions left and right and I had the sneaking suspicion that I was swimming upstream against "the Cocoa way".</p>
            
            <p>As an aside, if there's one thing I've learned in my brief time with Cocoa, it's that mucking about with your own threads is a pretty crummy way to do things asynchronously. Usually there's a much more elegant, built-in solution in the Cocoa APIs that will handle the threading for you. I'll riff on this theme in a later post.</p>
            
            <p>So back to the problem at hand. What to do? When I get stuck like this I often start thinking of how I'd do this in another language. This got me to thinking about how one would go about implementing this sort of thing in Javascript. This is standard Web-2.0 kind of stuff&mdash;right after drop-shadows and rounded corners, you learn about type-ahead search at AJAX School.</p>
            
            <p>See, Javascript has this very nifty little function named <code>setTimeout</code> where you hand it a function and tell it how long you want to wait until that function is executed, and it returns you a reference identifier. There is a companion function, <code>clearTimeout</code> that allows you to cancel any previously-created "timeout" function by handing it the reference identifier you got earlier from <code>setTimeout</code>.</p>
            
            <p>In the common AJAX-ified dynamic search that you often see, an event-handler observes the search field in a web form for key presses. Each key press cancels any existing timeout function, and starts a new one that will execute in whatever time-interval was chosen as the "quiescence period". The trick is that because you're scheduling future tasks, <em>and</em> you cancel any outstanding ones when you kick off a new one, you get the "measurable pause" feature in a simple, elegant solution.</p>
            
            <p>Cocoa provides a very similar mechanism, through the <code>NSTimer</code> class. In my view-controller, the code ended up looking something like this:</p>
            
            <div class="highlight"><pre><span class="k">@implementation</span> <span class="nc">FindViewController</span>&#x000A;    <span class="c1">// much implementation code has been elided for demonstration purposes</span>&#x000A;    &#x000A;    <span class="c1">// observe text-field changes and fire the scheduled task</span>&#x000A;    <span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nl">searchBar:</span><span class="p">(</span><span class="n">UISearchBar</span> <span class="o">*</span><span class="p">)</span><span class="n">searchBar</span> <span class="nl">textDidChange:</span><span class="p">(</span><span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="n">searchText</span> <span class="p">{</span>&#x000A;      <span class="k">if</span> <span class="p">([</span><span class="n">searchText</span> <span class="n">length</span><span class="p">]</span> <span class="o">&gt;</span> <span class="mi">2</span><span class="p">)</span> <span class="p">{</span>&#x000A;        <span class="k">if</span> <span class="p">(</span><span class="n">timer</span><span class="p">)</span> <span class="p">{</span>&#x000A;          <span class="p">[</span><span class="n">timer</span> <span class="n">invalidate</span><span class="p">];</span>&#x000A;          <span class="n">self</span><span class="p">.</span><span class="n">timer</span> <span class="o">=</span> <span class="nb">nil</span><span class="p">;</span>&#x000A;        <span class="p">}</span>&#x000A;        <span class="n">NSDictionary</span> <span class="o">*</span><span class="n">info</span> <span class="o">=</span> <span class="p">[</span><span class="n">NSDictionary</span> <span class="nl">dictionaryWithObjectsAndKeys:</span><span class="n">searchText</span><span class="p">,</span> &#x000A;                              <span class="s">@&quot;Text&quot;</span><span class="p">,</span> &#x000A;                              <span class="nb">nil</span><span class="p">];</span>&#x000A;        <span class="n">self</span><span class="p">.</span><span class="n">timer</span> <span class="o">=</span> <span class="p">[</span><span class="n">NSTimer</span> <span class="nl">scheduledTimerWithTimeInterval:</span><span class="mi">1</span>&#x000A;                                                      <span class="nl">target:</span><span class="n">self</span>&#x000A;                                                    <span class="nl">selector:</span><span class="k">@selector</span><span class="p">(</span><span class="nl">submitSearchRequest:</span><span class="p">)</span>&#x000A;                                                    <span class="nl">userInfo:</span><span class="n">info</span>&#x000A;                                                     <span class="nl">repeats:</span><span class="n">NO</span><span class="p">];</span>&#x000A;      <span class="p">}</span>&#x000A;    <span class="p">}</span>&#x000A;    &#x000A;    <span class="c1">// The method that runs in the NSTimer--equivalent to the function </span>&#x000A;    <span class="c1">// we&#39;d pass to Javascript&#39;s &quot;setTimeout&quot; function</span>&#x000A;    <span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nl">submitSearchRequest:</span><span class="p">(</span><span class="n">NSTimer</span> <span class="o">*</span><span class="p">)</span><span class="n">aTimer</span> <span class="p">{</span>&#x000A;      <span class="n">NSDictionary</span> <span class="o">*</span><span class="n">info</span> <span class="o">=</span> <span class="p">[</span><span class="n">aTimer</span> <span class="n">userInfo</span><span class="p">];</span>&#x000A;      <span class="n">NSString</span> <span class="o">*</span><span class="n">text</span> <span class="o">=</span> <span class="p">[</span><span class="n">info</span> <span class="nl">objectForKey:</span><span class="s">@&quot;Text&quot;</span><span class="p">];</span>&#x000A;      <span class="p">[</span><span class="n">activityIndicator</span> <span class="n">startAnimating</span><span class="p">];</span>&#x000A;      <span class="n">self</span><span class="p">.</span><span class="n">requestId</span> <span class="o">=</span> <span class="p">[</span><span class="n">EvriApi</span> <span class="nl">fetchEntitiesMatchingPrefix:</span><span class="n">text</span>&#x000A;                                            <span class="nl">performSelector:</span><span class="k">@selector</span><span class="p">(</span><span class="nl">didReceiveEntitiesDynamically:</span><span class="p">)</span>&#x000A;                                                   <span class="nl">onTarget:</span><span class="n">self</span><span class="p">];</span>&#x000A;    <span class="p">}</span>&#x000A;    &#x000A;    <span class="c1">// The callback from our API class, included for completeness</span>&#x000A;    <span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="nl">didReceiveEntitiesDynamically:</span><span class="p">(</span><span class="n">EvriApiResponse</span> <span class="o">*</span><span class="p">)</span><span class="n">response</span> <span class="p">{</span>&#x000A;      <span class="p">[</span><span class="n">timer</span> <span class="n">invalidate</span><span class="p">];</span>&#x000A;      <span class="n">self</span><span class="p">.</span><span class="n">timer</span> <span class="o">=</span> <span class="nb">nil</span><span class="p">;</span>&#x000A;      <span class="p">[</span><span class="n">activityIndicator</span> <span class="n">stopAnimating</span><span class="p">];</span>&#x000A;      <span class="k">if</span> <span class="p">([</span><span class="n">response</span> <span class="n">success</span><span class="p">])</span> <span class="p">{</span>&#x000A;        <span class="p">[</span><span class="n">entities</span> <span class="n">removeAllObjects</span><span class="p">];</span>&#x000A;        <span class="p">[</span><span class="n">entities</span> <span class="nl">addObjectsFromArray:</span><span class="p">(</span><span class="n">NSArray</span> <span class="o">*</span><span class="p">)[</span><span class="n">response</span> <span class="n">responseObject</span><span class="p">]];</span>&#x000A;        <span class="p">[</span><span class="n">tableView</span> <span class="n">reloadData</span><span class="p">];</span>&#x000A;      <span class="p">}</span>&#x000A;      <span class="k">else</span> <span class="p">{</span>&#x000A;        <span class="n">UIAlertView</span> <span class="o">*</span><span class="n">error</span> <span class="o">=</span> <span class="p">[[</span><span class="n">UIAlertView</span> <span class="n">alloc</span><span class="p">]</span> &#x000A;                              <span class="nl">initWithTitle:</span><span class="s">@&quot;Uh oh&amp;hellip;&quot;</span>&#x000A;                              <span class="nl">message:</span><span class="s">@&quot;Sorry, we were unable to execute your request. Please try again.</span>&#x000A;    <span class="s">&quot;</span>&#x000A;                              <span class="nl">delegate:</span><span class="nb">nil</span>&#x000A;                              <span class="nl">cancelButtonTitle:</span><span class="s">@&quot;OK&quot;</span>&#x000A;                              <span class="nl">otherButtonTitles:</span><span class="nb">nil</span><span class="p">];</span>&#x000A;        <span class="p">[</span><span class="n">error</span> <span class="n">show</span><span class="p">];</span>&#x000A;        <span class="p">[</span><span class="n">error</span> <span class="n">release</span><span class="p">];</span>&#x000A;      <span class="p">}</span>&#x000A;    <span class="p">}</span>&#x000A;    &#x000A;    <span class="k">@end</span>&#x000A;    </pre>
            </div>
            
            
            <p>In the <code>searchBar:textDidChange:</code> method we check to see if our instance field, <code>timer</code>, points at an existing <code>NSTimer</code> instance. If it does we cancel it, clear the reference, and start a new timer instance. That timer is configured to run in one second and will execute our <code>submitSearchRequest:</code> method. This makes a call to our API and designates the <code>didReceiveEntitiesDynamically:</code> method as the callback for that asynchronous operation.</p>
            
            <p>Finally, the <code>didReceivedEntitiesDynamically:</code> method handles the UI by disabling the wait indicator, dealing with any error cases and populating our table view if our request was successful. I'll write another post later about how the <code>EvriApi</code> class works and its design.</p>
            
            <p>So that's it. Simple and elegant. One of the things I love about working on several different programming languages is figuring out how to apply the best tricks of one language to another.</p>
            
            <p>That's all for now, but stay tuned! More iPhone posts to come&hellip;</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Introducing the EvriVerse</title>
      <link>
        http://alexvollmer.com/posts/2009/05/12/introducing-the-evriverse/
      </link>
      <comments>
        http://alexvollmer.com/posts/2009/05/12/introducing-the-evriverse/#comments
      </comments>
      <pubDate>
        Tue, 12 May 2009 17:28:15 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            evri
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2009/05/12/introducing-the-evriverse/
      </guid>
      <description>
        <![CDATA[
            <img src="/images/2009/05/graph.jpg" class="left">
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p><img src="/images/2009/05/graph.jpg" class="left"/></p>
            
            <p>After several months of work, I'm pleased to announce the release of Evri's EvriVerse for the iPhone. We've tried to capture the unique things we do at Evri into the awesomeness that is the iPhone. I won't spill all the details here, so check out out the "official" company <a href="http://blog.evri.com/index.php/2009/05/11/welcome-to-the-evriverse-evris-iphone-app/">post</a>.</p>
            
            <p>I've been working on this thing for a few months, mostly on a part-time basis. We have a 20% time program at Evri where devs can pitch ideas they want to spend one day a week on. I was <em>dying</em> to write an iPhone app and jumped at the opportunity to turn it into a 20% project. After some early prototypes I got enough attention that I was given a solid two-week sprint to finish it off.</p>
            
            <p>This was a ton of fun to work on and I have five or six posts on some things I discovered while working on it.</p>
            
            <p>It's free in the <a href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=312716560=8">App Store</a> now. We'd love to hear any feedback you may have, which you can post on the <a href="http://blog.evri.com/index.php/iphone-application-support/">Evri Support Page</a>.</p>
            
            <div style="clear: both;"></div>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>The Great Git Setup</title>
      <link>
        http://alexvollmer.com/posts/2009/05/08/the-great-git-setup/
      </link>
      <comments>
        http://alexvollmer.com/posts/2009/05/08/the-great-git-setup/#comments
      </comments>
      <pubDate>
        Fri, 08 May 2009 04:17:14 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            git
        ]]>
      </category>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2009/05/08/the-great-git-setup/
      </guid>
      <description>
        <![CDATA[
            One of the best ways to really learn the ins and outs of anything is to immerse yourself in all the gory details. Not only do you learn what works, what doesn't, what's elegant and what sucks, but you also start to grok the inner-workings. I just spent the last two weeks getting our own internal Git infrastructure up and running at work and I feel like Git and I have a new level of intimacy that we previously lacked. What follows is a review of the process and the solution that we've implemented.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>One of the best ways to really learn the ins and outs of anything is to immerse yourself in all the gory details. Not only do you learn what works, what doesn't, what's elegant and what sucks, but you also start to grok the inner-workings. I just spent the last two weeks getting our own internal Git infrastructure up and running at work and I feel like Git and I have a new level of intimacy that we previously lacked. What follows is a review of the process and the solution that we've implemented.</p>
            
            <p>For most of us at work, our primary Git experience is with GitHub. This is a good way to learn how Git's distributed model works, but GitHub provides some nice infrastructure that you simply don't have with a stock Git install. You have to cobble-together the rest from a variety of sources. <strike>So GitHub guys&mdash;if you're listening&mdash;it would be <em>fantastic</em> if you provided real white-label GitHub solution for organizations to install locally. You guys have really spoiled us.</strike> (oops, nevermind. Checkout <a href="http://fi.github.com/tour.html">GitHub:fi</a>). But until then, we have to roll our own&hellip;</p>
            
            <h1>The Goals</h1>
            
            <p>First, let's review what exactly we were trying to accomplish. We wanted a solution that:
            *  easily converted existing Subversion repositories to Git
            *  provided a usable web view over all repos that was equal to, or exceeded, the stock SVN/DAV model
            *  allowed us to designate a set of "authoritative" repositories
            *  provided for "developer" repositories to allow developers to publish their changes
            *  allowed for patch reviews (if teams decide to do that)</p>
            
            <h1>The Solution</h1>
            
            <p>Right away we knew that we wanted two different types of repositories: authoritative and developer. Authoritative repos are ones that host the "official" source code and from which we create release builds from. When a developer wants to start working on a project, these are the repos they clone to get started.</p>
            
            <p>The other type of repository belongs to an individual developer. Developers have read/write access to their own repos, but others only have read-only access. This allows developers to share changes in a pull-request model similar to how GitHub works.</p>
            
            <h2>Permissions &amp; Connectivity</h2>
            
            <p>Developers connect to these repositories in two different ways. We use the built-in <code>git://</code> protocol (with <code>git-daemon</code>) for read-only access and <code>ssh://</code> for read-write access. Developers clone authoritative repos and other developers' repos using the read-only <code>git://</code> protocol. Developers have a remote reference to their developer repository using ssh.</p>
            
            <p>For each project, we designated one or more maintainers that have write-access to the authoritative repositories. These folks accept patches from other developers, test and integrate them on their local machines, and push final changes to the authoritative repositories. We manage the authoritative repos using <a href="http://eagain.net/gitweb/?p=gitosis.git">gitosis</a>. Check out <a href="http://scie.nti.st/2007/11/14/hosting-git-repositories-the-easy-and-secure-way">this great tutorial</a> for details on setting up gitosis.</p>
            
            <h2>gitosis</h2>
            
            <p>On our central "git box" we create a "git" user which owns both the <code>gitosis-admin</code> repository as well as all the authoritative repositories. Gitosis is bootstrapped  with a single SSH public key that gives the associated user the ability to write to that Git repo over SSH. That user can then add more keys and configuration allowing other users to manage projects. Gitosis works a bit like the CVSROOT project in CVS&mdash;it is a special Git repo that allows us to configure other Git repositories. We use gitosis to configure our designated maintainers for each project.</p>
            
            <p>Our authoritative repos are housed in <code>/home/git/repositories</code>. Our developer repositories are located in <code>/opt/repos</code>, where each developer has their own directory to put Git repos in.</p>
            
            <h2>git daemon</h2>
            
            <p>We run <code>git daemon</code> to allow for anonymous read-only access. We configured <code>git daemon</code> to serve up all Git repositories found in the <code>/opt/repos</code> directory with read-only access. We also symlink the authoritative repositories (<code>/home/git/repositories</code>) into this directory so that we can serve up read-only access to authoritative repositories also.</p>
            
            <h2>gitweb</h2>
            
            <p>For all the power Git has, there sure are a lot of half-baked web interfaces for it. We did our best to vet the tools listed on the <a href="http://git.or.cz/gitwiki/InterfacesFrontendsAndTools">Git site</a>. A good number of the them were either abandoned or simply didn't work. In the end we went with the venerable old <a href="http://git.or.cz/gitwiki/Gitweb">gitweb</a>.</p>
            
            <p>Gitweb has a ton of functionality&mdash;including all kinds of search capability, different views (by tag, by commit, by tree) and even serves up an Atom feed of changes&mdash;but it is definitely a designed-by-developers-for-developers</code>. (NB: In case you missed the markup, that is considered an epithet on my team).</p>
            
            <p>Frankly I was amazed that somebody hadn't built a Rails app to manage this stuff. Yes, we  looked at <a href="http://gitorious.org/projects/gitorious">gitorious</a> and even tried to set it up. After three hours I still didn't have a working installation and my patience had run out. While gitorious appears to be a nice turn-key solution, I don't think it's actually that great of a fit for us. So gitweb it is. Sigh.</p>
            
            <h2>The Flow</h2>
            
            <p>With me so far? Maybe not. Let's look at a picture then&hellip;</p>
            
            <p><img src="/images/2009/05/98ee3ae7-c4f4-4d1d-b49b-dcc230ea7459.jpg" alt="98EE3AE7-C4F4-4D1D-B49B-DCC230EA7459.jpg" /></p>
            
            <p>In this picture the authoritative and developer repos are drawn in separate boxes (since they're logically separate), but they are located on the same machine in reality. Let's cover a couple of common scenarios:</p>
            
            <h2>New Developer, Old Project</h2>
            
            <p>A developer starts by cloning an authoritative repo using the <code>git://</code> protocol (remember, read-only). This will create, by default, a remote reference named <code>origin</code> that points back to the authoritative repo.</p>
            
            <p>The next step is for them to create a developer repo in their directory for the same project on the central git machine. They also want to a remote reference to their developer repo so that they can push changes to that. That would looks something like:</p>
            
            <div class="highlight"><pre>git remote add alex ssh://git/opt/repos/alex/circus/clown-car&#x000A;    </pre>
            </div>
            
            
            <p>If this user is also a maintainer, they need to add a <em>third</em> remote reference which gives them read/write access to the authoritative repository. The way that gitosis works is by accepting SSH keys on behalf of the git user. So if you're properly configured, you can push changes over SSH by logging in as the <code>git</code> user. A maintainer would add a read/write reference like so:</p>
            
            <div class="highlight"><pre>git remote add main ssh://git@git/home/git/repositories/circus/clown-car&#x000A;    </pre>
            </div>
            
            
            <p>Because of the way gitosis configures the SSH keys in <code>/home/git/.ssh/authorized_keys</code> and the fact that the default protocol is ssh, this remote reference could also be added like this:</p>
            
            <div class="highlight"><pre>git remote add main git@git:circus/clown-car&#x000A;    </pre>
            </div>
            
            
            <p>Now because there are number of steps involved here, we wrote a little command-line tool (as a RubyGem) that takes care of these steps all in one go. Right now, it's <em>very</em> specific to our setup at Evri, but I can see how we might extract a common tool (oh boy&hellip;another side-project&hellip;)</p>
            
            <h2>Old Developer, New Project</h2>
            
            <p>When it's time to create a new project, a developer usually starts out by creating a new Git repo on their local box while they're building out the initial version. Once it's time to share, that developer can create a new developer repo on our central Git machine just by SSH'ing in and making the appropriate directory, adding the remote ref to their local repo and pushing changes. Again, our internal tools sets this up all in one go.</p>
            
            <p>Once that project is ready to have an authoritative repo, a gitosis-enabled user will pull the latest changes for the <code>gitosis-admin</code> project. They'll edit the <code>gitosis.conf</code> file to add the new project (and members) and commit the changes. Then the developer adds a new remote ref (a read/write one as the <code>git</code> user) and pushes changes out to the main repo.</p>
            
            <h2>Sharing Patches</h2>
            
            <p>The most common flow is when developers share patches with each other. There are a couple of ways to do it. Developers keep their developer repository up-to-date and email requests to their teammates to pull changes from their repo (a la GitHub). Developers may also choose to share patches via email using <code>git format-patch</code>, <code>git send-mail</code> and <code>git am</code>.</p>
            
            <p>Ultimately the maintainers are responsible for gathering patches from developers and integrating them back into the authoritative repositories.</p>
            
            <p><img src="/images/2009/05/git-cycle.png" alt="git-cycle.png" /></p>
            
            <p>For folks used to version control systems like CVS or Subversion this feels like an awful lot of hoop-jumping. One part that I can't diagram or explain as a series of technical bullet-points is the stewardship the pro-Git folks have to take on. People who are switching to Git get frustrated by the byzantine nomenclature and steep learning curve, so a big part of the change and setup is simply helping people out when they get stuck.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Using Git as a Safety Net</title>
      <link>
        http://alexvollmer.com/posts/2009/03/14/using-git-as-a-safety-net/
      </link>
      <comments>
        http://alexvollmer.com/posts/2009/03/14/using-git-as-a-safety-net/#comments
      </comments>
      <pubDate>
        Sat, 14 Mar 2009 18:11:32 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            git
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2009/03/14/using-git-as-a-safety-net/
      </guid>
      <description>
        <![CDATA[
            I spent the last week on a top-secret iPhone application at work. It has been a blast, in part, because it's been so fun to learn so much new information so quickly. That has meant trying out lots of ideas and, more often than not, rolling them back and trying again. The problem is that doing this kind of experimentation can be an absolute productivity-killer in terms of managing your changes&mdash;unless you have a good tool to manage large chunks of changes, you can spend a lot of time trying to do it manually (and probably screwing it up in the process).
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>I spent the last week on a top-secret iPhone application at work. It has been a blast, in part, because it's been so fun to learn so much new information so quickly. That has meant trying out lots of ideas and, more often than not, rolling them back and trying again. The problem is that doing this kind of experimentation can be an absolute productivity-killer in terms of managing your changes&mdash;unless you have a good tool to manage large chunks of changes, you can spend a lot of time trying to do it manually (and probably screwing it up in the process).</p>
            
            <p>While doing Java development, I've often used Eclipse's file history feature to roll things back. The downside is that it's file-by-file and not easy to tag the current state of your entire project in one go. It appears that Xcode has this feature, but since I'm using Git for my SCM, I figured why not use that instead?</p>
            
            <p>So my new flow has been to stage changes to the index whenever I make <em>any</em> progress and the app is still in a working state. This is different from a commit, which I still like to think of as a succinct, whole change around a particular feature. The incremental staging is more like dribbling micro-changes to the next stage prior to committing. It can take several attempts to get to a real, first-class commit.</p>
            
            <p>I like using <a href="http://zagadka.vm.bytemark.co.uk/magit/">magit</a>, so I keep Emacs running in the background. When I get to a good checkpoint, I stage hunks or entire files to the index. When I get enough to make a full commit, I commit them. If you're running git exclusively on the command-line, this would be the equivalent of using <code>git add -i</code>.</p>
            
            <p>When I first started using Git, I couldn't see the value in Git's stage-commit-push workflow. Now I get it. Like most Git tricks, this one is probably obvious to a lot of folks, but for me it's been a real life-saver on this project. I've been able to be much more cavalier with experimentation because I can easily revert changes with a single keystroke. Nifty!</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>What Jersey Means To Java</title>
      <link>
        http://alexvollmer.com/posts/2009/03/04/what-jersey-means-to-java/
      </link>
      <comments>
        http://alexvollmer.com/posts/2009/03/04/what-jersey-means-to-java/#comments
      </comments>
      <pubDate>
        Wed, 04 Mar 2009 18:42:49 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            java
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2009/03/04/what-jersey-means-to-java/
      </guid>
      <description>
        <![CDATA[
            In the last few days at <a href="http://evri.com">work</a> I've been migrating a home-grown REST framework over to the <a href="https://jersey.dev.java.net/">Jersey project</a> (the reference implementation of JSR-311 or, JAX-RS). Previously I had done some work moving JRuby into the VM and launching Merb. It was satisfying to figure out how to do that, but involved an awful lot of wiring and special-casing.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>In the last few days at <a href="http://evri.com">work</a> I've been migrating a home-grown REST framework over to the <a href="https://jersey.dev.java.net/">Jersey project</a> (the reference implementation of JSR-311 or, JAX-RS). Previously I had done some work moving JRuby into the VM and launching Merb. It was satisfying to figure out how to do that, but involved an awful lot of wiring and special-casing.</p>
            
            <p>As anyone who has read this blog recently knows, the luster is coming off of Java for me in a big way. These days my goals are simply to co-exist with it in a way that keeps me happy. I'm not going to be able to toss Java overboard so my working-life becomes a question of how to be happy with the situation.</p>
            
            <p>While our home-grown framework has worked well for us, I'd much rather see us using something with wider adoption and use. So I took a look at Jersey and came away pretty impressed. Looking at how the API is built and what you need to do to build REST-ful web services in Java I couldn't help but feel like Java has learned some lessons from the rest of the world.</p>
            
            <p>The original Servlet API is what could be termed a "classic" Java API, wherein consistency and type-safety are the rules. In the Servlet API, you interact with requests and responses as monolithic objects. Each type of HTTP method (i.e. GET, POST, PUT and DELETE) has their own method which takes a <code>ServletRequest</code> and a <code>ServletResponse</code> object.</p>
            
            <p>It's a reasonable first solution, but it breaks down quickly. The primary issue is that by packing all possible request-related data into a single request object, and all possible response-related data into a single response object the classes themselves start to feel bloated. A more sinister second-order effect is that these classes, especially the response object, have some hidden state and "gotchas" because they are so general.</p>
            
            <p>As an example, consider the case where you want to return a response with a non-200 response code, an entity body and some headers. The order in which you set these on the response object is important because of how it's implemented. You need to set the status and headers <em>before</em> you write any output to the stream. This make sense because you want a stream-oriented interface which means not storing the entire response in memory and then flushing. But, due to the strict order of HTTP response messages, you need to flush the header information prior to sending the body. It's not a particularly difficult thing to remember, but it is unnecessary mental overhead that is an artifact of the implementation.</p>
            
            <p>The request object is its own special brand of fun to deal with. Again its broad coverage makes for a rather clunky API to deal with. You want request parameters? You have to grovel through <code>String</code> arrays if you want to capture all of them. When implementing a Servlet, often what you want is some combination of request parameters, cookies and header; rarely do you need all of them at once. However what you get is one big &uuml;ber-object that has everything. Enjoy!</p>
            
            <p>One final beef with the <code>ServletRequest</code> class is that getting path parameters out is an absolute nightmare. If you're building REST resources you really really care about the path as it is <em>the</em> way to identify resources. The poor support the <code>ServletRequest</code> class provides for this is simply shocking. Here's a <code>String</code> &mdash; you parse it and figure out what the hell the segments are.</p>
            
            <p>In contrast, Jersey has a much looser philosophy with how requests and responses are handled. First, the monolithic request and response objects go away. Instead your methods provide the narrowest possible interface, expressing only what they need in exacting terms. This is done by making extensive use of Java annotations to mark up simple method parameters. For example, if you have a resource that needs a request parameter, use the <code>@QueryParam</code> annotation. Need a header? Just use <code>@HeaderParam</code>. Interested in cookies? Use the <code>@CookieParam</code> annotation. Here's an example:</p>
            
            <div class="highlight"><pre><span class="kd">public</span> <span class="n">MyResult</span> <span class="nf">getMyResult</span><span class="o">(</span><span class="nd">@QueryParam</span><span class="o">(</span><span class="s">&quot;name&quot;</span><span class="o">)</span> <span class="n">String</span> <span class="n">name</span><span class="o">)</span> <span class="o">{</span>&#x000A;        <span class="o">...</span>&#x000A;    <span class="o">}</span>&#x000A;    </pre>
            </div>
            
            
            <p>This does away with a tremendous amount of "busy-work". You want the "name" parameter? By god you're going to get it&mdash;no intermediate objects to reach into and pull stuff out of.</p>
            
            <p>A really nice side-effect of this is that writing tests for your resources become <em>so much</em> cleaner than using the more general Servlet API. Instead of setting up the monolithic request and response objects, you simply pass in the bits you want.</p>
            
            <p>What about the response side? I think that it's in this area that we really see a fundamental philosophical shift emerge. In an earlier time the textbook answer to how to design an API like this would be to have each request method return some kind of superclass or interface. This would allow us to use polymorphism to vary the response, but keep our type-safety.</p>
            
            <p>Jersey takes a different approach based on real-world needs. Whereas the Servlet API is intended to keep everyone equally happy by making sure everyone suffers the same amount of pain, Jersey lets you vary what you return&mdash;no special markers, no special configuration. If you have a simple case where you want to serialize an object as the entity response, just return the object.</p>
            
            <p>What if you need to fiddle with the response some more? Maybe set some headers or alter the status code? In this case your method returns a <code>Response</code> instance. Now this may sound monolithic and perhaps, under the covers, it is. What saves it from degenerating into the Servlet API is the fact that you build a <code>Response</code> object with only as much as you need.</p>
            
            <p>In the Servlet API you might have to set a moderately complicated response like this:</p>
            
            <div class="highlight"><pre><span class="kd">public</span> <span class="kt">void</span> <span class="nf">doGet</span><span class="o">(</span><span class="n">HttpServletRequest</span> <span class="n">req</span><span class="o">,</span> <span class="n">HttpServletResponse</span> <span class="n">resp</span><span class="o">)</span> <span class="o">{</span>&#x000A;      <span class="c1">// do some stuff</span>&#x000A;      <span class="n">resp</span><span class="o">.</span><span class="na">addHeader</span><span class="o">(</span><span class="s">&quot;Expires&quot;</span><span class="o">,</span> <span class="n">computeExpires</span><span class="o">());</span>&#x000A;      <span class="n">resp</span><span class="o">.</span><span class="na">setStatus</span><span class="o">(</span><span class="mi">201</span><span class="o">);</span>&#x000A;      <span class="n">writeResponse</span><span class="o">(</span><span class="n">resp</span><span class="o">.</span><span class="na">getWriter</span><span class="o">(),</span> <span class="k">new</span> <span class="n">MyStuff</span><span class="o">());</span>&#x000A;    <span class="o">}</span>&#x000A;    &#x000A;    <span class="kd">private</span> <span class="kt">void</span> <span class="nf">writeResponse</span><span class="o">(</span><span class="n">Writer</span> <span class="n">writer</span><span class="o">,</span> <span class="n">MyStuff</span> <span class="n">stuff</span><span class="o">)</span> <span class="o">{</span>&#x000A;      <span class="c1">// do whatever you have to do to serialize your object</span>&#x000A;    <span class="o">}</span>&#x000A;    </pre>
            </div>
            
            
            <p>In Jersey it looks like this:</p>
            
            <div class="highlight"><pre><span class="kd">public</span> <span class="n">Response</span> <span class="nf">getMyStuff</span><span class="o">()</span> <span class="o">{</span>&#x000A;      <span class="n">MyStuff</span> <span class="n">stuff</span> <span class="o">=</span> <span class="k">new</span> <span class="n">MyStuff</span><span class="o">();</span>&#x000A;      <span class="k">return</span> <span class="n">Response</span><span class="o">.</span><span class="na">created</span><span class="o">(</span><span class="n">stuff</span><span class="o">).</span><span class="na">expires</span><span class="o">(</span><span class="n">computeExpires</span><span class="o">()).</span><span class="na">build</span><span class="o">();</span>&#x000A;    <span class="o">}</span>&#x000A;    </pre>
            </div>
            
            
            <p>The amount of code probably comes out to be the same, but the fact that you can build the response in a single line feels really good to me. The amount of vertical space dedicated to response-building is much more proportional to it's conceptual space in the method in Jersey than the Servlet API.</p>
            
            <p>Now I realize that comparing Jersey to the Servlet API is perhaps unfair. The Servlet API is really designed to operate a layer below where Jersey plays. But I think the same arguments apply to a majority of the other popular web frameworks. The models are all essentially the same.</p>
            
            <p>So here's where the philosophical sea-change comes in. To vary the response type to do the right thing means that after a decade of existence, people are finally embracing Java's reflection capabilities. Up until pretty recently this was considered the domain of the lunatic fringe.</p>
            
            <p>The primary argument against reflection has always been that it's too slow. While it's true that their can be a penalty <em>under certain circumstances</em>, the modern perspective is that it's an acceptable price to pay for a kinder, gentler API. Simply put, this level of dynamism is essential for reducing the amount of boilerplate and busywork.</p>
            
            <p>I think if other frameworks like Rails, Django and Merb hadn't gained so much traction in the last few years Java would have continued on it merry way. However I think the popularity of those alternatives has forced the Java greybeards to learn a thing or two. Strict static type-safety can become a burden and using reflection to vary behavior instead of polymorphism can make for some very clean APIs.</p>
            
            <p>After spending a few days with Jersey I find myself a little re-energized to work with Java again. We're even considering <a href="http://groovy.codehaus.org/">Groovy</a>  as a way to cut down Java's chubby syntax for another small productivity win. Of course, my personal preference would be to do this JRuby since I, personally, don't find Groovy to be terribly compelling on its own. But since Jersey is <em>so</em> dependent on annotations, using JRuby is a non-starter.</p>
            
            <p>Who knows? This could be a complete fiasco and another frustrated attempt at tilting windmills. I'm already a little suspicious of the "enterprisey" way this thing gets configured. However if nothing else, it's refreshing to see some new ideas make their way into Java after all these years.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>You Put Merb In My Jetty!</title>
      <link>
        http://alexvollmer.com/posts/2009/02/11/you-put-merb-in-my-jetty/
      </link>
      <comments>
        http://alexvollmer.com/posts/2009/02/11/you-put-merb-in-my-jetty/#comments
      </comments>
      <pubDate>
        Wed, 11 Feb 2009 23:31:53 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            java
        ]]>
      </category>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2009/02/11/you-put-merb-in-my-jetty/
      </guid>
      <description>
        <![CDATA[
            In the latest update of <em>The Chronicles of Stuff Alex Figures out at Work,</em> our intrepid hero figures out how to run Merb inside an embedded Jetty instance!
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>In the latest update of <em>The Chronicles of Stuff Alex Figures out at Work,</em> our intrepid hero figures out how to run Merb inside an embedded Jetty instance!</p>
            
            <p>Now you may ask yourself, "for the love of God, why would you want to do something like this?" Well, at <a href="http://www.evri.com%20Evri!">work</a> we do a lot of internal web services. For my particular team, we've found a real sweet-spot by using an embedded Jetty server sitting right next to a BDB instance. There are no extra processes or packages to manage (e.g. apache or a RDBMS). However we were becoming dissatisfied with our current web layer which is a homegrown REST framework that sits on top of the Servlet API. So in a fit of rage, I decided to see if I could stuff Merb in the middle of this mess.</p>
            
            <p>You may also be asking yourself, "why not use the <a href="http://blog.nicksieger.com/articles/2008/05/08/introducing-jruby-rack%20JRuby-Rack">jruby-rack</a> gem directly?" The answer is that the jruby-rack gem makes a lot of assumptions about how you want to run your application. First it assumes that you're cool with packaging things up as a WAR (which I'm not) and, secondly, that your application is <em>primarily</em> a Rails/Merb application. In my case, for better or worse, our app is really a BDB application with a Merb app glommed onto the side for web visibility.</p>
            
            <h1>The Solution</h1>
            
            <p>I can't take complete credit for this solution. If I hadn't found <a href="http://www.trampolinesystems.com/blog/machines/2008/11/27/rails-22-jruby-jetty-win/%20rails%202.2%20+%20jruby%20+%20jetty%20=%20win">Jan Berkel's post on putting Rails in Jetty</a> I would have <em>never</em> figured out how to stuff Merb in there. To give yourself some context, take a look at that post first. Then take a look at the "Merb-ified" version of the same recipe below. Both solutions assume that you're configuring Jetty within JRuby.</p>
            
            <div class="highlight"><pre><span class="n">server</span> <span class="o">=</span> <span class="n">org</span><span class="o">.</span><span class="n">mortbay</span><span class="o">.</span><span class="n">jetty</span><span class="o">.</span><span class="n">Server</span><span class="o">.</span><span class="n">new</span>&#x000A;    <span class="n">thread_pool</span> <span class="o">=</span> <span class="n">org</span><span class="o">.</span><span class="n">mortbay</span><span class="o">.</span><span class="n">thread</span><span class="o">.</span><span class="n">QueuedThreadPool</span><span class="o">.</span><span class="n">new</span>&#x000A;    <span class="n">thread_pool</span><span class="o">.</span><span class="n">min_threads</span>  <span class="o">=</span> <span class="mi">5</span>  <span class="c1"># adjust as needed</span>&#x000A;    <span class="n">thread_pool</span><span class="o">.</span><span class="n">max_threads</span>  <span class="o">=</span> <span class="mi">50</span>&#x000A;    <span class="n">server</span><span class="o">.</span><span class="n">set_thread_pool</span><span class="p">(</span><span class="n">thread_pool</span><span class="p">)</span>&#x000A;    <span class="n">context</span> <span class="o">=</span> <span class="no">Context</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="kp">nil</span><span class="p">,</span> <span class="s2">&quot;/&quot;</span><span class="p">,</span> <span class="no">Context</span><span class="o">::</span><span class="no">NO_SESSIONS</span><span class="p">)</span>&#x000A;    <span class="n">context</span><span class="o">.</span><span class="n">add_filter</span><span class="p">(</span><span class="s2">&quot;org.jruby.rack.RackFilter&quot;</span><span class="p">,</span> <span class="s2">&quot;/*&quot;</span><span class="p">,</span> <span class="no">Handler</span><span class="o">::</span><span class="no">DEFAULT</span><span class="p">)</span>&#x000A;    <span class="n">context</span><span class="o">.</span><span class="n">set_resource_base</span><span class="p">(</span><span class="no">Environment</span><span class="o">.</span><span class="n">resolve</span><span class="p">)</span>&#x000A;    <span class="n">context</span><span class="o">.</span><span class="n">add_event_listener</span><span class="p">(</span><span class="no">MerbServletContextListener</span><span class="o">.</span><span class="n">new</span><span class="p">)</span>&#x000A;    <span class="n">context</span><span class="o">.</span><span class="n">set_init_params</span><span class="p">(</span><span class="n">java</span><span class="o">.</span><span class="n">util</span><span class="o">.</span><span class="n">HashMap</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s1">&#39;merb.root&#39;</span><span class="o">=&gt;</span><span class="p">;</span> <span class="no">Environment</span><span class="o">.</span><span class="n">resolve</span><span class="p">,</span>&#x000A;        <span class="s1">&#39;merb.environment&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;production&#39;</span><span class="p">,</span>&#x000A;        <span class="s1">&#39;public.root&#39;</span> <span class="o">=&gt;</span> <span class="no">Environment</span><span class="o">.</span><span class="n">resolve</span><span class="p">(</span><span class="s1">&#39;public&#39;</span><span class="p">),</span>&#x000A;        <span class="s1">&#39;gem.path&#39;</span> <span class="o">=&gt;</span> <span class="no">Environment</span><span class="o">.</span><span class="n">resolve</span><span class="p">(</span><span class="s1">&#39;gems&#39;</span><span class="p">),</span>&#x000A;        <span class="s1">&#39;org.mortbay.jetty.servlet.Default.relativeResourceBase&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;/public&#39;</span><span class="p">,</span>&#x000A;        <span class="s1">&#39;jruby.max.runtimes&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;1&#39;</span><span class="p">))</span>&#x000A;    <span class="n">context</span><span class="o">.</span><span class="n">add_servlet</span><span class="p">(</span><span class="no">ServletHolder</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="no">DefaultServlet</span><span class="o">.</span><span class="n">new</span><span class="p">),</span> <span class="s2">&quot;/&quot;</span><span class="p">)</span>&#x000A;    <span class="n">server</span><span class="o">.</span><span class="n">set_handler</span><span class="p">(</span><span class="n">context</span><span class="p">)</span>&#x000A;    <span class="n">server</span><span class="o">.</span><span class="n">start</span>&#x000A;    </pre>
            </div>
            
            
            <h1>Tweaking</h1>
            
            <p>At first blush our performance seemed to be pretty lacking. This required two tweaks: putting Merb in "production" mode and dealing with poor I/O due to logging. In the previous snippet you will notice that we set the <tt>merb.environment</tt> to <tt>production</tt>. Yes we lose the quick dev turnaround, but since there is a lot of Java in this project we usually have to recompile anyway which requires a restart anyway (phooey).</p>
            
            <p>As for the I/O issue, a <a href="http://www.nabble.com/JRuby-vs-MRI---Petstore-shootout-td12211276.html%20JRuby%20vs%20MRI">little digging</a> revealed that shutting up Merb as much as possible would help reduce the amount of JRuby-level IO. In our <tt>config/init.rb</tt> we configure logging like so:</p>
            
            <div class="highlight"><pre><span class="no">Merb</span><span class="o">::</span><span class="no">Config</span><span class="o">.</span><span class="n">use</span> <span class="p">{</span> <span class="o">|</span><span class="n">c</span><span class="o">|</span>&#x000A;      <span class="n">c</span><span class="o">[</span><span class="ss">:environment</span><span class="o">]</span>         <span class="o">=</span> <span class="s1">&#39;production&#39;</span><span class="p">,</span>&#x000A;      <span class="n">c</span><span class="o">[</span><span class="ss">:framework</span><span class="o">]</span>           <span class="o">=</span> <span class="p">{},</span>&#x000A;      <span class="n">c</span><span class="o">[</span><span class="ss">:log_level</span><span class="o">]</span>           <span class="o">=</span> <span class="ss">:warn</span><span class="p">,</span>&#x000A;      <span class="n">c</span><span class="o">[</span><span class="ss">:log_file</span><span class="o">]</span>            <span class="o">=</span> <span class="no">Merb</span><span class="o">.</span><span class="n">root</span> <span class="o">/</span> <span class="s2">&quot;logs&quot;</span> <span class="o">/</span> <span class="s2">&quot;merb.log&quot;</span><span class="p">,</span>&#x000A;      <span class="n">c</span><span class="o">[</span><span class="ss">:use_mutex</span><span class="o">]</span>           <span class="o">=</span> <span class="kp">false</span><span class="p">,</span>&#x000A;      <span class="n">c</span><span class="o">[</span><span class="ss">:session_store</span><span class="o">]</span>       <span class="o">=</span> <span class="s1">&#39;cookie&#39;</span><span class="p">,</span>&#x000A;      <span class="n">c</span><span class="o">[</span><span class="ss">:session_id_key</span><span class="o">]</span>      <span class="o">=</span> <span class="s1">&#39;_facet-store_session_id&#39;</span><span class="p">,</span>&#x000A;      <span class="n">c</span><span class="o">[</span><span class="ss">:session_secret_key</span><span class="o">]</span>  <span class="o">=</span> <span class="s1">&#39;49411912879b879e13f89a9280c0f6aaa2e3ab58&#39;</span><span class="p">,</span>&#x000A;      <span class="n">c</span><span class="o">[</span><span class="ss">:exception_details</span><span class="o">]</span>   <span class="o">=</span> <span class="kp">true</span><span class="p">,</span>&#x000A;      <span class="n">c</span><span class="o">[</span><span class="ss">:reload_classes</span><span class="o">]</span>      <span class="o">=</span> <span class="kp">false</span><span class="p">,</span>&#x000A;      <span class="n">c</span><span class="o">[</span><span class="ss">:reload_templates</span><span class="o">]</span>    <span class="o">=</span> <span class="kp">false</span>&#x000A;    <span class="p">}</span>&#x000A;    </pre>
            </div>
            
            
            <p>Here we set the environment to "production" again (yes, you need to do both). Also we upped the log level to "warn" which significantly reduced the amount of logging merb does. With these tweaks in place we found that the Merb port of our service was operating within about 80% of the level of performance we were getting from our pure-Java solution.</p>
            
            <p>Benchmarking was done by running <a href="http://www.hpl.hp.com/research/linux/httperf/%20httperf%20%20it's%20so%20not%20JMeter!">httperf</a> tests against the resources we expose and comparing both the number of requests per second and the average response time. Given that the options for generating XML, HTML and JSON were all so much easier than what we were doing in the servlet version, we were willing to live with the performance hit.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Rewriting History with Git</title>
      <link>
        http://alexvollmer.com/posts/2009/01/31/rewriting-history-with-git/
      </link>
      <comments>
        http://alexvollmer.com/posts/2009/01/31/rewriting-history-with-git/#comments
      </comments>
      <pubDate>
        Sat, 31 Jan 2009 20:04:13 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            git
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2009/01/31/rewriting-history-with-git/
      </guid>
      <description>
        <![CDATA[
            This past week I spent some quality time with git's history-rewriting capabilities. Over the past few weeks I had been working on a rather long-lived branch full of JRuby and Merb patches. Some of the fixes and changes were ready to go in the next release, others were still a wee bit experimental and so my plan was to split the patches in two. The ones that were ready would get pushed upstream while the not-ready-for-primetime stuff stayed on a local branch.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>This past week I spent some quality time with git's history-rewriting capabilities. Over the past few weeks I had been working on a rather long-lived branch full of JRuby and Merb patches. Some of the fixes and changes were ready to go in the next release, others were still a wee bit experimental and so my plan was to split the patches in two. The ones that were ready would get pushed upstream while the not-ready-for-primetime stuff stayed on a local branch.</p>
            
            <p>That seemed like a great plan until I realized just how tangled some of the patches were. It isn't difficult for this to happen. As you extend the functionality of a system, you often refine earlier work. This was especially true in my case since we were introducing JRuby to a previously Java-only project and so there was a lot of experimentation and refinement. What I was really trying to do was re-write my commit history to separate the changes. Small cleanup commits could be collapsed with others to make the entire commit-set something that my teammates could easily understand.</p>
            
            <h1>Getting Started</h1>
            
            <p>The most basic kind of re-writing you can do is simply amending your last commit. On the command line this is accomplished with <tt>git commit --amend</tt>. The bigger rewrite-hammer is <tt>git rebase -i <ref></tt>. This command will pop off all of your commits to the point specified by <tt><ref></tt>, provide you with a control file to edit and then re-apply your patches as directed.</p>
            
            <p>The control file (a term I just made up) looks kinda like this:</p>
            
            <div class="highlight"><pre>pick 1cea777 Initial introduction of Merb.&#x000A;    pick 5fe7a19 Favor XML over HTML as the <span class="s2">&quot;default&quot;</span> content-type.&#x000A;    pick 6791134 Cleaned up the <span class="s1">&#39;views&#39;</span> directory, out with the old, in with the new.&#x000A;    pick ae6adac Put lots of back-navigation links to make it nice <span class="s1">&#39;n&#39;</span> easy to use.&#x000A;    pick 1da31ca Removed last vestiges of the RestServlet-related configuration and code.&#x000A;    pick b9fe3b9 Added error views, updated error-handling and improvised content dispatch.&#x000A;    pick bf92292 Routing cleanup. This is much more pleasant to read.&#x000A;    pick ec74c63 An attempt to get RSpec working with Maven and JRuby.&#x000A;    &#x000A;    <span class="c"># Rebase 92ddc87..ec74c63 onto 92ddc87</span>&#x000A;    <span class="c">#</span>&#x000A;    <span class="c"># Commands:</span>&#x000A;    <span class="c">#  p, pick = use commit</span>&#x000A;    <span class="c">#  e, edit = use commit, but stop for amending</span>&#x000A;    <span class="c">#  s, squash = use commit, but meld into previous commit</span>&#x000A;    <span class="c">#</span>&#x000A;    <span class="c"># If you remove a line here THAT COMMIT WILL BE LOST.</span>&#x000A;    <span class="c"># However, if you remove everything, the rebase will be aborted.</span>&#x000A;    </pre>
            </div>
            
            
            <p>The top section lists all of your patches, one per line. You can edit this section to do any of the following:
            *  Reorder the commits (just move lines up or down)
            *  Edit a commit (replace "pick" with "edit" or simply "e")
            *  Remove a commit (just remove the line)
            *  Merge with a previous commit (replace "pick" with "squash" or "s")</p>
            
            <p>Think of this list like a program of execution. Once you save the file and return control to git, it will attempt to execute this program. I say <em>attempt</em> here because sometimes git runs into conflicts when it comes to re-ordering patches.</p>
            
            <h1>Editing Commits</h1>
            
            <p>When you mark a commit for editing (with "edit" or "e") git will attempt to apply all patches up to, and including that commit, and stop. This threw me at first because for some reason I had the unreasonable expectation that somehow the changes in the last commit would only be applied to the working tree and, perhaps, the index. Instead, that commit is fully applied, but all commits past that are pending. To add to my confusion, there isn't an easily accessible marker to indicate that you are in the middle of rebase (unless you frequently scan the <tt>.git</tt> directory as a matter of habit). At this point you could <em>amend</em> the commit (with <tt>git commit --amend</tt>) or insert other commits.</p>
            
            <p>Sometimes I use this just to fix up the commit message. When I'm working on a new feature that has a lot of trial-and-error I tend to mark the first commit message with "WIP" to remind myself to review that patch and the ones following to see if I can clean them up. This is an area where git really shines. In a system like Subversion your audience (other coders who look at your changes) end up walking through whatever little mini-epic you've composed as you try things out, add things and remove things. This makes for some difficult reading for consumers of those patches and so a lot of folks tend to stack up big changes and then send in the big über-commit.</p>
            
            <p>The problem with building the mega-patch is that you don't have a lot of scaffolding under you while you are building it. If you go off and explore something that doesn't turn out right, you generally have to do a lot of manual reverting. This simply sucks and is a waste of time. With git I can work in lots of incremental commits, then go back and edit them into something sensible once I'm ready to push changes.</p>
            
            <p>Once you've finished doing whatever edit you want to do for that commit, simply type <tt>git rebase --continue</tt> and the remainder of the "script" will execute. If any other "edits" are in the pipeline, the process will repeat itself until the rebase is completed.</p>
            
            <h1>Squashing Commits</h1>
            
            <p>I <em>looooove</em> squashing commits in git. For any moderately complicated work, it's rare that I get it right the first time. Usually as I go along I find some mistake I made or find a refinement that cleans up the original work. As often as not, it's usually a couple of commits away so amending the last commit isn't going to help me. But hey, no worries! I simply commit the change and leave a log message for myself to merge it with another commit. Then, when I run the interactive rebase, I can simply move this commit up the list and change it from "pick" to "squash" (or "s") and let git merge the two commits.</p>
            
            <p>When rebasing encounters a "squash" it will apply the changes in both commits. If successful, git will prompt you with a commit message file that includes the original commit messages of <em>both</em> commits. You can choose either, both or neither of these and save the file to continue.</p>
            
            <h1>Resolving Conflicts</h1>
            
            <p>Sometimes when applying a commit, git will run into conflicts and bail out on a merge. Whenever this happens to me I always have a little moment of panic as if I've broken something, but fear not&mdash;you can fix this. When this happens git tries to stage as many of the changes as it can. Any conflicts are left unstaged and need to be edited (look for the standard conflict markers), and then re-staged into the index. When you commit, git should show you the original commit message of the patch it was attempting to apply, which you can edit or keep. Once the commit succeeds, rebasing should automatically continue.</p>
            
            <h1>The Big Red Button</h1>
            
            <p>Sometimes you may just give up on a rebase. Either you can't commit the time to it, you don't really want to go through with it, or your patience has reached its limit. At any point, before the rebase has completed, you can execute <tt>git rebase --abort</tt> and your working tree, index and commit-log will all be returned to the state prior to starting the rebase. Think of this as the big red "stop" button for rebasing.</p>
            
            <h1>Playing Fast and Loose with Branches</h1>
            
            <p>If you come from other SCM systems like Subversion, you tend to treat branches as expensive and something you only use on occasion. With git, branches are cheap and easy like drinks in Tijuana. Be fearless! Not sure about some exploration? Make a branch!</p>
            
            <p>If rebasing makes you nervous, and you're not entirely confident that aborting will save you, I suggest creating a branch on which to rebase. Simply create a new branch from where you're at with <tt>git checkout -b <branch></tt>. You'll be immediately switched to that branch with your commit log looking exactly like the branch you came from. Here you can rebase, edit, remove, add and generally go crazy with your commits.</p>
            
            <p>Once you've got your commits where you want them it's simply a matter of getting them back into whatever you consider your "main" branch to be and pushing any changes upstream. This works really well when you have rendez-vous point like a Github repo or an SVN server (we do a lot of git on top of Subversion at work). Assuming that you've been adding changes on your master branch, you might create a new branch off of <tt>master</tt> that you might call <tt>exp</tt>. On the <tt>exp</tt> branch you might rebase and wreak all sorts of havoc on the commit log. Once you have your commits where you want them, switch back to your <tt>master</tt> branch and reset it to point to the head of your upstream repo. If you're using git all the way, simply merge your <tt>exp</tt> branch over. If you're running git on top of Subversion, simply cherry-pick the commits on the <tt>exp</tt> branch (in order!!!) and push the changes upstream.</p>
            
            <p>It's hard to stress how important it is to shift your mindset into thinking in terms of sharing patches. Treat your commits like individual, digestible changes. Even if you're working by yourself or in a small team, I still think there's value in being disciplined with your commits.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Opening The Gates&amp;#8230;of Hell!!</title>
      <link>
        http://alexvollmer.com/posts/2009/01/27/opening-the-gatesof-hell/
      </link>
      <comments>
        http://alexvollmer.com/posts/2009/01/27/opening-the-gatesof-hell/#comments
      </comments>
      <pubDate>
        Tue, 27 Jan 2009 06:01:11 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2009/01/27/opening-the-gatesof-hell/
      </guid>
      <description>
        <![CDATA[
            &hellip;umm, no, actually not so much.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>&hellip;umm, no, actually not so much.</p>
            
            <p><img src="/images/2009/01/hellboy.jpg" class="left"/></p>
            
            <p>Instead, this is just a humble little notice about a <a href="http://github.com/alexvollmer/daemon-spawn/tree/master%20daemon-spawn">humble little gem</a> I put together today. It's called <em>daemon-spawn</em> and despite its simply terrifying name, it's really here to help all mankind. You see, I've been working like mad to stuff Merb smack-dab in the middle of an embedded Jetty project I've been working on. One of the last things I needed was a decent daemon-launcher/management gem-thingie to make it happen.</p>
            
            <p>I cast about for an existing solution and found each a little lacking. The <a href="http://daemons.rubyforge.org/%20Ruby%20Daemons">daemons</a> gem had the executable name hard-wired to the output log name and didn't give me a clean way to specify additional arguments to JRuby (unless I wrote <em>another</em> wrapper script, to which I say "boo, hiss"). Then I looked at <a href="http://simple-daemon.rubyforge.org/%20simple-daemon">simple-daemon</a> which seemed really promising. It was really really close to what I wanted but didn't extend very well as it required more and more class-methods. Yuck. I looked at <a href="http://kylemaxwell.typepad.com/everystudent/2006/08/after_writing_r.html%20daemon_generator">daemon_generator</a>, but it was very Rails-y and wanted to generate a bunch of code for me, which I didn't need. So I did what any honest, hard-working Ruby-dork does, and <em>made my own!</em></p>
            
            <p>It's simple&mdash;dead simple. Wanna see how simple? Here's a real-live echo server with daemon support:</p>
            
            <div class="highlight"><pre><span class="c1">#!/usr/bin/env ruby</span>&#x000A;    &#x000A;    <span class="nb">require</span> <span class="s1">&#39;daemon-spawn&#39;</span>&#x000A;    <span class="nb">require</span> <span class="s1">&#39;socket&#39;</span>&#x000A;    &#x000A;    <span class="k">class</span> <span class="nc">EchoServer</span> <span class="o">&lt;&lt;</span> <span class="no">DaemonSpawn</span><span class="o">::</span><span class="no">Base</span>&#x000A;    &#x000A;      <span class="kp">attr_accessor</span> <span class="ss">:server_socket</span>&#x000A;    &#x000A;      <span class="k">def</span> <span class="nf">start</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>&#x000A;        <span class="n">port</span> <span class="o">=</span> <span class="n">args</span><span class="o">.</span><span class="n">empty?</span> <span class="p">?</span> <span class="mi">0</span> <span class="p">:</span> <span class="n">args</span><span class="o">.</span><span class="n">first</span><span class="o">.</span><span class="n">to_i</span>&#x000A;        <span class="nb">self</span><span class="o">.</span><span class="n">server_socket</span> <span class="o">=</span> <span class="no">TCPServer</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s1">&#39;127.0.0.1&#39;</span><span class="p">,</span> <span class="n">port</span><span class="p">)</span>&#x000A;        <span class="n">port</span> <span class="o">=</span> <span class="nb">self</span><span class="o">.</span><span class="n">server_socket</span><span class="o">.</span><span class="n">addr</span><span class="o">[</span><span class="mi">1</span><span class="o">]</span>&#x000A;        <span class="nb">puts</span> <span class="s2">&quot;EchoServer started on port </span><span class="si">#{</span><span class="n">port</span><span class="si">}</span><span class="s2">&quot;</span>&#x000A;        <span class="kp">loop</span> <span class="k">do</span>&#x000A;          <span class="k">begin</span>&#x000A;            <span class="n">client</span> <span class="o">=</span> <span class="nb">self</span><span class="o">.</span><span class="n">server_socket</span><span class="o">.</span><span class="n">accept</span>&#x000A;            <span class="k">while</span> <span class="n">str</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">gets</span>&#x000A;              <span class="n">client</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">str</span><span class="p">)</span>&#x000A;            <span class="k">end</span>&#x000A;          <span class="k">rescue</span> <span class="no">Errno</span><span class="o">::</span><span class="no">ECONNRESET</span> <span class="o">=&gt;</span> <span class="n">e</span>&#x000A;            <span class="no">STDERR</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&quot;Client reset connection&quot;</span>&#x000A;          <span class="k">end</span>&#x000A;        <span class="k">end</span>&#x000A;      <span class="k">end</span>&#x000A;    &#x000A;      <span class="k">def</span> <span class="nf">stop</span>&#x000A;        <span class="nb">puts</span> <span class="s2">&quot;Stopping EchoServer...&quot;</span>&#x000A;        <span class="nb">self</span><span class="o">.</span><span class="n">server_socket</span><span class="o">.</span><span class="n">close</span> <span class="k">if</span> <span class="nb">self</span><span class="o">.</span><span class="n">server_socket</span>&#x000A;      <span class="k">end</span>&#x000A;    <span class="k">end</span>&#x000A;    &#x000A;    <span class="no">EchoServer</span><span class="o">.</span><span class="n">spawn!</span><span class="p">(</span><span class="ss">:working_dir</span> <span class="o">=&gt;</span> <span class="no">File</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="no">File</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="bp">__FILE__</span><span class="p">),</span> <span class="s1">&#39;..&#39;</span><span class="p">),</span>&#x000A;                      <span class="ss">:log_file</span> <span class="o">=&gt;</span> <span class="s1">&#39;/tmp/echo_server.log&#39;</span><span class="p">,</span>&#x000A;                      <span class="ss">:pid_file</span> <span class="o">=&gt;</span> <span class="s1">&#39;/tmp/echo_server.pid&#39;</span><span class="p">,</span>&#x000A;                      <span class="ss">:sync_log</span> <span class="o">=&gt;</span> <span class="kp">true</span><span class="p">,</span>&#x000A;                      <span class="ss">:singleton</span> <span class="o">=&gt;</span> <span class="kp">true</span><span class="p">)</span>&#x000A;    </pre>
            </div>
            
            
            <p>But what if you have non-Ruby code you want to daemonize? Well my friends, that's what <code>Kernel#exec</code> is for and it works like a champ. See the README for the full details. And of course to view the README, you have to install the gem which means you have <em>daemon-spawn</em> in the bowels of your machine! Mwaaa haa haa haa! Oops&hellip;I've said too much&hellip;</p>
            
            <p>In all seriousness though, I would like to thank the powers-that-be at <a href="http://www.evri.com%20Evri!!">work</a> who were very gracious to let me open-source this. You should start seeing more of this kind of stuff from Evri soon. As always, your feedback, comments, critiques and patches are welcome.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Ruby Threads Suck&amp;#8230;Just Not The Way You Think They Do</title>
      <link>
        http://alexvollmer.com/posts/2009/01/26/ruby-threads-suckjust-not-the-way-you-think-they-do/
      </link>
      <comments>
        http://alexvollmer.com/posts/2009/01/26/ruby-threads-suckjust-not-the-way-you-think-they-do/#comments
      </comments>
      <pubDate>
        Mon, 26 Jan 2009 01:47:24 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2009/01/26/ruby-threads-suckjust-not-the-way-you-think-they-do/
      </guid>
      <description>
        <![CDATA[
            At <a href="http://www.evri.com%20Evri!!">work</a>, we do a lot of scheduled tasks in which we process a "chunk" of data within a particular time-period. For example, we may tail log files, parse the lines and publish summary statistics "up-stream" on a fixed schedule of, say, ten minutes. Similarly, last week we were working on a Ruby wrapper script that launches <a href="http://www.danga.com/memcached/%20memcached">memcached</a> and maintains a registration lease within a home-grown registration service we run. The script needs to launch memcached, then periodically check it and renew its registration lease.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>At <a href="http://www.evri.com%20Evri!!">work</a>, we do a lot of scheduled tasks in which we process a "chunk" of data within a particular time-period. For example, we may tail log files, parse the lines and publish summary statistics "up-stream" on a fixed schedule of, say, ten minutes. Similarly, last week we were working on a Ruby wrapper script that launches <a href="http://www.danga.com/memcached/%20memcached">memcached</a> and maintains a registration lease within a home-grown registration service we run. The script needs to launch memcached, then periodically check it and renew its registration lease.</p>
            
            <p>We have a RubyGem written to handle registration and renewal that hides the HTTP and XML message bodies away from the user. You simply create a client, setup your initial registration and tell it to keep you registered.</p>
            
            <div class="highlight"><pre><span class="nb">require</span> <span class="s2">&quot;rubygems&quot;</span>&#x000A;    <span class="nb">require</span> <span class="s2">&quot;radar_love&quot;</span>&#x000A;    <span class="n">client</span> <span class="o">=</span> <span class="no">Radar</span><span class="o">::</span><span class="no">Client</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">&quot;http://radar-dev&quot;</span><span class="p">)</span>&#x000A;    <span class="n">service</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="s1">&#39;foobar&#39;</span><span class="p">,</span> <span class="s1">&#39;http://foobar:1234&#39;</span><span class="p">)</span>&#x000A;    <span class="n">service</span><span class="o">.</span><span class="n">keep_registered</span> <span class="c1"># fires up background thread</span>&#x000A;    </pre>
            </div>
            
            
            <p>That last line is implemented with a Ruby thread that loops indefinitely, sleeping and then renewing the registration lease. But a funny thing happened while implementing this. When we just fired up <code>irb</code> and tried to run this part (without doing any other work), the re-registration thread <em>never</em> executed. Man, I had heard that MRI threads were "broken", but this is completely non-functioning!</p>
            
            <p>Then I remembered a <a href="http://spec.ruby-doc.org/wiki/Ruby_Threading%20Ruby%20Threading%20Spec">very handy page</a> I ran across once about MRI Threading. This page is worth spending a little time with, but essentially because MRI threads are so-called "green threads" they aren't really giving you true concurrent processing. Instead they are merely a context-switching mechanism, and the circumstances under which those switches can happen are described in that spec page.</p>
            
            <p>In the case of our little <code>irb</code> session, the re-registration thread only began executing when we did something in the main thread. We weren't executing anything in the main thread that triggered one of these context-switches (remember, we're merely sitting at an irb prompt waiting for the next line). So getting the runtime to execute a context-switch merely required us to do <em>something</em> in the main thread:</p>
            
            <div class="highlight"><pre><span class="kp">loop</span> <span class="k">do</span>&#x000A;      <span class="nb">puts</span> <span class="s2">&quot;Howdy!&quot;</span>  &#x000A;      <span class="nb">sleep</span> <span class="mi">5</span>&#x000A;    <span class="k">end</span>&#x000A;    </pre>
            </div>
            
            
            <p>You may shake your head and mutter something derisive about this "hack". However, in reality, requiring your main thread to do something isn't terribly burdensome. If you didn't have any work to do in your main thread, you'd have to ask yourself why you created a separate thread in the first place!</p>
            
            <p>You may also think that since MRI threads don't provide true concurrency, they're worthless. One major limitation of green threads in MRI is that no matter how many you start, they will only execute on one processor. If you have a large multi-core machine, MRI threads will <em>not</em> be able to take advantage of them.</p>
            
            <p>However, that doesn't mean threads don't have their place in MRI environments. In the first example I mentioned (tailing logs and publishing summaries) we use a separate thread for the publishing activity. We <em>could</em> have done this entire action in a single loop, but the major downside is that we would essentially be relying on new lines in our log to appear to "crank" the mechanism forward. If we go a long time before we see another log line, our summary task will fail to execute.</p>
            
            <div class="highlight"><pre><span class="no">IO</span><span class="o">.</span><span class="n">popen</span><span class="p">(</span><span class="s2">&quot;tail -F /var/log/some.log&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">line</span><span class="o">|</span>&#x000A;      <span class="n">update_statistics</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>&#x000A;      <span class="k">if</span> <span class="no">Time</span><span class="o">.</span><span class="n">now</span> <span class="o">&gt;=</span> <span class="n">next_report_time</span>&#x000A;        <span class="c1"># we might not get here for a while unless the</span>&#x000A;        <span class="c1"># log lines keep coming</span>&#x000A;        <span class="n">report_statistics</span>&#x000A;      <span class="k">end</span>&#x000A;    <span class="k">end</span>&#x000A;    </pre>
            </div>
            
            
            <p>It would certainly be possible to read from the file with a timeout that is based on how much time is left before the next reporting period. However at that point the code starts to get a little cluttered, so we go with the threaded approach only to take advantage of its context-switching properties. In our case, this is a perfect solution for what we're trying to accomplish.</p>
            
            <p>If you come from a Java or .NET background, you may find that MRI threads fail to measure up to threading in those environments. It's absolutely true that MRI does not provide the same robust threading mechanisms that those languages do (<a href="http://spec.ruby-doc.org/wiki/JRuby_Threading%20JRuby%20Threading">JRuby</a>, and perhaps IronRuby, being special cases). It doesn't mean that threads in MRI are worthless, you just need to <a href="http://www.infoq.com/news/2007/05/ruby-threading-futures%20Ruby%20Threading">understand them</a> properly to know when to use them.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Meet Magit!</title>
      <link>
        http://alexvollmer.com/posts/2009/01/18/meet-magit/
      </link>
      <comments>
        http://alexvollmer.com/posts/2009/01/18/meet-magit/#comments
      </comments>
      <pubDate>
        Sun, 18 Jan 2009 20:58:06 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            emacs
        ]]>
      </category>
      <category>
        <![CDATA[
            git
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2009/01/18/meet-magit/
      </guid>
      <description>
        <![CDATA[
            In the spirit of Geoffrey Grossenbach's <a href="http://peepcode.com/products/meet-emacs%20Meet%20Emacs">Meet Emacs Peepcode Screencast</a>, I put together my own humble little screencast this weekend on <a href="http://zagadka.vm.bytemark.co.uk/magit/magit.html%20Magit">magit</a>, a fantastic <a href="http://git-scm.com%20Git">git</a> mode for Emacs.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>In the spirit of Geoffrey Grossenbach's <a href="http://peepcode.com/products/meet-emacs%20Meet%20Emacs">Meet Emacs Peepcode Screencast</a>, I put together my own humble little screencast this weekend on <a href="http://zagadka.vm.bytemark.co.uk/magit/magit.html%20Magit">magit</a>, a fantastic <a href="http://git-scm.com%20Git">git</a> mode for Emacs.</p>
            
            <object width="400" height="300" data="http://vimeo.com/moogaloop.swf?clip_id=2871241&amp;server=vimeo.com&amp;show_title=1&amp;show_byline=1&amp;show_portrait=0&amp;color=&amp;fullscreen=1" type="application/x-shockwave-flash"></object>
            
            
            <p><a href="http://vimeo.com/2871241">Meet Magit</a> from <a href="http://vimeo.com/alexvollmer">Alex Vollmer</a> on <a href="http://vimeo.com">Vimeo</a>.</p>
            
            <p>It's not meant to be an exhaustive survey of magit (check the <a href="http://zagadka.vm.bytemark.co.uk/magit/magit.html%20Magit%20User%20Manual">docs</a> for all the details), but to show off some of the cool features this mode has. I found that spending just a little time with docs and learning this mode has already paid off in terms of increasing my productivity. Enjoy!</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>clip version 1.0.1 has been released!</title>
      <link>
        http://alexvollmer.com/posts/2009/01/07/clip-version-101-has-been-released/
      </link>
      <comments>
        http://alexvollmer.com/posts/2009/01/07/clip-version-101-has-been-released/#comments
      </comments>
      <pubDate>
        Wed, 07 Jan 2009 02:58:13 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <category>
        <![CDATA[
            clip
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2009/01/07/clip-version-101-has-been-released/
      </guid>
      <description>
        <![CDATA[
            You like command-line parsing, but you hate all of the bloat. Why
            should you have to create a Hash, then create a parser, fill the Hash
            out then throw the parser away (unless you want to print out a usage
            message) and deal with a Hash? Why, for Pete's sake, should the parser
            and the parsed values be handled by two different objects?
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>You like command-line parsing, but you hate all of the bloat. Why
            should you have to create a Hash, then create a parser, fill the Hash
            out then throw the parser away (unless you want to print out a usage
            message) and deal with a Hash? Why, for Pete's sake, should the parser
            and the parsed values be handled by two different objects?</p>
            
            <p>Changes:</p>
            
            <h3>1.0.1 / 2009-01-06</h3>
            
            <ul>
            <li>Fixed a bug where generating help resulted in an infinite-loop</li>
            </ul>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>ActiveRecord, Associations and Counters</title>
      <link>
        http://alexvollmer.com/posts/2009/01/04/activerecord-associations-and-counters/
      </link>
      <comments>
        http://alexvollmer.com/posts/2009/01/04/activerecord-associations-and-counters/#comments
      </comments>
      <pubDate>
        Sun, 04 Jan 2009 22:39:21 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <category>
        <![CDATA[
            rails
        ]]>
      </category>
      <category>
        <![CDATA[
            moochbot
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2009/01/04/activerecord-associations-and-counters/
      </guid>
      <description>
        <![CDATA[
            Maybe this is old hat to all you grizzled vets out there, but today I thought I'd post about my experience with ActiveRecord's counter caches and the tricks I had to pull to get it working. Let me first set the stage with what I was trying to accomplish.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>Maybe this is old hat to all you grizzled vets out there, but today I thought I'd post about my experience with ActiveRecord's counter caches and the tricks I had to pull to get it working. Let me first set the stage with what I was trying to accomplish.</p>
            
            <p>In <a href="http://moochbot">moochbot</a>, your main transaction screen has a standard tabbed-interface. Each tab is a different view of all your transactions. In a tabbed display you can only show one view at a time so it helps the user when you can provide some hints in the non-selected tabs. Anything that helps them figure out whether or not they want to click on something <em>without actually having to click on it</em> is, in my opinion, a great help.</p>
            
            <p><img src="http://img.skitch.com/20090104-xi79khd8e9242dhbf5yes6nmg6.jpg" alt="Moochbot Tabs" /></p>
            
            <p>So I wanted to add a number in the tab to indicate how many items were there. The most na&iuml;ve way to implement this would be to issue three different SQL statements for the counts. However somewhere, in the back of my mind, I remembered that ActiveRecord has a feature known as the <em>counter cache</em>. The basic idea is to hook some additional code into the lifecycle of ActiveRecord's associations to update a single column in the parent record as you add and remove child records.</p>
            
            <p>Like an iceberg, the bulk of this feature lay deep below the surface. The actual view-layer changes were minimal, but I had to jump through some hoops to get the counter-cache working correctly.</p>
            
            <p>In moochbot, a <code>User</code> model object has multiple <code>Transaction</code> records. Each <code>Transaction</code> points at two separate <code>User</code> records: one for the lender and one for the borrower. All of a user's transactions are stored in the <code>TRANSACTIONS</code> table, each record differentiated by the <code>STATE</code> column.</p>
            
            <p>In ActiveRecord-land we express these relationships in the model with <em>three</em> <code>has_many</code> relations for the <code>User</code>: one for items they are lending, one for items they are borrowing and all closed transactions. The first two are fairly straight-ahead:</p>
            
            <div class="highlight"><pre><span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>&#x000A;    &#x000A;      <span class="n">has_many</span><span class="p">(</span><span class="ss">:lent_items</span><span class="p">,</span>&#x000A;               <span class="ss">:class_name</span> <span class="o">=&gt;</span> <span class="s2">&quot;Transaction&quot;</span><span class="p">,</span>&#x000A;               <span class="ss">:foreign_key</span> <span class="o">=&gt;</span> <span class="s2">&quot;lender_id&quot;</span><span class="p">,</span>&#x000A;               <span class="ss">:conditions</span> <span class="o">=&gt;</span> <span class="o">[</span><span class="s2">&quot;state IN (?)&quot;</span><span class="p">,</span>&#x000A;                               <span class="sx">%w(started lent returned overdue disputed)</span><span class="o">]</span><span class="p">)</span>&#x000A;    &#x000A;      <span class="n">has_many</span><span class="p">(</span><span class="ss">:borrowed_items</span><span class="p">,</span>&#x000A;               <span class="ss">:class_name</span> <span class="o">=&gt;</span> <span class="s2">&quot;Transaction&quot;</span><span class="p">,</span>&#x000A;               <span class="ss">:foreign_key</span> <span class="o">=&gt;</span> <span class="s2">&quot;borrower_id&quot;</span><span class="p">,</span>&#x000A;               <span class="ss">:conditions</span> <span class="o">=&gt;</span> <span class="o">[</span><span class="s2">&quot;state IN (?)&quot;</span><span class="p">,</span>&#x000A;                               <span class="sx">%w(started lent returned overdue disputed)</span><span class="o">]</span><span class="p">)</span>&#x000A;    &#x000A;      <span class="n">has_many</span><span class="p">(</span><span class="ss">:completed_items</span><span class="p">,</span>&#x000A;               <span class="ss">:class_name</span> <span class="o">=&gt;</span> <span class="s2">&quot;Transaction&quot;</span><span class="p">,</span>&#x000A;               <span class="ss">:finder_sql</span> <span class="o">=&gt;</span> <span class="s1">&#39;SELECT * &#39;</span> <span class="o">+</span>&#x000A;               <span class="s1">&#39;FROM transactions &#39;</span> <span class="o">+</span>&#x000A;               <span class="s1">&#39;WHERE state IN (\&#39;aborted\&#39;, \&#39;finished\&#39;) AND &#39;</span> <span class="o">+</span>&#x000A;               <span class="s1">&#39;(borrower_id = #{id} OR lender_id = #{id})&#39;</span><span class="p">)</span>&#x000A;    <span class="k">end</span>&#x000A;    </pre>
            </div>
            
            
            <p>However the third relationship requires some custom SQL because we want all records that are in either the "finished" or "aborted" state <em>and</em> where the user is <em>either</em> the lender or the borrower. I looked into doing this with a simple <code>:conditions</code> option on the <code>has_many</code> relationship, but couldn't figure out how to specify the ID of the user.</p>
            
            <p>One <em>really important</em> thing to recognize here is that the SQL is quoted with single-quotes. If the SQL is specified in double-quotes, the interpolation is evaluated <em>too early</em> and the id value is <em>not</em> the user record. Putting it in single-quotes defers evaluation until the proper time. I wish this were documented a little better because I was completely stuck until I stumbled across <a href="http://railsblaster.wordpress.com/2007/08/27/has_many-finder_sql/%20has_finder%20SQL">this post</a> on the <a href="http://railsblaster.wordpress.com%20RailsBlaster">RailsBlaster</a> blog. I had to enable debug-level logging for ActiveRecord to see that I was getting skooky IDs in my final SQL string.</p>
            
            <p>To add a counter cache to the User record, you declare a <code>:counter_cache</code> option on the reciprocal <code>belongs_to</code> relationship. This seemed counter-intuitive to me since if I didn't already have one in place, I'd have to add one. It seemed more obvious to me to put it in the <code>has_many</code> relationship but that ain't the way ActiveRecord rolls. So the next step was to update the <code>belongs_to</code> mappings in the <code>Transaction</code> class:</p>
            
            <div class="highlight"><pre><span class="k">class</span> <span class="nc">Transaction</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>&#x000A;    &#x000A;      <span class="n">belongs_to</span><span class="p">(</span><span class="ss">:lender</span><span class="p">,</span>&#x000A;                 <span class="ss">:class_name</span> <span class="o">=&gt;</span> <span class="s2">&quot;User&quot;</span><span class="p">,</span>&#x000A;                 <span class="ss">:foreign_key</span> <span class="o">=&gt;</span> <span class="s2">&quot;lender_id&quot;</span><span class="p">,</span>&#x000A;                 <span class="ss">:counter_cache</span> <span class="o">=&gt;</span> <span class="ss">:lent_items_count</span><span class="p">)</span>&#x000A;    &#x000A;      <span class="n">belongs_to</span><span class="p">(</span><span class="ss">:borrower</span><span class="p">,</span>&#x000A;                 <span class="ss">:class_name</span> <span class="o">=&gt;</span> <span class="s2">&quot;User&quot;</span><span class="p">,</span>&#x000A;                 <span class="ss">:foreign_key</span> <span class="o">=&gt;</span> <span class="s2">&quot;borrower_id&quot;</span><span class="p">,</span>&#x000A;                 <span class="ss">:counter_cache</span> <span class="o">=&gt;</span> <span class="ss">:borrowed_items_count</span><span class="p">)</span>&#x000A;    <span class="k">end</span>&#x000A;    </pre>
            </div>
            
            
            <p>The final step was to create a migration that would add the counter cache columns to the <code>USERS</code> table. Note that not only do we add the columns, but we also update everyone's counters.</p>
            
            <div class="highlight"><pre><span class="k">class</span> <span class="nc">AddCounterCacheToUsers</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Migration</span>&#x000A;      <span class="no">COLUMNS</span> <span class="o">=</span> <span class="o">[</span><span class="ss">:lent_items_count</span><span class="p">,</span>&#x000A;                 <span class="ss">:borrowed_items_count</span><span class="p">,</span>&#x000A;                 <span class="ss">:completed_items_count</span><span class="o">]</span>&#x000A;    &#x000A;      <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">up</span>&#x000A;        <span class="no">COLUMNS</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">c</span><span class="o">|</span>&#x000A;          <span class="n">add_column</span> <span class="ss">:users</span><span class="p">,</span> <span class="n">c</span><span class="p">,</span> <span class="ss">:integer</span><span class="p">,</span> <span class="ss">:default</span> <span class="o">=&gt;</span> <span class="mi">0</span>&#x000A;        <span class="k">end</span>&#x000A;    &#x000A;        <span class="no">User</span><span class="o">.</span><span class="n">reset_column_information</span>&#x000A;    &#x000A;        <span class="no">User</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="ss">:all</span><span class="p">)</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">user</span><span class="o">|</span>&#x000A;          <span class="no">User</span><span class="o">.</span><span class="n">update_counters</span><span class="p">(</span><span class="n">user</span><span class="o">.</span><span class="n">id</span><span class="p">,</span>&#x000A;                               <span class="ss">:lent_items_count</span> <span class="o">=&gt;</span> <span class="n">user</span><span class="o">.</span><span class="n">lent_items</span><span class="o">.</span><span class="n">length</span><span class="p">,</span>&#x000A;                               <span class="ss">:borrowed_items_count</span> <span class="o">=&gt;</span> <span class="n">user</span><span class="o">.</span><span class="n">borrowed_items</span><span class="o">.</span><span class="n">length</span><span class="p">,</span>&#x000A;                               <span class="ss">:completed_items_count</span> <span class="o">=&gt;</span> <span class="n">user</span><span class="o">.</span><span class="n">completed_items</span><span class="o">.</span><span class="n">length</span><span class="p">)</span>&#x000A;        <span class="k">end</span>&#x000A;      <span class="k">end</span>&#x000A;    &#x000A;      <span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">down</span>&#x000A;        <span class="no">COLUMNS</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">c</span><span class="o">|</span>&#x000A;          <span class="n">remove_column</span> <span class="ss">:users</span><span class="p">,</span> <span class="n">c</span>&#x000A;        <span class="k">end</span>&#x000A;      <span class="k">end</span>&#x000A;    <span class="k">end</span>&#x000A;    </pre>
            </div>
            
            
            <p>So there it is. Hopefully that helps the next poor sod that runs into that same problem.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>The Fast and the Quick</title>
      <link>
        http://alexvollmer.com/posts/2009/01/01/the-fast-and-the-quick/
      </link>
      <comments>
        http://alexvollmer.com/posts/2009/01/01/the-fast-and-the-quick/#comments
      </comments>
      <pubDate>
        Thu, 01 Jan 2009 16:39:38 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            software
        ]]>
      </category>
      <category>
        <![CDATA[
            philosophy
        ]]>
      </category>
      <category>
        <![CDATA[
            sports
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2009/01/01/the-fast-and-the-quick/
      </guid>
      <description>
        <![CDATA[
            <img alt="Walter Payton" src="http://media-2.web.britannica.com/eb-media/91/95591-004-36D62DDD.jpg" class="left">
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p><img alt="Walter Payton" src="http://media-2.web.britannica.com/eb-media/91/95591-004-36D62DDD.jpg" class="left"/></p>
            
            <p>I've been a football fan since I was about five years old. I have studied this game for almost thirty years, so forgive me if I bore you all with a trite football analogy. This is the old saw known as "fast vs. quick". Players like Barry Sanders or Walter Payton were "quick", but not necessarily fast. In general, "fast" guys are wide receivers and corner-backs (the guys that cover the receivers). Receives generally have further to run than running backs, so they need what is called "breakaway speed" which is enough raw power to get away from the defender covering them.</p>
            
            <p>Running backs however, need a combination of strength and the ability to move their bodies very quickly in a very small space. The difference between a gain of one or two yards and a breaking-off a twenty-yard run is usually a matter of inches. The runner has to be in just the right place at just the right moment and get just the right kind of blocking from his teammates. Quickness is the ability to shift weight, turn the body and plant a foot just a fraction of second faster than the next guy. It's what separates the good from the truly great.</p>
            
            <p>Hall of Fame running backs rarely survive on pure speed alone. Heck, most backs that were "burners" in college are usually forced to play another position at the professional level. Those that try to rely on pure brawn to break tackles generally don't last long (see, Eddie George, Priest Holmes or any back that has played in Denver in the last decade). The really great ones have that ethereal quality known as "quickness".</p>
            
            <p>So, I was thinking lately about "agile" in the software world and what it really means. The term <em>agile</em>, isn't clear enough&mdash;it could be fast or it could be quick. But which really matters? Which is the quality you really want to have?</p>
            
            <p>My answer? You want the agility associated with <em>quickness</em>, not raw speed. Being "quick" in software might look like:
            *  New requirements are handled in-stride and rarely, if ever, with The Big Re-Write
            *  The time to produce a meaningful feature is measured in hours or days, not weeks
            *  The time to produce a meaningful <em>release</em> is measured in days or weeks, not months
            *  The system almost <em>never</em> looks like the original design</p>
            
            <p>OK, so what would "fast" look like? Well&hellip;
            *  Releases are buggy
            *  Few if any tests
            *  Code diarrhea
            *  Quality is inconsistent
            *  There are lots of dependencies
            *  New requirements are an exercise in hammering reality into fixed model</p>
            
            <p>Actually, none of those sound particularly "fast" to me.</p>
            
            <p>When folks complain about the ineffectiveness of "Agile", I sometimes wonder if they aren't simply confusing fast with quick. There isn't any methodology or toolset I know of that will make you <em>faster</em>. Your raw speed is determined more or less by the skill of your team. Each member transfers a concept into code at a relatively consistent rate, so you simply aren't going to go any faster unless you change your team. However I think you <em>can</em> acquire new skills to improve your quickness.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Flaccid Attitudes</title>
      <link>
        http://alexvollmer.com/posts/2008/12/23/flaccid-attitudes/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/12/23/flaccid-attitudes/#comments
      </comments>
      <pubDate>
        Tue, 23 Dec 2008 05:47:54 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            philosophy
        ]]>
      </category>
      <category>
        <![CDATA[
            software
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/12/23/flaccid-attitudes/
      </guid>
      <description>
        <![CDATA[
            Today I had an impromptu conversation with a total stranger at my local caf&eacute; that got me thinking. He saw that I was working on some code and asked me what I was doing. I gave him the thirty-second whirlwind tour of Ruby and briefly explained what I was working on.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>Today I had an impromptu conversation with a total stranger at my local caf&#233; that got me thinking. He saw that I was working on some code and asked me what I was doing. I gave him the thirty-second whirlwind tour of Ruby and briefly explained what I was working on.</p>
            
            <p>He came from the .net side of things and had been doing contracting for quite awhile. Recently though, his shop closed up and he was between gigs. He hadn't been exposed to Ruby and thought it sounded kind of interesting. He was, as he put it, "a Windows-guy", but basically apologized for it saying that "everyone around here" runs Windows. <em>Really? You mean like the four-out-of-five-laptops that are Macs in this caf&#233;??!!! (</em>Note to self: this fella doesn't seem too observant.)</p>
            
            <p>So while I was trying to politely end the conversation to get back to my hacking and an IRC-meeting, he rambled on about his personal work philosophy. God knows how long he droned on, but his M.O. essentially boiled down to <em>"Hey I don't care what I work on. Languages are all the same, why fuss over this one or that one?"</em></p>
            
            <p>It stopped me in my tracks.</p>
            
            <p>Are you kidding me?</p>
            
            <p>Look, there is something laudable (in a kind of John-and-Yoko-stay-in-bed-for-peace way) about The Big World view where all the geeks get along and sing side by side at the great binary campfire. At a certain level arguing over this language vs. that language or this tool or that operating system is frivolous waste of time. Much of our "values" in these things are based merely on opinions, aesthetics and feelings. It's like mailing list arguments over whether or not the Enterprise could defeat the Galactica. <em>Ladies and Gentleman on your left is Dork City&#8230;</em></p>
            
            <p>But what this really told me more than anything else was that this guy was simply <em>indifferent</em>. You don't have to agree with my way of doing things, but when we talk about them I at least expect you to have an opinion and to be able to back that up with some reasoning. This guy's indifference stemmed from the fact that he treated this like a <em>job.</em></p>
            
            <p>"Passion" is a word that is overused in this biz. Every time I'm in a debrief about some candidate and the notion of "passion" comes up I started checking the room for forks to shove in my eyes. I don't care about passion for The Company or The Idea. That's rah-rah B.S. that I have no stomach or patience for. What I <em>do</em> want to see in a co-worker is someone who cares about what they work on and how they do it. <em>This</em> is your day-to-day existence. <em>This</em> is what you spend a lot of waking hours doing, being away from your loved ones. You damn-well better care about how you want to spend that time.</p>
            
            <p>If you wanted to fail spectacularly in a job interview with me, one way to score a boat-load of demerit-points quickly is give an indifferent answer to the question, "What do you like to work on?" When people shrug their shoulders and say, "Oh, I'll work on anything", it tells me that they don't care. The pay is the same, so why fuss over the details?</p>
            
            <p>Now I understand that some interviewees won't give me a straight answer because they <em>expect</em> that I want the answer an automaton would give. There are certainly plenty of shops that just want "human capital" in much the same way generals in WWI wanted troops (see fodder, cannon). But even so, I'm not sure that's the kind of person I want on my team. Show a little back-bone for pete's sake! Do you do this job just because it pays the bills? Are you so beaten-down by the industry and your experiences that you have no hope of it ever getting better? Are you just trudging forward in your profession because you don't know what else to do?</p>
            
            <p>I don't care so much whether or not we agree on tools, languages or operating systems, but I hope to God you care about how you spend your time on this earth in this profession. Don't sleepwalk through it.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Burning My Ships</title>
      <link>
        http://alexvollmer.com/posts/2008/12/21/burning-my-ships/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/12/21/burning-my-ships/#comments
      </comments>
      <pubDate>
        Sun, 21 Dec 2008 03:40:56 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            geekery
        ]]>
      </category>
      <category>
        <![CDATA[
            philosophy
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/12/21/burning-my-ships/
      </guid>
      <description>
        <![CDATA[
             When Columbus reached the New World, he burned his ships. As a result his men were well-motivated.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <blockquote><p> When Columbus reached the New World, he burned his ships. As a result his men were well-motivated.</p></blockquote>
            
            <p><a href="http://www.amazon.com/Hunt-Red-October-Blu-ray/dp/B001AII4SQ%3FSubscriptionId%3D0PZ7TM66EXQCXFVTMTR2%26tag%3Dhttplivollmne-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3DB001AII4SQ"><img src="http://ecx.images-amazon.com/images/I/51x%2B1Yxrb9L._SL160_.jpg" class="left"/></a></p>
            
            <p>This quote comes from the character of Marko Ramius, as played by Sean Connery, from one of my all-time favorite films, "The Hunt for Red October". I love that quote in particular because I always think of it whenever I decide to tweak my process, my work environment or my tools. For all but the most trivial changes, you usually have to immerse yourself in it. So I usually "burn my ships" by leaving behind the old way of doing things to really burn the new way into my brain.</p>
            
            <p>Sometimes the new way turns out to not be so great. That's fine because that's a satisfactory result of the experiment. If the "new way" turns out to suck, there's just as much to learn (if not more) from a real crash-and-burn than success.</p>
            
            <p>The first big "ship-burning" I did was very early in my career. I had been a few months into my professional developer career doing Java and decided that I really liked this Unix thing. So I completely wiped out my Windows NT work box and put one of the first versions of Red Hat on there. I figured if I was going to get good at Linux I was going to have to just jump in the deep end of the pool and learn to swim.</p>
            
            <p>It turned out to be an absolutely wonderful decision. Throughout the years I moved through several distributions: Red Hat to Mandrake to Gentoo to Ubuntu. Each new distribution taught me something more about Linux than I knew before (especially true for Gentoo).</p>
            
            <p>I've done the same trick with editors too. When I first started out I used <em>vi</em> for my Java development. I tried using the first IDEs (JBuilder, etc.) but they were all terrible. Plus, at that time a serious Java IDE for Linux was still a twinkle in Erich Gamma's eye. I had a co-worker who was an XEmacs power-user and it looked like he got a lot done so I figured I'd give it a go&#8212;especially since I had on-site technical support. This was my first experience with Emacs and I never really got beyond basic editing. I wrote a couple of macros but that was about it.</p>
            
            <p>Then NetBeans rolled around and I was more than happy to start using it. I had a decent intellisense feature and built-in Ant integration, both of which were a significant step forward to me. I ran with that for a couple of years until I started a new job where everyone was using this new-fangled thing called Eclipse. I tried to hold-out with NetBeans for a while, but after watching some others pull off some seriously cool tricks in Eclipse I knew it was time to give it a go. I've ridden that Eclipse horse for damn-near six years and I'd be willing to put my knowledge of Eclipse-editing arcana up against anyone.</p>
            
            <p>Several years laster, on the operating system front, another big change was coming. I had been a die-hard Linux fan since my switch back in the Old Days, but by winter of 2005 I had run into a tangled knot of Linux-related headaches that really sapped the fun out of it for me. Between home servers and 64-bit laptop (in 2005!) I spent more time just trying to keep my machines up to date and running than actually getting anything productive done. However, it wasn't until I went to my first Seattle Ruby Brigade meeting (when everyone there had a Mac) that I seriously considered switching operating systems.</p>
            
            <p>This was the same time that I discovered Ruby. What attracted me to the language was not only the beauty of the syntax, but the underlying philosophies of pragmatism, simplicity and no-bullshit attitude. There seemed to be a similar attitude in Mac users and I found this approach appealing. This is not a knock on Linux. I <em>love</em> Linux, I just don't want to use it for my desktop OS anymore. I don't have the patience to fiddle with things that much anymore. I have lots of ideas and I'll never get anywhere if I get bogged-down with irrelevant minutiae and yak-shaving.</p>
            
            <p>When I switched to the Mac I got myself a license for TextMate which has definitely been the editor-of-choice for lots of Rubyists. However, <a href="http://livollmers.net/index.php/2008/10/06/back-to-myemacs/%20Back%20to%20My%20Emacs">as I blogged about before</a>, it's now time to get off the TM-train and switch (back) to Emacs.</p>
            
            <p>So what's the point of all of this? Am I just thrashing? Am I desperately throwing myself at each and every new tool on the block? You might think so, but I actually have <em>some</em> discriminating taste. I do try out <em>a lot</em> of tools because I'm always looking for some kind of edge, anything that I can leverage to get stupid busy-work out of the way. As I get older I have less and less patience for this sort of thing for <em>productivity taxes</em>. I just want to get something important done.</p>
            
            <p>So, from time to time, a little alarm goes off inside my brain to remind me that it's time for a change. Sometimes it's something as simple as changing my terminal or editing font. Other times it's things like learning a <a href="http://erlang.org/%20Erlang">new</a> <a href="http://developer.apple.com/cocoa/%20Cocoa">language</a>. Regardless, all of these things keep me from getting mentally lazy. Once I get mentally complacent, it's game over. So consider if perhaps it's time to "burn some ships" just keep things interesting.</p>
            
            <p><img alt="Changed my keys around to Dvorak" src="http://farm4.static.flickr.com/3006/3113807315_77d2aa278c_m.jpg" class="right"/></p>
            
            <p>Good heavens, some day I may even do something as crazy as switch to a Dvorak keyboard layout. That just might make my head explore&#8212;in a good way.</p>
            
            <div style="clear: both;"></div>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>A Tale of Sandwiches and Happiness</title>
      <link>
        http://alexvollmer.com/posts/2008/12/18/a-tale-of-sandwiches-and-happiness/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/12/18/a-tale-of-sandwiches-and-happiness/#comments
      </comments>
      <pubDate>
        Thu, 18 Dec 2008 18:44:11 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            philosophy
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/12/18/a-tale-of-sandwiches-and-happiness/
      </guid>
      <description>
        <![CDATA[
            <img src="http://farm4.static.flickr.com/3228/3096590905_8ccb3341e4_m.jpg" class="left">
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p><img src="http://farm4.static.flickr.com/3228/3096590905_8ccb3341e4_m.jpg" class="left"/></p>
            
            <p>Yesterday the city of Seattle was paralyzed by the fear of snow (which arrived today). As a result downtown was a ghost-town. That meant that we could get sandwiches from <a href="http://www.salumicuredmeats.com/%20Salumi!%20Yum!">Salumi</a> (one of Seattle's culinary gems) in less than the usual hour-long wait.</p>
            
            <p>Even on a "quiet" day, the process still took about thirty minutes to order, receive and pay for our sandwiches. As we walked back to the office we started chatting about ways they could be faster. They could make sure that people are paying as quickly as they are getting their sandwiches made. They could make sure that the full menu is displayed further back in the line so customers know what they want as they arrive.</p>
            
            <p>We even ranked some of the local sandwich shops in terms of speed. If you want an incredible sandwich and have the time, Salumi is hard to beat. Next up would be our beloved <a href="http://www.tatsdeli.com/%20Cheesesteaks%20in%20Seattle!">Tat's</a>. Finally, you can go to a chain place, like Jimmy John's, and get a decent corporate sandwich in unbelievable time.</p>
            
            <p>So then I asked myself the question, "why doesn't Salmui try to go any faster?" Wouldn't their customers be happier? The lines are ridiculous at times and you can blow a whole lunch-hour just on getting a sandwich. The place is tiny too. It's near-impossible to eat it there because of the wait for a table. Wouldn't they like a bigger location to service more customers?</p>
            
            <p>And then it hit me: they don't go any faster and they don't grow any bigger because it wouldn't make them any <em>happier</em> to do so. The folks behind the counter clearly love what they're doing. It's not that they're indifferent to their customers, it's that they seem to be doing quite well the way things are and don't feel a need to "grow". I think there's something very laudable in that attitude.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Just Having Fun</title>
      <link>
        http://alexvollmer.com/posts/2008/12/16/just-having-fun/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/12/16/just-having-fun/#comments
      </comments>
      <pubDate>
        Tue, 16 Dec 2008 06:04:28 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <category>
        <![CDATA[
            philosophy
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/12/16/just-having-fun/
      </guid>
      <description>
        <![CDATA[
            After spending nearly all of my spare hours on <a href="http://moochbot.com%20moochbot%20%E2%80%94%20track%20what%20you%20lend%20and%20borrow">moochbot</a>, it was fun to take a lighter turn this week. Today I put up <a href="http://isthatfreedomrock.com%20Freedom%20Rock!!">http://isthatfreedomrock.com</a>. If you are lucky enough to have watched cable in the late 80's you might remember a TV ad in which two hippies sit in front of a VW bus doing their best Cheech &amp; Chong routine. One hippy says to another, "is that freedom rock? Then turn it up!" I don't know why, but that has <em>always</em> been funny to me. It's just so&hellip;stupid. Can you imagine the ad execs sitting around the table thinking this one up?
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>After spending nearly all of my spare hours on <a href="http://moochbot.com%20moochbot%20%E2%80%94%20track%20what%20you%20lend%20and%20borrow">moochbot</a>, it was fun to take a lighter turn this week. Today I put up <a href="http://isthatfreedomrock.com%20Freedom%20Rock!!">http://isthatfreedomrock.com</a>. If you are lucky enough to have watched cable in the late 80's you might remember a TV ad in which two hippies sit in front of a VW bus doing their best Cheech &amp; Chong routine. One hippy says to another, "is that freedom rock? Then turn it up!" I don't know why, but that has <em>always</em> been funny to me. It's just so&#8230;stupid. Can you imagine the ad execs sitting around the table thinking this one up?</p>
            
            <p>Anyway, one day it occurred to me that it would be funny (to me at least) to have a very simple, <a href="http://kottke.org/08/02/single-serving-sites">single-serving site</a> that told you whether a certain song or artist was, in fact, Freedom Rock. Note the capital letters. I'm not talking Toby Keith "freedom rock" with waving flags in the background and lots of shiny pickup trucks. I'm talking about the rather odd, rag-tag collection of songs that made it to this album. Try playing with the site a bit, then check out the <a href="http://www.urbandictionary.com/define.php?term=Freedom+Rock%20Freedom%20Rock">Urban Dictionary's entry on Freedom Rock</a>.</p>
            
            <p>This leads to one of the pillars of my personal philosophy:</p>
            
            <blockquote><p>Amuse yourself first, worry about your audience later.</p></blockquote>
            
            <p>I wrote this primarily because it make me laugh&#8230;a lot. I don't know why. It's dumb, it's juvenile and it's <em>totally</em> worth the $10 I paid to register the domain-name.</p>
            
            <p>But stupid-hippy jokes don't make up the whole picture. The other half of this fun little excursion was figuring out how to do this with as little code as possible. At first I wrote this as a flat <a href="http://merbivore.com/%20Merb">Merb</a> application. Unfortunately I wanted to host this on Dreamhost and they are still on Ancient Ruby (read, 1.8.5) and the newest Merb wants 1.8.6. That was a deal-breaker. So then I took a look at <a href="http://sinatra.rubyforge.org/%20Sinatra!">Sinatra</a>, and just like the bobby-soxers of the 40's I fell in love. Sigh&#8230;</p>
            
            <p>If you haven't had a chance to look at Sinatra, you should do yourself a favor and check it out. They call it a DSL for the web, <em>not</em> a framework. It has the nice terse syntax of <a href="http://camping.rubyforge.org/files/README.html%20Camping">Camping</a>, but has source that gives me half a chance to comprehend. It's short and sweet and gives enough things that will make sense to Rails-veteran, but with little extra cruft. The source is up on <a href="http://github.com/alexvollmer/freedomrock/tree/master%20GitHub%20repo">GitHub</a> (with a shocking number of commits for such a simple project).</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>This Evening's moochbot Lessons</title>
      <link>
        http://alexvollmer.com/posts/2008/12/11/this-evenings-moochbot-lessons/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/12/11/this-evenings-moochbot-lessons/#comments
      </comments>
      <pubDate>
        Thu, 11 Dec 2008 05:27:15 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <category>
        <![CDATA[
            moochbot
        ]]>
      </category>
      <category>
        <![CDATA[
            git
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/12/11/this-evenings-moochbot-lessons/
      </guid>
      <description>
        <![CDATA[
            Tonight's post-get-the-kid-to-bed hacking time was a mad dash to cleanup a handful of bugs that some of you fine folks found with <a href="http://moochbot.com">moochbot</a>. In no particular order is a list of this evening's "a-ha" moments:
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>Tonight's post-get-the-kid-to-bed hacking time was a mad dash to cleanup a handful of bugs that some of you fine folks found with <a href="http://moochbot.com">moochbot</a>. In no particular order is a list of this evening's "a-ha" moments:</p>
            
            <ul>
            <li>Receiving email works much much better when you configure your MX records correctly</li>
            <li>Mixing ERb and HAML is a really really bad idea</li>
            <li>I'm probably using RubyGems in my Rails app in about the stupidest way possible</li>
            <li>Good dynamic form validation with JavaScript is actually quite tricky</li>
            <li>Squashing commits in Git is still pretty damn cool.</li>
            </ul>
            
            
            <p>That's all for now. Good night and good luck.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>moochbot is out</title>
      <link>
        http://alexvollmer.com/posts/2008/12/10/moochbot-is-out/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/12/10/moochbot-is-out/#comments
      </comments>
      <pubDate>
        Wed, 10 Dec 2008 06:29:02 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            moochbot
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/12/10/moochbot-is-out/
      </guid>
      <description>
        <![CDATA[
            <img alt="moochbot" src="http://moochbot.com/images/logo.png" class="left">
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p><img alt="moochbot" src="http://moochbot.com/images/logo.png" class="left"/></p>
            
            <p>The secret side-project that I've been referring to in various posts finally reached its Version One milestone tonight. I closed out the remaining V1 issues off of my to-do list and suddenly realized that I could turn it loose on the world. So, without further ado, I present to you: <a href="http://moochbot.com%20moochbot%20%20track%20what%20you%20lend%20and%20borrow">moochbot</a>. Check it out and see what I've been up to all this time.</p>
            
            <p>I'd love to hear any feedback folks have; what you love and what you hate. I have a ton of V2 features planned so moochbot will certainly continue to evolve. It's running on a single Slicehost slice so it's not real high-powered. Your patience is appreciated.</p>
            
            <p>I'll have plenty of blog-material from this experience to write up later, but for now I'm just going to enjoy the satisfying feeling of getting something done. Enjoy!</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Code Personification</title>
      <link>
        http://alexvollmer.com/posts/2008/12/09/code-personification/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/12/09/code-personification/#comments
      </comments>
      <pubDate>
        Tue, 09 Dec 2008 04:31:19 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            software
        ]]>
      </category>
      <category>
        <![CDATA[
            philosophy
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/12/09/code-personification/
      </guid>
      <description>
        <![CDATA[
            Today I had a conversation with co-worker about the two software components we were trying to integrate in the system we work on. In that conversation I noticed that we both kept referring to the software components as "you" and "I". If you had been within earshot of this conversation you would have heard phrases like "&hellip;<em>you</em> should send the dates in GMT so that <em>I</em> can search correctly&hellip;" I don't think this conversational style is uncommon among developers. Clearly it's not logically correct&mdash;it's not <em>me</em> sending dates, it's <em>my code</em>. It's not <em>him</em> who is searching, it's <em>his code</em>. Why do we do this?
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>Today I had a conversation with co-worker about the two software components we were trying to integrate in the system we work on. In that conversation I noticed that we both kept referring to the software components as "you" and "I". If you had been within earshot of this conversation you would have heard phrases like "&#8230;<em>you</em> should send the dates in GMT so that <em>I</em> can search correctly&#8230;" I don't think this conversational style is uncommon among developers. Clearly it's not logically correct&#8212;it's not <em>me</em> sending dates, it's <em>my code</em>. It's not <em>him</em> who is searching, it's <em>his code</em>. Why do we do this?</p>
            
            <p>For some reason I've always been a little bothered by this and have always felt like it should be avoided. I dislike this way of speaking about a system because of how easy it is to personalize code that someone has written. I think it's simply too easy to make a statement that sounds like a criticism of the <em>person</em> instead of the <em>work.</em> A criticism of the latter can be hard enough to take, but when it's misconstrued with a personal critique it's very easy for feelings to get hurt and team morale to take a nose-dive. In my experience it takes far longer to gain the trust of your teammates than it does to lose it.</p>
            
            <p>The other reason I don't like it is that it reinforces ownership boundaries around particular parts of the code. In an ideal world your team members would be able to move fluidly about the codebase depending on the features to be implemented. In reality this can become difficult, but like a lot of thinks in life I think it's a good "stretch-goal". So when we speak in personal pronouns instead of component names it seems to reinforce these barriers (or maybe it simply highlights them.)</p>
            
            <p>I've found that it's difficult to avoid talking about systems in these terms. I have to make a conscious effort to not do this in much the same way I have to remember to floss. Both seem like unnatural activities that require a lot of discipline to maintain. I can understand why I might be inclined to forget flossing regularly, but it's not as clear to me why all of us developers want to personify code in our daily speech. Any ideas?</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Asynchronous Mail with DelayedJob, God &amp;#38; Daemons</title>
      <link>
        http://alexvollmer.com/posts/2008/11/06/asynchronous-mail-with-delayedjob-god-daemons/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/11/06/asynchronous-mail-with-delayedjob-god-daemons/#comments
      </comments>
      <pubDate>
        Thu, 06 Nov 2008 05:14:42 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <category>
        <![CDATA[
            rails
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/11/06/asynchronous-mail-with-delayedjob-god-daemons/
      </guid>
      <description>
        <![CDATA[
            Slowly but surely I've been pecking away at a little Rails-based side-project for the last four or five months. I'm <em>this close</em> to flipping the <em>on</em> switch&mdash;but in the meantime I've still got some "i"s to dot and "t"s to cross. One of those was switching from in-request mail delivery to asynchronous mail delivery. The app I've been working on involves two parties marching a particular transaction through a variety of state transitions, each of which usually sends an email to either or both parties.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>Slowly but surely I've been pecking away at a little Rails-based side-project for the last four or five months. I'm <em>this close</em> to flipping the <em>on</em> switch&mdash;but in the meantime I've still got some "i"s to dot and "t"s to cross. One of those was switching from in-request mail delivery to asynchronous mail delivery. The app I've been working on involves two parties marching a particular transaction through a variety of state transitions, each of which usually sends an email to either or both parties.</p>
            
            <p>Like a good boy I started out with the simplest thing that could work which was to simply call mailers in my model. However, I wanted to limit the number of activities performed during a request to keep the app feeling responsive. So I decided that asynchronous mail delivery was a "pre-launch" feature that I had to have.</p>
            
            <p>I looked at a variety of background processing tools, including Bj, Starling/Workling, Spawn and AP4R. Each had its strengths and weaknesses but none of them felt like the right fit. My research criteria included:
            *  Job persistence via the database
            *  Something that could get a Rails environment cheaply
            *  Runs outside of the Rails processes
            *  Minimum fuss to get it running</p>
            
            <p>In the end the one that hit the sweet-spot best was <a href="http://github.com/tobi/delayed_job%20GitHub%20Repo">delayed_job.</a> It had the DB persistence I was looking for, but didn't source the Rails environment for each worker and it was extremely simple to plumb it into my app.</p>
            
            <h2>Refactoring</h2>
            
            <p>The first step was creating the <code>DelayedJob</code> worker classes; one for each mail action. At first this turned into a big pile of five-line classes, so to keep things organized I put these all in <code>app/models/jobs</code> and put each class in the Jobs module namespace. This was better, but not good enough so the final step was putting <em>all</em> of the worker classes in a single file, <code>app/models/jobs.rb</code>.</p>
            
            <p>The second step was to find every place in the model where I called my mailer classes directly and replace them with calls to enqueue the appropriate worker job.</p>
            
            <p>Here is what things looked like at first:</p>
            
            <div class="highlight"><pre><span class="k">class</span> <span class="nc">UserObserver</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Observer</span>&#x000A;      <span class="k">def</span> <span class="nf">after_create</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>&#x000A;        <span class="k">unless</span> <span class="n">user</span><span class="o">.</span><span class="n">current_state</span> <span class="o">==</span> <span class="ss">:latent</span> <span class="ow">or</span> <span class="n">user</span><span class="o">.</span><span class="n">is_a?</span><span class="p">(</span><span class="no">Admin</span><span class="p">)</span>&#x000A;          <span class="no">UserNotifier</span><span class="o">.</span><span class="n">deliver_signup_notification</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>&#x000A;        <span class="k">end</span>&#x000A;      <span class="k">end</span>&#x000A;    &#x000A;      <span class="k">def</span> <span class="nf">after_save</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>&#x000A;        <span class="k">if</span> <span class="n">user</span><span class="o">.</span><span class="n">current_state</span> <span class="o">==</span> <span class="ss">:promoted</span>&#x000A;          <span class="no">UserNotifier</span><span class="o">.</span><span class="n">deliver_signup</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>&#x000A;        <span class="k">else</span>&#x000A;          <span class="no">UserNotifier</span><span class="o">.</span><span class="n">deliver_activation</span><span class="p">(</span><span class="n">user</span><span class="p">)</span> <span class="k">if</span> <span class="n">user</span><span class="o">.</span><span class="n">recently_activated?</span>&#x000A;        <span class="k">end</span>&#x000A;      <span class="k">end</span>&#x000A;    <span class="k">end</span>&#x000A;    </pre>
            </div>
            
            
            <p>Then the UserObserver was refactored like this:</p>
            
            <div class="highlight"><pre><span class="k">class</span> <span class="nc">UserObserver</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Observer</span>&#x000A;      <span class="k">def</span> <span class="nf">after_create</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>&#x000A;        <span class="k">unless</span> <span class="n">user</span><span class="o">.</span><span class="n">current_state</span> <span class="o">==</span> <span class="ss">:latent</span> <span class="ow">or</span> <span class="n">user</span><span class="o">.</span><span class="n">is_a?</span><span class="p">(</span><span class="no">Admin</span><span class="p">)</span>&#x000A;          <span class="no">Delayed</span><span class="o">::</span><span class="no">Job</span><span class="o">.</span><span class="n">enqueue</span><span class="p">(</span><span class="no">Jobs</span><span class="o">::</span><span class="no">UserNotifierSignupNotificationJob</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">user</span><span class="o">.</span><span class="n">id</span><span class="p">))</span>&#x000A;        <span class="k">end</span>&#x000A;      <span class="k">end</span>&#x000A;    &#x000A;      <span class="k">def</span> <span class="nf">after_save</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>&#x000A;        <span class="k">if</span> <span class="n">user</span><span class="o">.</span><span class="n">current_state</span> <span class="o">==</span> <span class="ss">:promoted</span>&#x000A;          <span class="no">Delayed</span><span class="o">::</span><span class="no">Job</span><span class="o">.</span><span class="n">enqueue</span><span class="p">(</span><span class="no">Jobs</span><span class="o">::</span><span class="no">UserNotifierSignupNotificationJob</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">user</span><span class="o">.</span><span class="n">id</span><span class="p">))</span>&#x000A;        <span class="k">else</span>&#x000A;          <span class="no">Delayed</span><span class="o">::</span><span class="no">Job</span><span class="o">.</span><span class="n">enqueue</span><span class="p">(</span><span class="no">Jobs</span><span class="o">::</span><span class="no">UserNotifierActivationJob</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">user</span><span class="o">.</span><span class="n">id</span><span class="p">))</span> <span class="k">if</span> <span class="n">user</span><span class="o">.</span><span class="n">recently_activated?</span>&#x000A;        <span class="k">end</span>&#x000A;      <span class="k">end</span>&#x000A;    <span class="k">end</span>&#x000A;    </pre>
            </div>
            
            
            <p>With the following workers (abridged):</p>
            
            <div class="highlight"><pre><span class="k">module</span> <span class="nn">Jobs</span>&#x000A;      <span class="k">class</span> <span class="nc">UserNotifierDisconnectJob</span> <span class="o">&lt;</span> <span class="no">Struct</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="ss">:user_id</span><span class="p">)</span>&#x000A;        <span class="k">def</span> <span class="nf">perform</span>&#x000A;          <span class="no">UserNotifier</span><span class="o">.</span><span class="n">deliver_disconnect</span><span class="p">(</span><span class="n">user_id</span><span class="p">)</span>&#x000A;        <span class="k">end</span>&#x000A;      <span class="k">end</span>&#x000A;    &#x000A;      <span class="k">class</span> <span class="nc">UserNotifierResetPasswordJob</span> <span class="o">&lt;</span> <span class="no">Struct</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="ss">:user</span><span class="p">)</span>&#x000A;        <span class="k">def</span> <span class="nf">perform</span>&#x000A;          <span class="no">UserNotifier</span><span class="o">.</span><span class="n">deliver_reset_password</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>&#x000A;        <span class="k">end</span>&#x000A;      <span class="k">end</span>&#x000A;    &#x000A;      <span class="k">class</span> <span class="nc">UserNotifierSignupNotificationJob</span> <span class="o">&lt;</span> <span class="no">Struct</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="ss">:user</span><span class="p">)</span>&#x000A;        <span class="k">def</span> <span class="nf">perform</span>&#x000A;          <span class="no">UserNotifier</span><span class="o">.</span><span class="n">deliver_signup_notification</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>&#x000A;        <span class="k">end</span>&#x000A;      <span class="k">end</span>&#x000A;    &#x000A;      <span class="k">class</span> <span class="nc">UserNotifierStartDisconnectJob</span> <span class="o">&lt;</span> <span class="no">Struct</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="ss">:user_id</span><span class="p">)</span>&#x000A;        <span class="k">def</span> <span class="nf">perform</span>&#x000A;          <span class="no">UserNotifier</span><span class="o">.</span><span class="n">deliver_start_disconnect</span><span class="p">(</span><span class="n">user_id</span><span class="p">)</span>&#x000A;        <span class="k">end</span>&#x000A;      <span class="k">end</span>&#x000A;    <span class="k">end</span>&#x000A;    </pre>
            </div>
            
            
            <h2>Testing</h2>
            
            <p>Prior to switching to asynchronous processing, mail delivery was triggered within my models, either via Observers or as Procs attached to state transitions (I'm using the <code>acts_as_state_machine</code> plugin). Therefore my tests had loads of assertions that various state changes in the model resulted in direct email delivery. In the asynchronous model of course, that changes slightly. While the state change ultimately ends in mail delivery, it only happens indirectly.</p>
            
            <p>So here I had a big pile of tests that asserted that poking the model in certain ways resulted in a mail delivery. In my unit-tests I really just wanted to test the interaction between the models and <code>DelayedJob</code>. After all, if something went hay-wire during mail delivery the culprit would likely be my new worker classes, <em>not</em> my model.</p>
            
            <p>However, for my integration tests I still wanted to keep the assertions about actual mail delivery since that was an important part of the stories. I could easily do this by monkey-patching the <code>DelayedJob::enqueue</code> method to call the worker's <code>perform</code> method directly. In my unit-tests I monkey-patched the <code>DelayedJob::enqueue</code> method to work more like a mock object which added some inquiry methods to check that it had been invoked correctly.</p>
            
            <p>In isolation this worked great, but running all the tests together resulted in a number of random failures. I've run into this enough times to recognize that some tests were somehow poisoning the run-time environment for the others. It turns out that my two approaches were incompatible with each other unless I was very diligent about cleaning everything up properly. I will admit with red-faced shame that I punted. I did the lamest thing one could possibly do and redefined <code>DelayedJob::enqueue</code> for <em>all</em> of my tests and kept all of my original assertions. I'm not proud of it, but it does work.</p>
            
            <h2>Running in Production</h2>
            
            <p>The next trick was getting this all running in a production environment and I needed to figure out how one or more workers would be started and kept running. While it's great to have this decoupled from the Rails environment, it means that it's a separate process that needs to be managed.</p>
            
            <p>My solution was to use the <code>daemons</code> gem to create a couple of scripts. Then I used Tom Preston-Warner's <code>god</code>, to monitor my process. The scripts look like this:</p>
            
            <div class="highlight"><pre><span class="c1">#!/usr/bin/env ruby</span>&#x000A;    &#x000A;    <span class="k">unless</span> <span class="no">ARGV</span><span class="o">.</span><span class="n">size</span> <span class="o">==</span> <span class="mi">1</span>&#x000A;      <span class="vg">$stderr</span><span class="o">.</span><span class="n">puts</span> <span class="s2">&quot;USAGE: </span><span class="si">#{</span><span class="mi">0</span><span class="si">}</span><span class="s2"> [environment]&quot;</span>&#x000A;      <span class="nb">exit</span> <span class="mi">1</span>&#x000A;    <span class="k">end</span>&#x000A;    &#x000A;    <span class="no">RAILS_ENV</span> <span class="o">=</span> <span class="no">ARGV</span><span class="o">.</span><span class="n">first</span>&#x000A;    <span class="nb">require</span> <span class="no">File</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="bp">__FILE__</span><span class="p">)</span> <span class="o">+</span> <span class="s1">&#39;/../config/environment&#39;</span>&#x000A;    &#x000A;    <span class="no">Delayed</span><span class="o">::</span><span class="no">Worker</span><span class="o">.</span><span class="n">new</span><span class="o">.</span><span class="n">start</span>&#x000A;    </pre>
            </div>
            
            
            <p>The "control" script looks like this:</p>
            
            <div class="highlight"><pre><span class="c1">#!/usr/bin/env ruby</span>&#x000A;    &#x000A;    <span class="nb">require</span> <span class="s2">&quot;rubygems&quot;</span>&#x000A;    <span class="nb">require</span> <span class="s2">&quot;daemons&quot;</span>&#x000A;    &#x000A;    <span class="k">def</span> <span class="nf">running?</span><span class="p">(</span><span class="n">pid</span><span class="p">)</span>&#x000A;      <span class="c1"># Check if process is in existence</span>&#x000A;      <span class="c1"># The simplest way to do this is to send signal &#39;0&#39;</span>&#x000A;      <span class="c1"># (which is a single system call) that doesn&#39;t actually</span>&#x000A;      <span class="c1"># send a signal</span>&#x000A;      <span class="k">begin</span>&#x000A;        <span class="no">Process</span><span class="o">.</span><span class="n">kill</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">pid</span><span class="p">)</span>&#x000A;        <span class="k">return</span> <span class="kp">true</span>&#x000A;      <span class="k">rescue</span> <span class="no">Errno</span><span class="o">::</span><span class="no">ESRCH</span>&#x000A;        <span class="k">return</span> <span class="kp">false</span>&#x000A;      <span class="k">rescue</span> <span class="o">::</span><span class="no">Exception</span>&#x000A;        <span class="c1"># for example on EPERM (process exists but does not belong to us)</span>&#x000A;        <span class="k">return</span> <span class="kp">true</span>&#x000A;      <span class="k">end</span>&#x000A;    <span class="k">end</span>&#x000A;    &#x000A;    <span class="k">if</span> <span class="no">ARGV</span><span class="o">.</span><span class="n">size</span> <span class="o">==</span> <span class="mi">1</span> <span class="ow">and</span> <span class="no">ARGV</span><span class="o">.</span><span class="n">first</span> <span class="o">==</span> <span class="s2">&quot;status&quot;</span>&#x000A;      <span class="n">pidfile</span> <span class="o">=</span> <span class="s2">&quot;/var/run/delayed_job_worker.pid&quot;</span>&#x000A;      <span class="k">if</span> <span class="no">File</span><span class="o">.</span><span class="n">exists?</span><span class="p">(</span><span class="n">pidfile</span><span class="p">)</span>&#x000A;        <span class="n">pid</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">pidfile</span><span class="p">)</span><span class="o">.</span><span class="n">readlines</span><span class="o">.</span><span class="n">first</span><span class="o">.</span><span class="n">strip</span><span class="o">.</span><span class="n">to_i</span>&#x000A;        <span class="k">if</span> <span class="n">running?</span><span class="p">(</span><span class="n">pid</span><span class="p">)</span>&#x000A;          <span class="nb">puts</span> <span class="s2">&quot;delayed_job_worker is running (</span><span class="si">#{</span><span class="n">pid</span><span class="si">}</span><span class="s2">)&quot;</span>&#x000A;        <span class="k">else</span>&#x000A;          <span class="nb">puts</span> <span class="s2">&quot;delayed_job_worker is NOT running (</span><span class="si">#{</span><span class="n">pid</span><span class="si">}</span><span class="s2">)&quot;</span>&#x000A;        <span class="k">end</span>&#x000A;      <span class="k">else</span>&#x000A;        <span class="nb">puts</span> <span class="s2">&quot;delayed_job_worker is NOT running (none)&quot;</span>&#x000A;      <span class="k">end</span>&#x000A;    <span class="k">else</span>&#x000A;      <span class="no">Daemons</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="no">File</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="bp">__FILE__</span><span class="p">)</span> <span class="o">+</span> <span class="s1">&#39;/delayed_job_worker&#39;</span><span class="p">,</span>&#x000A;                  <span class="ss">:backtrace</span> <span class="o">=&gt;</span> <span class="kp">true</span><span class="p">,</span>&#x000A;                  <span class="ss">:log_output</span> <span class="o">=&gt;</span> <span class="kp">true</span><span class="p">,</span>&#x000A;                  <span class="ss">:dir</span> <span class="o">=&gt;</span> <span class="no">File</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="bp">__FILE__</span><span class="p">)</span> <span class="o">+</span> <span class="s2">&quot;/../tmp/pids&quot;</span><span class="p">,</span>&#x000A;                  <span class="ss">:dir_mode</span> <span class="o">=&gt;</span> <span class="ss">:normal</span><span class="p">,</span>&#x000A;                  <span class="ss">:multiple</span> <span class="o">=&gt;</span> <span class="kp">false</span><span class="p">)</span>&#x000A;    <span class="k">end</span>&#x000A;    </pre>
            </div>
            
            
            <p>The first script is merely the smallest amount of chrome required to start a worker. Note that I'm using <a href="http://github.com/jbarnette/delayed_job/tree%20jbarnette's%20GitHub%20Repo">John Barnette's version of delayed_job</a> which gives us that nice little worker riff. The second script is essentially the <code>daemons</code> wrapper around my worker. For a little extra goodness I added my own "status" command which can be handy for debugging.</p>
            
            <p>Getting those to work properly took a little bit of testing by hand. Fortunately, the entire solution is really a series of layers applied on top of each other. Once you convince yourself that an inner-layer is working correctly you can move on to build the outer layers.</p>
            
            <p>The next step was to create a proper god configuration. I have more than one thing to monitor on my setup so I have a master god configuration that includes sub-configurations. My "main" configuration is a simple one-liner:
            <code>God.load "/etc/god/*.god"</code>
            My application-specific configuration looks like this (with a few edits for public consumption):</p>
            
            <div class="highlight"><pre><span class="no">RAILS_ROOT</span> <span class="o">=</span> <span class="s2">&quot;/var/www/moochbot/current&quot;</span>&#x000A;    &#x000A;    <span class="no">God</span><span class="o">::</span><span class="no">Contacts</span><span class="o">::</span><span class="no">Email</span><span class="o">.</span><span class="n">message_settings</span> <span class="o">=</span> <span class="p">{</span>&#x000A;      <span class="c1"># your config goes here</span>&#x000A;    <span class="p">}</span>&#x000A;    &#x000A;    <span class="no">God</span><span class="o">::</span><span class="no">Contacts</span><span class="o">::</span><span class="no">Email</span><span class="o">.</span><span class="n">server_settings</span> <span class="o">=</span> <span class="p">{</span>&#x000A;      <span class="c1"># your config goes here</span>&#x000A;    <span class="p">}</span>&#x000A;    &#x000A;    <span class="no">God</span><span class="o">.</span><span class="n">contact</span><span class="p">(</span><span class="ss">:email</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">c</span><span class="o">|</span>&#x000A;      <span class="c1"># your config goes here</span>&#x000A;    <span class="k">end</span>&#x000A;    </pre>
            </div>
            
            
            <p>After setting up some default notification details, we get into the meat of defining our "watch":</p>
            
            <div class="highlight"><pre><span class="no">God</span><span class="o">.</span><span class="n">watch</span> <span class="k">do</span> <span class="o">|</span><span class="n">w</span><span class="o">|</span>&#x000A;      <span class="n">w</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="s2">&quot;delayed_job_worker&quot;</span>&#x000A;      <span class="n">w</span><span class="o">.</span><span class="n">interval</span> <span class="o">=</span> <span class="mi">10</span><span class="o">.</span><span class="n">seconds</span>&#x000A;      <span class="n">w</span><span class="o">.</span><span class="n">start</span> <span class="o">=</span> <span class="s2">&quot;</span><span class="si">#{</span><span class="no">RAILS_ROOT</span><span class="si">}</span><span class="s2">/script/delayed_job_worker_control start -- production&quot;</span>&#x000A;      <span class="n">w</span><span class="o">.</span><span class="n">stop</span> <span class="o">=</span> <span class="s2">&quot;</span><span class="si">#{</span><span class="no">RAILS_ROOT</span><span class="si">}</span><span class="s2">/script/delayed_job_worker_control stop&quot;</span>&#x000A;      <span class="n">w</span><span class="o">.</span><span class="n">restart</span> <span class="o">=</span> <span class="s2">&quot;</span><span class="si">#{</span><span class="no">RAILS_ROOT</span><span class="si">}</span><span class="s2">/script/delayed_job_worker_control restart&quot;</span>&#x000A;      <span class="n">w</span><span class="o">.</span><span class="n">start_grace</span> <span class="o">=</span> <span class="mi">10</span><span class="o">.</span><span class="n">seconds</span>&#x000A;      <span class="n">w</span><span class="o">.</span><span class="n">restart_grace</span> <span class="o">=</span> <span class="mi">10</span><span class="o">.</span><span class="n">seconds</span>&#x000A;      <span class="n">w</span><span class="o">.</span><span class="n">pid_file</span> <span class="o">=</span> <span class="s2">&quot;</span><span class="si">#{</span><span class="no">RAILS_ROOT</span><span class="si">}</span><span class="s2">/tmp/pids/delayed_job_worker.pid&quot;</span>&#x000A;    &#x000A;      <span class="n">w</span><span class="o">.</span><span class="n">uid</span> <span class="o">=</span> <span class="s2">&quot;deploy&quot;</span>&#x000A;      <span class="n">w</span><span class="o">.</span><span class="n">gid</span> <span class="o">=</span> <span class="s2">&quot;root&quot;</span>&#x000A;      <span class="n">w</span><span class="o">.</span><span class="n">behavior</span><span class="p">(</span><span class="ss">:clean_pid_file</span><span class="p">)</span>&#x000A;    </pre>
            </div>
            
            
            <p>Next we need to define our transitions. My first attempt at this failed because I was missing these and my watched process was stuck in the "unmonitored" state. It's worth spending some time reading the docs on the <a href="http://god.rubyforge.org/">homepage</a> since at first glance the configuration wasn't obvious to me.</p>
            
            <div class="highlight"><pre>  <span class="c1"># determine the state on startup</span>&#x000A;      <span class="n">w</span><span class="o">.</span><span class="n">transition</span><span class="p">(</span><span class="ss">:init</span><span class="p">,</span> <span class="p">{</span> <span class="kp">true</span> <span class="o">=&gt;</span> <span class="ss">:up</span><span class="p">,</span> <span class="kp">false</span> <span class="o">=&gt;</span> <span class="ss">:start</span> <span class="p">})</span> <span class="k">do</span> <span class="o">|</span><span class="n">on</span><span class="o">|</span>&#x000A;        <span class="n">on</span><span class="o">.</span><span class="n">condition</span><span class="p">(</span><span class="ss">:process_running</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">c</span><span class="o">|</span>&#x000A;          <span class="n">c</span><span class="o">.</span><span class="n">running</span> <span class="o">=</span> <span class="kp">true</span>&#x000A;        <span class="k">end</span>&#x000A;      <span class="k">end</span>&#x000A;    &#x000A;      <span class="c1"># determine when process has finished starting</span>&#x000A;      <span class="n">w</span><span class="o">.</span><span class="n">transition</span><span class="p">(</span><span class="o">[</span><span class="ss">:start</span><span class="p">,</span> <span class="ss">:restart</span><span class="o">]</span><span class="p">,</span> <span class="ss">:up</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">on</span><span class="o">|</span>&#x000A;        <span class="n">on</span><span class="o">.</span><span class="n">condition</span><span class="p">(</span><span class="ss">:process_running</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">c</span><span class="o">|</span>&#x000A;          <span class="n">c</span><span class="o">.</span><span class="n">running</span> <span class="o">=</span> <span class="kp">true</span>&#x000A;        <span class="k">end</span>&#x000A;    &#x000A;        <span class="c1"># failsafe</span>&#x000A;        <span class="n">on</span><span class="o">.</span><span class="n">condition</span><span class="p">(</span><span class="ss">:tries</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">c</span><span class="o">|</span>&#x000A;          <span class="n">c</span><span class="o">.</span><span class="n">times</span> <span class="o">=</span> <span class="mi">5</span>&#x000A;          <span class="n">c</span><span class="o">.</span><span class="n">transition</span> <span class="o">=</span> <span class="ss">:start</span>&#x000A;        <span class="k">end</span>&#x000A;      <span class="k">end</span>&#x000A;    &#x000A;      <span class="c1"># start if process is not running</span>&#x000A;      <span class="n">w</span><span class="o">.</span><span class="n">transition</span><span class="p">(</span><span class="ss">:up</span><span class="p">,</span> <span class="ss">:start</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">on</span><span class="o">|</span>&#x000A;        <span class="n">on</span><span class="o">.</span><span class="n">condition</span><span class="p">(</span><span class="ss">:process_exits</span><span class="p">)</span>&#x000A;      <span class="k">end</span>&#x000A;    </pre>
            </div>
            
            
            <p>Finally I specify some resource-consumption boundaries to make sure that my little worker daemon doesn't take over my box:</p>
            
            <div class="highlight"><pre>  <span class="n">w</span><span class="o">.</span><span class="n">restart_if</span> <span class="k">do</span> <span class="o">|</span><span class="n">restart</span><span class="o">|</span>&#x000A;        <span class="n">restart</span><span class="o">.</span><span class="n">condition</span><span class="p">(</span><span class="ss">:memory_usage</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">c</span><span class="o">|</span>&#x000A;          <span class="n">c</span><span class="o">.</span><span class="n">above</span> <span class="o">=</span> <span class="mi">100</span><span class="o">.</span><span class="n">megabytes</span>&#x000A;          <span class="n">c</span><span class="o">.</span><span class="n">times</span> <span class="o">=</span> <span class="o">[</span><span class="mi">3</span><span class="p">,</span> <span class="mi">5</span><span class="o">]</span>&#x000A;        <span class="k">end</span>&#x000A;    &#x000A;        <span class="n">restart</span><span class="o">.</span><span class="n">condition</span><span class="p">(</span><span class="ss">:cpu_usage</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">c</span><span class="o">|</span>&#x000A;          <span class="n">c</span><span class="o">.</span><span class="n">above</span> <span class="o">=</span> <span class="mi">50</span><span class="o">.</span><span class="n">percent</span>&#x000A;          <span class="n">c</span><span class="o">.</span><span class="n">times</span> <span class="o">=</span> <span class="mi">5</span>&#x000A;        <span class="k">end</span>&#x000A;      <span class="k">end</span>&#x000A;    &#x000A;      <span class="n">w</span><span class="o">.</span><span class="n">lifecycle</span> <span class="k">do</span> <span class="o">|</span><span class="n">on</span><span class="o">|</span>&#x000A;        <span class="n">on</span><span class="o">.</span><span class="n">condition</span><span class="p">(</span><span class="ss">:flapping</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">c</span><span class="o">|</span>&#x000A;          <span class="n">c</span><span class="o">.</span><span class="n">to_state</span> <span class="o">=</span> <span class="o">[</span><span class="ss">:start</span><span class="p">,</span> <span class="ss">:restart</span><span class="o">]</span>&#x000A;          <span class="n">c</span><span class="o">.</span><span class="n">times</span> <span class="o">=</span> <span class="mi">5</span>&#x000A;          <span class="n">c</span><span class="o">.</span><span class="n">within</span> <span class="o">=</span> <span class="mi">5</span><span class="o">.</span><span class="n">minute</span>&#x000A;          <span class="n">c</span><span class="o">.</span><span class="n">transition</span> <span class="o">=</span> <span class="ss">:unmonitored</span>&#x000A;          <span class="n">c</span><span class="o">.</span><span class="n">retry_in</span> <span class="o">=</span> <span class="mi">10</span><span class="o">.</span><span class="n">minutes</span>&#x000A;          <span class="n">c</span><span class="o">.</span><span class="n">retry_times</span> <span class="o">=</span> <span class="mi">5</span>&#x000A;          <span class="n">c</span><span class="o">.</span><span class="n">retry_within</span> <span class="o">=</span> <span class="mi">2</span><span class="o">.</span><span class="n">hours</span>&#x000A;        <span class="k">end</span>&#x000A;      <span class="k">end</span>&#x000A;    <span class="k">end</span>&#x000A;    </pre>
            </div>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Election Infographics Redux</title>
      <link>
        http://alexvollmer.com/posts/2008/11/05/election-infographics-redux/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/11/05/election-infographics-redux/#comments
      </comments>
      <pubDate>
        Wed, 05 Nov 2008 05:43:36 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            information design
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/11/05/election-infographics-redux/
      </guid>
      <description>
        <![CDATA[
            With Election Night coming to a close and Senator Barack Obama declared as President-elect, I figured I'd spend a little time <a href="http://livollmers.net/index.php/2008/02/05/a-survey-of-super-tuesday-infographics/">re-surveying the infographics</a> of some of the major online publishers. This is by no means comprehensive, and doesn't consider a lot of the interesting visualizations that some folks are doing. I consciously stuck with the mainstream to see how design innovation was trickling down into conservative publishers.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>With Election Night coming to a close and Senator Barack Obama declared as President-elect, I figured I'd spend a little time <a href="http://livollmers.net/index.php/2008/02/05/a-survey-of-super-tuesday-infographics/">re-surveying the infographics</a> of some of the major online publishers. This is by no means comprehensive, and doesn't consider a lot of the interesting visualizations that some folks are doing. I consciously stuck with the mainstream to see how design innovation was trickling down into conservative publishers.</p>
            
            <p>So without further ado, let's start with the <a href="http://nytimes.com%20The%20New%20York%20Times">New York Times:</a></p>
            
            <p><img src="http://livollmers.net/wp-content/uploads/2008/11/200811041829.jpg" alt="200811041829.jpg" /></p>
            
            <p>If you remember my original post, I favored the tabular displays generally over map-based views. My favorite part of this is the electoral vote tally on the left-hand side. To me, the most important information is the electoral vote tally and the Times has done a good job of highlighting this both by making it a larger font and putting it on the left-hand side. Then as my eye moves to the right I can drill down to state-by-state detail. The color codes provide a nice way to get a "feel" for the state of things at a glance.</p>
            
            <p>Next stop, <a href="http://www.cnn.com%20CNN">CNN</a>:</p>
            
            <p><img src="http://livollmers.net/wp-content/uploads/2008/11/200811041832.jpg" alt="200811041832.jpg" /></p>
            
            <p>What you don't see here is the completely unnecessary animation of the left-hand counters like some alarm-clock from the seventies. The other three panels "flip" to various local races. Instead of using space show multiple data-points, CNN chose to use time. This feel like a real waste of space and <em>my time</em>. When compared with the Times site, there is really no contest.</p>
            
            <p>Let's move on to <a href="http://www.msnbc.msn.com/%20MSNBC">MSNBC</a>. These guys get bonus points for including the Senate races in aggregated form&#8212;a nice touch if you want to take the pulse of all the races. This is a nice tight display of a lot of information.</p>
            
            <p><img src="http://livollmers.net/wp-content/uploads/2008/11/200811041949.jpg" alt="200811041949.jpg" /></p>
            
            <p>Again, I could do without the map. It's not that I don't like maps (I like them quite a bit), but they don't tell me the most <em>important</em> information&#8212;the electoral college votes. It's <em>interesting</em> to see the geographic proclivities but it's <em>essential</em> that I know the electoral college votes. The geographically large, but sparsely-populated states in Big Sky Country and the Midwest distort our initial perception of the data. Their size belies their actual value in the race and so we have to constantly look elsewhere for electoral college vote totals. Unfortunately, this graph doesn't tell that story. I can't look at Ohio in this chart and understand its value in the race.</p>
            
            <p>Next, the <a href="http://www.washingtonpost.com/%20The%20Washington%20Post">Washington Post</a>:</p>
            
            <p><img src="http://livollmers.net/wp-content/uploads/2008/11/200811042112.jpg" alt="200811042112.jpg" /></p>
            
            <p>This is pretty uninspiring and uninformative. Oh look! Pictures! Just in case you forgot what the candidates looked like. However, if we start clicking the tabs, we get a little more meat:</p>
            
            <p><img src="http://livollmers.net/wp-content/uploads/2008/11/2008110421121.jpg" alt="200811042112.jpg" /></p>
            
            <p>I'm not sure why the map is all-gray, but the I like the bar at the top. This seems to be the new trend this year in info-graphics. I think for highlighting the changes in the balance-of-power, the line chart does a good job.</p>
            
            <p>Now we move onto <a href="http://www.usatoday.com/%20USA%20Today">USA Today</a>&#8230;</p>
            
            <p><img src="http://livollmers.net/wp-content/uploads/2008/11/200811042118.jpg" alt="200811042118.jpg" /></p>
            
            <p>That's it folks. That's all you get on the front page, up in the right-hand corner. I think that pretty much sums up the estimation the editors of McPaper have of their audience. Let's, for a moment, try to treat this with a serious eye. Could somebody please explain what the blue boxes on the right-hand side are for? Do you click them? What do they do? Why are they the only ones shown? Can you see others? This is what Edward Tufte would call "chart junk".</p>
            
            <p>On to the once-venerable <a href="http://online.wsj.com/public/us%20Wall%20Street%20Journal">Wall Street Journal</a>&#8230;</p>
            
            <p><img src="http://livollmers.net/wp-content/uploads/2008/11/200811042121.jpg" alt="200811042121.jpg" /></p>
            
            <p>Same old map, so few details. I can't tell if the lack of titles on the shapes indicates that the Journal's editors assume their audience can actually tell the states apart, or that they just couldn't be bothered. Like the Washington Post we get the candidate's faces. While it fits the Journal's famous stodgy etchings theme, it doesn't bring much information to the table. Oh yeah, just in case you lack the skill to compare numbers, the big checkbox next to Obama's name serves to tell you who won. Sheesh.</p>
            
            <p>One interesting note is that this is the first infographic of the major sites I surveyed that included results for any of the other candidates. It's interesting to note that just the simple quantity of <em>digits</em> gives you some idea of the magnitude of the votes the two major-party candidates get.</p>
            
            <p>Check back in four years and we'll do it again!</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Proportional Code</title>
      <link>
        http://alexvollmer.com/posts/2008/10/26/proportional-code/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/10/26/proportional-code/#comments
      </comments>
      <pubDate>
        Sun, 26 Oct 2008 23:53:14 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            software
        ]]>
      </category>
      <category>
        <![CDATA[
            philosophy
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/10/26/proportional-code/
      </guid>
      <description>
        <![CDATA[
            Few things are less sexy than command-line parsing. It is one of the most mundane tasks a programmer has to execute in their career. But, it surprises just how much code is required to do basic command-line parsing in a lot of languages, including Ruby. So I got to thinking, <em>why does this bug me so much?</em> I think the answer is that requiring so much code for such a relatively trivial task violates my sense of <em>proportionality</em> in the code. I hate having to say so much more about this teeny little task than I do about the "theme" of my code. I think it distorts the narrative of the code.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>Few things are less sexy than command-line parsing. It is one of the most mundane tasks a programmer has to execute in their career. But, it surprises just how much code is required to do basic command-line parsing in a lot of languages, including Ruby. So I got to thinking, <em>why does this bug me so much?</em> I think the answer is that requiring so much code for such a relatively trivial task violates my sense of <em>proportionality</em> in the code. I hate having to say so much more about this teeny little task than I do about the "theme" of my code. I think it distorts the narrative of the code.</p>
            
            <p>Let's say that the processing of writing your program is like launching spacecraft. Ideally you would like to get from launch to cruising around in space as quickly as possible. The Star Trek universe solves this quite elegantly with the transporter. We don't put you in a box and launch it, we break your atoms apart and transmit them to another location! That's pretty cool, but maybe we're just not quite that cool yet. Another solution is one proffered by the Star Wars universe. A ship like the Millenium Falcon can leave just about any planetary atmosphere any time it damn well pleases without the use of special equipment. It just flies away. Not bad.</p>
            
            <p><img alt="Atlantis (STS-125)"
            src="http://farm4.static.flickr.com/3173/2958037544_26e7973f17_m.jpg"
            class="left"/></p>
            
            <p>However, here on earth, our primitive space craft need a tremendous amount of disposable apparatus to reach escape-velocity. The proportion of useful vehicle (the shuttle) to the orbit-busting mechanism (the rocket boosters and fuel tanks) is a staggering 5.4:1 (based on liftoff weight).</p>
            
            <p>Command-line parsing in code exhibits a similar disproportion. The interesting part of your app isn't the command-line parsing. Why should it take up such a disproportionate amount of space in your code? Those boosters quickly become "space-junk", once the launch vehicle has left Earth; expensive trash that is never to be used again.</p>
            
            <p>Space-junk is dangerous for the next guy that wants to launch into space. It's also dangerous for the folks on the ground as it may decide to come crashing back down to earth. And those boosters have also been responsible for one of the worst disasters in NASA's history. If we could only get rid of those damn things the whole space program just might be a little better off. Unfortunately physics, and our current space-flight capabilities currently require them.</p>
            
            <p>But our code is a different story. We don't have such physical barriers that handicap us. Any barriers we run into are usually of our own making. So why not try to reduce those as much as possible? Why say in more code, what you could say in less? Wouldn't that cut down on bugs? Wouldn't that ease the burden of maintenance? Wouldn't that reduce the amount of information overhead you have to maintain each time you revisit that code?</p>
            
            <p>Command-line parsing is a stupid, menial task that should require very little attention. By extension, it should only be given a stupid, menial amount of code to make it work. We have big ideas! They shouldn't get bogged down by handling command-line options!</p>
            
            <p>This is why I wrote <a href="https://github.com/alexvollmer/clip/tree%20Clip%20on%20GitHub">Clip</a>. Clip is an expression of the need to make the simple things simple, but no simpler. If you have modest command-line parsing needs, Clip rewards you with minimal investment. If you need something trickier, Clip allows you to say a little more to it and gain more benefits from it. You get to decide how much you want to engage&mdash;not the library.</p>
            
            <p>This is one of the things I like about Ruby. The language is extremely flexible which gives me a lot of ways to "pack" ideas into code in a variety of ways. Having more than one way to do things isn't all that useful by itself. But it's <em>essential</em> when you want to write <em>expressive</em> code. Things like object literals, or one-line control-structure alternatives help me keep the lines of code proportional to the ideas they express.</p>
            
            <p>This is also something I find challenging to do in Java. In languages like Java, even just creating a collection of objects requires quite a bit of code:</p>
            
            <div class="highlight"><pre><span class="kn">import</span> <span class="nn">java.util.*</span><span class="o">;</span>&#x000A;    &#x000A;    <span class="kd">public</span> <span class="kd">class</span> <span class="nc">Designer</span> <span class="o">{</span>&#x000A;      <span class="kd">public</span> <span class="kt">void</span> <span class="nf">makeItWork</span><span class="o">(</span><span class="n">List</span><span class="o">&lt;</span><span class="n">Trash</span><span class="o">&gt;</span> <span class="n">trash</span><span class="o">)</span> <span class="o">{</span>&#x000A;        <span class="c1">// today&#39;s challenge: convert trash to wearable garments</span>&#x000A;        <span class="n">List</span><span class="o">&lt;</span><span class="n">Garment</span><span class="o">&gt;</span> <span class="n">garments</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ArrayList</span><span class="o">&lt;</span><span class="n">Garment</span><span class="o">&gt;(</span><span class="n">trash</span><span class="o">.</span><span class="na">size</span><span class="o">());</span>&#x000A;        <span class="k">for</span> <span class="o">(</span><span class="n">Trash</span> <span class="n">t</span> <span class="o">:</span> <span class="n">trash</span><span class="o">)</span> <span class="o">{</span>&#x000A;          <span class="n">Garment</span> <span class="n">g</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Garment</span><span class="o">(</span><span class="n">t</span><span class="o">.</span><span class="na">getName</span><span class="o">());</span>&#x000A;          <span class="n">garments</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">g</span><span class="o">);</span>&#x000A;        <span class="o">}</span>&#x000A;    &#x000A;        <span class="n">submitToJudges</span><span class="o">(</span><span class="n">garments</span><span class="o">);</span>&#x000A;      <span class="o">}</span>&#x000A;    <span class="o">}</span>&#x000A;    </pre>
            </div>
            
            
            <p>In Java we often solve this by pushing all of that code into a private method that is named something meaningful. This works pretty well, but does tend to result in an explosion of "helper methods". Sometimes folks take the "cheap" route and simply prefix these riffs with explanatory comments like "convert each Trash into a Garment". I'm not a real big fan of this. Generally I don't care about the object conversion in the first place because the rest of the code is presumably doing something interesting with the <em>Garments</em> and I don't give a damn about the <em>Trash</em>.</p>
            
            <p>So let's look at it in Ruby:</p>
            
            <div class="highlight"><pre><span class="k">class</span> <span class="nc">Designer</span>&#x000A;      <span class="k">def</span> <span class="nf">make_it_work</span><span class="p">(</span><span class="n">trash</span><span class="p">)</span>&#x000A;        <span class="n">submit_to_judges</span><span class="p">(</span><span class="n">trash</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">t</span><span class="o">|</span> <span class="no">Garment</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">t</span><span class="o">.</span><span class="n">name</span><span class="p">)</span> <span class="p">})</span>&#x000A;      <span class="k">end</span>&#x000A;    <span class="k">end</span>&#x000A;    </pre>
            </div>
            
            
            <p>By my count there are five lines in the Java example (including the comment) just to convert trash to garments. In contrast I boiled that down to one line in Ruby. OK I could have done this in two lines if you think that's too much of a long-train. But I think there are couple of important points here:
            *  The importance of the concept being expressed diminishes from left to right
            *  The attention-span of the reader diminishes from top to bottom</p>
            
            <p>The Ruby example beats Java on both counts. I don't waste a lot of the reader's attention span up-front on book-keeping details (in the vertical space) and I state the important thing I'm trying do (submit my top Foos) quickly (on the left). The details of <em>which </em>Garments I'm dealing with are merely a qualification of <em>what</em> I'm trying to do.</p>
            
            <p>How you handle these two dimensions is greatly affected by both the language you use and the APIs you deal with. This is one of the reasons that I do <em>not</em> find the use of scripting languages for Java's Swing API all that compelling. Scripting languages like JRuby or Jython help me with the horizontal space, but don't do a damn thing for the vertical requirements. With an awful API like Swing I have to say a <em>lot</em> of words to make it go, regardless of the language I do it with.</p>
            
            <p>Getting back to my dumb example, being on such a small scale this may not seem like a large impact. But multiplied several times over to match the size of most projects, this kind savings can really pile up. The difference in the amount of code required by these two approaches is manifested in a savings in cognitive investment required to grok these projects. This is the very <em>essence</em> of maintainability and sustainability. Anytime you can do more with less, you come out ahead.</p>
            
            <p>At great peril to my own geek cred, I will say that this is why I find <em>The Lord of the Rings</em> to be such an awful piece of writing. It is so full of peripheral and non-essential information that finding the real story or characters requires extraordinary patience and concentration on the part of the reader. If Tolkien had been more concerned with the story and less with "world-building" I'll bet he could have gotten that story boiled down to a single book.</p>
            
            <p>Now I realize that a lot of folks <em>love</em> the Tolkien books for the very reasons I criticize it. That's fine, <em>that</em> is an argument about aesthetics, not facts. However I would strongly argue that "world-building" in your code is a <em>bad idea</em>. I think you're much more likely to build a decent piece of software if you pack your ideas tightly like a William Gibson novel than as a sprawling "trilogy" of epic code. Go ahead, prove me wrong. I double dog-dare ya.</p>
            
            <p>OK, so by this point any credibility I may have had is gone. Look at the size of a post about saying more with less. In the hope that you might be lazy and like to skip to the end:
            <em>Do as much as you can&hellip;with as little as possible.</em></p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Back To My...Emacs</title>
      <link>
        http://alexvollmer.com/posts/2008/10/07/back-to-myemacs/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/10/07/back-to-myemacs/#comments
      </comments>
      <pubDate>
        Tue, 07 Oct 2008 03:36:51 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            emacs
        ]]>
      </category>
      <category>
        <![CDATA[
            geekery
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/10/07/back-to-myemacs/
      </guid>
      <description>
        <![CDATA[
            Emacs. Love it or hate it, it is undeniably a monument of software engineering. At best it's an amazingly customizable work environment that can be shaped to your every whim. At worst it's a giant time-sink where productivity is skewered by endless "fiddling".
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>Emacs. Love it or hate it, it is undeniably a monument of software engineering. At best it's an amazingly customizable work environment that can be shaped to your every whim. At worst it's a giant time-sink where productivity is skewered by endless "fiddling".</p>
            
            <p>Since switching to the Mac over a year and half ago, I've used <a href="http://macromates.com/%20TextMate">TextMate</a> as my non-Java editor. TextMate is <em>great</em> text editor. It's relatively simple to extend, has a very active community and only takes a little investment before a user gets productive. But I'll admit that in the last month or so my eyes started wandering from TextMate&#8212;I felt like maybe I was ready to go back to Emacs.</p>
            
            <p>Perhaps it's worth a little history first. Back in the Dark Ages of Java Development (anyone remember Gnu Server Pages?), I was a pretty hard-core XEmacs guy for my Java dev. At the time Java IDEs were painful exercises in waiting and crash-recovery. JBuilder, early NetBeans, Visual Cafe were all valiant attempts but terrible failures. So I rode with XEmacs until the IDEs caught up.</p>
            
            <p>Then one day I met Eclipse. Unlike any other Java editing tool, Eclipse was much more than a text-editor. Rather than treating your code like text to be manipulated, Eclipse actually <em>understood your code</em>. This allows you to think in terms of manipulating Java syntax and symbols instead of simple text tokens and lines. For a static language like Java this is incredibly powerful since you can determine up-front all of the possible things you might want to write. This is why a full-on "intelli-sense" feature for dynamic languages is hard to do as well in static, compiled languages.</p>
            
            <p>I spent the next five years learning just about every nook and cranny of Eclipse and would put my raw Java-writing skills up against anyone. Even now, I still use Eclipse for Java and I can't imagine going back to a plain text-editor&#8212;even a souped-up hot-rod like Emacs. This says less about the deficiencies of any text editors as it does about the requirements for developing in Java. Writing Java without all of the code-completion and refactoring tools an IDE like Eclipse has would simply exceed my pain threshold. So while Eclipse has become quite bloated lately and, when coupled with tools like Maven, is an exercise machine-servitude, it will be a cold day in hell before I give it up for Java. (NB: I plan on giving up Java-dev first.)</p>
            
            <p>Now I'm writing a lot more non-Java than I did a year ago. That's what brought me to TextMate. The integration for Ruby, HTML, CSS, JavaScript and shell was outstanding. I bought the <a href="http://www.amazon.com/TextMate-Power-Editing-Pragmatic-Programmers/dp/097873923X%3FSubscriptionId%3D0PZ7TM66EXQCXFVTMTR2%26tag%3Dhttplivollmne-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D097873923X">TextMate book</a> and was on my way to editor ass-kicking.</p>
            
            <p>Unlike my Java days, now I identify myself less with a single language. Back in the day I was a Java Programmer&#8482; dad-gummit. It was The One Language To Rule Them All and it served me well. Do you remember those days? Yeah, that was also the time when certified Oracle dudes were writing their own checks. But as time went on, I noticed that those Oracle guys pretty much knew how to do one thing and if and when the Day of Database Reckoning ever came, these guys were gonna be left standing in the cold wondering what the hell happened. I think that day has come and those dudes are becoming the equivalent of COBOL programmers&#8212;relegated to propping up legacy systems and talking wistfully about "the good ol' days".</p>
            
            <p>So it got me to thinking about what I needed to do to survive in this business. Clearly I was going to have to evolve. So I started looking around for some inspiration and came across Ruby. Right now it's my favorite language, but it's not the only game in town. Ruby borrows a lot of features from other languages and a lot of other Ruby-ists are also spending time with other languages like Haskell, Erlang, io, and Lisp just to get exposure to other ideas. Moving away from a single-language focus to broader interests has been an important part of moving from rookie to journeyman. Evolve or die, right?</p>
            
            <p>So how does this relate to Emacs? Well, like a lot of folks, I've been interested in learning more about functional programming. The problem is, I simply haven't had the time to crack open the <a href="http://www.amazon.com/Programming-Erlang-Software-Concurrent-World/dp/193435600X%3FSubscriptionId%3D0PZ7TM66EXQCXFVTMTR2%26tag%3Dhttplivollmne-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D193435600X">Erlang book</a> and bend my brain in new ways. Don't get me wrong, it's on the list, but it's going to be a while before I get to it. So I was eager to dip my toe in FP without a major commitment. Enter ELisp.</p>
            
            <p>Before I dropped out of college, my brief career in academic computer science was spent with Scheme. My time in school didn't end well and so I've associated Scheme (and by extension, Lisp) with that particularly unpleasant time in my life. I've only recently overcome the shakes when I see <code>car</code> or <code>cdr</code>. But a little time and some professional success has healed old wounds. In my first go-round with Emacs, I was really just using it as a power-editor. The internals of Emacs pretty much escaped me and in my mind there was a pretty major barrier between an ELisp user and an ELisp <em>writer.</em></p>
            
            <p>This time I've made a concentrated effort to learn more about how Emacs works. I've been racing through the <a href="http://www.amazon.com/Learning-Elliot-Raymond-Rosenblatt-Cameron/dp/B001E3G45M%3FSubscriptionId%3D0PZ7TM66EXQCXFVTMTR2%26tag%3Dhttplivollmne-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3DB001E3G45M">O'Reilly Emacs book</a> and this time <em>it makes sense</em>. I don't know what the hang-up was before, but this time it all seems so logical! The entire editor is simply a collection of ELisp functions and variables built on top of other functions and variables. It's all self-documented and incredibly dynamic. Wanna try something out? Just put a little Lisp in your <code>*scratch*</code> buffer and evaluate that sucker. Hell, in Eclipse I'd have to download the freakin' source and compile it or swim in XML writing an extension. That's <em>incredibly</em> powerful&#8212;the barrier to "trying stuff out" is mind-bogglingly low.</p>
            
            <p>So while I've definitely take a short-term hit by trading in TextMate for Emacs, but I'm reaching productivity-parity pretty quickly. A couple of modes/libraries I've found that I've really liked are:</p>
            
            <ul>
            <li><a href="http://code.google.com/p/js2-mode/%20js2%20Mode">Steve Yegge's JS2 mode</a></li>
            <li><a href="http://rinari.rubyforge.org/%20Rinari%20Is%20Not%20a%20Rails%20IDE">Rinari,</a> for Rails</li>
            <li><a href="http://orgmode.org/%20Org-mode">Org-mode</a></li>
            <li><a href="http://zagadka.vm.bytemark.co.uk/magit/magit.html%20Magit">Magit</a> for Git integration</li>
            </ul>
            
            
            <p>I plan on spending some quality time with some of the various snippets libraries too to port over some of the templates that TextMate uses. For something like Rails development, which is about the most idiomatic programming I can imagine, having short-hand snippets is a major productivity boost.</p>
            
            <p>So why go through this? After all, switching editors can be like converting from Catholicism to Judaism for some. Well, I think I'm doing this because:</p>
            
            <ul>
            <li>I've already overcome the initial steep-learning curve of basic editing in Emacs</li>
            <li>I get a nice, no-commitment introduction to functional programming</li>
            <li>I get a great environment to learn new stuff in</li>
            <li>With a little effort and a willingness to learn, I can make this sucker to <em>anything</em></li>
            <li>Emacs has quite a track-record. It's close association with Unix is no accident.</li>
            <li>Emacs customization is <em>easy</em> to write and a snap to track in SCM</li>
            </ul>
            
            
            <p>I can't say that I'll use Emacs for <em>everything</em>. I'm getting into Cocoa development too and it looks like doing that outside of XCode is a fool's errand. But that's okay. I don't need a single tool to do everything for me, just a handful of tools that help me get a variety of things done. That said, I'm pretty sure I'll be solving a lot more problems in Emacs than I ever did in any other single tool.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>A Philosophy of Testing</title>
      <link>
        http://alexvollmer.com/posts/2008/10/02/a-philosophy-of-testing/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/10/02/a-philosophy-of-testing/#comments
      </comments>
      <pubDate>
        Thu, 02 Oct 2008 16:44:27 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            software
        ]]>
      </category>
      <category>
        <![CDATA[
            TDD
        ]]>
      </category>
      <category>
        <![CDATA[
            philosophy
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/10/02/a-philosophy-of-testing/
      </guid>
      <description>
        <![CDATA[
            Did you hear that? Did you feel it? I did. Agile just took another punch to the gut. The Agile Backlash is picking up a full head of steam. Rather than becoming a term to embrace, it's become a term of ridicule. I think it's probably deserved. As a guy who picked up the first <a href="http://www.amazon.com/Extreme-Programming-Explained-Embrace-Change/dp/0321278658%3FSubscriptionId%3D0PZ7TM66EXQCXFVTMTR2%26tag%3Dhttplivollmne-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D0321278658">Extreme Programming</a> book as soon as it was released and is a long-time practitioner, I feel qualified to comment on this.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>Did you hear that? Did you feel it? I did. Agile just took another punch to the gut. The Agile Backlash is picking up a full head of steam. Rather than becoming a term to embrace, it's become a term of ridicule. I think it's probably deserved. As a guy who picked up the first <a href="http://www.amazon.com/Extreme-Programming-Explained-Embrace-Change/dp/0321278658%3FSubscriptionId%3D0PZ7TM66EXQCXFVTMTR2%26tag%3Dhttplivollmne-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D0321278658">Extreme Programming</a> book as soon as it was released and is a long-time practitioner, I feel qualified to comment on this.</p>
            
            <p>The second-generation acolytes turned a very pragmatic set of principles and practices into an esoteric scripture that could never meet the needs of reality. The "agile" moniker makes me cringe almost as much as "best practices". Both are cavalier substitutes for critical thinking, too often thrown about in the hopes that their mere mention will work magic. Ugh.</p>
            
            <p>A real unfortunate side-effect of the backlash is the disregard some have for automated testing. Before I go any further, let me be the first to say that, like any other recommendation or practice, automated testing is <em>not</em> a panacea. I shouldn't have to say this. It's painfully obvious, like explicitly stating up front that I am against beating up defenseless children. Who <em>is</em>?</p>
            
            <p>However, just because automated testing isn't a cure-all doesn't mean that:</p>
            
            <ul>
            <li> testing isn't worth doing</li>
            <li> testing isn't hard</li>
            <li> sometimes testing is so hard that it isn't worth doing</li>
            </ul>
            
            
            <p>Note that last point. That there is what we in the biz call <em>pragmatism</em>. Yes kids, there are times when the cost of getting sufficient test-coverage is enormously expensive. Assuming that economics are part of your project somehow, you need to pay attention to this (which is why code coverage metrics can be dangerous in the hands of the nuance-free thinker).</p>
            
            <p>Critics of automated testing like to point out that if developers write buggy code, how can they not write buggy tests? Assuming that it's as impossible to write perfect tests as it is to write perfect code, is there a point to writing the test? There is if the benefits you get from testing exceed the cost of developing and maintaing them.</p>
            
            <h1>Test-Driven Design/Behavior-Driven Design</h1>
            
            <p><img alt="Marysville Falls" src="http://farm4.static.flickr.com/3084/2906603525_eea04c7769_m.jpg" class="left"/></p>
            
            <p>The TDD/BDD philosophies are nearly as abstract and misunderstood as the entire concept of testing itself. So rather than parrot the original manifestos, let me break down the tangible benefits of driving your code with tests. The first misconception is the notion that you write all the tests first then you write all the code. This works almost as well as gathering all the requirements up front first and then writing all the code. I believe the technical term is "Waterfall". This is just dumb. Honestly folks, use your brains. Only a complete rookie would think that's a sensible practice to adopt.</p>
            
            <p>Putting testing before writing code provides no magical effect that radically increases the quality of your code. Where testing <em>does</em> improve your code is when you use them as part of the code-building process. I like to use unit-tests as a form of mental scaffolding while I develop.</p>
            
            <h1>Breaking It Down</h1>
            
            <p>Any non-trivial problem needs to be broken down in some way. Few of us have the mental capacity to keep an entire solution in our heads. Even if we could, it generally doesn't last beyond the period in which the solution is developed. And even if you could do this, chances are few of your teammates would be able to. So at worst, breaking a problem down is a pro-social practice; at best it's a tool to ensure that you really understand what's going both now and in the future.</p>
            
            <p>Generally when you break a problem down, you get into the business of assigning responsibilities to various components. This is a fractal pattern that starts at the highest-level architecture and repeats itself all the way down to the lowest-level classes, modules and functions of your system. It must be an inherent part of human-nature&#8212;we simply love putting things in boxes. Perhaps this is because that in doing so, we can hang a simple tag on a collection of related concepts as a form of shorthand. That shorthand allows us to talk about things in an efficient way. Imagine a design conversation where you couldn't name anything, but had to describe a piece over and over by its details. Yuck. So yeah, boxes are important.</p>
            
            <p>So where does testing fit in? Testing allows you to write automated assertions about each of those boxes. It's like a logic game where you can start draw conclusions based on what you already know. It's like being able to say, "If this test is passing and that test is passing, the only thing that could torpedo this is if <em>that</em> doesn't work". I'll come back to this later when I talk about troubleshooting.</p>
            
            <p>What unit-testing does <em>not</em> cover here, are the connections between the boxes. It should be obvious that it's entirely possible to have a totally sweet set of unit-tests that never fail that cover each piece of your solution in full and <em>still</em> have bugs or represent and incorrect solution.</p>
            
            <p>This is because unit-tests only cover the boxes, not the connections.</p>
            
            <p>Tests that cover the entire system are the best way to exercise the connections. They cover the boxes too, but they cover the <em>integration</em> (hence the name) of the boxes and connections. Since unit-tests can't cover the system as a whole, are they still worth writing? You bet. Keep reading&#8230;</p>
            
            <h1>Efficient Troubleshooting</h1>
            
            <p>When something goes wrong, you enter a different frame of mind from programming. Now you're in the detective business. You start with some scraps of information (stack traces, error messages, late-night pages) and with a combination of logic, guile and intuition (hopefully) find the root cause.</p>
            
            <p>I could do an entire post on root-cause analysis, but let it suffice to say that most folks don't ask enough questions when troubleshooting. Often the conclusion people come to is an observation of a symptom rather than a thoughtful, system-aware diagnosis. In these situations it's a good idea to consider the <a href="http://en.wikipedia.org/wiki/5_Whys%20The%205%20Whys">5 Whys Approach</a>.</p>
            
            <p>How do unit-tests help with troubleshooting? Like I said earlier, unit-tests are really good at testing the boxes, but not the connections. Let's assume that your unit-test accurately covers the box that you believe is the root of the issue you're seeing. Given that, there is a handful of conclusions you can draw:
            *  The problem isn't really in this box
            *  The problem is the connection coming into or going out of this box
            *  Your understanding of the box is incorrect</p>
            
            <p>That's it. It <em>has</em> to be one of those. Unit-tests allow you to confirm that your box is working correctly. If it's being invoked or interacted with in a way you didn't expect, it's generally a trivial exercise to write a test that <em>does</em> use your box in that way. If it passes then you probably need to keep looking.</p>
            
            <p>If you're pretty confident that your box is good, then checking the connections is the next thing to look at. It's just as likely that the output of your box is being misused as the input to it. Integration tests are usually the best mechanism to catch this, but are generally much more expensive to write and maintain. For this reason they generally don't exhaust all of the possibilities. But with a real-world bug you now have a candidate for a new integration test.
            I can't stress enough the benefits of using bugs as opportunities to strengthen your test-suites. This is as true for integration tests as it is for unit-tests. It's generally not practical to write tests for <em>every</em> situation. So we use our intellect to make educated guesses that cover the most likely situations. When you observe funny behavior in your system, consider it a gift to your understanding. It's a golden opportunity to add a test for a real behavior, not a speculative one that may or may not occur.</p>
            
            <p>So let's consider a world in which we have <em>no</em> tests. When something goes haywire you have to start from first principles <em>every time</em>. You have no mechanisms to get any leverage on the issue. Instead we have to rely on understanding the entire solution and holding it in our head at once. What do you think the chances are that we'll effectively troubleshoot an issue under these conditions?</p>
            
            <h1>Finding Smells</h1>
            
            <p>It is simply no fun to work on low-quality code day after day. As anyone who has every read <a href="http://www.amazon.com/Zen-Art-Motorcycle-Maintenance-Inquiry/dp/0061673730%3FSubscriptionId%3D0PZ7TM66EXQCXFVTMTR2%26tag%3Dhttplivollmne-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D0061673730">Zen &amp; The Art of Motorcycle Maintenance</a> can attest, "quality" is a pretty squishy word. But in concrete terms, quality code means:
            *  You enjoy working on it (or at least don't hate it)
            *  You can expand its capabilities with reasonable effort
            *  Somebody else can work on it too</p>
            
            <p>I find the process of test-driving the development of code is a great opportunity to find "smells" in your code. These "smells" generally detract from code quality. The best catalog of these smells that I've encountered is the <a href="http://www.amazon.com/Refactoring-Improving-Existing-Addison-Wesley-Technology/dp/0201485672%3FSubscriptionId%3D0PZ7TM66EXQCXFVTMTR2%26tag%3Dhttplivollmne-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D0201485672">Refactoring</a> book by Fowler and Beck. You don't need to read the book for a detailed description of the mechanics of refactoring, but it is worth reading to learn the terminology of "code smells".</p>
            
            <p>At a minimum tests are the first "customers" of your code. If it's painful to test your code, you probably haven't factored it very well. Paying attention to this while your test-driving the development is a great way to keep your code high-quality. To really benefit from this, you need experience, a highly-developed sense of aesthetics and intuition. If you keep at it, you will gain these.</p>
            
            <p>Note that I don't equate code quality with "success". There are plenty of systems out there that are successful that are <em>not</em> built on what I would call quality code. That's fine. I've made a conscious choice not to work on those systems. I spend an awful lot of my waking hours working on code&#8212;personally I don't want to waste that time being frustrated. I want a sense of accomplishment and satisfaction when I'm done for the day. Honestly, I'd trade success for quality code just about anytime. I expect to be doing this for a long time, so why not enjoy it?</p>
            
            <h1>Safety Net for Refactoring</h1>
            
            <p>When you need to extend or enhance a system, having automated tests serve as a great safety-net for refactoring. If you're confident in the efficacy of your unit-tests, you can be confident when you make changes and your tests still pass. You can refactor with a higher level of confidence when your tests keep passing. Of course there are no guarantees that you <em>haven't</em> introduced some kind of regression bug. But let's face it folks, the testing game is one of probabilities. It's too expensive to make sure you're right all of the time&#8212;instead shoot for being right <em>most</em> of the time.</p>
            
            <p>There will <em>always</em> be times when something goes wrong and the only way to diagnose and fix it is to walk through the code painstakingly, line by line. No amount of testing will ever make that go away. My goal is to <em>reduce</em> that as much as possible. It's inevitable, but it doesn't have to be the norm.</p>
            
            <p>Consider the case where you <em>don't</em> have any tests while you refactor. Your code/build/test/debug cycle gets a whole lot longer and more expensive. Because of that, you do it less often. Which means that when you do go through the cycle you have more things that can go wrong and more stuff you have to hold in your head. That increases both the likelihood that you will mis-diagnose a problem and that you will write more bad code to fix the latest bug.</p>
            
            <p>I think this is how really twisted code is born. It almost reads like a blow-by-blow account of the developer's mounting frustration. The pace and phrasing of the code gets more and more frenetic: more odd comments, more TODOs and FIXMEs littered throughout, more long procedural stanzas. It becomes obvious that they were trying to get this done as quickly as possible, and that's generally not the path to quality.</p>
            
            <h1>Confidence</h1>
            
            <p>When it comes human-nature and software I'm a pessimist. I think we're really good at the intuitive parts and achieving the "ah-ha" moment. We are <em>not</em> good at holding giant complicated systems in our heads. There <em>are</em> people who can do that, but it's not the norm. For this reason, I think it's extremely foolish to build a software organization that relies on raw mental capacity. This definition of "smart" is one that can't scale and is unsustainable.</p>
            
            <p>Automated testing is one teeny-tiny way we can offload the need for that capacity to a better long-term storage mechanism. While I write tests my mind is very focused on a particular part of the system. As I flesh out the tests I fill out my understanding of the problem-space <em>at that time</em>. Now that it's written down in code, I can safely leave it there and let my mind free up that space to do other things.</p>
            
            <p>If I've written my tests well, I can get back into that focused frame of mind when I need to (e.g. for new features or bug-fixes). Without these, I have to spend a lot more time understanding a larger system just to get "up to speed". The tests can serve as an index into the design of the system. This is particularly effective when you have been paying attention to code smells and have been refactoring aggressively&#8212;your tests will map effectively to the underlying concepts of the system design.</p>
            
            <p>This kind of confidence has a real tangible benefit. Without it I'm easily distracted by small details and dead-ends. As soon as I have more I have to worry about at once, I have less of a chance of succeeding at the task at hand in that moment. When I have such a thing in place, I have an instant force-multiplier in my hand. I can get something done. I don't have to repeat the entire experience of developing the code the first time. Instead I can leverage that experience to be more productive the next time I come back to that part of the system.</p>
            
            <h1>Conclusion</h1>
            
            <p>So there you have it. That's where I stand.</p>
            
            <p>Is testing hard? <em>Yes.</em></p>
            
            <p>Does it catch everything? <em>No.</em></p>
            
            <p>Is it still worth doing? <em>I think so.</em></p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Make View Helpers a Little Less &quot;Helpful&quot;</title>
      <link>
        http://alexvollmer.com/posts/2008/09/23/make-view-helpers-a-little-less-helpful/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/09/23/make-view-helpers-a-little-less-helpful/#comments
      </comments>
      <pubDate>
        Tue, 23 Sep 2008 15:41:27 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <category>
        <![CDATA[
            rails
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/09/23/make-view-helpers-a-little-less-helpful/
      </guid>
      <description>
        <![CDATA[
            I stumbled across a little bit of hidden Rails fun last night when I was trying to get the form_for method to <em>stop</em> wrapping error fields with extra div tags. Did you know that? Maybe you never noticed, but when you use the field helper methods, like text_field, password_field, etc, Rails will wrap fields with errors in a <code>&lt;div&gt;</code> with the class 'fieldWithErrors'.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>I stumbled across a little bit of hidden Rails fun last night when I was trying to get the form_for method to <em>stop</em> wrapping error fields with extra div tags. Did you know that? Maybe you never noticed, but when you use the field helper methods, like text_field, password_field, etc, Rails will wrap fields with errors in a <code>&lt;div&gt;</code> with the class 'fieldWithErrors'.</p>
            
            <p>This was causing all sorts of grief for me in some JavaScript I was trying to write. At first I tried to go with the flow and fix my JS, but it got really hacky really fast. So I went on a little source-code spelunking to figure how to make the problem go away.</p>
            
            <p>In actionpack-2.1.0 there is a Proc attached to the <code>ActionView::Base.field_error_proc</code> class attribute. It's not documented in the RDoc, but this is also a <em>writable</em> attribute which means I can shut the damn thing up. Here's what it looks like in the original file, <code>GEM_HOME/actionpack-2.1.0/lib/action_view/helpers/active_record_helper.rb</code>:</p>
            
            <div class="highlight"><pre><span class="nb">require</span> <span class="s1">&#39;cgi&#39;</span>&#x000A;    <span class="nb">require</span> <span class="s1">&#39;action_view/helpers/form_helper&#39;</span>&#x000A;    &#x000A;    <span class="k">module</span> <span class="nn">ActionView</span>&#x000A;      <span class="k">class</span> <span class="nc">Base</span>&#x000A;        <span class="vc">@@field_error_proc</span> <span class="o">=</span> <span class="no">Proc</span><span class="o">.</span><span class="n">new</span><span class="p">{</span> <span class="o">|</span><span class="n">html_tag</span><span class="p">,</span> <span class="n">instance</span><span class="o">|</span> <span class="s2">&quot;&lt;div class=</span><span class="se">\&quot;</span><span class="s2">fieldWithErrors</span><span class="se">\&quot;</span><span class="s2">&gt;</span><span class="si">#{</span><span class="n">html_tag</span><span class="si">}</span><span class="s2">&lt;/div&gt;&quot;</span> <span class="p">}</span>&#x000A;        <span class="n">cattr_accessor</span> <span class="ss">:field_error_proc</span>&#x000A;      <span class="k">end</span>&#x000A;      <span class="o">.</span><span class="n">.</span><span class="o">.</span>&#x000A;    <span class="k">end</span>&#x000A;    </pre>
            </div>
            
            
            <p>My solution was to create a little file in <code>config/initializers</code> named <code>field_errors.rb</code> with this text:</p>
            
            <div class="highlight"><pre><span class="no">ActionView</span><span class="o">::</span><span class="no">Base</span><span class="o">.</span><span class="n">field_error_proc</span> <span class="o">=</span> <span class="no">Proc</span><span class="o">.</span><span class="n">new</span> <span class="k">do</span> <span class="o">|</span><span class="n">html_tag</span><span class="p">,</span> <span class="n">instance</span><span class="o">|</span>&#x000A;      <span class="n">html_tag</span>&#x000A;    <span class="k">end</span>&#x000A;    </pre>
            </div>
            
            
            <p>Et voila! Just a simple pass-through with no more fancy-pants markup. Putting stuff like this in separate files in the <code>initializers</code> directory keeps your config and environment files from getting out of hand.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Clip 1.0.0 Released!</title>
      <link>
        http://alexvollmer.com/posts/2008/09/19/clip-100-released/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/09/19/clip-100-released/#comments
      </comments>
      <pubDate>
        Fri, 19 Sep 2008 19:17:33 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            clip
        ]]>
      </category>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/09/19/clip-100-released/
      </guid>
      <description>
        <![CDATA[
            Command-line parsing made short and sweet.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>Command-line parsing made short and sweet.</p>
            
            <h3>1.0.0 / 2008-09-19</h3>
            
            <ul>
            <li>Added support for mapping dashes to underscores for flags</li>
            <li>Define Clip.hash.remainder as a singleton method instead of reopening Hash</li>
            <li>remainder works with Clip.hash now</li>
            <li>Reimplemented Clip.hash to use a parser.</li>
            </ul>
            
            
            <p>Check out the docs at <a href="http://clip.rubyforge.org">http://clip.rubyforge.org</a>.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Oracle's Listening&amp;hellip;</title>
      <link>
        http://alexvollmer.com/posts/2008/09/19/oracles-listening/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/09/19/oracles-listening/#comments
      </comments>
      <pubDate>
        Fri, 19 Sep 2008 16:17:51 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            gripe
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/09/19/oracles-listening/
      </guid>
      <description>
        <![CDATA[
            Following a re-tweet of @timoreilly, I went to check out <a href="http://oracle.com">Oracle's home pag</a>e today. Here's what I got:
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>Following a re-tweet of @timoreilly, I went to check out <a href="http://oracle.com">Oracle's home pag</a>e today. Here's what I got:</p>
            
            <p><img src="http://livollmers.net/wp-content/uploads/2008/09/picture-1.jpg" alt="Picture 1.png" /></p>
            
            <p>So I put in my proposal and got:</p>
            
            <p><img src="http://livollmers.net/wp-content/uploads/2008/09/picture-2.jpg" alt="Picture 2.png" /></p>
            
            <p>I wonder if they'll actually consider what I proposed. For that matter, I wonder if they'll consider <em>any</em> of the proposals users send them.</p>
            
            <p>What is the bloody point?</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Book Review: &quot;JavaScript: The Good Parts&quot;</title>
      <link>
        http://alexvollmer.com/posts/2008/08/15/book-review-javascript-the-good-parts/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/08/15/book-review-javascript-the-good-parts/#comments
      </comments>
      <pubDate>
        Fri, 15 Aug 2008 03:41:45 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            book review
        ]]>
      </category>
      <category>
        <![CDATA[
            javascript
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/08/15/book-review-javascript-the-good-parts/
      </guid>
      <description>
        <![CDATA[
            <img src="/images/2008/08/dsc-0204.jpg" alt="DSC_0204.NEF">
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p><img src="/images/2008/08/dsc-0204.jpg" alt="DSC_0204.NEF" /></p>
            
            <p>It takes a brave author to give a book this title and keep it at 150 pages. The number of jokes about the proportionality of the "good parts" to the size of the book are endless. Be that as it may, when I saw this book at the <a href="http://www.powells.com/">Powell's</a> stand at <a href="http://en.oreilly.com/rails2008">RailsConf '08</a>, I figured anything written by Crockford on the subject of JavaScript was probably worth taking a look at.</p>
            
            <p><img src="/images/2008/08/javascript.jpg" class="left"/></p>
            
            <p>Given the diminutive nature of the the book, I suspect the author was attempting to do for JavaScript what Kernighan and Richie did for C with their book. I found an abused first edition copy of that C book and it taught me more about C than any other book. It is a triumph of restrained, focused technical writing (for other examples check out any of Kent Beck's books). At times I felt that some chapters in "The Good Parts" were superfluous, but I imagine that they were included for completeness if nothing else. For example, the second-longest chapter in the book is about the grammar of the language. While important, it seems disproportionately large to rest of the contents.</p>
            
            <p>If you have any exposure to JavaScript a good part of this book will be a review for you. However, there are three chapters worth reading for any JavaScript programmer. Chapter 3, <em>Objects</em>, discusses the properties of objects in JavaScript and covers everything from enumeration, to prototypes to attributes. Do you know why property enumeration is usually worthless as a debug tool? That's because of the prototype mechanism. If you use <code>typeof</code> filters and the <code>hasOwnProperty</code> method, you can significantly cut down the properties you dump on an object. I never knew that before.</p>
            
            <p>Chapter 4, <em>Functions</em>, is the real money-maker of the book. This is the chapter that I put the most post-its in and will certainly be one I'll have to revisit. Here Crockford goes into the different ways functions can be invoked and what the consequences are of each invocation style. There is a <em>lot</em> of good functional programming material here. He also discusses how Functions are intimately tied to Objects. Take heed, thar be dragons!</p>
            
            <p>Chapter 5, <em>Inheritance</em>, closes the loop on Functions and Objects. It explains the important differences between JavaScripts prototype-based inheritance model and the class-based model used in many other popular object-oriented languages. Like the previous chapter, this will be one that I'm sure I'll have to re-read in the future.</p>
            
            <p>It's taken a long time, but JavaScript seems to finally be growing up a bit. Given how truly awful it was to use it in the Early Days on more than one browser, it's a bit surprising that it has come to dominate the web landscape. Now it seems to be finally getting (begrudging) respect from the rest of the software community. Unfortunately to be effective, you really need to learn some of the deep voodoo that, to me, is a bit counterintuitive. Is the prototype approach really all that innovative? I'm not sure. Arguing that it's better than inheritance is like saying that something is better than poking yourself in the eyes with a sharp stick&#8212;faint praise indeed. But I do love passing around functions as objects with complete abandon. When I finally grokked this part of JavaScript, things really clicked for me.</p>
            
            <p>Is this book a must-read? No, probably not. If you're doing any serious JavaScript in the browser you're probably using one of the popular JavaScript frameworks out there that hide some of the yucky details of Function/Object interaction. But, lacking a decent language spec (and the ECMA spec sucks rocks), it's not a bad resource. It certainly won't take up much space in your bookshelf.</p>
            
            <p>2.5 stars out of 5.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Book Review: About Face 3</title>
      <link>
        http://alexvollmer.com/posts/2008/07/30/book-review-about-face-3/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/07/30/book-review-about-face-3/#comments
      </comments>
      <pubDate>
        Wed, 30 Jul 2008 04:35:18 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            book review
        ]]>
      </category>
      <category>
        <![CDATA[
            UX
        ]]>
      </category>
      <category>
        <![CDATA[
            information design
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/07/30/book-review-about-face-3/
      </guid>
      <description>
        <![CDATA[
            Alan Cooper's <em>About Face</em> is one of those pillars of UI/UX design, the reading of which is a rite of passage. I figured few books would be more appropriate as a capstone to my long list of design-oriented reads. It is nearly an institution in and of itself. Last night I turned the final page and ticked a pretty big 560-page book off of my reading list.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p><a href="http://www.amazon.com/About-Face-Essentials-Interaction-Design/dp/0470084111%3FSubscriptionId%3D0PZ7TM66EXQCXFVTMTR2%26tag%3Dhttplivollmne-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D0470084111"><img src="http://ecx.images-amazon.com/images/I/41PoEitkH1L._SL160_.jpg" class="left"/></a></p>
            
            <p>Alan Cooper's <em>About Face</em> is one of those pillars of UI/UX design, the reading of which is a rite of passage. I figured few books would be more appropriate as a capstone to my long list of design-oriented reads. It is nearly an institution in and of itself. Last night I turned the final page and ticked a pretty big 560-page book off of my reading list.</p>
            
            <p>There was enough material in this book that I adopted a new habit of using small post-its to mark important passages. This was helpful not only in referring back to earlier material in the book, but also in cementing some of the concepts in my head. This practice was so useful I've started doing it with other books too.</p>
            
            <p><img src="http://livollmers.net/wp-content/uploads/2008/07/dsc-0124.jpg" alt="DSC_0124.JPG" /></p>
            
            <p>You may have noticed that the post-its don't really kick in until about 2/5 of the way through the book (Chapter 10 to be specific). I'll be honest and admit that the first nine chapters felt like a rehash to me. That's not to say they are without value, but they are a bit long-winded and, at times, excruciating in detail.</p>
            
            <p>So let me go ahead and get my beefs with the book out of the way. If you guessed that a book on its third edition that is nearly three inches thick would have some opinions you would be right. I don't have a problem with opinions&mdash;that's why I bought the book. But I did find the tone a bit much. Haughty, imperious, self-righteous and overbearing might be good descriptions too. There is a definite Moses-coming-down-from-the-mountain-with-fresh-tablets feel to this book. Mr. Cooper and his cohorts are here to set you straight. Don't let it turn you off. Think of these guys as that cranky-but-humorous guy you work with. He's a riot as long as you don't take what he says <em>too</em> seriously.</p>
            
            <p>Secondly this is a <em>long</em> book. I'm not convinced it really needed to be this long. There's an awful lot of repetition spread throughout the chapters. All of that is nit-picking really. This was an incredibly informative book. Again, the number of stickies you see should give you some idea of the value that I got from it. There is a <em>lot</em> a person can learn here.</p>
            
            <p>A big part of Cooper's philosophy is the focus on Goal-Oriented Design. The first part of the book spends a significant amount of ink describing the concepts of personas, goals and user research. As a developer I couldn't help but feel that a lot of this was <a href="http://c2.com/xp/BigDesignUpFront.html%20BDUF">Big Design Up Front</a>. "Just let the arch-priests of interaction design get it all working and <em>then</em> hand it off to the developers", they say. Hmmm, my experience tells me otherwise. To be fair, the authors attempt to address in the afterword of the book. But still...</p>
            
            <p>Once goal-oriented design is out of the way, the book picks up significantly by covering such concepts as the "excise" (tax) that systems place on humans, how people get into "flow" and how bad software can remove them from it or prevent it. One of my favorite little gems is a discussion of possible vs. probable. While it's <em>possible</em> that a user may want to choose one option over another, in cases where one option is disproportionately chosen over the other (the <em>probable</em>) use a default with an override. Think of how many times you've had to answer the same question over and over. It's pretty irritating, no?</p>
            
            <p><img src="http://upload.wikimedia.org/wikipedia/en/thumb/6/69/Magic_Cap_OS.gif/300px-Magic_Cap_OS.gif" class="left"/></p>
            
            <p>Another great section is the discussion of metaphor. Of the book's numerous examples, the ones demonstrating bad metaphor really shine. General Magic's Magic Cap interface is an absolute nightmare of conflicting messages and unnecessary navigation. What do these icons mean? If I rub the lamp do I get three wishes? If I push the rubber stamp am I getting notarized or checking out a book? Just what the hell is going on here?</p>
            
            <p>Cooper describes Magic Cap's failure as an over-reliance on <em>global metaphor</em>, where the system is essentially trapped in its slavish adherence to its metaphor. The rubber-stamp is there because real desks have rubber-stamps. But the need to be consistent with the desk metaphor weakens the interaction.</p>
            
            <p>Cooper proposes <em>idiomatic design</em> as the alternative to metaphors. User interfaces have been around long enough that a large number of interactions have already been figured out. Users are already familiar with them and, generally, don't require additional ramp-up to recognize them. Obviously there are a lot of <em>bad</em> idioms out there too so, like anything in life, take that advice with some degree of moderation.</p>
            
            <p>If there's one over-arching theme to the book it's that there are basically three groups of users you have to consider: beginners, intermediates and advanced users. The first and last groups are usually the smallest with the bulk of your user population consisting of perpetual intermediates. Beginners generally graduate quickly to intermediates. From there it's a much larger jump to advanced users. However, a lot of interfaces are often geared towards beginners. I would suspect (with little evidence to back this up) that a lot of this has to do with too much focus on customer-acquisition.</p>
            
            <p>Without a means for potential intermediate users to shed their training wheels, users can get quickly frustrated. However you can't build a power-user-only application either. Very few users run the gauntlet from beginner all the way to the advanced user. So the trick to is spend the correct amount of effort on features that match the proportions on your users.</p>
            
            <p>With this idea in mind the final eleven chapters of the book provide a fantastic, detailed look at how the principles described above apply to common visual idioms. For example, in the chapter devoted to menuing systems, the authors describe not as the sole interface, but a "pedagogic vector" for beginners. When combined with shortcut keys and accelerators, menus provide a way for beginners to graduate to intermediates and beyond.</p>
            
            <p>Another important theme in the last section is just how far software really needs to come to meet users. The authors review example after example of "computer-first" design where the user seems be treated as a necessary irritant. One of my favorite passages in the entire book is on the topic of wizards: "Programmers like wizards because they get to treat users as peripheral devices."</p>
            
            <p><img src="http://farm4.static.flickr.com/3108/2694787797_3c7d0ffb93_m.jpg" class="left"/></p>
            
            <p>Another chapter is devoted just to disk storage and how most of our idioms around disk storage should really be the computer's problem, not the users. At first this kind of talk seems like the crazy guy in Hyde Park, but after some reflection I think he's right. There is an awful lot of software that couldn't care less about the user. We can do better than this. We <em>should</em> do better than this.</p>
            
            <p>One technical detail of the book that I just love is how they integrate images with captions. A particular pet peeve of mine is when images and captions seemed to be dropped into the text in willy-nilly fashion with little regard to the reader's flow. When figures that are referred to in the text aren't placed close to the text, I have to context switch and try not to lose my place. I either have to remember to look at the figure soon, or I have to switch to the figure immediately and then find my place back in the text. It drives me nuts. Whoever did the layout for <em>About Face 3</em> obviously thought about the usability of the text. God bless you, whoever you may be.</p>
            
            <p>While I'm pretty sure that I wouldn't want to have to talk to any of these guys at cocktail party (I doubt I'd get a word in edge-wise), they put together an incredibly informative book. I'm pretty sure I'll need those sticky notes in the future to revisit a number of concepts presented here.</p>
            
            <p>5 out 5 stars.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>ActiveRecord Fun Thay May Stump Only Me</title>
      <link>
        http://alexvollmer.com/posts/2008/07/24/activerecord-fun-thay-may-stump-only-me/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/07/24/activerecord-fun-thay-may-stump-only-me/#comments
      </comments>
      <pubDate>
        Thu, 24 Jul 2008 04:52:15 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <category>
        <![CDATA[
            rails
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/07/24/activerecord-fun-thay-may-stump-only-me/
      </guid>
      <description>
        <![CDATA[
            I've just spent the last two hours pulling my hair out trying to get Single-Table Inheritance (STI) working with associations in <code>ActiveRecord</code>. After essentially walking through all of the possible <code>ActiveRecord</code> options in this setup, I finally stumbled upon a configuration that seems to work. So this post is an attempt to help the next poor bastard who is Googling in earnest for a solution to a similar problem.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>I've just spent the last two hours pulling my hair out trying to get Single-Table Inheritance (STI) working with associations in <code>ActiveRecord</code>. After essentially walking through all of the possible <code>ActiveRecord</code> options in this setup, I finally stumbled upon a configuration that seems to work. So this post is an attempt to help the next poor bastard who is Googling in earnest for a solution to a similar problem.</p>
            
            <p>So let's start with the domain model. I'm too spent at this point in the evening to port this to one of the standard examples. Instead I'll expose you to the domain of my particular problem. The app I'm working on is one that tracks (non-financial) lending transactions between two individuals. The parties involved, the item in question and when it's due are all tracked in the <code>Transaction</code> model (and <code>transactions</code> table). A <code>Transaction</code> has a number of states it walks through, using the <a href="http://elitists.textdriven.com/svn/plugins/acts_as_state_machine/%20acts_as_state_machine%20SVN%20repository"><code>acts_as_state_machine</code></a> plugin. These state transitions are triggered by opaque-looking URLs that are sent via email to either party. These are one-time use actions that once consumed are no longer available. When an <code>Action</code> instance is created it also has a <code>before_save</code> callback that generates a unique ID (used in the URL) using <code>Digest::SHA1</code>.</p>
            
            <p>So my plan was to have my <code>Transaction</code> class write one or more <code>Action</code> records for each possible action based on my state transitions. Take a look at the state diagram below:</p>
            
            <p><img src="http://livollmers.net/wp-content/uploads/2008/07/state-transitions.jpg" alt="state-transitions.png" /></p>
            
            <p>I want to encapsulate the actual work to be performed within the <code>Action</code> instance the user invokes by following the link in their email. So my plan is to use STI to have different sub-classes of <code>Action</code> that operate on a transaction and march it forward to its next state polymorphically.</p>
            
            <p>Now STI may appear to be total overkill for this problem, but here are my reasons for going this route:</p>
            
            <ul>
            <li>I want to have these opaque IDs written down somewhere to associate a specific action with a URL</li>
            <li>When the action is complete, I want to remove the record so it can't be performed again</li>
            <li>The state for a given <code>Transaction</code> can have more than one possible action. I want a separate for each action.</li>
            </ul>
            
            
            <p>Whew. Okay, clear so far? So my initial code looked something like this:</p>
            
            <div class="highlight"><pre><span class="nb">require</span> <span class="s2">&quot;digest/sha1&quot;</span>&#x000A;    &#x000A;    <span class="k">class</span> <span class="nc">Action</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>&#x000A;    &#x000A;      <span class="n">belongs_to</span> <span class="ss">:transaction</span>&#x000A;      <span class="n">before_save</span> <span class="ss">:create_guid</span>&#x000A;    &#x000A;      <span class="k">def</span> <span class="nf">create_guid</span>&#x000A;        <span class="n">sha1</span> <span class="o">=</span> <span class="no">Digest</span><span class="o">::</span><span class="no">SHA1</span><span class="o">.</span><span class="n">new</span>&#x000A;        <span class="n">sha1</span><span class="o">.</span><span class="n">update</span> <span class="n">transaction_id</span><span class="o">.</span><span class="n">to_s</span>&#x000A;        <span class="n">sha1</span><span class="o">.</span><span class="n">update</span> <span class="n">type</span><span class="o">.</span><span class="n">downcase</span>&#x000A;        <span class="n">sha1</span><span class="o">.</span><span class="n">update</span> <span class="no">DateTime</span><span class="o">.</span><span class="n">to_s</span>&#x000A;        <span class="nb">self</span><span class="o">.</span><span class="n">guid</span> <span class="o">=</span> <span class="n">sha1</span><span class="o">.</span><span class="n">hexdigest</span>&#x000A;      <span class="k">end</span>&#x000A;    <span class="k">end</span>&#x000A;    &#x000A;    <span class="k">class</span> <span class="nc">ReturnAction</span> <span class="o">&lt;</span> <span class="no">Action</span>&#x000A;      <span class="k">def</span> <span class="nf">execute</span>&#x000A;        <span class="n">transaction</span><span class="o">.</span><span class="n">return!</span>&#x000A;      <span class="k">end</span>&#x000A;    <span class="k">end</span>&#x000A;    &#x000A;    <span class="k">class</span> <span class="nc">AbortAction</span> <span class="o">&lt;</span> <span class="no">Action</span>&#x000A;      <span class="k">def</span> <span class="nf">execute</span>&#x000A;        <span class="n">transaction</span><span class="o">.</span><span class="n">abort!</span>&#x000A;      <span class="k">end</span>&#x000A;    <span class="k">end</span>&#x000A;    &#x000A;    <span class="k">class</span> <span class="nc">DisputeAction</span> <span class="o">&lt;</span> <span class="no">Action</span>&#x000A;      <span class="k">def</span> <span class="nf">execute</span>&#x000A;        <span class="n">transaction</span><span class="o">.</span><span class="n">abort!</span>&#x000A;      <span class="k">end</span>&#x000A;    <span class="k">end</span>&#x000A;    </pre>
            </div>
            
            
            <p>It seemed like a good idea at the time, but the strange thing was that no matter which incantation I tried, I simply couldn't create a new <code>Action</code> instance and have it write a record to the database. This simply didn't work:</p>
            
            <div class="highlight"><pre><span class="no">ReturnAction</span><span class="o">.</span><span class="n">create!</span> <span class="ss">:transaction_id</span> <span class="o">=&gt;</span> <span class="mi">1</span>&#x000A;    </pre>
            </div>
            
            
            <p>There were no errors on the returned object. No exceptions were thrown. No queries to the database and certainly no insert statements executed. Just complete and utter silence. Out of desperation, as much as anything else, I removed the <code>belongs_to</code> declaration from the <code>Action</code> class and instead declared a <code>has_many</code> on the <code>Transaction</code> class. Voila! It worked like a champ.</p>
            
            <p>After a bit of thought, the <code>has_many</code> association makes complete sense to me in the case where we want to create new <code>Action</code> instances for a particular <code>Transaction</code>. However, if you look in the code above, the <code>execute</code> methods of each sub-class are referring to a <code>transaction</code> object/method&mdash;which I no longer have. However I don't necessarily need the full-blown <code>belongs_to</code> association here. I can just fake the bits I want in the parent <code>Action</code> class like so:</p>
            
            <div class="highlight"><pre><span class="k">class</span> <span class="nc">Action</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>&#x000A;      <span class="k">def</span> <span class="nf">transaction</span>&#x000A;        <span class="vi">@transaction</span> <span class="o">||=</span> <span class="no">Transaction</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="nb">self</span><span class="o">.</span><span class="n">transaction_id</span><span class="p">)</span>&#x000A;      <span class="k">end</span>&#x000A;    <span class="k">end</span>&#x000A;    </pre>
            </div>
            
            
            <p>So none if this is particularly earth-shattering. Sorry folks, no great gems of philosophical wisdom today. Just one man's small accomplishment blown completely out of proportion.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>clip version 0.0.6 has been released!</title>
      <link>
        http://alexvollmer.com/posts/2008/07/10/clip-version-006-has-been-released/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/07/10/clip-version-006-has-been-released/#comments
      </comments>
      <pubDate>
        Thu, 10 Jul 2008 17:12:27 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            clip
        ]]>
      </category>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/07/10/clip-version-006-has-been-released/
      </guid>
      <description>
        <![CDATA[
            You like command-line parsing, but you hate all of the bloat. Why
            should you have to create a Hash, then create a parser, fill the Hash
            out then throw the parser away (unless you want to print out a usage
            message) and deal with a Hash? Why, for Pete's sake, should the parser
            and the parsed values be handled by two different objects?
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>You like command-line parsing, but you hate all of the bloat. Why
            should you have to create a Hash, then create a parser, fill the Hash
            out then throw the parser away (unless you want to print out a usage
            message) and deal with a Hash? Why, for Pete's sake, should the parser
            and the parsed values be handled by two different objects?</p>
            
            <p>Changes:</p>
            
            <h3>0.0.6 / 2008-07-10</h3>
            
            <ul>
            <li><p>Fixed a bug with getting the 'remainder' when only flags are declared.</p></li>
            <li><p><a href="http://clip.rubyforge.org">http://clip.rubyforge.org</a></p></li>
            </ul>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Military History</title>
      <link>
        http://alexvollmer.com/posts/2008/06/28/military-history/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/06/28/military-history/#comments
      </comments>
      <pubDate>
        Sat, 28 Jun 2008 16:15:19 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            philosophy
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/06/28/military-history/
      </guid>
      <description>
        <![CDATA[
            In addition to technical geekery and social commentary, one of my favorite intellectual pursuits is the study of military history. It's one of my many interests that makes me so very thankful that I met my wife when I did or I would never have had a date in my adult life.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>In addition to technical geekery and social commentary, one of my favorite intellectual pursuits is the study of military history. It's one of my many interests that makes me so very thankful that I met my wife when I did or I would never have had a date in my adult life.</p>
            
            <p>I am not a pro-war person. Anyone who reads <em>good</em> military history will often find that the best historians are quite <em>anti-war</em>. I had the opportunity to see <a href="http://www.liberationtrilogy.com/%20Rick%20Atkinson's">Rick Atkinson</a> speak in Seattle last winter as he was promoting his book, <em>Day of Battle</em>. He has written several titles about both WWII and Iraq and said that all of his books as anti-war and hopes his readers understand that. No historian has summed that notion up as well as <a href="http://www.google.com/url?q=http://books.google.com/books%3Fas_auth%3DJohn%2BKeegan&amp;sa=X&amp;oi=book_group&amp;resnum=1&amp;ct=title&amp;cad=author-navigational&amp;usg=AFQjCNH1nGziOaBfW4F8cI42VFrbtcCdYw%20John%20Keegan">John Keegan</a> in his introduction to <em>The History of Warfare</em>.</p>
            
            <p>To study war is not necessarily to glorify it. To be sure, a good bit of the military history bookshelf is filled with martial hagiography (see Ambrose, Stephen), but to those who study the history with more than a simple fascination of the technologies and armies there is a rich depth of human experience and tragedy to explore.</p>
            
            <p><img src="http://farm4.static.flickr.com/3083/2378828503_8a9e683206_m.jpg" class="left"/></p>
            
            <p>Like most men (and I'm sure that 99.9% of all military historians are male), I began with that very boyish fascination with all things violent and martial. As I grew older I realized that this was a silly childish interest. I either needed to stop reading about this subject, or I needed to understand it better. I chose the latter in an attempt to cull meaning from so many centuries of meaningless slaughter.</p>
            
            <p>Humans go to war for a variety of reasons–most of them not good. Greed, hatred, and lust for power have fueled more than their fair share of conflict and tragedy. However that doesn't mean that I think all war is useless. There are some things worth fighting and dying for, but those moments are few and far between in human history. While I cannot condone military action in most of the cases in which it occurs, I believe that it is an inevitable part of the human experience. We were simply bred to beat each others brains out. This is a depressing and tragic conclusion, but I believe that human history is on my side when I make that statement.</p>
            
            <p>So what's the point of reading military history? If all we are going to do is make the same mistakes over and over again what possible good can from studying the past? Despite my pessimistic outlook on the short-term chances of humanity straightening itself out, I think in long-term we may learn from our mistakes. Military history is nothing if not a study of mistakes. Yes the Hannibals, Napoleons and Pattons of history get the accolades, but there isn't nearly as much to learn from their successful campaigns. We need to look at the spectacular failures too.</p>
            
            <p><img src="http://upload.wikimedia.org/wikipedia/commons/thumb/0/0a/Jacques-Louis_David_017.jpg/300px-Jacques-Louis_David_017.jpg" class="left"/></p>
            
            <p>For example, we can learn far more from the fateful decisions of both Napoleon and Hitler to campaign in the Russian winter. While the technical reason for their defeat was weather and logistics, the true cause of their downfall was hubris. It wasn't that neither failed to comprehend the risks, but rather they felt that their leadership and the elan of their troops transcended reality. Can anyone else think of a recent example where a leader ignored the facts on the ground in pursuit of their own personal policy? Hmm? Anyone?</p>
            
            <p>Another fine example is the book I'm currently reading, Mr. Atkinson's aforementioned <em>The Day of Battle</em>. The Italian campaign is an oft-forgotten part of World War II. This is due in no small part because there was very little to celebrate in that campaign. No gutsy fortitude like Guadalcanal or Stalingrad, no tragedy like Poland or Saipan, just lots of dumb decisions that required a tremendous amount of cannon-fodder to achieve victory.</p>
            
            <p>Few campaigns offer such a textbook example of group-think gone horribly wrong as the Allies in Italy during World War II. From Churchill and Roosevelt, to Eisenhower, to the theater commanders, everyone in the chain of command was eager to cast what they saw with their own eyes in terms of what they desired, not what the actual realities on the ground were. While we may not be in a war in our day-to-day work, each of us can certainly come up with examples where group-think led everyone to bad conclusions.</p>
            
            <p>The other valuable outcome of studying military history to come to a better understanding of just what the cost of war truly is. Obviously anyone who has <em>not</em> experienced combat cannot imagine the horrors of that experience. I thank my lucky stars that I never had to go to war. But I feel that it's disingenuous to rail against the horrors of war without making an attempt to understand them.</p>
            
            <p><img src="http://farm4.static.flickr.com/3272/2338835862_6eed1417f1_m.jpg" class="left"/></p>
            
            <p>I attended the University of Oregon and there are few places on earth that have a stronger innate anti-war bias. I have no problem with the pursuit of peace, but to do so in ignorance serves no one. It's vital that we all understand what the consequences of going to war are. I fear that the cavalier attitude with which the United States has prosecuted the Iraq War and insulated the public from the horrifying facts on the ground only serves to keep us ignorant of the true costs of war. This is not a game of cops and robbers–people are dying daily for questionable causes, to say nothing of the long-term political consequences. But I digress...</p>
            
            <p>One final reward of the study of military history is to recognize how leadership within a corporate, in the purest sense of the word,  setting can work. I don't mean the famous generals such as Grant, Lee or Washington. Rather, a kind of leadership that few successfully execute consciously. Instead it is the <a href="http://en.wikipedia.org/wiki/Joshua_Chamberlain%20The%20Hero%20of%20Little%20Rount%20Top">Joshua Chamberlains</a> of the world that provide insightful, meaningful case studies of what true leadership is. Most of us don't work in groups of one thousand or more, but instead in "squads" of ten or less people. Learning how Captains and Sergeants command, push and prod their troops and maintain esprit de corps is worth the time of anyone who is interested in exercising any form of leadership.</p>
            
            <p>It's rare that at the end of a good military history read I don't feel the need to weep. Maybe I'm a sucker for heartache but I can't help but feel just a teeny bit smarter after turning the last page of a well-written piece of military history. So before parting, let me leave you with a short list of great military reads (in no particular order) that I've come across over the years:</p>
            
            <ul>
            <li><em>The Peloponnesian War</em> &mdash; Thucydides</li>
            <li><em>The Civil War (Trilogy)</em> &mdash; Shelby Foote</li>
            <li><em>At Dawn We Slept</em> &mdash; Gordon Prange</li>
            <li>Just about anything by John Keegan</li>
            <li><em>The Best and the Brightest</em> &mdash; David Halberstam</li>
            <li><em>We Were Soldiers Once...And Young</em> &mdash; Harold G. Moore</li>
            <li><em>The Rise and Fall of the Great Powers</em> &mdash; Paul Kennedy</li>
            <li><em>Diplomacy</em> &mdash; Henry Kissinger</li>
            </ul>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>gemdoc completion in zsh</title>
      <link>
        http://alexvollmer.com/posts/2008/06/25/gemdoc-completion-in-zsh/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/06/25/gemdoc-completion-in-zsh/#comments
      </comments>
      <pubDate>
        Wed, 25 Jun 2008 16:30:24 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <category>
        <![CDATA[
            zsh
        ]]>
      </category>
      <category>
        <![CDATA[
            geekery
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/06/25/gemdoc-completion-in-zsh/
      </guid>
      <description>
        <![CDATA[
            This week I stumbled upon Stephen Celis' awesome bit of shell-fu, <a href="http://stephencelis.com/archive/2008/6/bashfully-yours-gem-shortcuts%20gemdoc">gemdoc</a>, which allows you to quickly get to the HTML docs for installed gems via command-line. Unfortunately I abandoned bash years ago for zsh and Stephen's shell bits needed a little porting. For me, zsh, is a bit like swiss-army knife where about 95% of it is a mystery to me, but the 5% I use I couldn't live without. So simply switching back to bash is a no-go.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>This week I stumbled upon Stephen Celis' awesome bit of shell-fu, <a href="http://stephencelis.com/archive/2008/6/bashfully-yours-gem-shortcuts%20gemdoc">gemdoc</a>, which allows you to quickly get to the HTML docs for installed gems via command-line. Unfortunately I abandoned bash years ago for zsh and Stephen's shell bits needed a little porting. For me, zsh, is a bit like swiss-army knife where about 95% of it is a mystery to me, but the 5% I use I couldn't live without. So simply switching back to bash is a no-go.</p>
            
            <p>My setup is little-bit complicated, but I believe the following, stripped-down, recipe should work:</p>
            
            <div class="highlight"><pre><span class="nv">GEMDIR</span><span class="o">=</span><span class="k">$(</span>gem env gemdir<span class="k">)</span>&#x000A;    <span class="nv">OPEN</span><span class="o">=</span><span class="k">$(</span>whence xdg-open <span class="o">||</span> whence open<span class="k">)</span>&#x000A;    &#x000A;    gemdoc<span class="o">()</span> <span class="o">{</span>&#x000A;      <span class="k">${</span><span class="nv">OPEN</span><span class="k">}</span> <span class="nv">$GEMDIR</span>/doc/<span class="sb">`</span><span class="k">$(</span>which ls<span class="k">)</span> <span class="nv">$GEMDIR</span>/doc | grep <span class="nv">$1</span> | sort | tail -1<span class="sb">`</span>/rdoc/index.html&#x000A;    <span class="o">}</span>&#x000A;    &#x000A;    _gemdocomplete<span class="o">()</span> <span class="o">{</span>&#x000A;      <span class="nv">reply</span><span class="o">=(</span> <span class="sb">`</span><span class="k">$(</span>which ls<span class="k">)</span> <span class="nv">$GEMDIR</span>/doc<span class="sb">`</span> <span class="o">)</span>&#x000A;    <span class="o">}</span>&#x000A;    &#x000A;    compctl -K _gemdocomplete gemdoc&#x000A;    </pre>
            </div>
            
            
            <p><em>Update 6/25/08-10:30</em>: Updated to work for both Penguins and Macs.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Launch Day!!!</title>
      <link>
        http://alexvollmer.com/posts/2008/06/24/launch-day/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/06/24/launch-day/#comments
      </comments>
      <pubDate>
        Tue, 24 Jun 2008 04:17:58 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            evri
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/06/24/launch-day/
      </guid>
      <description>
        <![CDATA[
            I've spilled a lot of (virtual) ink in this blog, but almost none of it about what I do all day. That's because I've been working at a startup in "stealth mode" for darn near two years and haven't been able to really say much about it. Until today.<a href="http://evri.com" title="Evri" target="_blank"></a>
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>I've spilled a lot of (virtual) ink in this blog, but almost none of it about what I do all day. That's because I've been working at a startup in "stealth mode" for darn near two years and haven't been able to really say much about it. Until today.<a href="http://evri.com" title="Evri" target="_blank"></a></p>
            
            <p><a href="http://evri.com" title="Evri" target="_blank"><img src="http://livollmers.net/wp-content/uploads/2008/06/picture-4.jpg" alt="Picture 4.png" /></a></p>
            
            <p>Today at <a href="http://evri.com%20Evri">Evri</a> we're launching the beta version of our site. If you head to the <a href="http://evri.com%20Evri">home page</a> and signup, you'll get yourself a free set of brand-new shiny credentials that will give you the keys to data-surfing heaven.
            <img src="http://blog.evri.com/wp-content/uploads/2008/06/evri-homepage-aaman.png" alt="homepage" />
            The <a href="http://blog.evri.com/index.php/2008/06/24/little-room/%20Evri%20Blog">company blog post</a> does a good job of highlighting what we have available, but for the truly lazy I'll give you the quick highlights.</p>
            
            <p>First, is the home page which gives you a look at entities and their relations as we understand them currently. We start out with lists ranking the top people, places and things. In addition to popularity, you can also see who is rising and falling in popularity over time. All of these lists are clickable and enable our super-whizzy widget which provides a nice way of pivoting between entities, all the while getting related content.</p>
            
            <p>This is one of my favorite parts of the product, as it's really easy to just get lost wandering around from link to link to see how things are related. It's like a big six-degrees-of-Kevin-Bacon game, except that you can do with just about anything. Part of that link-hopping experience is visiting specific pages about each entity.</p>
            
            <p>Here you can find more detailed content about an entity. Currently these details comes from Wikipedia, but we anticipate adding several other specific sources of structured content. And of course, these pages link you to other pages, so between the hub-and-spoke visualization and the detail pages you can spend quite a bit of time just data-grazing.
            <img src="http://blog.evri.com/wp-content/uploads/2008/06/bon-iver.png" alt="evri profile page for bon iver" />
            So take it for a spin and have some fun exploring! Give us your feedback (a link is at the top of each page) and let us know what you like and don't like. Most importantly, stay tuned. This is just the beginning for us, we have some pretty exciting stuff in the works and you won't want to miss out.</p>
            
            <p>Things have been a bit nutty the last few days&#8211;as they are for any release&#8211;but it will be worth it for the satisfaction of finally lighting this candle.</p>
            
            <p>Enjoy!</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Smorgasm!</title>
      <link>
        http://alexvollmer.com/posts/2008/06/21/smorgasm/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/06/21/smorgasm/#comments
      </comments>
      <pubDate>
        Sat, 21 Jun 2008 04:13:31 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            food
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/06/21/smorgasm/
      </guid>
      <description>
        <![CDATA[
            While on vacation last week, my wife and some friends of ours solved the age-old problem of melting the chocolate <em>and</em> the marshmallow in a s'more. You stuff a chunk of chocolate (we prefer good old-fashioned Hershey's waxy American milk chocolate) <em>inside</em> the mallow and then toast the entire unit.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>While on vacation last week, my wife and some friends of ours solved the age-old problem of melting the chocolate <em>and</em> the marshmallow in a s'more. You stuff a chunk of chocolate (we prefer good old-fashioned Hershey's waxy American milk chocolate) <em>inside</em> the mallow and then toast the entire unit.</p>
            
            <p><a href="http://www.flickr.com/photos/36455265@N00/2579055859/"><img src="http://farm4.static.flickr.com/3164/2579055859_8f20a4194c_m.jpg" alt="DSC_0182.JPG" /></a></p>
            
            <p>Do you see where this is going? No? How 'bout this?</p>
            
            <p><a href="http://www.flickr.com/photos/36455265@N00/2579061059/"><img src="http://farm4.static.flickr.com/3179/2579061059_fc9547bb05_m.jpg" alt="DSC_0190.JPG" /></a><a href="http://www.flickr.com/photos/36455265@N00/2579890496/"><img src="http://farm4.static.flickr.com/3031/2579890496_db32c29b53_m.jpg" alt="DSC_0187.JPG" /></a><a href="http://www.flickr.com/photos/36455265@N00/2579882146/"><img src="http://farm4.static.flickr.com/3126/2579882146_19007c158c_m.jpg" alt="DSC_0178.JPG" /></a></p>
            
            <p>Amen sisters and brothers. Now <em>that's</em> a properly-melted s'more.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Book Reviews: Designing the Obvious/Designing the Moment</title>
      <link>
        http://alexvollmer.com/posts/2008/06/20/book-reviews-designing-the-obviousdesigning-the-moment/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/06/20/book-reviews-designing-the-obviousdesigning-the-moment/#comments
      </comments>
      <pubDate>
        Fri, 20 Jun 2008 04:55:00 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            book review
        ]]>
      </category>
      <category>
        <![CDATA[
            UX
        ]]>
      </category>
      <category>
        <![CDATA[
            information design
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/06/20/book-reviews-designing-the-obviousdesigning-the-moment/
      </guid>
      <description>
        <![CDATA[
            I've been on a usability/design kick for about the last six months. Somehow I stumbled across a link to <a href="http://rhjr.net">Robert Hoekman Jr's site</a> which was described as great design books for programmers. I fully recognize the fact that I really don't have that little spark that good designers have, but I'd like to be better at it than I am. So I've been eager to find usability and design books that work for visually-clumsy folks like me. Robert Hoekman's pair of books, <em>Designing the Obvious</em> and <em>Designing the Moment</em> were wonderful additions to my growing design-for-code-dorks library.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>I've been on a usability/design kick for about the last six months. Somehow I stumbled across a link to <a href="http://rhjr.net">Robert Hoekman Jr's site</a> which was described as great design books for programmers. I fully recognize the fact that I really don't have that little spark that good designers have, but I'd like to be better at it than I am. So I've been eager to find usability and design books that work for visually-clumsy folks like me. Robert Hoekman's pair of books, <em>Designing the Obvious</em> and <em>Designing the Moment</em> were wonderful additions to my growing design-for-code-dorks library.</p>
            
            <p>The covers of both books were what initially piqued my interest. Both have very simple white covers. Unlike a lot of design books, this one isn't about showing off a bunch of pyrotechnics on the cover (see Jenifer Tidwell's <em><a href="http://designinginterfaces.com/">Designing Interfaces</a></em> which includes a colored version of O'Reilly's usual animal lithograph). I figured anyone willing to put such a sparse cover on the page was pretty confident about the content inside. I also really liked the fact that the form-factor of both books is smaller than the usual trade paperback and comes in at a very economic 200 pages, or about 1/4" thick.</p>
            
            <p>Alright, I'll admit that I was taken in by the author's use of my <a href="http://blog.livollmers.net/index.php/2008/03/13/one-geeks-aesthetics/%20Previous%20post%20of%20font%20aesthetics">favorite font, Gill Sans</a>. I just love this font (it's the font this blog is set to if you don't override CSS) because it's clean and elegant with a minimum&#8211;or complete absence&#8211;of decorative fuss. Unlike the Pragmatic Programmers or O'Reilly the publisher, New Riders, doesn't seem enforce a particular font style for their books. Therefore I think it's safe to assume that the author made a conscious decision to use this font which, at a microscopic level, supports many of the notions of simplicity and cleanliness presented in the books.</p>
            
            <h2><a href="http://rhjr.net/dto/">Designing The Obvious</a></h2>
            
            <p><a href="http://www.amazon.com/Designing-Obvious-Common-Approach-Application/dp/032145345X%3FSubscriptionId%3D0PZ7TM66EXQCXFVTMTR2%26tag%3Dhttplivollmne-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D032145345X"><img src="http://ecx.images-amazon.com/images/I/31tDU7ayPvL._SL160_.jpg" class="left"/></a></p>
            
            <p>His first book, <em>Designing the Obvious</em> focuses on translating from what a user needs to creating workable screen-flows. While I've knocked out several smaller design books, I've been slowly making my way through Alan Cooper's seminal <em>About Face</em> in which the concept of <em>goal-oriented design</em> is introduced. Hoekman's book is the second text I've come across that offers a contrasting opinion on goal-oriented design the author calls <em>task-oriented design</em>. Whereas Cooper's approach is to begin design by understanding a user's goal within the larger context of their lives or career aspiration, <em>task-oriented design</em> is focused on more immediate desires.</p>
            
            <p>A goal-oriented design might start with something like "Anna is a small-business owner who wants to balance career and family. She needs particular help with payroll for her small three-person company." A task-oriented design might start with "A user with a small-company must be able to setup employees with a minimum of fuss: perhaps just name, address and social security number". Hoekman even titles one of his chapters "Understand Users, Then Ignore Them".</p>
            
            <p>Each chapter is tightly-focused on a single concept and few supporting ones. For example, the chapter titled "Turn Beginners Into Intermediates, Immediately" spends a thrifty thirty-five pages outlining the basic distribution of user expertise (hint: the big fat blob in the middle are the intermediate users) and then enumerating several concrete examples of how to serve the intermediates, how to get the beginners to immediates as quickly as possible, and how to keep the advanced users still engaged.</p>
            
            <p>The ability of Hoekman to outline a concept and back it up with several concrete examples is the real strength of the book. This is not a patterns or recipe book. Similarly it's not a grand philosophical tome (see Cooper, above). Instead it's a very practical work intent on getting the ideas across, but leaving plenty of room for the reader to explore on their own.</p>
            
            <p>The fact that he gets such a rich amount of information is such a small package is a testament to the author's well-thought, lean design approach.</p>
            
            <p>5 out of 5 stars</p>
            
            <h2><a href="http://rhjr.net/dtm/%20Designing%20The%20Moment">Designing The Moment</a></h2>
            
            <p><a href="http://www.amazon.com/Designing-Moment-Interface-Design-Concepts/dp/0321535081%3FSubscriptionId%3D0PZ7TM66EXQCXFVTMTR2%26tag%3Dhttplivollmne-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D0321535081"><img src="http://ecx.images-amazon.com/images/I/41AfJf7CygL._SL160_.jpg" class="left"/></a></p>
            
            <p>Hoekman's second title focuses specifically on web application design. Unlike his previous book which is more philosophical and abstract, <em>Designing The Moment</em> is much more concrete. With thirty-one chapters spread out over 220 pages, each chapter is tightly-focused. Those chapters are split up into seven major sections.</p>
            
            <p>The first section is titled <em>Getting Oriented</em> and focuses on getting the user oriented with your site. He delves into how users' eyes flow over a page, navigation, links and dealing with common web paradigms like tag clouds.</p>
            
            <p>The second section, <em>Learning</em>, provides specifics about getting users "over the novice hump". This theme was an important one in his first book and here he offers several ways to teach your users about your site.</p>
            
            <p>The third section, <em>Searching</em>, walks you through the pitfalls of search interface design. A common theme in both books is that of a <a href="http://en.wikipedia.org/wiki/Poka-yoke">poke-yoka,</a> which means to fool-proof in Japanese. The term was originally derived from Toyota's Production System which was developed in the '80's (note: Toyota is currently kicking serious rear-end in the car biz). Here he uses the term <em>poke-yoka device</em> to mean any mechanism that will help prevent the user from hurting themselves. This is not about treating users as idiots but rather hiding ugly implementation details away from the users if at all possible. For example, you if need users to enter a phone number in a particular format, design a form that makes so that users enter phone numbers in that format. Don't just give them a text field and then complain when they don't get it right.</p>
            
            <p>Moving on we get to fourth section, titled <em>Diving In,</em> where we really start to get into the nitty-gritty details of things like media player controls, form layout, wizards, and validation. This is the longest section of the book and meatiest. Again, Hoekman nails the concepts with well-chosen representative examples and solutions.</p>
            
            <p>The fifth section, <em>Participating</em> focuses on the mechanics of features commonly associated with "social-networking" web applications. This chapter ranges from concrete implementation recommendations like how to build user profiles, to more abstract, strategic concepts like how to embrace user feedback and how to channel your most vocal users.</p>
            
            <p>The sixth section, <em>Managing Information</em>, provides some tips on helping users digest your site's contents. Tips here include how to effectively use syndication, dealing with tags and folksonomies, where drag-and-drop is appropriate and dealing with system notifications.</p>
            
            <p>The final section, <em>Moving On</em>, embraces I concept I first saw articulated in 37Signals' <em><a href="http://gettingreal.37signals.com/toc.php">Getting Real</a></em> . Don't build your apps as walled gardens where you make every attempt to keep your users from leaving. This ain't Vegas you aren't a casino. Yes, you want to give users another chance to reconsider and you certainly want to know why they're leaving, but don't be a jerk about it. This section offers some guidelines for parting ways with your users. I think to design something without this in mind is to the fact that not everyone is going to dig what you've built. Let them go easily and don't make things worse by making parting a painful experience.</p>
            
            <p>I loved this book as much as Hoekman's first title. Both are handy references I'll keep nearby.</p>
            
            <p>5 out 5 stars.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Somebody Hates Me</title>
      <link>
        http://alexvollmer.com/posts/2008/06/19/somebody-hates-me/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/06/19/somebody-hates-me/#comments
      </comments>
      <pubDate>
        Thu, 19 Jun 2008 22:43:49 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            personal
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/06/19/somebody-hates-me/
      </guid>
      <description>
        <![CDATA[
            When I see a forecast like this, I gotta think that life just isn't fair sometimes...
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>When I see a forecast like this, I gotta think that life just isn't fair sometimes...</p>
            
            <p><img src="http://livollmers.net/wp-content/uploads/2008/06/picture-2.jpg" alt="Picture 2.png" /></p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>It Would Be Nice If...</title>
      <link>
        http://alexvollmer.com/posts/2008/06/14/it-would-be-nice-if/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/06/14/it-would-be-nice-if/#comments
      </comments>
      <pubDate>
        Sat, 14 Jun 2008 22:00:02 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            software
        ]]>
      </category>
      <category>
        <![CDATA[
            philosophy
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/06/14/it-would-be-nice-if/
      </guid>
      <description>
        <![CDATA[
            When talking about building software, few sentences set off more red flags than those beginning with "it would be nice if..". I don't mean some variation of this phrase, I mean exactly this phrase. It's like those words are a specific code-phrase for "speculative features coming your way!"
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>When talking about building software, few sentences set off more red flags than those beginning with "it would be nice if..". I don't mean some variation of this phrase, I mean exactly this phrase. It's like those words are a specific code-phrase for "speculative features coming your way!"</p>
            
            <p>What the heck is "nice" anyway? The very language of that statement does nothing more than imply; it makes no assertions and offers no proof. A piece of candy is nice. So are flowers and a new house. But those things all require widely varying levels of planning, effort and cost. So "nice" isn't a very precise word and certainly fails us when we try to evaluate what goes into our product and what stays on the sidelines for further evaluation.</p>
            
            <p>In a geek-heavy setting, such as my workplace, I often observe this phrase used as a desire to establish authority in a conversation. This happens when features are proposed not so much for their value, but as a way of showing how deep and nuanced the proposer's understanding of the domain is. For example in a brainstorming session we had recently about new visualizations for rich data structures, a lot of IWBNI ideas were proposed that were fairly baroque and hard to imagine being interesting to a wider audience. In this instance instead of focusing on the goal, making sense of a big pile of information, the exercise turned into a demonstration of various participants' grasp of market dynamics, web trends and cutting-edge features. You can imagine how useful the final set of ideas were.</p>
            
            <p>Another issue with IWBNI features is that they are often very implementation-focused. For example if you are working on a web service that is consumed by other services, you might want to track usage statistics to generate a regular report to see who is using it. The real feature is tracking usage, but that original need can easily be obscured by an overly-specific implementation. Ideas for these features often emerge out of the system constraints that are a result of the system design, not necessarily a natural outcome of the problem domain. Differentiating between these two is probably one of the hardest things to do in any kind of design.</p>
            
            <p>It's vital to differentiate between these because IWBNI features seem especially prone to calcifying the current implementation. Back to our web services example, let's say we implement this usage report, which dumps a text file every hour of usage statistics. We love that feature so much that we hook it up to our automated monitoring system as it seems like a nice way to monitor the "heartbeat" of the system. However, down the road we discover that we need a maintenance window that makes the file unavailable for a period of time that causes the monitoring system to alert us. Now we have a choice: we can patch up the monitoring to manage this exception, or we can re-think just how important the text-file interface is.</p>
            
            <p>In this case I would argue that the text-file dump might instead be replaced with a simple web-request. When the system is in its maintenance mode, it could still answer questions about general availability (which is what we were originally interested in) without tying a more specific feature, usage, to monitoring. I don't think that making the monitoring script more sophisticated is the right answer. More complexity there means a higher likelihood of breaking and it spreads some very implementation-specific details to other parts of the system.</p>
            
            <p>The features and attributes of a system can be viewed like walls in a house. Some are load-bearing and some are not. The load-bearing features are those that without which, the house would simply fall. In your applications these are features that are the very essence of the software. An application like Quicken has an awful lot of walls. The load-bearing features of Quicken are the ledgers and reconciliation process. Without these, the other features of Quicken are irrelevant.</p>
            
            <p>However features like downloading transactions over the internet or viewing pie-charts are mostly decorative. This doesn't mean they are without worth, but they are not as essential as the load-bearing features. These could be removed and Quicken's essence would still be preserved. Quicken is certainly more useful with these features, but those aren't the features that generally drive users to use it. (NB: This is not to say Quicken is a well-designed application. But I'll save that for another post...)</p>
            
            <p>You can move the decorative walls around to change the space of a room without major fuss. Moving a load-bearing wall is a fairly major operation and has a huge impact on the character of the house and requires extra planning so that the house doesn't collapse during the change. The danger of IWBNI is that it's easy to confuse these features with essential "load-bearing" ones. Worse yet, the compound of multiple IWBNI features can end up as a load-bearing walls that are difficult to move. Revisiting our web services example, if more features like the text file were piled on and external parties began to rely on these, it would become much more difficult to move these in the future. It's not difficult to imagine getting to a point where the original role of the web service is obscured by all of the other tangential bells and whistles.</p>
            
            <p>Sometimes IWBNI features are user- or domain-driven. These seem like they might have a better chance of withstanding the litmus test. More often than not these ideas end up obscuring the core of the application, but these can be, relatively, easy to test with tools like mockups, user interviews and usability testing. In supporting services it's much harder to evaluate these features. How do we do usability testing for a web service? Does the variable for a request belong in the path or as a query parameter? How do we figure out what consumers want? This is tricky because in this case we're designing something by geeks, for geeks. This doesn't mean that it's okay to pile on a bunch of implementation details and stop thinking about separating our load-bearing features from our decorative ones. But it does mean that we have to be extra vigilant about the IWBNI features and view them with a particularly suspicious eye.</p>
            
            <p>So I think this is the real trick&#8211;whether you are a visual designer, information architect or software developer&#8211;is to separate the essential from the decorative. Being able to sort features in this way gives you a chance to properly evaluate the cost/benefit tradeoffs. Without this I believe it is much more difficult to clearly see the value of a feature and its overall impact on the system.</p>
            
            <p>So let me offer up a challenge: treat IWBNI as a codeword for something requiring exceptional scrutiny. When something is proposed as a IWBNI feature, regard it with a suspicious eye. When you feel yourself proposing a IWBNI feature, think long and hard about whether or not it is really "nice" or whether it might "essential" or "superfluous". And for goodness' sake, don't get hung up on your IWBNI features. If they're "nice" they probably aren't a core feature anyway. You're a smart, creative person and you'll have other ideas in the future.</p>
            
            <p>And finally, let's remember the most common trait that nearly all IWBNI features share...</p>
            
            <div style="font-size: 72px; font-weight: bold;">YAGNI*</div>
            
            
            <ul>
            <li>Ya ain't gonna need it</li>
            </ul>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>clip version 0.0.5 has been released!</title>
      <link>
        http://alexvollmer.com/posts/2008/06/13/clip-version-005-has-been-released/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/06/13/clip-version-005-has-been-released/#comments
      </comments>
      <pubDate>
        Fri, 13 Jun 2008 22:06:04 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <category>
        <![CDATA[
            clip
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/06/13/clip-version-005-has-been-released/
      </guid>
      <description>
        <![CDATA[
            You like command-line parsing, but you hate all of the bloat. Why should you have to create a Hash, then create a parser, fill the Hash out then throw the parser away (unless you want to print out a usage message) and deal with a Hash? Why, for Pete's sake, should the parser and the parsed values be handled by two different objects?
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>You like command-line parsing, but you hate all of the bloat. Why should you have to create a Hash, then create a parser, fill the Hash out then throw the parser away (unless you want to print out a usage message) and deal with a Hash? Why, for Pete's sake, should the parser and the parsed values be handled by two different objects?</p>
            
            <p>Changes:</p>
            
            <h3>0.0.5 / 2008-06-12</h3>
            
            <ul>
            <li>Removed sample_parser from bin (technomancy)</li>
            <li>fix a stupid bug causing an infinite loop for empty ARGV (technomancy)</li>
            <li><a href="http://clip.rubyforge.org">http://clip.rubyforge.org</a></li>
            </ul>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>The End of an Era</title>
      <link>
        http://alexvollmer.com/posts/2008/06/07/the-end-of-an-era/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/06/07/the-end-of-an-era/#comments
      </comments>
      <pubDate>
        Sat, 07 Jun 2008 04:58:07 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            geekery
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/06/07/the-end-of-an-era/
      </guid>
      <description>
        <![CDATA[
            Well, okay maybe that title is a bit misleading. I mean, we're not talking about a transition from when dinosaurs ruled the earth to the Ice Age or the introduction of the combustible engine. But today I shut down my remaining home Linux server which ran this blog since its inception.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>Well, okay maybe that title is a bit misleading. I mean, we're not talking about a transition from when dinosaurs ruled the earth to the Ice Age or the introduction of the combustible engine. But today I shut down my remaining home Linux server which ran this blog since its inception.</p>
            
            <p>After overcoming a few technical hurdles I got this blog moved over to Dreamhost. Things seem to be running well, but if you notice anything, let me know.</p>
            
            <p><img src="http://livollmers.net/wp-content/uploads/2008/06/shutdown.jpg" alt="shutdown.png" /></p>
            
            <p>It sort of felt like the end of "T2" when Arnie willingly destroys himself in the forgery and signals a final thumbs-up before incineration. It was definitely time for that box to shutdown, but just a tiny bit sad just the same.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>clip version 0.0.4 has been released!</title>
      <link>
        http://alexvollmer.com/posts/2008/06/07/clip-version-004-has-been-released/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/06/07/clip-version-004-has-been-released/#comments
      </comments>
      <pubDate>
        Sat, 07 Jun 2008 04:34:01 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            clip
        ]]>
      </category>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/06/07/clip-version-004-has-been-released/
      </guid>
      <description>
        <![CDATA[
            You like command-line parsing, but you hate all of the bloat. Why
            should you have to create a Hash, then create a parser, fill the Hash
            out then throw the parser away (unless you want to print out a usage
            message) and deal with a Hash? Why, for Pete's sake, should the parser
            and the parsed values be handled by two different objects?
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>You like command-line parsing, but you hate all of the bloat. Why
            should you have to create a Hash, then create a parser, fill the Hash
            out then throw the parser away (unless you want to print out a usage
            message) and deal with a Hash? Why, for Pete's sake, should the parser
            and the parsed values be handled by two different objects?</p>
            
            <p>Changes:</p>
            
            <h3>0.0.4 / 2008-06-06</h3>
            
            <ul>
            <li>Fixed typo in error message (thanks francois!)</li>
            </ul>
            
            
            <h3>0.0.3 / 2008-06-05</h3>
            
            <ul>
            <li>Merged technomancy's patches for simple 1 LOC parsing -> hash</li>
            </ul>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>RailsConf '08 Wrapup</title>
      <link>
        http://alexvollmer.com/posts/2008/06/03/railsconf-08-wrapup/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/06/03/railsconf-08-wrapup/#comments
      </comments>
      <pubDate>
        Tue, 03 Jun 2008 16:56:46 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <category>
        <![CDATA[
            rails
        ]]>
      </category>
      <category>
        <![CDATA[
            railsconf
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/06/03/railsconf-08-wrapup/
      </guid>
      <description>
        <![CDATA[
            RailsConf closed up last Sunday afternoon and after three-hour drive back and day of work to contemplate, here's what I've boiled it down to:
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>RailsConf closed up last Sunday afternoon and after three-hour drive back and day of work to contemplate, here's what I've boiled it down to:</p>
            
            <h1>The Good:</h1>
            
            <p><img src="http://farm4.static.flickr.com/3122/2547719330_0eae9343db_m.jpg" class="left"/></p>
            
            <p>I met a ton of people this year. Last year I went with a co-worker and we pretty much stuck together. This year I was on my own and made a concerted effort to just meet folks. By the end of the conference I couldn't go more than about fifteen minutes without running into someone I had previously met. I ordered a fresh batch of <a href="http://www.moo.com%20Moo">Moo</a> cards before I left and I was hell-bent to hand as many out as I could. Just meeting people turned out to be my favorite part of the conference.</p>
            
            <p>The Kent Beck address was fantastic. I've had the fortune earlier in my career to work at a company that had Kent come out and run XP workshops with us. Those experiences left a last impression on me (much like Chad Fowler expressed in the introduction) and so I was eager to hear his talk after seeing him on the roster. While his main content probably dragged for a bit, the Q &amp; A ended with a bang. The answer he gave to the final question expressing a mixture of hope and concern brought the crowd to its feet. Go on, Kent.</p>
            
            <p>There were a couple of presentations I went to that I though really knocked it out of the park. In some cases the material alone saved the day (in spite of the presenters) and in a few other cases the two came together nicely. I thought some of these presentations were particularly good:</p>
            
            <ul>
            <li>"Facebook Development and Performance with Rails" &#8211; Mike Mangino</li>
            <li>"The Launch: Do's and Don'ts of Real-life Deploys" &#8211; Chris Wanstrath</li>
            <li>"Assembling Pages Last: Edge Caching, ESI &amp; Rails" &#8211; Aaron Batalion</li>
            <li>"Skynet: A Ruby Map/Reduce Framework" &#8211; Adam Pisoni</li>
            <li>"Vertebra" &#8211; Ezra Zygmuntowicz</li>
            <li>"Advanced ActiveRecord Techniques: Best Practice Refactoring" &#8211; Chad Pytel</li>
            </ul>
            
            
            <h1>The Bad:</h1>
            
            <p>Sadly, a number of the presentations were pretty lacking. Now I think presenting is just plain hard and very few people are good at it. Keynote helps a bit, but really it's a crutch for people who don't have good public speaking skills (which I'm not necessarily claiming I have). Really exceptional content can help overcome the stylistic short-comings of a particular speaker, but I think that's rare. I think it's pretty easy to lose an audience quickly if your material can't shine in the way you present it.</p>
            
            <p>Since I have another year of Ruby and Rails experience under my belt, many more of the talks just really didn't do anything for me. That's why I'm psyched that the RailsConf team has decided to incorporate Caboose Conf as the "hallway track" in next year's meeting. I think that's the track I'll be taking next year. I did a lot of ingesting this year, next year I'd really like to produce more.</p>
            
            <h1>The Style Report:</h1>
            
            <p><img src="http://farm3.static.flickr.com/2089/2547838261_48efb49aa8_m.jpg" class="left"/></p>
            
            <p>Apparently the black t-shirt is king in the Rails community. Something like 99% of all attendees had black t-shirts on. Of the free t-shifts to be had, the GitHub tees were the only ones that weren't black. All of the others were red on black. So apparently the new black is, well, black. I'm hoping that nuclear orange makes a comeback next year. All-black is just a little too Depeche Mode for me.</p>
            
            <div style="clear: both;"></div>
            
            
            <h1>Portland:</h1>
            
            <p><a href="http://www.flickr.com/photos/75756154@N00/2549373887/"><img alt="Voodoo Doughnuts Parade" src="http://farm3.static.flickr.com/2332/2549373887_7b29066b2a_m.jpg class="left"/></a></p>
            
            <p>Portland deserves special mention because I just flat-out love that city. Besides Seattle, it's probably the only other city I would choose to live in. One of the highlights of experiencing the local flair was getting involved in the 1000-person "doughnut march" held by Portland's beloved <a href="http://voodoodoughnut.com/%20Voodoo%20Doughnuts">Voodoo Doughnuts</a>. Somehow they convinced officials to get a parade permit and police escort as they crossed from their Pearl District digs to their new shop in East PDX. It was a very "Portland" experience with a whole crowd of folks letting their freak-flags fly high.</p>
            
            <p><a href="http://www.flickr.com/photos/36455265@N00/2539344898/"><img alt="Bacon maple bar" src="http://farm3.static.flickr.com/2305/2539344898_ea077b65e6_m.jpg" class="right"/></a></p>
            
            <p>The big treat at the end was the bacon maple-bar. I'm not kidding folks, this is real and it's freakin' brilliant. So hear this Chad Fowler and the rest of the RailsConf committee, please don't move this to Vegas! Besides, can you imagine putting a bunch of hygienically-challenged nerds in 100-degree heat in a desert? That is simply not a good idea.</p>
            
            <div style="clear: both;"></div>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>1Password To Rule Them All...</title>
      <link>
        http://alexvollmer.com/posts/2008/05/26/1password-to-rule-them-all/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/05/26/1password-to-rule-them-all/#comments
      </comments>
      <pubDate>
        Mon, 26 May 2008 23:31:21 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            software
        ]]>
      </category>
      <category>
        <![CDATA[
            mac
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/05/26/1password-to-rule-them-all/
      </guid>
      <description>
        <![CDATA[
            I came across a fantastic piece of Mac software thanks to a <a href="http://twitter.com/hotdogsladies">Merlin Mann tweet</a>. The app is called <a href="http://agilewebsolutions.com/products/1Password">1Password</a> and, to paraphrase Mr. Mann's original tweet, it solves a problem I didn't even realize I had. Well...that's not entirely true. I knew that the various passwords I have scattered across the net weren't as secure as they could be. I had heard of master-password apps but hadn't really done much research on them. Now I'm not going to bother because 1Password so completely blew me away that I went ahead and bought the license (more on that in a bit).
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>I came across a fantastic piece of Mac software thanks to a <a href="http://twitter.com/hotdogsladies">Merlin Mann tweet</a>. The app is called <a href="http://agilewebsolutions.com/products/1Password">1Password</a> and, to paraphrase Mr. Mann's original tweet, it solves a problem I didn't even realize I had. Well...that's not entirely true. I knew that the various passwords I have scattered across the net weren't as secure as they could be. I had heard of master-password apps but hadn't really done much research on them. Now I'm not going to bother because 1Password so completely blew me away that I went ahead and bought the license (more on that in a bit).</p>
            
            <p>As you might suspect, 1Password is a master-password application. You can easily generate hard-to-crack passwords (like, '81239jsdfj912jlksdf8981'), but only have to remember one master-password. This in and of itself isn't particularly interesting (though the password-generation feature is really well done). What makes 1Password so compelling is how well it is integrated into your online activities. 1Password works with just about every kind of web browser you can imagine on a Mac.</p>
            
            <p>On Firefox, 1Password can install a toolbar that puts all of 1Password's features right there. When you navigate to a site for which you stored a password, you can automatically fill out a form with the click of a button. If you haven't re-authorized with 1Password in a bit (often after waking up my laptop) you have to give your master password again (nice touch boys).</p>
            
            <p><img src="/images/2008/05/200805261621.jpg" alt="200805261621.jpg" /></p>
            
            <p>Also whenever you submit a page that 1Password thinks is auth-related, it will offer to remember these credentials for you. When you update your credentials on a site, the app will ask if you want to update your existing credentials. That is absolutely killer. Clearly these guys understand the achilles' heel to most security systems--people are lazy and are the weakest link. Having such smooth, inline integration makes it difficult for even the lazy user to subvert their own security.</p>
            
            <p>1Password doesn't just live in your browser, it's also a nice stand-alone application. You can easily modify and update your secure information as well as import and export it (a handy way for me to sync my work and home machines).</p>
            
            <p>You're not just limited to passwords either. The app also comes with a notion of identities where you can specify the contact information you might need at various web sites. For myself, I keep both "work" and "personal" profiles. Whenever I have to sign up for something I can simply click the profile I want to use and 1Password fills out as many of the relevant form fields as it can. It also has a "wallet" feature where you can stuff various other secure information like credit card numbers (including the verification number on the back), AWS keys and host of other things. Very very very cool.</p>
            
            <p>Perhaps the most supremely cool feature of 1Password is the ability to sync encrypted javascript bookmarklets with your iPhone that gives you access to those same secret items on your iPhone! 1Password creates two special bookmarklets (synced via iTunes through your Safari bookmarks). One displays your passwords, the other will fill out web forms with with the selected credentials. Both require you to enter a password to unlock the bookmarklet, which you set when you export the bookmarklets from the application. Being able to get that secure information away from my computer makes sure that I don't set simple passwords for iPhone access just because I can't get to my secret stuff. Well done, boys, well done.</p>
            
            <p>The features of this app alone are enough to have sold me. But a good part of the impression this tool has made is how nicely it's been done: no sharp corners, wonderful usability, smooth integration. Even the experience of entering the license key is cool. Your license is an image file (no doubt with some extra steganographic goodies embedded) that you simply drag to a target area. No funny numbers to type. How freakin' cool is that?</p>
            
            <p><img src="/images/2008/05/license.jpg" alt="license.png" /></p>
            
            <p>I've got a whole 'nuther post cooking up about my master-list of indispensable nerd-tools, but I felt that this app was worth a standalone post. If you're on a Mac, do yourself a favor and check this app out.</p>
            
            <p>P.S. No, I wasn't paid a dime to write this.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Passing The Mom Test</title>
      <link>
        http://alexvollmer.com/posts/2008/05/24/passing-the-mom-test/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/05/24/passing-the-mom-test/#comments
      </comments>
      <pubDate>
        Sat, 24 May 2008 05:07:26 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            software
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/05/24/passing-the-mom-test/
      </guid>
      <description>
        <![CDATA[
            How many times have you excitedly tried to show your parents some project you're working on and gotten a confused or indifferent response? My reaction has often been to dismiss this disconnect as the result of a generation-gap, but a recent experience has led me to rethink that.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>How many times have you excitedly tried to show your parents some project you're working on and gotten a confused or indifferent response? My reaction has often been to dismiss this disconnect as the result of a generation-gap, but a recent experience has led me to rethink that.</p>
            
            <p>At <a href="http://www.evri.com%20evri">work</a>, we're getting very close to start unleashing our product on the world and so I was doing a little demo for my mom. At first this started to feel like other times when I've showed her things I've worked and she'll focus on some seemingly insignificant detail or re-imagines the application in terms that seem very specific to her world. I felt my usual generation-gap defenses warming up and I started to wrap up the demo. No point in going any further, right?</p>
            
            <p>But after a few more moments I was able to demonstrate enough compelling features that she was engaged. More importantly I became engaged in her reaction to the system. The barrier between us where each side couldn't really grok the other seemed to fall. I was able to have a better understanding of how she might look at it, instead of how I've been looking at for the past year and a half.</p>
            
            <p>So perhaps it's trite and obvious to state that one can benefit from giving yourself the time and patience to understand your users. But I think it's especially true when dealing with other generations. If you can come up with something that crosses those barriers like my impromptu demo did, by George you're probably onto something.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>clip version 0.0.2 has been released!</title>
      <link>
        http://alexvollmer.com/posts/2008/05/21/clip-version-002-has-been-released/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/05/21/clip-version-002-has-been-released/#comments
      </comments>
      <pubDate>
        Wed, 21 May 2008 04:35:32 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            clip
        ]]>
      </category>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/05/21/clip-version-002-has-been-released/
      </guid>
      <description>
        <![CDATA[
            You like command-line parsing, but you hate all of the bloat. Why
            should you have to create a Hash, then create a parser, fill the Hash
            out then throw the parser away (unless you want to print out a usage
            message) and deal with a Hash? Why, for Pete's sake, should the parser
            and the parsed values be handled by two different objects?
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>You like command-line parsing, but you hate all of the bloat. Why
            should you have to create a Hash, then create a parser, fill the Hash
            out then throw the parser away (unless you want to print out a usage
            message) and deal with a Hash? Why, for Pete's sake, should the parser
            and the parsed values be handled by two different objects?</p>
            
            <p>Changes:</p>
            
            <h3>0.0.2 / 2008-05-20</h3>
            
            <ul>
            <li>Cleaned up README</li>
            <li>Added support for late-binding option processing with blocks</li>
            </ul>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>The Great Music Backup</title>
      <link>
        http://alexvollmer.com/posts/2008/04/24/the-great-music-backup/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/04/24/the-great-music-backup/#comments
      </comments>
      <pubDate>
        Thu, 24 Apr 2008 03:58:41 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            geekery
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/04/24/the-great-music-backup/
      </guid>
      <description>
        <![CDATA[
            My to-do list is a mile-long and never ends. That thought can be quite depressing at times, but sometimes you finally get to tick something off of the list that has sat there so long that you can briefly enjoy a fleeting moment of smug self-satisfaction. I had just such a moment this week when my Great Big Music Backup to S3 finally completed.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>My to-do list is a mile-long and never ends. That thought can be quite depressing at times, but sometimes you finally get to tick something off of the list that has sat there so long that you can briefly enjoy a fleeting moment of smug self-satisfaction. I had just such a moment this week when my Great Big Music Backup to S3 finally completed.</p>
            
            <p>Why S3? Well, something as important as backing up my music (which hasn't come from a CD in about three years) means I need multiple copies. I certainly plan on getting a local backup happening soon, but having one out "in the cloud" provides a little extra comfort if things go totally haywire. Of course none of this would be possible if S3 weren't so darn cheap. By my estimates my 50GB music collection will cost me about $6/mo to backup. That's pretty cheap peace of mind if you ask me.</p>
            
            <p>I did the backup using excellent <a href="http://s3sync.net/wiki">S3Sync</a> utility. Every S3 tool has its own way of mapping a filesystem to S3's bucket system. I thought about using JungleDisk in headless mode, but that turned out to be more complicated than I was willing to deal with. The nice thing about S3sync is that it maps files to your buckets in a way that is compatible with the <a href="https://addons.mozilla.org/en-US/firefox/addon/3247">S3Fox</a> extension for Firefox. Most excellent.</p>
            
            <p>Running over SSL with my crippled Comcast connection it took about a full week to push all of that data out to S3. Thank goodness for Gnu's <a href="http://www.gnu.org/software/screen/">screen</a> utility which hosted the entire sync process. That's right kids, I did this all with a single invocation of s3sync.rb that ran for a solid week. Boo yah. Total upload cost was $3.58. Not too freakin' bad.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>clip version 0.0.1 has been released!</title>
      <link>
        http://alexvollmer.com/posts/2008/04/12/clip-version-001-has-been-released/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/04/12/clip-version-001-has-been-released/#comments
      </comments>
      <pubDate>
        Sat, 12 Apr 2008 03:41:45 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <category>
        <![CDATA[
            clip
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/04/12/clip-version-001-has-been-released/
      </guid>
      <description>
        <![CDATA[
            You like command-line parsing, but you hate all of the bloat. Why should you have to create a Hash, then create a parser, fill the Hash out then throw the parser away (unless you want to print out a usage message) and deal with a Hash? Why, for Pete&rsquo;s sake, should the parser and the parsed values be handled by two different objects?
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>You like command-line parsing, but you hate all of the bloat. Why should you have to create a Hash, then create a parser, fill the Hash out then throw the parser away (unless you want to print out a usage message) and deal with a Hash? Why, for Pete&#8217;s sake, should the parser and the parsed values be handled by two different objects?</p>
            
            <p>Introducing Clip...</p>
            
            <p>Checkout the details at <a href="http://clip.rubyforge.org%20Clip">http://clip.rubyforge.org</a>, or just dive right in and install that sucker as a gem.</p>
            
            <p>Changes:</p>
            
            <h3>0.0.1 / 2008-04-10</h3>
            
            <ul>
            <li>Initial release for y'all to throw rotten veggies at.</li>
            </ul>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>S3 Sync Update</title>
      <link>
        http://alexvollmer.com/posts/2008/04/05/s3-sync-update/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/04/05/s3-sync-update/#comments
      </comments>
      <pubDate>
        Sat, 05 Apr 2008 15:49:18 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            geekery
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/04/05/s3-sync-update/
      </guid>
      <description>
        <![CDATA[
            A while back <a href="http://blog.livollmers.net/index.php/2008/01/21/poor-mans-mac/">I wrote about my strategy for synchronizing between two machines</a> using Amazon's S3 and the JungleDisk tool. I just wanted to post a quick update that refines that strategy a bit. First, let me describe what needed improvement. I sync the <code>~/Documents</code> directory between my home and work MacBooks. However, on my home machine I have some extra files that really don't belong on my work machine (like Quicken files), so I have a small text file (called <code>sync_files</code>) that enumerates which sub-directories and file in ~/Documents are to be synchronized between the two machines.This all worked pretty well until I noticed duplicates of files appearing in different places. I realized that what had happened was that I had moved the files on one of the disks and then sync'd with S3. With my current scripts this resulted in copying the file to the new location, but not removing the old one.So with a quick glance at the <code>rsync</code> man-page, I found the <code>--delete</code> option. I refined my scripts and ran them. It all looked good--until I got home. Oops, I just lost a whole bunch of files. Uh-oh. It turns out I forgot to use the <code>sync_files</code> file for both directions. This was an easy tweak but reminded me of the Golden Rule of Rsync:
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>A while back <a href="http://blog.livollmers.net/index.php/2008/01/21/poor-mans-mac/">I wrote about my strategy for synchronizing between two machines</a> using Amazon's S3 and the JungleDisk tool. I just wanted to post a quick update that refines that strategy a bit. First, let me describe what needed improvement. I sync the <code>~/Documents</code> directory between my home and work MacBooks. However, on my home machine I have some extra files that really don't belong on my work machine (like Quicken files), so I have a small text file (called <code>sync_files</code>) that enumerates which sub-directories and file in ~/Documents are to be synchronized between the two machines.This all worked pretty well until I noticed duplicates of files appearing in different places. I realized that what had happened was that I had moved the files on one of the disks and then sync'd with S3. With my current scripts this resulted in copying the file to the new location, but not removing the old one.So with a quick glance at the <code>rsync</code> man-page, I found the <code>--delete</code> option. I refined my scripts and ran them. It all looked good--until I got home. Oops, I just lost a whole bunch of files. Uh-oh. It turns out I forgot to use the <code>sync_files</code> file for both directions. This was an easy tweak but reminded me of the Golden Rule of Rsync:</p>
            
            <blockquote><p> Always run <code>rsync</code> with <code>--verbose</code> and <code>--dry-run</code> to make sure it's doing what you think it's doing</p></blockquote>
            
            <p>So I decided it was time to re-write the script to support this. While you can do command-line options with bash, it quickly gets kinda oogy, so I fell back on Ruby instead. I've also collapsed the synchronizing down into a single script--one that goes both ways. So without further ado, you can download the script <a href="/wp-content/uploads/2008/04/sync_s3">here</a>. This should work with a stock Ruby install, no special gems required.</p>
            
            <p><em>Update 4/8/2008:</em> Okay, I still don't know what the hell I'm doing. There is a bug with this script in that if you create a new file locally then try to sync from S3, your new file will get obliterated. Well guess what kids? Synchronization is hard. I've been noodling on a variety of hacks to get around this but none are terribly satisfying. Anyway, my Golden Rule (see above) still stands: make sure you test the thing out before you run it "live".</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>PCI4R Update</title>
      <link>
        http://alexvollmer.com/posts/2008/03/23/pci4r-update/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/03/23/pci4r-update/#comments
      </comments>
      <pubDate>
        Sun, 23 Mar 2008 18:13:24 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <category>
        <![CDATA[
            pci4r
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/03/23/pci4r-update/
      </guid>
      <description>
        <![CDATA[
            We finally made some progress this week on the languishing <a href="http://github.com/alexvollmer/pci4r/tree/master%20pci4r%20on%20GitHub">pci4r</a> project. First, congrats to <a href="http://www.railsonwave.com/">Sandro Paganotti</a> for the first commit to pci4r--the prize is in the mail. This morning, after a bit of git-fiddling, I managed to get the second commit for the project in. It's code for document classification, which is the topic of Chapter 6 of Toby Segaran's <a href="http://www.oreilly.com/catalog/9780596529321/">"Programming Collective Intelligence"</a>. I deviated quite a bit from Toby's original code. In some cases this was simply a side-effect of porting from Python to idiomatic Ruby. In other cases though changes were made for simple aesthetic reasons.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>We finally made some progress this week on the languishing <a href="http://github.com/alexvollmer/pci4r/tree/master%20pci4r%20on%20GitHub">pci4r</a> project. First, congrats to <a href="http://www.railsonwave.com/">Sandro Paganotti</a> for the first commit to pci4r--the prize is in the mail. This morning, after a bit of git-fiddling, I managed to get the second commit for the project in. It's code for document classification, which is the topic of Chapter 6 of Toby Segaran's <a href="http://www.oreilly.com/catalog/9780596529321/">"Programming Collective Intelligence"</a>. I deviated quite a bit from Toby's original code. In some cases this was simply a side-effect of porting from Python to idiomatic Ruby. In other cases though changes were made for simple aesthetic reasons.</p>
            
            <p>In short, here's what you can do:</p>
            
            <div class="highlight"><pre><span class="n">c</span> <span class="o">=</span> <span class="no">Filtering</span><span class="o">::</span><span class="no">NaiveBayes</span><span class="o">.</span><span class="n">new</span>&#x000A;    &#x000A;    <span class="n">c</span><span class="o">.</span><span class="n">train</span><span class="p">(</span><span class="s2">&quot;Nobody owns the water&quot;</span><span class="p">,</span> <span class="ss">:good</span><span class="p">)</span>&#x000A;    <span class="n">c</span><span class="o">.</span><span class="n">train</span><span class="p">(</span><span class="s2">&quot;the quick rabbit jumps fences&quot;</span><span class="p">,</span> <span class="ss">:good</span><span class="p">)</span>&#x000A;    <span class="n">c</span><span class="o">.</span><span class="n">train</span><span class="p">(</span><span class="s2">&quot;buy pharmaceuticals now&quot;</span><span class="p">,</span> <span class="ss">:bad</span><span class="p">)</span>&#x000A;    <span class="n">c</span><span class="o">.</span><span class="n">train</span><span class="p">(</span><span class="s2">&quot;make quick money at the online casino&quot;</span><span class="p">,</span> <span class="ss">:bad</span><span class="p">)</span>&#x000A;    <span class="n">c</span><span class="o">.</span><span class="n">train</span><span class="p">(</span><span class="s2">&quot;the quick brown fox jumps&quot;</span><span class="p">,</span> <span class="ss">:good</span><span class="p">)</span>&#x000A;    &#x000A;    <span class="n">c</span><span class="o">.</span><span class="n">prob</span><span class="p">(</span><span class="s2">&quot;quick rabbit&quot;</span><span class="p">,</span> <span class="ss">:good</span><span class="p">)</span>  <span class="c1">#=&gt; ~ 0.156</span>&#x000A;    <span class="n">c</span><span class="o">.</span><span class="n">prob</span><span class="p">(</span><span class="s2">&quot;quick rabbit&quot;</span><span class="p">,</span> <span class="ss">:bad</span><span class="p">)</span>   <span class="c1">#=&gt; ~ 0.050`</span>&#x000A;    </pre>
            </div>
            
            
            <p>Here we create a new <code>NaiveBayes</code> classifier, train it with some text and then query it with other text. Nifty eh? There is another classifier included in the package called <code>Fisher</code>, which has a slightly more clever classification algorithm.</p>
            
            <p>Both of these default to in-memory storage of classification data. You can override it by using the built-in ActiveRecord persistence adapter like so:</p>
            
            <div class="highlight"><pre><span class="n">ar_config</span> <span class="o">=</span> <span class="no">Filtering</span><span class="o">::</span><span class="no">Persistence</span><span class="o">::</span><span class="no">ActiveRecordAdapter</span><span class="p">(</span>&#x000A;      <span class="ss">:adapter</span> <span class="o">=&gt;</span> <span class="s2">&quot;sqlite3&quot;</span><span class="p">,</span>&#x000A;      <span class="ss">:database</span> <span class="o">=&gt;</span> <span class="s2">&quot;mydb.sqlite3&quot;</span>&#x000A;      <span class="ss">:timeout</span> <span class="o">=&gt;</span> <span class="mi">5000</span>&#x000A;    <span class="p">)</span>&#x000A;    <span class="n">c</span> <span class="o">=</span> <span class="no">Filtering</span><span class="o">::</span><span class="no">NaiveBayes</span><span class="p">(</span><span class="n">ar_config</span><span class="p">)</span><span class="sb">`</span>&#x000A;    </pre>
            </div>
            
            
            <p>Finally, there's an executable in the 'bin' directory where you can interactively classify RSS feeds using either of the classifiers or persistence mechanisms provided. This relies on the
            <a href="http://feedtools.rubyforge.org/%20RDoc%20for%20feedtools">feed_tools</a> gem.</p>
            
            <p>So there it is. The rest of the pci4r team should be spooling up soon and hopefully we'll make some more progress. Stay tuned...</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>One Geek's Aesthetics</title>
      <link>
        http://alexvollmer.com/posts/2008/03/13/one-geeks-aesthetics/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/03/13/one-geeks-aesthetics/#comments
      </comments>
      <pubDate>
        Thu, 13 Mar 2008 15:36:01 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            geekery
        ]]>
      </category>
      <category>
        <![CDATA[
            information design
        ]]>
      </category>
      <category>
        <![CDATA[
            UX
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/03/13/one-geeks-aesthetics/
      </guid>
      <description>
        <![CDATA[
            I'm a fussy guy when it comes to fonts. I like them a certain way because I have strong opinions about which fonts look good and because I spend a lot of time throughout the day looking at them. I like being an avid reader, a software developer and a guy with 20/20 vision. As a result I spend more time than most fiddling with the fonts on my machine to get them just so.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>I'm a fussy guy when it comes to fonts. I like them a certain way because I have strong opinions about which fonts look good and because I spend a lot of time throughout the day looking at them. I like being an avid reader, a software developer and a guy with 20/20 vision. As a result I spend more time than most fiddling with the fonts on my machine to get them just so.</p>
            
            <p>At work I have a MacBook Pro hooked up to a large Dell flat-screen LCD monitor. The screen real estate is fantastic, but it has a weird effect on my terminal program. Here's a snapshot of what the Monaco font looks like when I start <a href="http://iterm.sourceforge.net/%20iTerm">iTerm</a> (or Terminal.app) while attached to the Dell:</p>
            
            <p><img src="/images/2008/03/200803130811.jpg" alt="200803130811.jpg" /></p>
            
            <p>Here's a screenshot of what iTerm looks like when I launch it without the Dell attached:</p>
            
            <p><img src="/images/2008/03/200803130812.jpg" alt="200803130812.jpg" /></p>
            
            <p>See how much thicker the letters are in the second shot? I much prefer the latter setup because it's easier on my eyes. Also I find the variation in the thickness of the lines (probably a result of anti-aliasing with the Dell) in the first screenshot distracting. But that's just my font-related <a href="http://en.wikipedia.org/wiki/Obsessive-compulsive_disorder">OCD</a> kicking in. It bugs me enough that if I have to start iTerm again, I'll unplug the Dell, wait for my Mac to figure out I have one screen, relaunch iTerm and then plug the monitor back in. Yup, it matters that much to me.</p>
            
            <p>It seems stereotypical that dudes who write Ruby on Macs love big fonts. Meeting other Ruby dudes with Macs was certainly my first exposure to terminals set to 18-point type. Before then I felt like 14 was pushing it. But then I realized that there really isn't that much I can look at at once. I used to be one of those guys that would get four terminals going simultaneously in 10-point type. Just look at me, I'd think to myself, I am soooooo productive. Now I'm on the other side of the fence. I'm a big-font guy.</p>
            
            <p>In part this is because I know that I can get distracted too easily. Having a bunch of stuff open at once only opens me up to more opportunities for something to steal my attention. These days I really don't need to exacerbate my ADD so cutting down on what I have to look at is generally a good thing. I've actually returned to using virtual desktops just to partition my running applications. Oh yeah, I turn off all those notifications too. It was fun for awhile, but now it's just irritating.</p>
            
            <p>I like to keep the fonts pretty big. I don't like reading from a screen nearly as much as I like reading from paper. I can feel a sort of mental fatigue set in after reading too much online. One way I've found to combat that fatigue is to simply increase the font-size. Honestly I can't read five paragraphs at once...I can read only read one so I really don't need to cram a bunch in a single space.</p>
            
            <p>I think I'm not alone in my preference for big bold, stripped-down fonts. I've seen an increasing number of web applications that go with a real stark, big-font look. One of my favorites is <a href="http://github.com%20GitHub!">GitHub</a>:</p>
            
            <p><img src="/images/2008/03/200803192007.jpg" alt="200803192007.jpg" /></p>
            
            <p>I think it's pretty clear what you're supposed to do here. Very little extraneous BS, just direct to the point. Give them a login and a password, they'll give you GitHub. It couldn't possibly be any simpler. Oftentimes you see the login box relegated to a corner of the screen with the remaining 80-90% devoted to some kind of market-ese with stock photos of trim, athletic, happy people who are ostensibly using the same product. What a total waste of space.</p>
            
            <p>Size isn't my only bone to pick with fonts. I'm also quite particular about which faces I like. In general I'm not a big fan of serif fonts. I find the clutter of the extra lines distracting, so I generally stick to sans-serif fonts as much as possible. Among sans-serif fonts I have two favorites: Monaco for fixed-width and Gill Sans for variable width.</p>
            
            <p>Fixed-width fonts are popular in programming because the code just lays out better to our eyes. It could be that with things like blocks and indentation, programmesr have a natural tendency to scan code horizontally and vertically in rows and columns. Vertical scanning would be much more problematic with a variable-width font.</p>
            
            <p>However for general reading, I like Gill Sans as a variable-width font. I like it so much that whenever I setup a new desktop, I go fiddle with the font settings in Firefox to get 'em the way I like 'em. I even go so far as to trump a site's stylesheets just to keep my font. Sometimes this results in pretty weird looking pages, but more often than not it turns out okay.</p>
            
            <p><img src="/images/2008/03/ff-prefs.jpg" alt="ff-prefs.png" /></p>
            
            <p>However, I realize that not everyone likes the same font I do. Because I do some web development I need to be able to view sites the same way as the poor unwashed masses view them. For that I take advantage of Firefox's profiles which allow me to have my font-fascist settings for general browsing and factory default font-settings for testing.</p>
            
            <p>So there it is. I like big fonts and I'm not ashamed to admit it. Sure it might look like I'm reading the code-equivalent of the large-print section from the library. But hey, I still have great vision and you small-font people aren't processing any more at once than I am.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Book Review: Information Dashboard Design</title>
      <link>
        http://alexvollmer.com/posts/2008/03/07/book-review-information-dashboard-design/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/03/07/book-review-information-dashboard-design/#comments
      </comments>
      <pubDate>
        Fri, 07 Mar 2008 22:15:32 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            book review
        ]]>
      </category>
      <category>
        <![CDATA[
            information design
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/03/07/book-review-information-dashboard-design/
      </guid>
      <description>
        <![CDATA[
            Dashboards are one of those phrases that the synergy-loving, tassle-loafer, khaki-loving business-types really like. I was worried at first that this book would be targeted more toward the executive officer crowd and less about helping designers. The first two chapters didn't ease my concerns with lots of discussion around designing for executive goals and summaries. However once we got past the introductory chapters, the book really picks up...
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p><a href="http://www.amazon.com/Information-Dashboard-Design-Effective-Communication/dp/0596100167%3FSubscriptionId%3D0PZ7TM66EXQCXFVTMTR2%26tag%3Dhttplivollmne-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D0596100167"><img src="/images/2008/03/information-dashboard-design.jpg" class="left"/></a>Stephen Few's <em>Information Dashboard Design</em> is the latest in a series of books I've been reading about visual design and the display of quantitative information. It's a surprisingly heavy book for being relatively skinny (~200 pp), but is printed on gorgeous high-quality paper. This O'Reilly book feels much more like a coffee-table book than the usual animal-cover books for which they are famous.</p>
            
            <p>Dashboards are one of those phrases that the synergy-loving, tassle-loafer, khaki-loving business-types really like. I was worried at first that this book would be targeted more toward the executive officer crowd and less about helping designers. The first two chapters didn't ease my concerns with lots of discussion around designing for executive goals and summaries. However once we got past the introductory chapters, the book really picks up...</p>
            
            <h2>Chapter 3: Common Mistakes</h2>
            
            <p>This chapter highlights a litany of common dashboard design mistakes. These aren't just limited to visual tips, but also includes important concepts like providing context through comparison and scaling displayed data appropriately. The examples given here are fairly egregious examples of bad dashboard design, but acclimate the reader to critical review.</p>
            
            <h2>Chapter 4: Tapping Into The Power of Visual Perception</h2>
            
            <p>This is where the fun begins as you learn some of the science behind visual perception. Few describes how shape, position, color, hue and encapsulating marks all affect how we process and perceive data visually. This is one of three chapters that a designer will return to again and again for review.</p>
            
            <h2>Chapter 5: Eloquence Through Simplicity</h2>
            
            <p>Here we head to a particularly Tufte-ian territory with the consistent refrains of "maximizing data ink" and "removing non-data marks". While the words make sense, the concepts are driven home with some great examples. My favorite part of this chapter was the rectangle depicting which screen regions get the most attention and why. After you see it, it's obvious and yet you'll easily be able to recall many examples you've seen that violate these principles.</p>
            
            <h2>Chapter 6: Effective Dashboard Display Media</h2>
            
            <p>The party continues with a detailed survey of the most common graphical visual idioms. The common chart styles like line plots, bar graphs and scatter plots are explored as well as the more exotic ones such as radar graphs, stem-plots and even strategic icon placement. This chapter serves as a great foundation for the final chapters where you will use your newly-acquired visual vocabulary to perform a detailed critique of a number of sample dashboards. Oh and if you take nothing else from this book, remember this: <em>pie-charts are useless.</em></p>
            
            <h2>Chapter 7: Designing for Usability</h2>
            
            <p>This is the shortest chapter of the book, but the two great takeaways from this chapter are encouraging useful comparisons and avoiding senseless ones. If unrelated data-sets are placed too closely or use the same visual representation, it may encourage meaning comparison. Conversely using different styles for data that <em>should</em> be compared simply makes the user's job harder.</p>
            
            <p>Few dives into a few tips around making an esthetically pleasing dashboard without resorting to decorative gew-gaws that clutter the display. In particular things such as using a muted color palette, designing for high resolution and picking the right fonts (a particular sticking point for me) are all ways to be informative without distracting the user.</p>
            
            <h2>Chapter 8: Putting It All Together</h2>
            
            <p>This is easily the best part of the book. However I wouldn't suggest skipping the other chapters in a effort to get to the payoff. I think the reader would best be served by reading this book in sequential order because the principles and concepts covered in the earlier chapters help to give the reader a visual vocabulary for what comes in this chapter.</p>
            
            <p>The begins by showing a dashboard the author designed designed for a competition designing dashboards for a mock business. The dashboard is concise, dense without being cluttered and draws focus to important items quickly. In contrast the author shows eight other dashboards that contestants submitted for the same mock business. Each one has a number of defects, some easy to find and others less so. It's definitely worth studying these dashboards with a careful eye prior to reading the author's criticism.</p>
            
            <p>The chapter ends with three more dashboards targeted to other audiences: the CIO, tele-sales and marketing. Each shows off the other motifs highlighted in earlier chapters. However they all have a consistent style--one that is clear, concise, dense but uncluttered. The prospective designer would be well-advised to spend some quality time studying these examples to distill them into principles for their own designs. This chapter really caps the book off well.</p>
            
            <p><em>Finally...</em></p>
            
            <p><em>Information Dashboard Design</em> is a well-written book full of useful information. The high-level principles are well-described and the catalog of details motifs make this book worth having.</p>
            
            <p>4 out of 5 stars.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Green Fields</title>
      <link>
        http://alexvollmer.com/posts/2008/03/04/green-fields/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/03/04/green-fields/#comments
      </comments>
      <pubDate>
        Tue, 04 Mar 2008 15:44:07 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            geekery
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/03/04/green-fields/
      </guid>
      <description>
        <![CDATA[
            Ahhh...a fresh new server install. It's like getting the first squeeze of toothpaste or the first scoop of peanut butter. It feels especially good because it's all yours. It brims with potential and has no marks of anyone else's will upon it. Setting a server for yourself is liberating because there are no constraints, no corporate policies to adhere to. Right or wrong, you get to call all the shots and take all the responsibility. You finally get to do what you always dremed of when that sneering, omnipotent system administrator show down all your ideas. Petty bureacracy will not stand in your way!
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>Ahhh...a fresh new server install. It's like getting the first squeeze of toothpaste or the first scoop of peanut butter. It feels especially good because it's all yours. It brims with potential and has no marks of anyone else's will upon it. Setting a server for yourself is liberating because there are no constraints, no corporate policies to adhere to. Right or wrong, you get to call all the shots and take all the responsibility. You finally get to do what you always dremed of when that sneering, omnipotent system administrator show down all your ideas. Petty bureacracy will not stand in your way!</p>
            
            <p>I'm waxing poetically because I just acquired my first <a href="http://www.slicehost.com%20Slicehost">Slicehost</a> account for a side-project I'm working on. I've dozens upon dozens of Linux installs over the years, why should this be special? Perhaps it's worth a trip down memory-lane first...</p>
            
            <p>If you discount those early years writing BASIC for the Apple II and TRS-80, I've been living Unix/Linux longer than I've been programming. In that time I've gotten rather particular about my distros and how I like to configure them. I first cut my teeth on HP-UX working at a wireless telco. Some co-workers there introduced me to Linux which like having an equivalent HP-UX-like interface on a 486 under my desk at home.</p>
            
            <p>Like many, my first distro was the tower of floppies known as <a href="http://www.slackware.com/%20Slackware">Slackware</a>. Ah, the good old days. When setting up X might have entailed destroying your monitor if you didn't get the parameters right. When I started doing actual development I moved over to <a href="http://www.redhat.com/%20Red%20Hat">Red Hat</a> because it was the most polished distro at the time. After that I had a brief flirtation with <a href="http://www.novell.com/linux/%20SuSE">SuSE</a>, but found the configuration frustrating although I did have a major success getting dial-up internet working via number of arcane Hayes modem commands and some network scripts I found on the web.</p>
            
            <p>Then I discovered <a href="http://www.mandriva.com/%20Now%20Mandriva">Mandrake</a>, which I stuck with for a number of years. It was Red Hat-based, but had a much better installer and better package manager. In the end though, even the package management improvements could not overcome the inherent flaws of RPM. On countless occasions I would upgrade and the entire sweater of package dependencies would unravel and suddenly I need to upgrade to a new version of glibc just to get a decent RSS reader working in KDE. Ugh.</p>
            
            <p>It was then that my most Linux-geeky friend turned me on to <a href="http://www.gentoo.org/%20Gentoo">Gentoo</a>. You will learn more about linux kernels and configuration running Gentoo than you ever imagined. Mind you, I didn't get into Gentoo because I wanted to hyper-optimize my install of emacs. I switched because Gentoo's package management and configuration beat the snot out of Mandrake and I found the ebuild system rather elegant.</p>
            
            <p>Unfortunately Gentoo took a tool on my patience with Linux on the desktop. Gentoo required far more care and feeding that I could give it. I spent far more time building and re-building my system than actually doing anything with it. I will point out though, that doing a bare-metal Gentoo install all the way to a full-blown desktop manager like KDE or Gnome has to be about the best hardware burn-in test there is.</p>
            
            <p>So then I hopped on the <a href="http://www.ubuntu.com/%20Ubuntu">Ubuntu</a> band-wagon. While there are far too many ways to install packages (apt-get? dpkg? adept? wirble?), the Debian package management is a nice compromise between pre-built package systems like RPM and configurability (a la Gentoo). So that's what I've put on my new Slicehost host. It's well-documented, has tremendous community support behind and is much more up to date than it's older, conservative sibling, Debian.</p>
            
            <p>So back to Slicehost. So far the experience has been tremendous. I would say that less than a minute elapsed between the time I decided to give them a credit card number and when I got a console login. They have several pre-built server images you can use to provision your slice. And now it's mine...all mine. I get to set it up just the way I like.</p>
            
            <p>So what's in the soup, you may ask?</p>
            
            <p><em>Web Server:</em> <a href="http://nginx.net/%20nginx">nginx</a></p>
            
            <p>I've done a lot of Apache over the years and it's hard to overestimate the impact that it has had on the web. However, it's configuration has become nightmarish. In this case my needs are pretty narrow, so I'll go with something easier to work with. Plus it has a rad name.</p>
            
            <p><em>App Server:</em> <a href="http://www.rubyonrails.org/%20Ruby%20on%20Rails">Rails!</a></p>
            
            <p>I hope I never have to do Java-based web development again. As far as dynamic language go, I've thrown my hat in with Ruby rather than Python, although I hear great things about Django and I'd like to play with it. There are certainly other viable Ruby web frameworks (Merb, Camping, ...) but for this project I'm more interested in getting things done than learning a new web-stack.</p>
            
            <p><em>Database Server:</em> <a href="http://www.mysql.com/%20MySQL">MySQL</a></p>
            
            <p>I really really really wanted to do this with <a href="http://www.postgresql.org/%20PostgreSQL">Postgres</a> since it has just about the best command-line tool I've seen for any RDBMS. I also find the permission model in MySQL to be byzantine and difficult to debug. But alas, scaling out to multiple nodes with read/write master/slave and replication is much more do-able in MySQL than Postgres. There's a ton of existing literature out there on it and I've had <a href="http://www.wetpaint.com%20WetPaint">experience doing it</a>.</p>
            
            <p><em>Mail:</em> <a href="http://www.postfix.org/%20Postfix">Postfix</a></p>
            
            <p>Eventually this app will need to support incoming mail so simple MTA's like exim or esmtp weren't going to cut it. Since I'm deathly afraid of Sendmail, I figured I'd go with Postfix. This is probably the tool I know the least about right now, but it should be interesting to learn.</p>
            
            <p><em>Monitoring:</em> <a href="http://www.tildeslash.com/monit/%20monit">monit</a> or <a href="http://god.rubyforge.com/%20god">god</a></p>
            
            <p>I've used other commercial and open-source monitoring tools and they've all felt really heavy-weight and invasive. I don't so much mean "heavy-weight" in a CPU-sense, but in a process and procedure sense. Looking at the sample configurations for both god and monit put a smile on my face. god looks particularly interesting because it's in Ruby.</p>
            
            <p><em>Text Search:</em> <a href="http://lucene.apache.org/solr/%20Solr">solr</a>, <a href="http://ferret.davebalmain.com/trac%20Ferret">ferret</a>, <a href="http://www.sphinxsearch.com/%20Sphinx">sphinx</a></p>
            
            <p>I've spent a little time looking at all of these solutions to supported inverted-index, full-text search support. I'm not 100% sure where (or if) this app will need full-text search so this area is a little bit hazy. I'm not committed to a particular implementation technology here (i.e. running Solr which is in Java is totally viable).</p>
            
            <p><em>Message Queue:</em> .....</p>
            
            <p>I'm more sure that we'll eventually need to have asynchronous, queue-based work processing to handle things like image resizing and storage or document generation. Like the text search, running something like a Java-based message queue (like <a href="http://activemq.apache.org/%20Active%20MQ">ActiveMQ</a>) is entirely possible. A major piece of the selection criteria will be based on ease of integration.</p>
            
            <p>So we'll see how this goes. I run my own Linux box at home to host this blog, a Subversion repo and couple of other things. It's nice to have a virtual equivalent sitting on much fatter pipes. I'm excited to see where this goes.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Fat Proxies and the Danger of Reuse</title>
      <link>
        http://alexvollmer.com/posts/2008/02/19/fat-proxies-and-the-danger-of-reuse/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/02/19/fat-proxies-and-the-danger-of-reuse/#comments
      </comments>
      <pubDate>
        Tue, 19 Feb 2008 01:50:25 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            UX
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/02/19/fat-proxies-and-the-danger-of-reuse/
      </guid>
      <description>
        <![CDATA[
            UIs are, essentially, collections of widgets. These widgets act as visual and manipulative proxies between the user and the underlying conceptual model. If we think of the ultimate UI as being one which minimizes the mental distance between what the user wants to do and what they have to do to accomplish it, perhaps the ideal interface would be like the fictional jet-fighter, <a href="http://imdb.com/title/tt0083943/">Firefox</a>. What could possibly be more direct than thinking, fire that missile, and seeing that missile burst forth from under the wing? Unfortunately, since most of us work on slightly less fantastic technologies than those portrayed in the movie, we have to figure how to work best within our milieu. This is the real challenge of what we do. How can we take the crude medium of computer software and diminish the distance between thinking and doing?
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>UIs are, essentially, collections of widgets. These widgets act as visual and manipulative proxies between the user and the underlying conceptual model. If we think of the ultimate UI as being one which minimizes the mental distance between what the user wants to do and what they have to do to accomplish it, perhaps the ideal interface would be like the fictional jet-fighter, <a href="http://imdb.com/title/tt0083943/">Firefox</a>. What could possibly be more direct than thinking, fire that missile, and seeing that missile burst forth from under the wing? Unfortunately, since most of us work on slightly less fantastic technologies than those portrayed in the movie, we have to figure how to work best within our milieu. This is the real challenge of what we do. How can we take the crude medium of computer software and diminish the distance between thinking and doing?</p>
            
            <p>Yesterday I checked out a site that plays in the same space as a project I'm working on. These sorts of things are bit like going to the dentist, you don't necessarily look forward to it, but you do it because it's good for you. It's not that I'm trying to ignore what is out there, but for a side-project there is a certain bliss in being ignorant of what else is out there--especially when there really isn't any money at stake. But I was a good boy and checked it out. I went in fearing the worst--that these guys would have nailed the concept and the interface, and there was simply no reason to put any more time on our version. After about fifteen minutes I was pretty convinced that we could come up with a more compelling user experience.</p>
            
            <p>Like mom always said, if you don't have anything nice to say, say it anyway--just don't name names. So let me try to provide some detail on what I found without tipping my hand. This app had all the right rounded-corner-drop-shadow-muted-blue patina that any self-respecting Web 2.0 app should. But digging a bit under the surface revealed a pretty face on what was essentially a Windows for Workgroups-era application mentality. The user flow felt like it was driven from the underlying relational data model, not necessarily from sensible use cases. As I filled out wizard form after wizard form, I couldn't help imagining each one mapping to a particular table in a database. In addition, each form had several tabs with a mix of required and optional features to be identified by the user. The tabs were clearly meant to further sub-categorize the input, however because of the limited real-estate in a tab, only single-word descriptions were available. I had to click on each tab to see if I needed fill anything out in there or not. Too much clicking to satisfy the data model, not enough payoff for the user to keep it up.</p>
            
            <p>Then I ran into something called "templates". I can't help but wonder if templating concepts aren't a smell rather than a feature. In the case of this application, it wasn't really clear what the benefits of templates would be. Normally templates are there to avoid repeating work and providing a model of re-use. Looking at this "feature", I couldn't help but wonder why I should care. If templates are so damn important, do I really want to use an app in which templates are a necessity? Again, too much work, no clear payoff. Humans serve the machine. Bah!</p>
            
            <p>So why would somebody put something like templates in? The answer is re-use. Software developers simply love re-use wherever they can get it. I can imagine the developers and product team sitting around reviewing this complicated system they've built and finally having to address the fact that the complexity makes it hard to use. Templates to the rescue! We can reduce the user's work-load with reuse! Ugh. It's only an economic win if I really care to continue the app. Otherwise the real problem lies in the original interaction model.</p>
            
            <p>The Golden Age of Perfect Software is nowhere on the horizon so building software will continue to be a type of gruesome sausage-making for the foreseeable future. One of the great lies about software development is the false economics of reuse. The theory goes like this: if applications can be built by assembling more-powerful, higher-level components together, the overall cost of software development should decrease. At face value this theory makes a lot of sense. I'm sure many of us can think of time we have wasted (or seen wasted) on building rounder wheels. Why should anyone spend time working on a library of sorting algorithms when for 99.999% of all cases the sorting routines available with your platform are adequate?</p>
            
            <p>The problem is that the component-based thinking doesn't always match the needs of the application. Sure we may have built it more cheaply than if we had written it from scratch, but we have to ask ourselves how much did we compromise in that effort? Software is a notoriously tricky business and the number of failures greatly outweigh the number of successes. Google wasn't built in one shot. Heck, the first version of Google Reader was so badly panned, they threw it away and wrote another one. Apple is on the tenth major version of the OS software and fifth major revision within the latest family. Let's not kid ourselves, we're much more likely to get it wrong than get it right. So let's just assume that it's possible that none of the available, reusable components is going to suffice for your needs. That doesn't have to be your starting assumption, but we have to keep the possibility in the back of our minds.</p>
            
            <p>This leads to a second point I'd like to make about re-use. Many UI toolkits come with a pretty rich array of widgets that are used to build nearly every application out there. This is true of native clients as it is of web applications. If our goal is to minimize the conceptual distance between the user's mental model of the application (remember, the developer's mental model is almost irrelevant) and what they need to do to meet that model, then restricting ourselves only to what the widget palette offers will be fraught with compromise. Developers may see applications built as collections of widgets, but users often don't. It's quite possible that using the "standard" widgets for your application greatly increases the mental distance between thinking and doing for the user. When this happens the user is serving the application--not the other way around. Uh oh. Wrong turn. Back up. Try again.</p>
            
            <p>Don't misunderstand me. I'm not saying that every application has to be written from the ground up because there is no hope in getting the interaction right by reusing off-the-shelf components. That's ridiculous. We'd never get anything done if we approached our apps like that. Rather, my point is that we can't necessarily limit ourselves to the widget sets and conventions of our platforms. In doing so we may leave the user out in the cold. However, visual or interactive exceptionalism for the sake of differentiation invites similar risk. Dressing up your app to stand out is akin to putting lipstick on a pig. If you app is compelling, dressing it up will be unnecessary.</p>
            
            <p>Let's return to our anonymous whipping boy...er, sample web site. Not only did the wizards and multiple-layers of object hierarchy force me to learn the tool's model (one I don't care about), but the expression of those wizards (the widgets) similarly required me (the user) to play along, rather than getting me to the payoff. Some applications are very sophisticated and will require more of the user, but that's not the case for this one. Indeed the selling point is how easy it is to use and how user-focused it really was! Boo, hiss. F-.</p>
            
            <p>So while the wizards had a very consistent and recognizable look and feel to them, they did a horrible job of getting me to where I wanted to be. I can't help but feel that this application was built with a primary focus on a relational data model and component reuse. This ended up with fat proxies between me and what I wanted to get done--an increase in the mental distance between thinking and doing. This is the real shame. Increasing that distance is the primal sin of application design.</p>
            
            <p>One final note: a lot of the inspiration for this post came from a <a href="http://katidev.com/blog/wp-trackback.php?p=20%20Computer%20Administrative%20Debris">fantastic post</a> by Cathy Shive on, what she terms, "Administrative Debris". I interpreted "debris" as that which increases this conceptual distance I keep harping on. Her post, unlike mine, provides a lot more visual examples to get the point across. I would highly recommend reading her article to get another angle on these ideas.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Shopping for Men</title>
      <link>
        http://alexvollmer.com/posts/2008/02/11/shopping-for-men/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/02/11/shopping-for-men/#comments
      </comments>
      <pubDate>
        Mon, 11 Feb 2008 17:37:15 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            apple
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/02/11/shopping-for-men/
      </guid>
      <description>
        <![CDATA[
            <img src="/images/2008/02/photo-2.jpg" class="left">
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p><img src="/images/2008/02/photo-2.jpg" class="left"/></p>
            
            <p>This is all I walked out of the local Apple Store with today. No unnecessary bag. No receipt I have to dispose of securely. Just the product I wanted and a friendly little "Thank you" sticker. If all stores were like this, I might actually like to shop.</p>
            
            <p>The local Apple Store also switched over to a cool new portable POS system where just about any sales associate can swipe your credit-card and allow you to make the purchase there. They must have been paying attention to customer complaints because before the remodel, the lines to the cash register were absolute heinous.</p>
            
            <p>This was fantastic. I walked in, a sales associated asked what I was looking for. I told her and she immediately directed me to the available products. I picked one and she took to me another guy who had one of these little POS thingies. I gave him my card, he asked if I wanted my receipt e-mailed to me and I was out the door in less than two minutes. In the time it took my wife to pick out a wedding present I walked a block to the Apple Store, completed this transaction and made it back to where she was before she was ready to go. Fantastic.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Washington Caucuses</title>
      <link>
        http://alexvollmer.com/posts/2008/02/10/washington-caucuses/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/02/10/washington-caucuses/#comments
      </comments>
      <pubDate>
        Sun, 10 Feb 2008 18:53:04 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            politics
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/02/10/washington-caucuses/
      </guid>
      <description>
        <![CDATA[
            This past week has seen Washington State abuzz with the excitement over Saturday's Caucuses. For the Democrats especially, this is an exciting time since the two candidates are running neck and neck and in the Evergreen State the Caucus is for all the marbles. So Saturday we headed over the local church for our precinct Caucus which was an absolute madhouse. There was a record turnout for the Caucuses and my personal experience certainly validated that.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>This past week has seen Washington State abuzz with the excitement over Saturday's Caucuses. For the Democrats especially, this is an exciting time since the two candidates are running neck and neck and in the Evergreen State the Caucus is for all the marbles. So Saturday we headed over the local church for our precinct Caucus which was an absolute madhouse. There was a record turnout for the Caucuses and my personal experience certainly validated that.</p>
            
            <p>Our particular location covered six precincts. I wish I had taken a snapshot of the precinct signup--it was utter chaos. I have no idea if there were any Fire Marshalls in the crowd, but they wouldn't have been happy with what they saw. However, I did manage to get a snapshot of our precinct's meeting:</p>
            
            <p><img src="/images/2008/02/img-0178.jpg" alt="IMG_0178.JPG" /></p>
            
            <p>This picture doesn't really do justice to the size of the crowd. Our precinct had over sixty people register (though not all of them stayed for the meeting). This was my first Caucus and it was certainly an interesting and eye-opening experience. It was completely disorganized--our precinct head wasn't there so we had to elect one. What we lacked in direction was made up for in earnest desire to pick the best candidate for the Democrats.</p>
            
            <p>Like most of Washington State Democrats, our precinct was overwhelmingly for Barack Obama. That's not to say that it was a room full of Hilary-haters. Indeed many Obama supporters voiced their pledge to support Clinton if she wins the nomination. However two themes emerged as people pled the case of Obama.</p>
            
            <p>The first was that the sense of change that Senator Obama would bring to the nation is one that is desperately needed. The second is that having Senator Clinton in the Whitehouse would lead to four years of fractious fighting with the Republicans which would prevent the nation from addressing the stack of dire issues we face.</p>
            
            <p><img src="/images/2008/02/img-0183.jpg" class="right"/></p>
            
            <p>After we voted a couple of times, the final tally was held. We then split into the two camps. The Clinton camp needed to select one delegate to send to the city caucus and the Obama camp needed four delegates. At this point my wife and daughter left because Audrey, bless her heart, hung in like a trooper but was starting to fade. A Tootsie-pop can only take a kid so far. I stuck around for what I thought would be a lengthy delegate-selection process but what, in fact, only took about ninety seconds to decide. We selected four delegates and four backups in record time which sort of made up for the previous hour's worth of bumbling.</p>
            
            <p>We'll see how the race turns out as this primary/caucus season unfolds, but it was certainly exciting to participate in the process and feel like it mattered.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>A Survey of Super Tuesday Infographics</title>
      <link>
        http://alexvollmer.com/posts/2008/02/06/a-survey-of-super-tuesday-infographics/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/02/06/a-survey-of-super-tuesday-infographics/#comments
      </comments>
      <pubDate>
        Wed, 06 Feb 2008 05:23:46 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            information design
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/02/06/a-survey-of-super-tuesday-infographics/
      </guid>
      <description>
        <![CDATA[
            On Super Tuesday, I forwent attending the usual Seattle Ruby Brigade meeting and stayed at home glued to the radio and TV keeping up on the primary and caucus results across the nation. I love the Public Radio/TV talking heads, but I was really lacking the overall picture. So I warmed up the Internet tubes and started searching for some helpful at-a-glance snapshot of the state of Super Tuesday.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>On Super Tuesday, I forwent attending the usual Seattle Ruby Brigade meeting and stayed at home glued to the radio and TV keeping up on the primary and caucus results across the nation. I love the Public Radio/TV talking heads, but I was really lacking the overall picture. So I warmed up the Internet tubes and started searching for some helpful at-a-glance snapshot of the state of Super Tuesday.</p>
            
            <p>Here then, is an amateur's critique of the various infographics I tripped across. What I was looking for was something that would tell me:</p>
            
            <ul>
            <li> Margin of victory for each candidate</li>
            <li> Percentage of precincts reporting</li>
            <li> The number of delegates available</li>
            <li> The number of delegates each candidate won</li>
            <li> Clear indication of races that have finished, and those that have not.</li>
            <li> I want as much information as I can get in a small space</li>
            <li> I don't want to navigate through data</li>
            </ul>
            
            
            <h2>The New York Times</h2>
            
            <p>So let's start with New York Times. This tabular graphic came from the New York Times front page:</p>
            
            <p><img src="/images/2008/02/picture-2.png" alt="Picture 2.png" /></p>
            
            <p>This one came the closest to the goals I was looking for. The Times doesn't waste any space on images of either the candidates or the states themselves (something a lot of other graphics couldn't avoid). I get just about everything I'm looking for on my list, except for the delegates.</p>
            
            <p>From the front page, I followed the "Full Coverage" link to this pictorial table which shows the breakdown by state. The conversion of images to black and white for candidates that have dropped out is a nice visual touch. I don't mind this too much because the table would still be quite readable without them. This table is essentially the same information as the previous one, except that it shows all of the states and has the additional bonus of indicating states whose primaries haven't happened yet.</p>
            
            <p><img src="/images/2008/02/picture-3.png" alt="Picture 3.png" /></p>
            
            <p>And then, of course, we have the ubiquitous geographic map. This view was predominant one across most of these sites. Many sites touted these things as "interactive" which amazes me that it could be considered a possible selling point. I'll take "informative" over "interactive" any day of the week thank you.</p>
            
            <p><img src="/images/2008/02/200802052027.jpg" alt="200802052027.jpg" /></p>
            
            <h2>The Washington Post</h2>
            
            <p>This little tidbit was at the bottom of each party's news column on the <a href="http://www.washingtonpost.com/wp-dyn/content/politics/index.html">Washington Post Politics page</a>. I'm not so hot on the geographic map, but the fact that it was placed at the bottom of a text column instead dominating the page (as many of the maps do) earns this a few points.</p>
            
            <p><img src="/images/2008/02/picture-5.png" alt="Picture 5.png" /></p>
            
            <p>Next, we turn to the Post's "Super Map", which is a glut of hyperactive mouse-oriented popups. It looks promising, but the popup comments came up too easily and weren't easily dismissed. While this map gives us some notion of the delegates each state has, I can't tell which candidate got what portion of delegates. Also states that have declared winners don't show any margin of victory details. This map takes up the entire screen and doesn't really earn the space it takes up.</p>
            
            <p><img src="/images/2008/02/200802052031.jpg" alt="200802052031.jpg" /></p>
            
            <p>At the bottom of the map was a little trend-line chart for the Democrats. Care to guess when Edwards dropped out of the race?</p>
            
            <p><img src="/images/2008/02/2008020520311.jpg" alt="200802052031.jpg" /></p>
            
            <h2>National Public Radio</h2>
            
            <p>This is the table from the front page of National Public Radio's page. This table gives me most of the things I'm looking for with no space wasted on unnecessary graphics. While we get delegate information, the lack of percentages is unfortunate. This is the only tabular display I found that ordered the per-state results in the chronological order in which the polls will close.</p>
            
            <p><img src="/images/2008/02/picture-7.png" alt="Picture 7.png" /></p>
            
            <p>This table was a nice compact little sidebar on NPR's Super Tuesday coverage page. This is probably the best delegate information I found out of any of the sites surveyed here.</p>
            
            <p><img src="/images/2008/02/picture-8.png" alt="Picture 8.png" /></p>
            
            <h2>CNN</h2>
            
            <p>I have to come clean and admit that I simply can't stand CNN. It's always on and has so little useful content. The election coverage I've seen to-date has some of the glitziest, lowest-density graphics in their live broadcasts of any I've seen. The touch displays are merely a cover up for the fact that the content is empty and newscasters cannot improv. However, I have to give CNN credit with this tabular display. Not fantastic, but at least no wasted space on graphics.</p>
            
            <p><img src="/images/2008/02/picture-9.png" alt="Picture 9.png" /></p>
            
            <p>However, this table falls short on a number of counts. The biggest problem is the need to paginate through results. Requiring mouse-overs to get popups on a map is one thing, but clicking through a list of data to find what you're looking for is simply inexcusable. This is a particularly obnoxious example of failing to making the data dense enough.</p>
            
            <p>From the "Full Coverage" link:</p>
            
            <p><img src="/images/2008/02/picture-10.png" alt="Picture 10.png" /></p>
            
            <p>I find the stylized map insulting. The northern border of the United States is not a flat. The state borders don't line up neatly in rows. Not only does this map have the same faults of the others (low data density, too much interaction required), it's also simply wrong.</p>
            
            <h2>USA Today</h2>
            
            <p>The USA Today made it's name in glitzy, over-wrought graphics and typing the URL in the browser bar I fully expected some useless 3D pie-charts and other such non-sense. I was pleasantly surprised to find this box on the front page:</p>
            
            <p><img src="/images/2008/02/picture-11.png" alt="Picture 11.png" /></p>
            
            <p>This is a surprisingly informative view. The densely-packed links dispense with state navigation via map. I don't mind navigation here so much because I can quickly get to any state. I don't have paginate through results and I don't have to float over states whose shapes I can't remember to find them. Instead, clicking on a well-known state abbreviation link in the navigation bar gives you a nice detailed breakdown:</p>
            
            <p><img src="/images/2008/02/picture-12.png" alt="Picture 12.png" /></p>
            
            <p>The inclusion of the advertisement in the outlined space of the first graphic works to draw your attention, but is really a turn-off. It's only because I was giving each site one click that I continued navigating through the site. Normally, I would have left the site for that reason. To be fair, I understand the these sites have to support themselves with advertising. But putting an ad in the middle of a graphic depicting who we pick as our president in one of the most critical times of our history really cheapens the subject being covered.</p>
            
            <h2>MSNBC</h2>
            
            <p>Sigh...</p>
            
            <p><img src="/images/2008/02/picture-13.png" alt="Picture 13.png" /></p>
            
            <p>By now you've probably figured out that I think the inclusion of visual geographic information is superfluous. The state images take a third of each state's space for information. Other than the letter and color, which indicate the candidate and party, this space doesn't help me. I can tell that the box is about California because it is titled "California" at the top. The inclusion of state images seems like a weak attempt to "spice up" the presentation of the data.</p>
            
            <p>This graphic gives us the usual percentages, including the percentage of precincts reporting, and the total delegates which is helpful. However, since each state apportions delegates differently, we can't really tell how important a margin of victory is in a particular state. I can't really harp on MSNBC for this, nobody included that level of detail in the graphic summaries.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>High Resolution</title>
      <link>
        http://alexvollmer.com/posts/2008/02/02/high-resolution/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/02/02/high-resolution/#comments
      </comments>
      <pubDate>
        Sat, 02 Feb 2008 19:44:26 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            information design
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/02/02/high-resolution/
      </guid>
      <description>
        <![CDATA[
            <img src="/images/2008/02/img-01721.jpg" class="left">
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p><img src="/images/2008/02/img-01721.jpg" class="left"/></p>
            
            <p>This week I tripped across <a href="http://www.edwardtufte.com/bboard/iphone-video.adp%20ET%20and%20the%20iPhone">Edward Tufte's review of the iPhone interface</a>. His focus in the video, as it is in his workshops and books, is on data density and high resolution. In short, when you have a high resolution surface, you should take advantage of it by providing high data density.</p>
            
            <p>In this vein, I had a small epiphany yesterday around this very feature of the iPhone. I had a paired-programming interview with a candidate yesterday who needed 'net access. At work, we have a public access-point for this very situation. However to get on the network requires credentials which were posted on the wall in a different room. So I walked over to the other meeting room and tried to take a mental snapshot of the information that was posted on the wall. Uh-oh, the password was all in <a href="http://en.wikipedia.org/wiki/Leet%20l33t">l33t-speak</a> and I was not going to be able to retain that in my short-term buffer on the walk back to the other room.</p>
            
            <p>So I pulled my iPhone out of my pocket to write the SSID and password down. But then I thought to myself, "Wait a sec. Why go through that many layers of translation?" What I really wanted was to take the paper off the wall and back to the other room, but then I'd have to carefully pull the tape off the wall and then remember to put the paper back. Yuck. Too much effort.</p>
            
            <p>So I took a picture. That's it. So simple. The most direct translation of the information from one location to another. I snapped the photo, made the twenty-foot journey back to the interview room and just laid the phone down on the table. The interviewee was able to easily read the words off of the iPhones high-res screen and we were off and running.</p>
            
            <p>Perhaps this is an indictment of the poor quality of your typical embedded camera. Perhaps it's an indictment of our low expectations of that technology. We've simply come to expect crappy photos that, at best, outline an image but could never actually provide any detail. This was different. You could actually read the words clearly. It was such a simple thing to transport that information with a picture, but powerful all the same.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Geek Manners</title>
      <link>
        http://alexvollmer.com/posts/2008/02/01/geek-manners/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/02/01/geek-manners/#comments
      </comments>
      <pubDate>
        Fri, 01 Feb 2008 17:44:56 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            software
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/02/01/geek-manners/
      </guid>
      <description>
        <![CDATA[
            A couple of days ago I was fly-on-the-wall for what can only be described as a great example of Geek Manners. One definition of a "geek" is one whose intense interest in a specific topic is offset by a lack of standard social skills. That doesn't mean that all geeks are completely oblivious to social customs, just that they invent their own at times. Below is a transcript (from memory) of the conversation I heard.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>A couple of days ago I was fly-on-the-wall for what can only be described as a great example of Geek Manners. One definition of a "geek" is one whose intense interest in a specific topic is offset by a lack of standard social skills. That doesn't mean that all geeks are completely oblivious to social customs, just that they invent their own at times. Below is a transcript (from memory) of the conversation I heard.</p>
            
            <p><em>Developer A</em>: You broke the build last night because the blah blah blah source wasn't in UTF-8.</p>
            
            <p><em>Developer B</em>: No, I always save source in UTF-8.</p>
            
            <p><em>Developer A</em>: Well I don't know what to tell you...you broke the build.</p>
            
            <p>Dev B frowns and there is a notable increase in tension...</p>
            
            <p><em>Dev B</em>: Hmmm, I don't think so, but I'll check my settings--</p>
            
            <p><em>Dev A</em>: Yeah you need to go to your Preferences and--</p>
            
            <p><em>Dev B</em>: Yeah, I know where that is! I've probably done more with character encoding than just about anyone around here!</p>
            
            <p><em>Dev A</em>: I'd have to disagree there.</p>
            
            <p><em>Dev B</em>: Oh yeah? Well have you supported <impossibly large number> character set conversions over <exaggerated number> of document formats?</p>
            
            <p><em>Dev A</em>: Ha. I've done <grossly increased> <made up conversion technologies> over <another suspect number> of <obscure widget names>.</p>
            
            <p>Let the games begin! It's clear at this point that I'm observing what can only be described as the developer-equivalent of two rams head-butting on a mountain top for geek supremacy. There is a long pause as the tension hangs...</p>
            
            <p><em>Dev B</em>: OK...yeah, I see the encoding is set to Mac Roman. Let me fix that and re-commit.</p>
            
            <p>Now Dev B finds an opening and subtly deflects aggression from himself to Apple...</p>
            
            <p><em>Dev B</em>: That's so typical of Macs, they have to have their own encoding for everything.</p>
            
            <p>Dev A picks up on the clue and ritual of commiseration through technical criticism begins...</p>
            
            <p><em>Dev A</em>: I hear you. Doesn't it just drive you crazy when <deeply technical complaint about things you can't really change>?</p>
            
            <p><em>Dev B</em>: Yeah. It reminds me of the time when...</p>
            
            <p>At this point our two devs have moved into the closing stage of their conversation, deftly moving from challenge and response to an uneasy acknowledgement of pack order. Both showed their horns and both stood their ground, then they made up by talking for another twenty-five minutes about their various horror stories around character encoding.</p>
            
            <p>To be sure, there was still the regular one-upsmanship in the story-telling phase, but the atmosphere had tangibly improved. What was fascinating about this final exchange was the need for the long ramp-down of story-swapping as way of expressing, "I'm sorry I was short with you. I think you're a smart guy and I hope you think I'm one too".</p>
            
            <p>Quite frankly, I amazed that any of us ever get married at all.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Poor Man's .mac</title>
      <link>
        http://alexvollmer.com/posts/2008/01/21/poor-mans-mac/
      </link>
      <comments>
        http://alexvollmer.com/posts/2008/01/21/poor-mans-mac/#comments
      </comments>
      <pubDate>
        Mon, 21 Jan 2008 20:47:32 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            mac
        ]]>
      </category>
      <category>
        <![CDATA[
            apple
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2008/01/21/poor-mans-mac/
      </guid>
      <description>
        <![CDATA[
            The discontent with Apple's .mac service seems to be <a href="http://www.43folders.com/2008/01/18/mac-future-sleeping-giant%20.Mac:%20Future%20of%20a%20sleeping%20giant?%20%7C%2043%20Folders">growing</a> and <a href="http://www.43folders.com/2006/09/28/dot-mac-lameness%20LifeClever:%20Dot%20Mac%20needs%20more%20than%20a%20paint%20job%20%7C%2043%20Folders">growing</a>. I've looked at .mac a couple of times but couldn't really find a good reason to get on board. Oh sure it has some nice features like syncing contacts, but honestly most of the features .mac offers I don't or can get in other ways. In short it's hard to imagine ponying up $99 for this service when it can be beaten with other tools.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>The discontent with Apple's .mac service seems to be <a href="http://www.43folders.com/2008/01/18/mac-future-sleeping-giant%20.Mac:%20Future%20of%20a%20sleeping%20giant?%20|%2043%20Folders">growing</a> and <a href="http://www.43folders.com/2006/09/28/dot-mac-lameness%20LifeClever:%20Dot%20Mac%20needs%20more%20than%20a%20paint%20job%20|%2043%20Folders">growing</a>. I've looked at .mac a couple of times but couldn't really find a good reason to get on board. Oh sure it has some nice features like syncing contacts, but honestly most of the features .mac offers I don't or can get in other ways. In short it's hard to imagine ponying up $99 for this service when it can be beaten with other tools.</p>
            
            <p>Let's start by looking at what features .mac offers. According to the <a href="http://www.apple.com/dotmac/%20Apple%20-%20.Mac">.mac website</a> the features are:</p>
            
            <ul>
            <li>Web Gallery</li>
            <li>Website Hosting</li>
            <li>IMAP Mail</li>
            <li>Back To My Mac</li>
            <li>Sync</li>
            <li>iDisk</li>
            <li>Groups</li>
            <li>10GB Storage</li>
            </ul>
            
            
            <p>OK, the first two I can handle easily with my little Linux box. IMAP isn't a compelling feature as I've had a GMail address since its inception and really don't need another email to watch. Back To My Mac is kind of interesting, but honestly I really haven't had a need to do this.</p>
            
            <p>Now the Sync feature is very interesting. Right now my contacts sync (some of the time) between my work and home machines via my iPhone plugging into iTunes. I didn't intend for this to be a solution to syncing calendars and contacts so I'm not too bummed when it does odd things. Regardless, .mac wouldn't help me with calendars where the ultimate source of authority for my schedule is Google's Calendar system. More on this in a bit&hellip;</p>
            
            <p>Groups. Uh, unless I don't get this correctly, isn't this what Yahoo! and Google offer <em>for free</em>? No thanks Apple, not interested.</p>
            
            <p>iDisk is completely uninteresting to me. I use Amazon's S3 along with the brilliant <a href="http://jungledisk.com/%20JungleDisk%20-%20Reliable%20online%20storage%20powered%20by%20Amazon%20S3%20%E2%84%A2%20-%20Jungle%20Disk">JungleDisk tool</a> and, even with a license for JungleDisk, is seriously cheaper (and much bigger) than .mac's storage options. 10MB is a paltry amount. So let's start with how I solved this problem&hellip;</p>
            
            <h2>File Storage</h2>
            
            <p>So since I only need a fraction of .mac's features, I've found cheaper ways to get the same functionality. The first is scoring a JungleDisk license ($20). When you fire it up it simply creates a file-like volume on the desktop (and in <code>/Volumes</code>) that adheres to regular file-system semantics. Acting like a real file-system means I can use the <code>rsync</code> utility (which is you can installed via <a href="http://www.macports.org/%20The%20MacPorts%20Project%20--%20Home">MacPorts</a>) to easily update either my local machine or my S3 account.</p>
            
            <p>I have two scripts to sync files: one to sync from my machine to S3 and one to sync the other way. Both my work and home machines have these scripts on them. So a common usage scenario is using my work machine all day then executing the <code>sync_to_s3</code> script before I go home. When I get home I can simply run <code>sync_from_s3</code> and get the latest changes on to my home machine. If I make changes to those files on my home machine, I can run <code>sync_to_s3</code> once more at home and the next morning run <code>sync_from_s3</code> on my work machine.</p>
            
            <p>Currently the only files I sync through S3 are all in my <code>~/Documents</code> directory, though it would be easy to sync other files. However the <code>~/Documents</code> directories on my two machines have some differences between them. For example my Quicken data is on my home machine which is something I don't need to sync back and forth between my two machines. So to lock down exactly which files I want sync, I create a little file named <code>sync_files</code> that enumerates which files I want to sync. Additionally the <code>sync_files</code> is also synchronized so that I only have to update it one place.</p>
            
            <p>So here are the scripts. The first is <code>sync_to_s3</code>:</p>
            
            <div class="highlight"><pre><span class="c">#!/bin/sh</span>&#x000A;    rsync --recursive --size-only <span class="se">\</span>&#x000A;      --files-from ~/Documents/sync_files <span class="se">\</span>&#x000A;      ~/Documents/ /Volumes/JungleDisk/documents/&#x000A;    </pre>
            </div>
            
            
            <p>The <code>sync_files</code> file is simply a list of matching files and directories to sync. Mine looks like this:</p>
            
            <div class="highlight"><pre>books&#x000A;    cheatsheets&#x000A;    markdown&#x000A;    OmniFocus.ofocus&#x000A;    presentations&#x000A;    resume&#x000A;    screencasts&#x000A;    specifications&#x000A;    whitepapers&#x000A;    work&#x000A;    sync_files&#x000A;    </pre>
            </div>
            
            
            <p>Next is the <code>sync_from_s3</code> which goes the other way.</p>
            
            <div class="highlight"><pre><span class="c">#!/bin/sh</span>&#x000A;    rsync  --recursive --size-only <span class="se">\</span>&#x000A;      /Volumes/JungleDisk/documents/ ~/Documents/&#x000A;    </pre>
            </div>
            
            
            <p>Note that in the second script I don't refer to the <code>sync_files</code> file. That's because the only way files end up on S3 is via the <code>sync_to_s3</code> script which already limits what files get uploaded. I could use the <code>sync_files</code> file to sync <em>from</em> S3, but if the <code>sync_files</code> were updated on S3 I wouldn't get the changes until my second sync.</p>
            
            <p>One thing to be aware of is a new feature in JungleDisk that, if enabled, will wreak havoc on this setup. Under the 'Jungle Disk Plus' settings, be sure to disable the checkbox marked 'Only upload changed portions of large files'. This absolutely wrecked the sync-ing process for my <a href="http://www.omnigroup.com/applications/omnifocus/%20The%20Omni%20Group%20-%20OmniFocus">OmniFocus</a> document. Given how cheap S3 is, this is an utter non-concern for me.</p>
            
            <h2>Calendars</h2>
            
            <p>Since I'm unwilling to give up my Google Calendar setup and there is no built-in support for sync-ing with them, I needed some kind of tool that can play in both worlds. Fortunately the <a href="http://spanningsync.com/%20Spanning%20Sync%20-%20Sync%20iCal%20and%20Google%20Calendar">Spanning Sync</a> tool does exactly this. It's not exactly cheap at $60, but does a good job and, up until recently, it was the only game in town. Another tool has emerged in this space called <a href="http://blog.busymac.com/blog/2008/01/busymac-announc.html%20BusyBlog:%20BusyMac%20announces%20BusySync%202.0:%20Sync%20iCal%20with%20Google%20Calendar">BusySync</a> which claims to do the same thing. Since I have a Spanning Sync license, I haven't tried this tool out. But it's about half the cost of Spanning Sync and probably worth a look.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>The Java Conundrum</title>
      <link>
        http://alexvollmer.com/posts/2007/12/30/the-java-conundrum/
      </link>
      <comments>
        http://alexvollmer.com/posts/2007/12/30/the-java-conundrum/#comments
      </comments>
      <pubDate>
        Sun, 30 Dec 2007 20:41:22 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            java
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2007/12/30/the-java-conundrum/
      </guid>
      <description>
        <![CDATA[
            <a href="http://steve-yegge.blogspot.com/2007/12/codes-worst-enemy.html%20Stevey's%20Blog%20Rants:%20Code's%20Worst%20Enemy">Steve Yegge's latest blog post</a> was one that really struck a chord with me personally. I think he hit most of the nail right on the head in describing a common frustration with the Java language. It seems like every attempt to refine the language ends up as some controversial, inelegant hack that often reduces the readability of the code. The same day I read that post I came across <a href="http://www.javac.info/bloch-closures-controversy.ppt%20Josh's%20PowerPoint%20Presentation">Josh Bloch's presentation on Java closures</a>. This was my first introduction to closures in Java-land and my first reaction was one of horror. This is supposed to enhance the expressiveness of the language? Sheesh, not as far as I can tell&hellip;
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p><a href="http://steve-yegge.blogspot.com/2007/12/codes-worst-enemy.html%20Stevey's%20Blog%20Rants:%20Code's%20Worst%20Enemy">Steve Yegge's latest blog post</a> was one that really struck a chord with me personally. I think he hit most of the nail right on the head in describing a common frustration with the Java language. It seems like every attempt to refine the language ends up as some controversial, inelegant hack that often reduces the readability of the code. The same day I read that post I came across <a href="http://www.javac.info/bloch-closures-controversy.ppt%20Josh's%20PowerPoint%20Presentation">Josh Bloch's presentation on Java closures</a>. This was my first introduction to closures in Java-land and my first reaction was one of horror. This is supposed to enhance the expressiveness of the language? Sheesh, not as far as I can tell&#8230;</p>
            
            <p>Steve talks about the size of your Java codebase and just touches on IDEs, but I&#8217;d like to explore that aspect a bit. I&#8217;ve been a dedicated Eclipse user for about five years. Before that I used NetBeans and before that Emacs. These days I think the three major IDEs (Eclipse, NetBeans and IntelliJ) are essentially equivalent. They all have great code completion, integrated refactoring tools and built-in unit-testing support. By the time I switched to Eclipse my Java projects were reaching a magnitude that <em>required</em> these sorts of tools. Switching back to plain old Emacs would have been possible, but a very counter-productive move. In essence, our projects mandate a certain set of tools in order to achieve some minimal baseline of productivity. There is no going back.</p>
            
            <p>Maybe this isn&#8217;t a bad thing. I&#8217;ve certainly dismissed my fair share of kooks who live by the &#8220;desert-island&#8221; coding theory where any developer should be prepared to have to work on thirty-year old servers with no UI system and where only <code>edlin</code> is available. That&#8217;s nuts. We <em>have</em> evolved. BUT&#8230;is Java <em>requiring</em> additional evolution that we otherwise wouldn&#8217;t need? Like Steve&#8217;s point about dependency injection, are we creating new sacred cows whose existence is justified only by the requirements of working with Java?</p>
            
            <p>I&#8217;m a big fan of dependency injection, but when I started working more seriously in Ruby I realized that it was a concept that had much less worth simply because of how the language works. So while the concepts behind projects like Spring are admirable, the amount of knowledge required to use these tools (all in the name of finding a shortcut around a bunch of manual work), seems to exceed any benefit as far as I can see. It seems that the evolution of Java has required additional complexity to manage the previous rounds of complexity we were trying to get around in the first place. A conundrum indeed.</p>
            
            <p>Java seems to be suffering from a serious case of feature-envy and lacks a firm hand to guide its future. Java 5 brought a few helpful (and sometimes controversial) features, but since then I&#8217;ve started to tune Java out. The JCP process seems to have devolved into feature-addition-by-committee process where, if any sort of quorum can be achieved, any features can make it as a JSR. As other languages have gained popularity and offered developers alternatives in expression, many have tried to evolve Java to &#8220;keep up&#8221;. I would put forth that this is both a disservice to the language and its users.</p>
            
            <p>Right, wrong or indifferent, Java has a point of view of how the world should work. Everything in Java is an <code>Object</code> and every object is of some defined <code>Class</code>. There are almost no literal expressions in Java so just about everything you want to do in Java must be carries out as methods invoked on objects. Many of the new language extensions seem counter to this original design and show all the signs of a conceptual impedance mismatch. Extensions like closures are trying to act like anonymous functions where we don&#8217;t really care about the type. Oh, except that we do. Well, but we don&#8217;t want to. Yeah, but we have to.</p>
            
            <p>As a result, Java has evolved from a relatively simple and consistent language (at least compared to C++) to one that requires programmers to understand Java's entire <a href="http://eawc.evansville.edu/essays/brown.htm%20EAWC%20Essay:%20Storytelling,%20the%20Meaning%20of%20Life,%20and%20The%20Epic%20of%20%20Gilgamesh">Tale of Gilgamesh</a> before they can be trusted with the language. I can&#8217;t imagine how they teach how Java 6 works in introductory programming classes. Instructors must be faced with innumerable choices where they trade completeness for clarity. Given the tome-like size of the <a href="http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html%20AngelikaLanger.com%20-%20Java%20Generics%20FAQs%20-%20Frequently%20Asked%20Questions%20-%20Angelika%20Langer%20Training/Consulting">Generics FAQ</a>, I can&#8217;t see how anyone could feel comfortable sicking this subject on the young tender minds of new programming students.</p>
            
            <p>It just seems to me that whatever stewards there are for the Java language have been fairly hands-off in setting or restricting the direction of the language. Perhaps Java would be better off accepting the fact that it will, at best, play the role of an equal among a number of peers and that it would best sustain itself by adhering to its core design principles and allow other languages to evolve their features to meet needs that Java can&#8217;t adequately satisfy.</p>
            
            <p>To paraphrase <a href="http://www.baseball-reference.com/bullpen/Chico_Escuela%20Chico%20Escuela%20-%20BR%20Bullpen">Chico Escuela</a>, &#8220;Java has been bery bery good to me.&#8221; Java has sustained a nearly decade-long career in a profession I love. For me though, I&#8217;m pretty sure that my current gig will be my last Java gig. My attention to and love for the language has waned. I&#8217;ve found alternatives that work better for the majority of problems I have to deal with. In short, the thrill is gone.</p>
            
            <p>There are few tools in my toolbox that are timeless must-haves. I am a self-described &#8220;tool whore&#8221;. If I can find a tool that gives me a significant amount of productivity gain (one that exceeds the investment effort), I&#8217;ll gladly switch to them. I was a long-time dedicated Linux user, but switched to Mac when I found that the effort to maintain a Linux laptop exceeded the energy I was willing to put into it. I haven&#8217;t rejected Linux, I&#8217;ve simply put it in a place in my technical life where I can best take advantage of it.</p>
            
            <p>I feel the same way about languages. While I have called myself a &#8220;Java developer&#8221; for a number of years, I&#8217;ve come to learn that I&#8217;m really a software engineer whose tool of late has been Java. Given the constant change within this field, it is simply self-destructive to assume that I can sustain a long-term career with the same set of tools and skills. I firmly believe that a good developer has to be aware of what else is going on in the world and must constantly evaluate and adapt new technologies and approaches. In short, we must evolve or face irrelevancy and, ultimately, professional extinction.</p>
            
            <p>Today I&#8217;m doing more Ruby and I hope to be doing more Ruby professionally for a number of years. But I expect that in a few years some new language will emerge that will address the future shortcomings of Ruby. Who would assume that any one tool/language/operating system has really got it right? It&#8217;s <em>never</em> happened in the history of technology. At best, we&#8217;ve had systems that did better than their competitors. I&#8217;ll even go so far as to say that I don&#8217;t dismiss the possibility that Microsoft may come up with an operating system or development environment that beats the alternatives. It would require quite a lot to overcome my personal biases, but I won&#8217;t say it couldn&#8217;t happen.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Yet More RSpec Fun With TextMate!</title>
      <link>
        http://alexvollmer.com/posts/2007/12/16/yet-more-rspec-fun-with-textmate/
      </link>
      <comments>
        http://alexvollmer.com/posts/2007/12/16/yet-more-rspec-fun-with-textmate/#comments
      </comments>
      <pubDate>
        Sun, 16 Dec 2007 03:08:38 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <category>
        <![CDATA[
            TDD
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2007/12/16/yet-more-rspec-fun-with-textmate/
      </guid>
      <description>
        <![CDATA[
            Why is it that I just can&rsquo;t leave well enough alone? Here, after several weeks for forehead-impact conditioning, I <em>finally</em> get a working setup with Ruby on Leopard with RSpec and TextMate. Life is good, I have my pretty spec runner window back, I&rsquo;m a BDD&rsquo;ing fool. But oh no, I have to keep fiddling with stuff.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>Why is it that I just can&#8217;t leave well enough alone? Here, after several weeks for forehead-impact conditioning, I <em>finally</em> get a working setup with Ruby on Leopard with RSpec and TextMate. Life is good, I have my pretty spec runner window back, I&#8217;m a BDD&#8217;ing fool. But oh no, I have to keep fiddling with stuff.</p>
            
            <p>So I&#8217;m finally putting some foundation work into a nifty little webapp that a (childhood) buddy of mine and I are working on. &#8220;Hey&#8221;, I think to myself, &#8220;why not make it a Rails 2.0 app? Better yet, why not get yer RSpec on too?&#8221; Brilliant. Why not? What could possibly go wrong? Well after installing the trunk versions of the RSpec plugin for Rails (of course) my beloved RSpec bundle stopped working. :-(</p>
            
            <p>After a little digging around (including the OS X equivalent of <code>printf</code> debugging, <a href="http://growl.info/%20Welcome%20to%20Growl!">Growl</a>) I realized that I&#8217;m probably facing incompatibility issues with the TM bundle and the version of RSpec. Now keep in mind that I&#8217;ve got two different RSpec-based projects that I&#8217;m using the bundle with. One is a straight-up lib-and-spec directory app (the <a href="http://svn.livollmers.net/public/pci/%20/pci">Programming Collective Intelligence port</a>), and the other is this new Rails app. The former was using version 1.0.8 of the gem, while the latter is using the plugin code that I installed.</p>
            
            <p>So I go back to the <a href="http://rspec.rubyforge.org/tools/extensions/editors/textmate.html%20TextMate">RSpec bundle that the RSpec folks put out</a> and lo and behold it works like a champ for my Rails app&#8230;but not so much for the standalone project. Oh dear. Now what? Like <a href="http://www.youtube.com/watch?v=r-qkpsygNYo%20YouTube%20-%20Flutie%20Hail%20Mary">Doug Flutie vs. Miami</a> I chuck a Hail Mary and run <code>sudo gem update rspec</code>&#8230;</p>
            
            <pre><code>Updating installed gems...&#x000A;    Attempting remote update of rspec&#x000A;    Successfully installed rspec-1.1.0&#x000A;    1 gem installed&#x000A;    Installing ri documentation for rspec-1.1.0...&#x000A;    Installing RDoc documentation for rspec-1.1.0...&#x000A;    Gems: [rspec] updated&#x000A;    </code></pre>
            
            <p>Yayyy! A new version! Now the RSpec bundle for TextMate works in both projects. So, the final recipe goes a little something like this:</p>
            
            <ol>
            <li>Install the latest bleeding-edge RSpec plugin for Rails</li>
            <li>Install the RSpec version of the TextMate bundle</li>
            <li>Make sure the latest gem (1.1.0) is installed.</li>
            </ol>
            
            
            <p>OK, now has anyone seen my productivity around here? I&#8217;d swear I saw it just before Halloween&#8230;</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Putting Leopard Back Together</title>
      <link>
        http://alexvollmer.com/posts/2007/12/15/putting-leopard-back-together/
      </link>
      <comments>
        http://alexvollmer.com/posts/2007/12/15/putting-leopard-back-together/#comments
      </comments>
      <pubDate>
        Sat, 15 Dec 2007 05:50:01 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <category>
        <![CDATA[
            mac
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2007/12/15/putting-leopard-back-together/
      </guid>
      <description>
        <![CDATA[
            Ever since I upgraded to Leopard my wonderful RSpec TextMate bundle simply stopped working. This may not have been a result of the Leopard upgrade per se, but things went seriously south about the same time. Between the day after Leopard was released and now I&rsquo;ve been tearing my hair out trying to figure just what went wrong. Finally, last night, I capped off several weeks of frustrating exploration by getting the damn bundle working again.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <h2>RSpec and TextMate</h2>
            
            <p>Ever since I upgraded to Leopard my wonderful RSpec TextMate bundle simply stopped working. This may not have been a result of the Leopard upgrade per se, but things went seriously south about the same time. Between the day after Leopard was released and now I&#8217;ve been tearing my hair out trying to figure just what went wrong. Finally, last night, I capped off several weeks of frustrating exploration by getting the damn bundle working again.</p>
            
            <p>My big &#8220;A-Ha&#8221; moment was figuring out that there are, apparently, <em>two</em> RSpec bundles for TextMate. One is hosted at RubyForge by the rspec guys and another that is hosted at Macromates. I had installed the one from RubyForge which gave me a host of odd errors. When I finally realized that there were two parallel projects and I installed the Macromates one, I was back in business. Also, another problem that I think I had was upgrading to the latest rspec version (1.0.8), though downgrading didn&#8217;t seem to help.</p>
            
            <p>So to get this working I first had to troll around for any existing RSpec bundles that I had installed. All of the HOWTOs I read had me install the bundle (via subversion) into <code>~/Library/Application Support/TextMate/Bundles</code>. My original RSpec bundle wasn&#8217;t there but was installed in <code>~/Library/Application Support/TextMate/Pristine Copy/Bundles</code>. Another location to check for is <code>/Library/Application Support/TextMate/Bundles</code> (note the lack of &#8216;~&#8217;).</p>
            
            <h2>RubyGems and RDoc</h2>
            
            <p>My other outstanding irritant in the post-Leopard world was the sudden disappearance of the RDoc of the gems I&#8217;ve installed from my beloved <code>gem_server</code>. I love the <code>ri</code> command and use it extensively, but being able to search around and expand the source from within a browser is <em>extremely</em> useful for me. For example, I&#8217;m still in the process of gaining fluency with RSpec and to be able to browse around to see what&#8217;s available is absolutely crucial to any kind of productivity while I&#8217;m still in a steep learning curve.</p>
            
            <p>After trolling around it appears that once you&#8217;ve taken the step to upgrade RubyGems (the dreaded <code>sudo gem update --system</code>) it&#8217;s all about setting up the <code>GEM_HOME</code> and <code>GEM_PATH</code> variables. There seem to be a couple of ways to do this: you can set this up in the shell configuration file of your choice (e.g. <code>~/.bashrc</code> for bash users, <code>~/.zshrc</code> for zsh users, etc.) <em>and/or</em> you can do this (on Mac) by entering these in the <code>~/.MacOSX/environment.plist</code>.</p>
            
            <p>My <code>environment.plist</code> looks like this:</p>
            
            <div class="highlight"><pre><span class="cp">&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;</span>&#x000A;      <span class="nt">&lt;dict&gt;</span>&#x000A;        ...&#x000A;        <span class="nt">&lt;string&gt;</span>/opt/local/lib/libtidy<span class="nt">&lt;/string&gt;</span>&#x000A;        <span class="nt">&lt;key&gt;</span>GEM_HOME<span class="nt">&lt;/key&gt;</span>&#x000A;        <span class="nt">&lt;string&gt;</span>/Library/Ruby/Gems/1.8<span class="nt">&lt;/string&gt;</span>&#x000A;        <span class="nt">&lt;key&gt;</span>GEM_PATH<span class="nt">&lt;/key&gt;</span>&#x000A;        <span class="nt">&lt;string&gt;</span>/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8<span class="nt">&lt;/string&gt;</span>&#x000A;      <span class="nt">&lt;/dict&gt;</span>&#x000A;    <span class="nt">&lt;/plist&gt;</span>&#x000A;    </pre>
            </div>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Close the Laptop, Dude</title>
      <link>
        http://alexvollmer.com/posts/2007/12/10/close-the-laptop/
      </link>
      <comments>
        http://alexvollmer.com/posts/2007/12/10/close-the-laptop/#comments
      </comments>
      <pubDate>
        Mon, 10 Dec 2007 03:11:11 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            personal
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2007/12/10/close-the-laptop/
      </guid>
      <description>
        <![CDATA[
            I've got a million projects. My GTD list is a mile long with home and personal geek projects. But some weekends you need to just put all that aside and give yourself a play-day. I spent my Sunday bundled up watching the Seahawks wrap up their fourth consecutive NFC West title. Not a bad way to spend a day.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>I've got a million projects. My GTD list is a mile long with home and personal geek projects. But some weekends you need to just put all that aside and give yourself a play-day. I spent my Sunday bundled up watching the Seahawks wrap up their fourth consecutive NFC West title. Not a bad way to spend a day.</p>
            
            <p><a href="http://flickr.com/photos/livollmers/sets/72157603416336668/" title="IMG_0120.JPG by livollmers, on Flickr"><img src="http://farm3.static.flickr.com/2173/2099831208_3831ed3f9a_m.jpg" alt="IMG_0120.JPG" /></a></p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Ruby and Leopard</title>
      <link>
        http://alexvollmer.com/posts/2007/12/06/ruby-and-leopard/
      </link>
      <comments>
        http://alexvollmer.com/posts/2007/12/06/ruby-and-leopard/#comments
      </comments>
      <pubDate>
        Thu, 06 Dec 2007 14:11:19 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <category>
        <![CDATA[
            mac
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2007/12/06/ruby-and-leopard/
      </guid>
      <description>
        <![CDATA[
            I've spent the bulk of this week trying to dig myself out of Ruby/Leopard hell. I'm surprised that things were as borked as they were. So much so, that I'm inclined to believe that there was something special happening on my machine. I got my shiny new Leopard install the day after it came out. I did an upgrade (not a clean install) with no hiccups and was happy with all of my <a href="http://www.apple.com/macosx/features/300.html%20Apple%20-%20Mac%20OS%20X%20Leopard%20-%20Features%20-%20300+%20New%20Features">shiny happy new Leopard features</a>. I knew that first-class Ruby support was coming so I figured I would abandon my MacPorts install and go with what Mr. Jobs deigned to give me.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>I've spent the bulk of this week trying to dig myself out of Ruby/Leopard hell. I'm surprised that things were as borked as they were. So much so, that I'm inclined to believe that there was something special happening on my machine. I got my shiny new Leopard install the day after it came out. I did an upgrade (not a clean install) with no hiccups and was happy with all of my <a href="http://www.apple.com/macosx/features/300.html%20Apple%20-%20Mac%20OS%20X%20Leopard%20-%20Features%20-%20300+%20New%20Features">shiny happy new Leopard features</a>. I knew that first-class Ruby support was coming so I figured I would abandon my MacPorts install and go with what Mr. Jobs deigned to give me.</p>
            
            <p>At first things seemed to go well. But as I was spooling up again on my <a href="http://svn.livollmers.net/public/pci/%20/pci">"Programming Collective Intelligence"</a> project, <a href="http://www.zenspider.com/ZSS/Products/ZenTest/%20ZenTest:%20Automated%20test%20scaffolding%20for%20Ruby">autotest</a> with <a href="http://rspec.rubyforge.org/%20RSpec-1.0.8:%20Home">rspec</a> was just flat-out broken. When I ran <code>autotest</code> I would get an error like the following:</p>
            
            <div class="highlight"><pre><span class="go">/Library/Ruby/Gems/1.8/gems/rspec-1.0.8/lib/autotest/rspec.rb:80:in spec_command&#39;: No spec command could be found! (RspecCommandError)</span>&#x000A;    <span class="go">    from /Library/Ruby/Gems/1.8/gems/rspec-1.0.8/lib/autotest/rspec.rb:10:in initialize&#39;</span>&#x000A;    <span class="go">    from /Library/Ruby/Gems/1.8/gems/ZenTest-3.6.1/lib/autotest.rb:123:in new&#39;</span>&#x000A;    <span class="go">    from /Library/Ruby/Gems/1.8/gems/ZenTest-3.6.1/lib/autotest.rb:123:in run&#39;</span>&#x000A;    <span class="go">    from /Library/Ruby/Gems/1.8/gems/ZenTest-3.6.1/bin/autotest:48</span>&#x000A;    <span class="go">    from /usr/bin/autotest:16:in `load&#39;</span>&#x000A;    <span class="go">    from /usr/bin/autotest:16</span>&#x000A;    </pre>
            </div>
            
            
            <p>After <em>lots</em> of digging, I figured out that <code>autotest</code> uses the built-in <code>Config</code> class which defines a <code>bindir</code> that rspec (with autotest) uses to derive possible locations for the <code>spec</code> command. On Leopard the default 'bindir' for Leopard was <code>/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin</code> which was <em>not</em> where the <code>spec</code> command was installed. With a little help from <a href="http://www.rubybyraeli.org/blog/articles/2007/11/28/262-hacking-at-the-heads-of-a-hydra-ruby-install%20Hacking%20at%20the%20heads%20of%20a%20Hydra%20Ruby%20install">a blog post</a> I simply symlinked <code>spec</code> into that impossibly long path and things seemed to work.</p>
            
            <p>For now I'm sticking with the Leopard install. I've fiddled a little bit with the <a href="http://www.apple.com/applescript/features/scriptingbridge.html%20AppleScript:%20Scripting%20Bridge">Scripting Bridge</a> stuff and it's pretty nifty so I'm hesitant to abandon the Apple install. I just hope that Ruby support doesn't rot the same way Java support has.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Ruby Port of &quot;Programming Collective Intelligence&quot;</title>
      <link>
        http://alexvollmer.com/posts/2007/12/01/ruby-port-of-programming-collective-intelligence/
      </link>
      <comments>
        http://alexvollmer.com/posts/2007/12/01/ruby-port-of-programming-collective-intelligence/#comments
      </comments>
      <pubDate>
        Sat, 01 Dec 2007 23:49:08 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2007/12/01/ruby-port-of-programming-collective-intelligence/
      </guid>
      <description>
        <![CDATA[
            Thanks to a quick comment from Toby Seagaran, the author of <a href="http://www.oreilly.com/catalog/9780596529321/%20O'Reilly%20Media%20%7C%20Programming%20Collective%20Intelligence">"Programming Collective Intelligence"</a>, my motivation finally exceeded my laziness in getting the porting code available in a public place. So I&rsquo;m proud to announce that you can take a look at the ongoing porting effort at <a href="http://github.com/alexvollmer/pci4r">my Ruby port of Programming Collective Intelligence</a>. I'm in the midst of Chapter 4 right now, so I still have a long way to go.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>Thanks to a quick comment from Toby Seagaran, the author of <a href="http://www.oreilly.com/catalog/9780596529321/%20O'Reilly%20Media%20|%20Programming%20Collective%20Intelligence">"Programming Collective Intelligence"</a>, my motivation finally exceeded my laziness in getting the porting code available in a public place. So I&#8217;m proud to announce that you can take a look at the ongoing porting effort at <a href="http://github.com/alexvollmer/pci4r">my Ruby port of Programming Collective Intelligence</a>. I'm in the midst of Chapter 4 right now, so I still have a long way to go.</p>
            
            <p>There are number of things in the code that make me cringe (it always amazes me how a better solution to something occurs usually about five minutes after I commit), but if nothing else it&#8217;s a monument to the effort I&#8217;ve made thus far. Hopefully the thought of public shame will motivate me to get in there and clean some of that stuff up.</p>
            
            <p>Note that I&#8217;ve deviated somewhat from the book in the naming of things. By and large I&#8217;ve stuck to the same basic names, but I&#8217;ve followed Ruby&#8217;s underscore naming convention for methods and camel-case for module and class names. All of the code is under a single <code>PCI</code> module namespace (though spread around several files). I&#8217;ve also included underscore in the database table and column names that go in the <a href="http://www.sqlite.org/%20SQLite%20Home%20Page">SQLite</a> database that is created in Chapter 4 of Toby&#8217;s book.</p>
            
            <p>Pedagogically, organizing the code into chapters would probably be more useful to people, but I don&#8217;t currently have it setup this way. With a beer and little trolling through the SVN logs I could probably reorganize the repository to match this. If folks want this, lemme know.</p>
            
            <p>One final note, I&#8217;ve organized the code into a structure loosely based on a standard Ruby Gem layout. I&#8217;ve tried putting some <a href="http://rspec.rubyforge.org/%20RSpec-1.0.8:%20Home">specs</a> around the code that is there, but the overall coverage is pretty underwhelming. I wrote specs to prove that my porting worked, not necessarily to test the underlying code that Toby originally wrote. In short, anytime I ran a manual test more than two times I have (usually) written a spec for it.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Many Spinning Plates</title>
      <link>
        http://alexvollmer.com/posts/2007/12/01/many-spinning-plates/
      </link>
      <comments>
        http://alexvollmer.com/posts/2007/12/01/many-spinning-plates/#comments
      </comments>
      <pubDate>
        Sat, 01 Dec 2007 05:12:42 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <category>
        <![CDATA[
            cocoa
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2007/12/01/many-spinning-plates/
      </guid>
      <description>
        <![CDATA[
            Like many folks in this age of immediacy and information overload, I've been a big fan of multi-tasking. We multi-taskers believe that by multi-tasking we are cutting out any "down-time" and making nearly every previously-idle second a productive one. However, lately I've come to realize that the amount time and effort spent on switching my brain from context to context is incurring an overhead that exceeds any gains I might get in multi-tasking.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>Like many folks in this age of immediacy and information overload, I've been a big fan of multi-tasking. We multi-taskers believe that by multi-tasking we are cutting out any "down-time" and making nearly every previously-idle second a productive one. However, lately I've come to realize that the amount time and effort spent on switching my brain from context to context is incurring an overhead that exceeds any gains I might get in multi-tasking.</p>
            
            <p>So lately I've decided to be a little more single-threaded in my approach to "getting things done" these days. For example, when I first got my shiny new MacBook Pro I installed every kind of whizzy <a href="http://growl.info/%20Welcome%20to%20Growl!">Growl</a> notification I could find. However I realized that the constant interruptions were really taking a toll on my ability to focus on any particular task. So these days I have very few notifications turned on. I check email when I damn well feel like it. I make myself available via IM when I feel I'm in a position to be interrupted. That's about it. Anymore and I know I'll never get anything useful accomplished.</p>
            
            <p>So despite all of what I've just said, my personal geek-time has been quite split-brained between two separate efforts. The first is a methodical (and sometimes maddeningly slow) port of the Python code in Toby Segaran's excellent <a href="http://www.oreilly.com/catalog/9780596529321/%20O'Reilly%20Media%20|%20Programming%20Collective%20Intelligence">"Programming Collective Intelligence"</a> to Ruby. The second project is working my way through another fine technical book, Aaaron Hillegas' <a href="http://www.bignerdranch.com/products/cocoa1.shtml%20Cocoa%20Programming%20for%20Mac%20OS%20X">"Cocoa Programming for Mac OS X"</a>. Oh yeah, I've also been noodling around with the latest darling of the SCM world&mdash;<a href="http://git.or.cz/%20Git%20-%20Fast%20Version%20Control%20System">git</a>. This is to say nothing of the three or four other personal projects I've let dormant for the moment.</p>
            
            <p>I'm a hands-on kind of guy and I get so much more out of nerd books when I can actually write, test, debug and run some code. So while porting code from from one language to another very similar language may feel ridiculous donkey-work, I feel like I internalize the underlying concepts better. As a sort of interesting side-effect I've worked up a number of nice collection-processing snippets around Ruby's <code>Enumerable</code> that I hope to bundle up someday soon as a separate post. Also, if I can figure out a simple, secure way to host a read-only repository (Subversion or git) I'll put the code out in case anyone gives a hoot.</p>
            
            <p>Meanwhile, I've fallen in love with the Cocoa development environment. Objective-C is an interesting language, but perhaps not without its negative points (a compiled language with no type checking?). But the clean separation of MVC components coupled with the easiest WYSIWYG GUI designer is such a <em>huge</em> improvement over pure code-only environments like Java Swing. Anyway, I've had the idea for a simple little Growl app for some time. I'm pretty sure that once I get through the Hillegas book I can knock that out in pretty short order.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Happy Mac Day To Me, Happy Mac Day To Me</title>
      <link>
        http://alexvollmer.com/posts/2007/10/29/happy-mac-day-to-me-happy-mac-day-to-me/
      </link>
      <comments>
        http://alexvollmer.com/posts/2007/10/29/happy-mac-day-to-me-happy-mac-day-to-me/#comments
      </comments>
      <pubDate>
        Mon, 29 Oct 2007 04:30:54 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            apple
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2007/10/29/happy-mac-day-to-me-happy-mac-day-to-me/
      </guid>
      <description>
        <![CDATA[
            This weekend has been one of the best birthday weekends I've had in a while. First, I got to see my Oregon Ducks beat the USC Trojans to put themselves right in the hunt for the BCS. Second, I scored a couple of copies Leopard which I've installed on my laptop. And lastly, I joined the ranks of happy iPhone owners.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>This weekend has been one of the best birthday weekends I've had in a while. First, I got to see my Oregon Ducks beat the USC Trojans to put themselves right in the hunt for the BCS. Second, I scored a couple of copies Leopard which I've installed on my laptop. And lastly, I joined the ranks of happy iPhone owners.</p>
            
            <p>When the iPhone was first announced I thought it would be a success, but I wasn't particularly interested in having one myself simply because I've felt that the U.S. wireless industry has had a very difficult time coming up with a compelling product for me. I had a Crackberry from work, which was kind of fun to check scores with or play Sudoku. But if I had to <em>pay</em> for it? No way buster. I wouldn't pony up for that. Simple telephony would be plenty for me.</p>
            
            <p>Well, that was until a couple of co-workers bought iPhones and I finally got to put my hands&hellip;er fingertips&hellip;on one. Oh man, I was in love at first touch. The screen is gorgeous and the feel of the interaction is brilliant.</p>
            
            <p>So I waited and waited until my actual birthday came along. Today I walked into my local AT&amp;T store and five minutes later walked out with a brand spanking new iPhone. When I got home I plugged it right into my Mac, iTunes launched and in twenty minutes I had a working iPhone.</p>
            
            <h2>What I Like So Far</h2>
            
            <p>Google's announcement of <a href="http://gmailblog.blogspot.com/2007/10/sync-your-inbox-across-devices-with.html%20Official%20Gmail%20Blog:%20Sync%20your%20inbox%20across%20devices%20with%20free%20IMAP">IMAP support for GMail</a> support couldn't have come at a better time. Setting up IMAP in the iPhone was a snap following <a href="https://mail.google.com/support/bin/answer.py?answer=77702">Google's updated instructions</a>. They've also done a nice mapping of standard IMAP actions to GMails behavior. Check out <a href="https://mail.google.com/support/bin/answer.py?answer=77657">this page</a> for details.</p>
            
            <p>I had a mail in my inbox in which someone sent me a link to a YouTube video. I simply clicked the link and -poof- the built-in YouTube player fired up with the video. When I finished watching the video I clicked the "done" button and I was right back in my inbox. Slick&hellip;very slick.</p>
            
            <p>I like the multi-document support for Safari. You can have several Safari instances running which is nice when you want to maintain a couple of different browsing contexts. This is utilized by the mail application when links are opened. Opening a new link won't trounce whatever you were last looking at.</p>
            
            <p>Did I mention it's just plain beautiful to look at? The screen is glorious. The icons are bright and crisp. Video looks fantastic.</p>
            
            <h2>What Gives Me Cause For Pause</h2>
            
            <p>I have a pretty long password for my wireless access point and not having the ability to see what I typed in was a little tricky. You do get feedback from the virtual keypad as you hit a particular letter, but it's not as handy as viewing the field.</p>
            
            <p>The synchronization via iTunes is a little goofy as you can only do it through playlists. Of course the solution is to simply create an iPhone-specific playlist and synchronize things that way. Not a big deal, but it seems a little crufty to me.</p>
            
            <h2>Overall</h2>
            
            <p>Maybe I'm just in the honeymoon phase and that two-year contract will feel like a ball and chain. But right now I'm very excited to be the proud owner of a new iPhone.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>How To Make URLs</title>
      <link>
        http://alexvollmer.com/posts/2007/10/26/how-to-make-urls/
      </link>
      <comments>
        http://alexvollmer.com/posts/2007/10/26/how-to-make-urls/#comments
      </comments>
      <pubDate>
        Fri, 26 Oct 2007 22:05:18 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            REST
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2007/10/26/how-to-make-urls/
      </guid>
      <description>
        <![CDATA[
            One of the things that makes a web application &ldquo;look&rdquo; RESTful is the type of URLs it presents. Like many things, whether or not these URLs really meet a particular criteria is a matter of degrees. But anyone who has some basic understanding of a resource-oriented view versus a functional view can tell the extremes apart (e.g. <code>/system?sport=football&amp;amp;team=seahawks&amp;amp;year=2006</code> vs. <code>/football/seattle_seahawks/2006</code>).
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>One of the things that makes a web application &#8220;look&#8221; RESTful is the type of URLs it presents. Like many things, whether or not these URLs really meet a particular criteria is a matter of degrees. But anyone who has some basic understanding of a resource-oriented view versus a functional view can tell the extremes apart (e.g. <code>/system?sport=football&amp;amp;team=seahawks&amp;amp;year=2006</code> vs. <code>/football/seattle_seahawks/2006</code>).</p>
            
            <p>However it&#8217;s the in-between URLs that seem to provoke the most heated discussions between the REST-anistas and the non-believers. Those not impressed with REST would argue that all we&#8217;re doing is prettying-up our URLs with no real functional improvement. Now I happen to prefer good-looking URLs over ugly ones because I think they are easier to work with. But outside of the aesthetic argument I couldn&#8217;t really make a good case for RESTful URL design. However, after a bit of thought, I&#8217;ve come up with a theory on how URLs ought to be constructed.</p>
            
            <h3>The Theory</h3>
            
            <p>Requests for resources have two basic components: the <em>identity</em> of the resource and <em>variation</em> information. Identity is the minimum amount of information required to distinguish one resource from another. Variation criteria is additional information that refines or qualifies the <em>representation</em> of the requested resource. Variation information can include things like:
            *  type of requester
            *  content type
            *  session or user
            *  portions of the representation</p>
            
            <p>In a proper URL design, identity information should be expressed as a first-class notion. In URLs this is best expressed within the path portion of the URL. This information does <em>not</em> belong in request parameters. This not only makes your URL structure easier to manage mentally, it also stands a better chance of working well with HTTP caches (more on this in a bit) and proxies. Variation information can be expressed by the requester in any combination of headers (e.g. Content-Type) or request parameters.</p>
            
            <h3>Hold the query parameters!!!</h3>
            
            <p>So why do so many people fall back on request parameters? In the Java world, I think a lot of it comes from people developing applications in a Servlet environment. The servlet specification provides no easy way to access path information. You have to get the request path and split the string by hand. Yecchh. Also, the built-in URL mapping in Servlets leaves quite a bit to be desired. So a lot of folks just fall back on the simple dictionary interface that servlets provide for request parameters. Unfortunately this has bred a lot of bad habits in Java web developers.</p>
            
            <p>I think the other reason this design is prevalent, is the holdover of the first great paradigm shift on the web when we moved from "web sites" to "web applications". Many of the people writing the first web applications (myself included) tended to view HTTP requests as function or method calls where variable information is passed in as arguments. What many of us failed to realize was that requests for dynamic content didn't require a different way of structuring URLs. We were still, by and large, asking for <em>things</em>.</p>
            
            <p>There are two problems with the functional approach. The first is that a lot of request parameter information could be better expressed with existing headers. The second is action-oriented view that developers take of these requests which violates a core principle of REST where a finite set of actions are applicable over an infinite set of nouns.</p>
            
            <h3>Collections</h3>
            
            <p>One place where query parameters make sense are specifying views across collections. This is also known as providing a search parameter. Think of the Most Popular URL In The World, <code>http://www.google.com/q?=</code>. When you search, that 'q' parameter is a specification for a filtered view of that top-level Google resource. In other words when you query for <code>http://www.google.com/q?=Led+Zeppelin</code> you're really asking for all things Led Zeppelin from the Google collection.</p>
            
            <p>On a smaller scale, query parameters are quite appropriate when asking for a filtered view over a collection that has arbitrary dimensions. However, not all views over a collection should use  query parameters. For example, in a case like Flickr where tags are first-class aspects of certain resources, <code>/tags/</code> is expressed as a path segment. When resources are normalized like this, you should favor paths over query parameters. However when you want to select a sub-set of resources out of some larger heap <em>and</em> the way you specify that subset is open-ended, query parameters are a good way to go.</p>
            
            <h3>Caching</h3>
            
            <p>Perhaps the best rationale I can give you for the RESTful approach is that playing by these rules will allow you to take full advantage of HTTPs built-in caching semantics. When you think about what a cache has to do and all of the rules it has to follow, its main function is identifying and returning cached responses to cacheable requests.</p>
            
            <p>That Wild West Frontier that is the "query parameters" section of a URL can give HTTP caches fits. Heck, by default <a href="http://www.squid-cache.org/">Squid</a> ignores query parameters for resource identification. <a href="http://wiki.squid-cache.org/ConfigExamples/DynamicContent?highlight=%28%5EConfigExamples/%5B%5E/%5D%2A%24%29">You have to go out of your way to get this to work</a>. You could argue that Squid "isn't doing it right", but I think it would be more fair to say that Squid is trying to be safe with regard to query parameters. This is similar to the way Google's Web Accelerator <a href="http://webaccelerator.google.com/webmasterhelp.html">treats URLs with query parameters</a> (i.e. an opaque Pandora's Box not to be trifled with.)</p>
            
            <p>So keep your URLs clean! Figure out what information you need identify a resource and keep it in the path of the URL. Use headers or request parameters for any of the variation information.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Ten Things I Think I Think</title>
      <link>
        http://alexvollmer.com/posts/2007/10/17/ten-things-i-think-i-think/
      </link>
      <comments>
        http://alexvollmer.com/posts/2007/10/17/ten-things-i-think-i-think/#comments
      </comments>
      <pubDate>
        Wed, 17 Oct 2007 04:24:41 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            software
        ]]>
      </category>
      <category>
        <![CDATA[
            java
        ]]>
      </category>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <category>
        <![CDATA[
            erlang
        ]]>
      </category>
      <category>
        <![CDATA[
            cocoa
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2007/10/17/ten-things-i-think-i-think/
      </guid>
      <description>
        <![CDATA[
            In the spirit of Peter King's <a href="http://sportsillustrated.cnn.com/writers/peter_king/archive/index.html%20SI.com">Monday Morning Quarterback</a>, here are ten things that I think I think:
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>In the spirit of Peter King's <a href="http://sportsillustrated.cnn.com/writers/peter_king/archive/index.html%20SI.com">Monday Morning Quarterback</a>, here are ten things that I think I think:</p>
            
            <p><em>10. Maven 2 Moves The Ball Down The Field (well, sortof):</em> All Java build systems suck. They all suck in different ways and the selection of one over the other is largely dictated by what types of pain you are willing to tolerate. I've used Maven since it's inception. It solved some things Ant didn't but also came with its own problems. Maven 2 attempts to solve some of the problems with Maven Mark I (namely scripting in XML, <em>yecch</em>), but lacks coherent documentation and a clear roadmap for learning the tool.</p>
            
            <p>That being said, it makes tasks like creating a WAR file and deploying to a web server relatively trivial when compared with the amount of Ant XML you would have to write to do the same thing. There's still too much XML for my liking and god help you if you want to do something outside of the box.</p>
            
            <p><em>9: Maybe Static Imports Ain't So Bad:</em> When Java 5 first rolled around, I identified static imports as one of those features I would <em>never</em> use. Well, heh heh, never say never I guess. I have started to use static imports in a few places where I need something like the equivalent of a Ruby mixin. Interestingly enough the the only places I've felt comfortable with their use have been in unit-testing when I want to use <a href="http://www.easymock.org/%20EasyMock%20:%20Home">EasyMock's</a> methods or <a href="http://www.junit.org/%20Welcome%20to%20The%20New%20JUnit.org!%20|%20JUnit.org">JUnit 4</a> methods.</p>
            
            <p><em>8: Java Annotations Are Curious Creatures:</em> I can't tell yet whether or not Java annotations are really all that great or not. If you're a hard-core-no-good-thing-has-come-in-the-damn-JDK-since-the-collections-API kinda guy, you can make the argument that annotations are simply syntactic sugar for problems that could just as easily be solved with a proper object model or external configuration. In the old days (like the first version of EJBs) we wrote our configuration as actual code or, perhaps, as properties files. Then XML and Java went out on a hot date during the first dot-com bubble and spawned all sorts of illegitimate XML configuration children. I can't tell you how many XML configuration systems I've seen that essentially reinvented properties files in XML (same configuration, twice the typing!)</p>
            
            <p>Configuration via a pure object model can be, at times, extremely clunky (anyone remember writing EJB 1.0 deployment descriptors?) I think the evils of external file configuration have been experienced by just about any moderately experienced Java developer (Struts config anyone?) So it seems to me that annotations are simply a way to get some configuration closer to your code in something approaching a DSL (which most configuration files are <em>attempting</em> to be) without external files. I haven't played enough with them to be totally sold, but the idea is intriguing&hellip;</p>
            
            <p><em>7: I Couldn't Code Java Without Eclipse:</em> I say this not because Eclipse is just the greatest Java IDE of all time, but because I've invested so much time learning so many of its tricks. Really any modern Java IDE probably has equivalent functionality, so the issue is not so much that I can't imagine coding Java without Eclipse, but rather that I can't imagine coding Java with a plain ol' text-editor. I don't think this says as much about me as it does about the language itself. Does anyone doing Java professionally on a daily basis still use Vim or Emacs?</p>
            
            <p><em>6: I Code Java, I Code Ruby. When Will I Use JRuby?:</em> It's funny that for as much Java and Ruby as I've done in the last eighteen months that I still have done no more than dabble in JRuby. The day can't be far off, but I'm a little surprised that it hasn't yet arrived.</p>
            
            <p><em>5: I'm Tired of Algorithms:</em> Well, that's not exactly right. I'm tired of algorithms as the measure of one's knowledge of software development. It's important, but it's not the <em>most important</em>. Apparently most of the software world doesn't agree with me so it's up to me to decide whether or not I want to continue swimming upstream or simply relax and go with the flow. Some little voice in my head tells that the latter is not a good idea&hellip;</p>
            
            <p><em>4: I Think About Scaling All The Time:</em> Aside from sustainable development, scalability is probably the most important thing I think about at my current position. I've worried about it before, but the scope of previous projects that I've worked on don't approach the ambitions of this one. I've spent a <em>lot</em> of time in the last year reading up on all sorts of technologies around scalability. I've been pretty interested lately in things like the <a href="http://www.spread.org/%20The%20Spread%20Toolkit">Spread Toolkit</a> and <a href="http://www.backhand.org/wackamole/%20Wackamole:%20use%20your%20resources">Wackamole</a>. I haven't used them yet, but I'm keen to try them out. I've been using <a href="http://www.jini.org/%20Main%20Page%20-%20Jini.org">Jini</a> lately&hellip;the jury's still out on that one.</p>
            
            <p><em>3: RDBMS Is Dead! Long Live RDBMS!</em> Part of the result of all this scalability reading and thinking is coming to the realization that a relational database doesn't work everywhere as the be-all, end-all of persistence. To that end I've been immersing myself in a number of different technologies. Inverted indices provided by the likes of <a href="http://lucene.apache.org/java/docs/%20Apache%20Lucene%20-%20Overview">Lucene</a> or <a href="http://ferret.davebalmain.com/trac/%20Ferret%20-%20Trac">Ferret</a> are fascinating alternatives to the traditional RDBMS approach. If you twist your brain the right way you can satisfy most requirements you would have of an RDBMS with inverted indices.</p>
            
            <p>Similarly various key/value databases such as <a href="http://couchdb.org/CouchDB/CouchDBWeb.nsf/Home?OpenForm%20CouchDb%20Project%20Website">CouchDB</a> and even <a href="http://www.oracle.com/database/berkeley-db.html%20berkeley-db.html">Sleepycat</a> make very sensible replacements for a general-purpose RDBMS. I find these interesting not so much because of their performance characteristics viz-a-viz RDBMS solutions&mdash;though that is certainly compelling&mdash;but rather because of their simplicity. For a lot of problems I really only care about storing and retrieving heterogeneous data via simple keys. Why make it any harder than that with a traditional relational column/row structure?</p>
            
            <p><em>2: I'm Going To Ask Erlang Out On A Date:</em> <a href="http://www.erlang.org/%20Erlang">Erlang</a> has gotten a lot of hype (and criticism) lately. Normally the simple churn of the blogosphere isn't enough to get me interested in some johnny-come-lately, but Erlang strikes me as something with a little more staying power than the normal flavor-of-the-month technology. Perhaps it's the track record as being "carrier-grade". Perhaps it's the move by some towards functional programming for solving particular problems. This, in particular, fascinates me. I've been an object-oriented guy for a long time, but there have been occasions when the OO approach seemed a little obtuse, but I didn't really have a good alternative. I'm hoping that the process of learning Erlang will  be a great foil to "get" functional programming.</p>
            
            <p><em>1: I Think The OLPC Project Is Brilliant:</em> I can't think of a project that embodies more of the things I value than the <a href="http://laptop.org/%20One%20Laptop%20per%20Child%20(OLPC">One Laptop Per Child Project</a>, a $100 laptop for the world's children's education). It's about putting the right amount of technology in the hands of kids. It's about giving them a sturdy playground where they are encouraged to learn through experimentation. It's about building sensible collaboration so that students can learn how to achieve more together as a group than they could as individuals. It's about choosing and using a tool that gets things done. It's <em>not</em> about learning a particular platform and it's <em>not</em> about market-share.</p>
            
            <p>I signed up for the <a href="http://www.xogiving.org/%20One%20Laptop%20Per%20Child%20--%20XO%20Giving">Give 1 Get 1 program</a>, but I can't really justify having my own OLPC laptop. However I may get one just to play with it for a few days and then find a worthy home for it. I can't imagine a better way to empower a kid than to put a tool like this in their hands.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>The Ache of Swing Programming</title>
      <link>
        http://alexvollmer.com/posts/2007/09/07/the-ache-of-swing-programming/
      </link>
      <comments>
        http://alexvollmer.com/posts/2007/09/07/the-ache-of-swing-programming/#comments
      </comments>
      <pubDate>
        Fri, 07 Sep 2007 15:45:42 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            java
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2007/09/07/the-ache-of-swing-programming/
      </guid>
      <description>
        <![CDATA[
            As I type this post, I have to fight every urge to grab my hands in agony and cry out like a hurt child. This hand-ache is not unfamiliar. I realize that my hands have <em>always</em> felt like this anytime I've had to do some <a href="http://java.sun.com/products/jfc/tsc/index.html%20Java%20SE%20Desktop%20Articles">Swing</a> programming.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>As I type this post, I have to fight every urge to grab my hands in agony and cry out like a hurt child. This hand-ache is not unfamiliar. I realize that my hands have <em>always</em> felt like this anytime I've had to do some <a href="http://java.sun.com/products/jfc/tsc/index.html%20Java%20SE%20Desktop%20Articles">Swing</a> programming.</p>
            
            <p>Why does it have to be like this? Because Swing requires just so much damn typing. My lord my fingers are buzzing with a solid week of keyboard contact. Swing is a tremendously verbose API&mdash;it simply requires a lot of letters to make it go. I consider myself a pretty advanced Eclipse user who takes advantage of most of its features to let it write my code for me, but my hands still hurt. I shudder at the thought of having to code Swing like I did "back in the day" with good old <a href="http://www.gnu.org/software/emacs/" title="GNU Emacs - GNU Project - Free Software Foundation">Emacs</a>.</p>
            
            <p>I find it interesting that demonstrations of Java code being called from a scripting language (<a href="http://jruby.codehaus.org/%20JRuby%20-%20Home">JRuby</a>, <a href="http://www.jython.org/">Jython</a>, <a href="http://groovy.codehaus.org/%20Groovy%20-%20Home">Groovy</a>, etc.) always include an example of rapid GUI prototyping with the Swing API. I say this because I think that while these scripting languages really beat Java in terseness, they don't really help much when dealing with Swing. In other words, a scripting language may reduce the length of a line I might otherwise write in Java, but the sheer number <em>lines</em> I have to write is about the same. I don't think this is an indictment of scripting-wrapped-around-Java so much as what it says about the Swing API itself.</p>
            
            <p>The other reason Swing wears your hands out is that it is not a well-designed, intuitive API. You spend a lot of time experimenting to figure out the magic recipe that will make your app work. Despite the verbosity of the <a href="http://java.sun.com/javase/6/docs/technotes/guides/swing%20JDK%206%20Swing%20(Java%20Foundation%20Classes%20(JFC">API docs</a>)-related APIs &amp; Developer Guides -- from Sun Microsystems) and various <a href="http://java.sun.com/docs/books/tutorial/uiswing/%20Trail:%20Creating%20a%20GUI%20with%20JFC/Swing%20(The%20Java%E2%84%A2%20Tutorials">Swing Tutorials</a>), I find that I still have to play (nee fight) with the API to figure out how to get it to work.</p>
            
            <p>Take, for example the juggernaut that is the <code>JTable</code>. I wanted to have a <code>JTable</code> instance update with new rows when the underlying model changed. I wanted to set a custom renderer for one of the cells to display a URI as a hyperlink and allow the user to click it and view the URI in their browser (via <code>Desktop.browse(URI)</code>). OK, let's see here, let's go to our IDE and put the cursor next to our <code>JTable</code> instance variable and trigger the autocomplete drop-down. Hmmm, what can we use here? Oh here, we go, <code>setDefaultCellRenderer</code>, hey that looks good. OK, let's see, takes a <code>Class</code> and <code>TableCellRenderer</code>, yeah that looks good. Alright, let's fire it up&hellip;wait for the VM to get warmed up&hellip;load our data model&hellip;click the table and&hellip;aw hell, a <code>NullPointerException</code> during table painting. OK, let's go through the stack trace and look for one of my classes in there. I must be passing some null reference around. Hmmm, no&hellip;actually these are all Swing and internal classes. Great, now what?</p>
            
            <p>Eventually I worked out that the problem is that despite the plethora of options for cell rendering, only one combination worked in my particular case. Only by trying every square in the Swing-API-matrix was I able to stumble upon a combination that worked correctly. Now I will admit that my Swing skills are a bit rusty and that I have to blame myself to a certain extent. However my big beef with Swing API is that it gives you so many ways to hurt yourself in spectacular and unintended ways. Looking at the API and its base-classes with 200+ methods, it's pretty clear that the API was developed from the implementation on up. Any design with regard to usability seems to be an afterthought.</p>
            
            <p>I feel like whenever I've had to work on a Swing project, I have to change my whole approach. It's much more difficult for me to cull decent unit-tests out of the code. In part this is because it's so easy to embed testable code inside of un-testable code (think of event handlers), but also because UI testing is difficult. Even the various UI-oriented unit-testing tools don't really enable easier unit-testing so much as make it barely possible.</p>
            
            <p>Despite all of this and the persistent ache in my digits, I can't say I didn't have any fun with this. I've always said that programming Swing is a bit like riding a mo-ped, it's fun for a bit but you would never want your friends to catch you doing it.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Pro-Social Code</title>
      <link>
        http://alexvollmer.com/posts/2007/07/19/pro-social-code/
      </link>
      <comments>
        http://alexvollmer.com/posts/2007/07/19/pro-social-code/#comments
      </comments>
      <pubDate>
        Thu, 19 Jul 2007 03:48:29 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            software
        ]]>
      </category>
      <category>
        <![CDATA[
            philosophy
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2007/07/19/pro-social-code/
      </guid>
      <description>
        <![CDATA[
            There is a growing trend of geeks getting hip to the principles of usability and user experience design. The idea is that when developers start taking a more user-centric approach to their development, their design of their applications will be influenced more by the user's needs and less by the internal mechanisms or frameworks upon which it is built. As a kind of agile, post-modern, less-is-more kinda guy I think this is a wonderful approach.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>There is a growing trend of geeks getting hip to the principles of usability and user experience design. The idea is that when developers start taking a more user-centric approach to their development, their design of their applications will be influenced more by the user's needs and less by the internal mechanisms or frameworks upon which it is built. As a kind of agile, post-modern, less-is-more kinda guy I think this is a wonderful approach.</p>
            
            <p><img src="http://farm1.static.flickr.com/5/9798019_bc86ac5653_m.jpg" alt="Iceburg (courtesy of ae2005 via flickr.com)" class="right"/></p>
            
            <p>However I think this user-centric approach can be taken far beyond point-and-click interfaces. I've noticed that when developing a so-called "back-end" feature, an awful lot of developers tend to work inside-out. The approach often starts with <em>how</em> something will be done and, based on that process/function/algorithm, the rest of the design accretes until enough connective tissue is in place to connect the feature to the rest of the application. One danger in this thinking is that the implementation can drive the design in such a way that precludes other implementations. But an even more insidious problem is that the inside-out approach can leave the end-user with a pretty lousy interface. I'll borrow Joel Spolsky's <a href="http://www.joelonsoftware.com/articles/fog0000000356.html%20The%20Iceberg%20Secret,%20Revealed%20-%20Joel%20on%20Software">Iceberg Analogy</a> here and say that your clever little algorithm is far below the water-line. Please&hellip;I beg of you&hellip;please spend some time on your interface.</p>
            
            <p>The inside-out approach often manifests itself as a lack of refinement on the visible edges of a feature. For software with an obvious visual interface this is relatively cut and dried. However for other features this is less clear, but just as important. Let's take the simple example of a command-line utility that does some internal housekeeping. An inside-out approach typically yields a core bit of code that has just enough command-line option parsing to connect a terminal to the core bit. But having a few command-line switches for configurability isn't enough. Here are few things to consider to make that visible 10% of your feature rock:</p>
            
            <ul>
            <li>Implement a standard "help" option like <code>--help</code> and provide terse but descriptive prose. The output should be able to answer "what does this thing do?"</li>
            <li>Useful documentation of command-line parameters. What is this parameter for? Is it required? Is their a default value?</li>
            <li>Provide sensible defaults. If the typical use-case is run against developers own machines, <em>even if it's run differently in production</em>, for Pete's-sake make localhost the default. Don't make the thing you do the most often, the most painful.</li>
            <li>Provide a "pretend" or "dry-run" mode. Show what <em>would</em> happen without committing the user to the final consequences.</li>
            <li>Handle termination gracefully. Don't just let your app barf all over itself when someone kills it with CTRL+C</li>
            <li>Provide <em>useful</em> output for long-running jobs. Consider the interplay between this suggestion and the previous one.</li>
            </ul>
            
            
            <p>Even though your "customers" are perhaps internal team-members, that's no reason to short-change them with some lousy tool that only works under the narrowest of circumstances. This is just plain rude to the rest of your team. Do you pee in their coffee cups in the morning? No? Then don't stick them with bad tools. So then let me enumerate a few things that will guarantee you a fast-track onto my rake-whacking-list:
            *  Failures without explanation or entrails left behind to debug.
            *  Cryptic messages (success or failure).
            *  Not playing nicely with standard input and output streams so that your utility can work nicely in a shell.
            *  Printing a bunch of debug output that I can't turn off.
            *  Lack of sensible defaults. Please don't make me type the same option over..and over&hellip;and over&hellip;</p>
            
            <p>Since this horse ain't quite dead yet, let me flog it just a wee bit more. If you're with me this far, then perhaps you've bought off on the idea that the 10% of your non-visual apps deserves first-class treatment. Hold on&hellip;I've got one more for you. In addition to spending the time to polish the user-visible features you also need to <em>test</em> them. I don't mean the regular WOMB (works on my box) manual testing. I mean automated, unit-tested features. Yes&hellip;even for things like command-line parsing. If you want to call yourself a craftsperson, don't slack on this. Do it right. If you think unit-testing takes too long then you need to get better at it, not ignore it altogether. In my book you can't possibly be so brilliant that you can design beautiful algorithms or object models but can't test-drive something as simple as configuration parameters. Sorry&hellip;you just can't.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Book Review: Information Architecture for the World Wide Web</title>
      <link>
        http://alexvollmer.com/posts/2007/07/08/book-review-information-architecture-for-the-world-wide-web/
      </link>
      <comments>
        http://alexvollmer.com/posts/2007/07/08/book-review-information-architecture-for-the-world-wide-web/#comments
      </comments>
      <pubDate>
        Sun, 08 Jul 2007 02:50:51 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            book review
        ]]>
      </category>
      <category>
        <![CDATA[
            information architecture
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2007/07/08/book-review-information-architecture-for-the-world-wide-web/
      </guid>
      <description>
        <![CDATA[
            <em>Peter Morville &amp; Louis Rosenfeld</em>
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p><em>Peter Morville &amp; Louis Rosenfeld</em></p>
            
            <p><em>504 pages</em></p>
            
            <p><em>2006 O&#8217;Reilly Press</em></p>
            
            <p><a href="http://www.amazon.com/Information-Architecture-World-Wide-Web/dp/0596527349%3FSubscriptionId%3D0PZ7TM66EXQCXFVTMTR2%26tag%3Dhttplivollmne-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D0596527349"><img src="/images/2007/07/information-architecture.jpg" class="left"></a>I&#8217;ve recently become fascinated with some of the &#8220;softer&#8221; edges of my profession such as user interaction, visual design and information architecture. These are difficult topics to write about because their abstract nature makes it hard to provide useful, tangible information while not treating the subject as a simple set of boiled-down &#8220;how-tos&#8221;. A couple of weeks ago I picked up this book as a way to delve into more formalized information structure and theory. By and large this book did a good job of rounding-out those areas of knowledge for me.</p>
            
            <p>This book is in its third edition and I couldn&#8217;t help feeling at times like the authors were trying to justify that. While the table of contents lists six &#8220;parts&#8221;, I really felt the book was broken up into three major sections: the first is essentially a multi-chapter treatise on what IA is and why it&#8217;s important. The second section is some of the nuts-and-bolts theory, and the third section is about implementing IA with a couple of case studies. Of the three sections, the middle one was the most useful to me and, in my opinion, carried the others.</p>
            
            <p>I understand why the authors would feel compelled to write the first section&#8212;the intended audience is no doubt large and would certainly contain a number of skeptics. However I found the voice of these chapters to be a little too earnest and tried to hard to justify the profession. At times I wondered if the authors really had confidence in what they were talking about because they spent so much time justifying their statements. I was a bit surprised at the defensive posture of the opening chapters and can&#8217;t help but wonder if other readers won&#8217;t start wondering <em>what kind of uphill battle am I in for?</em></p>
            
            <p>Part two is where I felt like I learned the most&#8212;this is the real selling value of the book. Most of the information in this part of the book wasn&#8217;t new to me, but this section clearly articulated concepts I had previously only understood in abstract, internal terms. Concepts such as the inverse relation between relevance and recall, labeling, taxonomies, different relation types and thesauri were helpful to learn about in concrete terms. If nothing else, I feel like I&#8217;ve become more conversant in these topics.</p>
            
            <p>Part three, by comparison, felt like a bit of a letdown. It was dedicated mostly to strategy and tactics around implementing IA within an organization. I won&#8217;t argue that the information covered isn&#8217;t worthwhile, but it didn&#8217;t seem particularly exclusive to information architecture and therefore, in my opinion, was given far too much ink relative to the content-packed middle section. I also felt like the two case studies given at the end of the book lacked the clarity and energy of the middle section of the book. These did not feel like a particularly satisfying, put-it-all-together summary of the concepts covered.</p>
            
            <p>In all, the core information specific to IA was very enlightening and I can imagine that I&#8217;ll be thumbing through those chapters again. Also, I&#8217;m not sure I&#8217;ve read a professional trade paperback with quite so many cross-references (hint: that&#8217;s a good thing). Kudos to the authors for giving the reader <em>lots</em> of pointers to related materials. I just wish I could buy a version that only contained the 120 or so pages that I found most useful.</p>
            
            <p>3 out of 5 stars.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Daughters in Computing</title>
      <link>
        http://alexvollmer.com/posts/2007/07/04/daughters-in-computing/
      </link>
      <comments>
        http://alexvollmer.com/posts/2007/07/04/daughters-in-computing/#comments
      </comments>
      <pubDate>
        Wed, 04 Jul 2007 23:59:19 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            philosophy
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2007/07/04/daughters-in-computing/
      </guid>
      <description>
        <![CDATA[
            I'm a big podcast fan. There aren't many I listen to, but the few I enjoy accompany me and my dog on a near-daily basis. One of my favorites is Geoffrey Grossenbach's excellent <a href="http://podcast.rubyonrails.org/">Ruby On Rails Podcast</a>. In particular the last <a href="http://podcast.rubyonrails.org/programs/1/episodes/roundtable-women-in-open-source">two</a> <a href="http://podcast.rubyonrails.org/programs/1/episodes/roundtable-women-in-development-ii">episodes</a>, about women in computing, really got me thinking about education and technology. If you haven't had a chance to check them out, they are worth listening to.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>I'm a big podcast fan. There aren't many I listen to, but the few I enjoy accompany me and my dog on a near-daily basis. One of my favorites is Geoffrey Grossenbach's excellent <a href="http://podcast.rubyonrails.org/">Ruby On Rails Podcast</a>. In particular the last <a href="http://podcast.rubyonrails.org/programs/1/episodes/roundtable-women-in-open-source">two</a> <a href="http://podcast.rubyonrails.org/programs/1/episodes/roundtable-women-in-development-ii">episodes</a>, about women in computing, really got me thinking about education and technology. If you haven't had a chance to check them out, they are worth listening to.</p>
            
            <p>RailsConf 2007 had the best female turnout that I've observed by far of any conference I've attended&mdash;and sadly, it was still too little. I'm not talking about having gender diversity for the sake of diversity, but that the field of software is truly missing out on a lot of talented women who have been turned away for one reason or another. Why is this happening?</p>
            
            <p>I believe that it's a combination of education and culture that has made it this way. The curriculum that has formed around computers in schools is frankly appalling. By the time students leave high school very few have had the stomach to persevere through restricted access to machines, social stigma and bad teaching (no, learning MS Word does not count as computer education).</p>
            
            <p>Culturally, computing reveres the lonely genius who springs some great idea on us. Social interaction through and with computers has always been awkward and fringey, despite the success of things like IM, Twitter or MySpace. Within the so-called "geek-culture", independence is prized over group interaction. Frankly I think this turns a lot of girls off. The cultural fabric of computing has become so rooted in shielded individuals that it simply doesn't occur to many girls that getting involved with computing is something they might enjoy. I hold out hope that various agile methods will be adopted more widely with their emphasis on collaboration.</p>
            
            <p>So what can we do? I may get hate-posts for this but I'm not sure there is much we can do for the current high-school aged female population and beyond. Each person (male and female) has acclimated to gender norms in our culture and has enough experience that changing many attitudes would be difficult. I think we have to improve the curriculum, access and social attitudes at a much younger age.</p>
            
            <p>We are obligated to remove the cult of priesthood, masculinity and mystery from computing and put these wonderful tools in front of kids as soon as we can. So here's my proposal: since the field of computing is so male-dominated right now, fathers who work with computers owe it to their daughters to make extra-sure that they get exposed to computers. This means when you're working at home on some deathly-important thing and your little girl wants to type on the keyboard you stop what you're doing and let her type in a word-processor or text editor. This means that you let them play around with and click on things without telling them what to do. This means you don't scare them out of interacting with it. This means when they ask questions you try to answer them the best way you can.</p>
            
            <p>My four-year-old daughter likes to "type some letters" and "play the drums that I like" on my MacBook. This translates to saying and typing letters into TextMate and playing drums via the keyboard in GarageBand. I never say no. I always haul her up in my lap, close whatever else I'm doing and let her play, explore and ask questions. Yes, this once resulted in crashing the window manager on my Linux box, but that was okay. Just the simple tactile experience of touching the machine is important.</p>
            
            <p>Do I hope she follows in dad's footsteps? Not necessarily. I only hope that she's as fortunate as I am to have discovered what I like to do and can make a career out of it. What I certainly <em>don't</em> want is to have this avenue closed off to her as was the common experience of my wife and most of my female friends. It's going to take some time and effort to get girls engaged. We (computer nerds) desperately need them if we're to continue to innovate, learn and grow.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Why Can't Web Apps Be REST-ful?</title>
      <link>
        http://alexvollmer.com/posts/2007/06/27/why-cant-web-apps-be-rest-ful/
      </link>
      <comments>
        http://alexvollmer.com/posts/2007/06/27/why-cant-web-apps-be-rest-ful/#comments
      </comments>
      <pubDate>
        Wed, 27 Jun 2007 02:55:14 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            REST
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2007/06/27/why-cant-web-apps-be-rest-ful/
      </guid>
      <description>
        <![CDATA[
            In case the recent posts haven&rsquo;t made it obvious, I&rsquo;ve been on a bit of a tear about REST lately. In large part this is due to <a href="http://www.oreilly.com/catalog/9780596529260/">Leonard Richardson's and Sam Ruby's "RESTful Web Services"</a> book that I picked up at RailsConf. One of the thoughts that has been bouncing around in my head is how web service-oriented the book was and, besides a chapter about Ajax, said very little about how REST applied to user-facing <em>web applications</em>.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>In case the recent posts haven&#8217;t made it obvious, I&#8217;ve been on a bit of a tear about REST lately. In large part this is due to <a href="http://www.oreilly.com/catalog/9780596529260/">Leonard Richardson's and Sam Ruby's "RESTful Web Services"</a> book that I picked up at RailsConf. One of the thoughts that has been bouncing around in my head is how web service-oriented the book was and, besides a chapter about Ajax, said very little about how REST applied to user-facing <em>web applications</em>.</p>
            
            <p>I wondered to myself, &#8220;are browsers so broken that we can&#8217;t make REST-ful web applications?&#8221; I know that in the last year there have been times that I have really struggled with applying REST consistently to web applications. Things like the sign-up and sign-in process seem like square pegs trying to go into round holes. Should I have just given up on REST?</p>
            
            <p>Before we can answer this question we first have to tackle the sticky question of just what, in practice, makes a web app REST-ful or not. One of the obvious places to start is the URLs of your application. It feels pretty un-RESTful to have URLs like <code>/signup</code> or <code>/signin</code> since these are very verb-oriented&#8212;REST prefers an infinite array of nouns with a very constrained set of verbs.</p>
            
            <p>So how do URLs like <code>/signup</code> and <code>/signin</code> become REST-ful? One possibility is looking at what the underlying nouns are for these actions. The act of &#8220;signing up&#8221; for an account could be modeled as POSTing user information to a noun like <code>/accounts</code>&#8212;the resource we are attempting to create with the POST message is an account. Similarly, the act of &#8220;signing in&#8221; is really the act of creating some authorization within an application. We could POST our credentials to a resource like <code>/sessions</code> or <code>/credentials</code> to create an internal resource representing our session with the site. Now things get a little sticky when strictly adhering to REST-ful guidelines and trying to do authentication. That&#8217;s a whole other topic for a different post at a different time. Let&#8217;s say that with a little thought, you can create REST-ful URLs that still work quite well within a web application.</p>
            
            <p>The next thing to consider is one of the biggest places where strict adherence to REST falls down: the lack of support for any HTTP verbs beyond GET and POST within (X)HTML. Frankly it&#8217;s just shocking that we are in this predicament. I really hope that there&#8217;s either a good rationale or funny story for why things are this way. I really <em>really</em> hope that it gets fixed soon. Currently there are two ways around this: Ajax and the &#8220;_method&#8221; parameter hack a la <a href="http://www.rubyonrails.org/">Rails</a>.</p>
            
            <p>The first technique is reviewed in Richardson and Ruby&#8217;s book. It turns out that Ajax-driven applications fit pretty nicely with REST. Other than Safari/Konqueror, browsers can support other HTTP verbs quite easily with <code>XMLHttpRequest</code>. Because the Ajax client code shoulders the bulk of the responsibility for the UI, the backend resources can limit their returned representations to data that the Ajax clients work with. This gets rid of a lot of sticky problems that come with having to render fully-functional pages with each request.</p>
            
            <p>The second technique is essentially a hack, but one that allows to code our resources REST-fully. Frameworks like <a href="http://www.rubyonrails.org/">Rails</a> and <a href="http://www.restlet.org/">Restlet</a> do this for us automatically. Even if you are using another framework or rolling your own, you should be able to apply this little &#8220;hack&#8221; in a nice little corner of the request-response dispatching code without too much fuss. In short, I&#8217;m willing to live with this little turd to keep the rest of my system consistently organized.</p>
            
            <p>OK, we&#8217;ve got our nouns squared away with a consistent set of verbs. Well-factored nouns implies a good URI design (how else will we identify resources?) What else do we need to have in place to be REST-ful? Hmmm, how about proper state responsibility? You will often hear people say that REST-ful applications are stateless. I think this misses the point. The <em>HTTP protocol</em> is stateless, but your applications undoubtedly have state. Any app with a shopping cart has state. So the question then becomes, do user-facing web applications have different needs that necessitate a violation of REST regarding state?</p>
            
            <p>The short answer is probably not. By and large your application state is usually server-driven and ideally done via <a href="http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm">"hypermedia as the engine of application state"</a>. In practice this means that the current state of a user&#8217;s shopping cart is probably persisted in a shared store allowing any application node to serve the client&#8217;s request. Yes folks, a good bit of why REST guys harp on statelessness is for the sake of scalability.</p>
            
            <p>Things get a little grayer when we want to have small bits of saved state on the client for things like &#8220;remember me&#8221; features or other quick, token-based identification schemes. I won&#8217;t lie to you, I&#8217;ve used cookies a lot. I know the strict REST approach decries their use and characterizes them as the root of all internet evil, but if they weren&#8217;t so damn useful I&#8217;d be in complete agreement. Like real-life, having cookies often is probably not a good thing, but once in a while isn&#8217;t going to do any long-term damage.</p>
            
            <p>So, can user-facing web applications be REST-ful? Sure. Are there some compromises to be made along the way? Most likely. Does that mean that REST is an overly-idealistic approach that fails in real-world implementations? I would say not&#8212;however, your mileage may vary.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Book Review: Agile Retrospectives</title>
      <link>
        http://alexvollmer.com/posts/2007/06/23/book-review-agile-retrospectives/
      </link>
      <comments>
        http://alexvollmer.com/posts/2007/06/23/book-review-agile-retrospectives/#comments
      </comments>
      <pubDate>
        Sat, 23 Jun 2007 02:58:45 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            book review
        ]]>
      </category>
      <category>
        <![CDATA[
            agile
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2007/06/23/book-review-agile-retrospectives/
      </guid>
      <description>
        <![CDATA[
            <em>Esther Derby and Diana Larsen</em>
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p><em>Esther Derby and Diana Larsen</em></p>
            
            <p><a href="http://www.amazon.com/Agile-Retrospectives-Making-Teams-Great/dp/0977616649%3FSubscriptionId%3D0PZ7TM66EXQCXFVTMTR2%26tag%3Dhttplivollmne-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D0977616649"><img src="http://ecx.images-amazon.com/images/I/41UaBBxd3yL._SL500_AA240_.jpg" class="left"/></a>A new book from the <a href="http://www.pragmaticprogrammer.com/">Prags</a> rolled into the office this week called <a href="http://www.pragmaticprogrammer.com/titles/dlret/index.html">"Agile Retrospectives"</a>. I glanced at the title, saw the word &#8220;Agile&#8221;, looked at the size of the book (about 1/4&#8221;) and decided it was worth a read. I&#8217;ll admit that when I started the book I thought the title was &#8220;Agile Perspectives&#8221;, but quickly realized that the book was much more focused than just a loosely-related set of essays on Agile software methodology.</p>
            
            <p>This book is focused entirely on one subject: the techniques of retrospective meetings as an Agile practice. This is something I&#8217;ve done at other places, but usually not in a very structured fashion. Once I realized what the book was about I was ready to pick up a few tricks to sharpen the focus of future retrospectives.</p>
            
            <p>At times I felt a natural urge to recoil from the touchy-feely corporate speak that pervades the book. I&#8217;m just not a corporate rah-rah kind of guy and the thought of having posters up to remind us all to have S.M.A.R.T. goals (Specific, Measurable, Attainable, Realistic and Timely) makes we want to run out the</p>
            
            <p>room screaming. But I&#8217;ll cut the authors some slack&#8212;they are self-described &#8220;facilitators&#8221; and anyone who puts themselves in that role is going to be living and breathing that vernacular.</p>
            
            <p>At times this book reinforced the chronic, pseudo-scientific navel-gazing that strikes me as a particularly American corporate obsession. But taken as a whole, the book is worth a read if you are part of a team that is consciously striving to improve all the time. Some of the exercises struck me as a little childish, but overall there were some good tips.</p>
            
            <p>For example, many of the techniques focused on quick, simple ways to measure team attitudes about the state of the team. While the actual measurement may not particularly scientific, they can serve as a rough guides for the team mood and dynamics. One important thing to note here is that sometimes <em>anonymous</em> measurement is really important to get honest evaluations from team members.</p>
            
            <p>Is it worth a read? I think if you&#8217;re looking for some concrete techniques to improve retrospectives this books is worth a look.</p>
            
            <p>3 out of 5 stars.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Book Review: Don't Make Me Think</title>
      <link>
        http://alexvollmer.com/posts/2007/06/23/book-review-dont-make-me-think/
      </link>
      <comments>
        http://alexvollmer.com/posts/2007/06/23/book-review-dont-make-me-think/#comments
      </comments>
      <pubDate>
        Sat, 23 Jun 2007 02:58:34 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            book review
        ]]>
      </category>
      <category>
        <![CDATA[
            UX
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2007/06/23/book-review-dont-make-me-think/
      </guid>
      <description>
        <![CDATA[
            <em>Steve Krug</em>
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p><em>Steve Krug</em></p>
            
            <p><a href="http://www.amazon.com/Dont-Make-Me-Think-Usability/dp/0321344758%3FSubscriptionId%3D0PZ7TM66EXQCXFVTMTR2%26tag%3Dhttplivollmne-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D0321344758"><img src="http://ecx.images-amazon.com/images/I/51W8l2Zy3WL._SL160_.jpg" class="left"/></a>This book served as an <em>amuse-bouche</em> for my foray in to the softer side of application development: usability, design and information architecture. I have to give big kudos to the author for keeping the book focused and short. He has a pretty succint thesis to get across and doesn't pad the book unnecessarily to add weight. It's worth considering that the publisher probably printed the book on really high-quality paper to make the retail price fall in line with other professional titles. Hmmm...</p>
            
            <p>There is nothing terribly earth-shattering about the contents--the cover pretty much covers it. However, I did enjoy the sections covering user-testing, especially the part about doing it on the cheap. I think the mystery of usability has created a market for boutique firms that provide these services at a premium. Once people get hip to DIY usability-testing, I would forsee a "market correction" in the future.</p>
            
            <p>3 out 5 stars.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Book Review: RESTful Web Services</title>
      <link>
        http://alexvollmer.com/posts/2007/06/23/book-review-restful-web-services/
      </link>
      <comments>
        http://alexvollmer.com/posts/2007/06/23/book-review-restful-web-services/#comments
      </comments>
      <pubDate>
        Sat, 23 Jun 2007 02:58:21 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            book review
        ]]>
      </category>
      <category>
        <![CDATA[
            REST
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2007/06/23/book-review-restful-web-services/
      </guid>
      <description>
        <![CDATA[
            <em>Leonard Richardson &amp; Sam Ruby</em>
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p><em>Leonard Richardson &amp; Sam Ruby</em></p>
            
            <p><a href="http://www.amazon.com/RESTful-Web-Services-Leonard-Richardson/dp/0596529260%3FSubscriptionId%3D0PZ7TM66EXQCXFVTMTR2%26tag%3Dhttplivollmne-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D0596529260"><img src="/images/2007/06/restful-web-services.jpg" class="left"/></a>I picked this book up from the <a href="http://www.powells.com/%20A%20must-see%20site%20in%20Portland,%20OR">Powell's</a> booth while at <a href="http://conferences.oreillynet.com/rails/%20RailsConf%202007">RailsConf 2007</a>. I had been following the online manuscript for a while and as soon as I saw that the book was in print I snapped it right up. I wasn't the only one either. REST was a very hot topic at RailsConf and I saw a lot of other attendees with a copy under their arm.</p>
            
            <p>I've been keenly interested in REST for about the last year and a half. I've read</p>
            
            <p><a href="http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm%20Roy%20Fielding's%20Dissertation">Fielding's dissertation</a> a couple of times and read some good blogs, but really felt like REST was missing a more accessible, canonical resource. After completing this book, I think I've finally found one.</p>
            
            <p>The author's do an excellent job of distilling the principles of REST and describe an imlementation of it they term Resource-Oriented Architecture (ROA) which has specific recommendations. I'm pretty sure that my copy will be referred to time and again and that I will probably eventually re-read its contents. I only wish the authors had done more to address using REST in human-facing web applications, not just web services.</p>
            
            <p>4 out 5 stars.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Why REST Matters</title>
      <link>
        http://alexvollmer.com/posts/2007/06/13/why-rest-matters/
      </link>
      <comments>
        http://alexvollmer.com/posts/2007/06/13/why-rest-matters/#comments
      </comments>
      <pubDate>
        Wed, 13 Jun 2007 02:42:58 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            REST
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2007/06/13/why-rest-matters/
      </guid>
      <description>
        <![CDATA[
            I first ran across REST in my reading about a year and a half ago. While it took me some time to "get" what REST was, I quickly became a fan. However I've found that aside from pure aesthetics, I've had a hard time articulating why REST is not only beautiful, but effective. I've spent a little more time thinking about REST and I think I may have a couple of concrete arguments for REST that go beyond a simple appreciation of its beauty.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>I first ran across REST in my reading about a year and a half ago. While it took me some time to "get" what REST was, I quickly became a fan. However I've found that aside from pure aesthetics, I've had a hard time articulating why REST is not only beautiful, but effective. I've spent a little more time thinking about REST and I think I may have a couple of concrete arguments for REST that go beyond a simple appreciation of its beauty.</p>
            
            <h1>Uniformity of Interface</h1>
            
            <p><img src="http://farm2.static.flickr.com/1030/543372651_5e789e984d_t.jpg" alt="uniform interface" class="left"/></p>
            
            <p>When resources present a uniform interface, we can take advantage of this simplifying assumption. We can focus more on what we can do with resource rather than <em>how</em> we interact with them. This simple convention takes a lot of churn out of the development and integration process.</p>
            
            <p>Uniformity also constrains system design to the minimum required for distributed computing. Anything else is simply baroque elaboration that provides little to no value (think of SOAP's <code>SOAPAction</code> directive) and is nearly always at the cost of the uniformity.</p>
            
            <h1>Works with HTTP</h1>
            
            <p><img src="http://farm2.static.flickr.com/1127/543397887_6a3775ded5_t.jpg" alt="works with HTTP" class="left"/></p>
            
            <p>Assuming that you are going to provide data via the web, you are working within the constraints of HTTP. Since HTTP is an expression of the principles of REST, it is both foolish and counter-productive to go against the grain of HTTP's architecture. Unfortunately, HTTP is a very misunderstood protocol. It is sad that so few people who develop with HTTP understand it so very little.</p>
            
            <p>One of the least understood parts of HTTP are its caching mechanisms. Not only is caching misunderstood, it is terribly under-utilized. Most developers simply know caching as something to disable at all costs. This reflects an abuse of the protocol and an unnecessary effort expended to go against the grain.</p>
            
            <p>When you express your resources as distinct URLs, you can take advantage of the built-in caching semantics HTTP provides. A large reason (though not the only reason) that many web developers work so hard to turn off caching is because their applications are verb-oriented and tend to operate through one or a few separate URLs.</p>
            
            <p>Similarly, the semantics of content negotiation are also rarely well-understood let alone used in the wild. HTTP's content negotiation mechanism separates the concerns of <em>what</em> an underlying resource is from <em>how</em> it will be represented. This is a <em>good</em> separation of concerns&mdash;a user's account details are a user's account details regardless whether they are displayed in HTML, JSON or XML.</p>
            
            <h1>Connectivity</h1>
            
            <p><img src="http://farm2.static.flickr.com/1021/543273982_040b8addae_t.jpg" alt="connectivity" class="left"/></p>
            
            <p>Connectivity is the way a web application connects within itself. One of Roy Fielding's main assertions is that REST uses "hypermedia as the levers of application state". This is achieved by the linked hypermedia returned by a resource. Connectivity provides further simplifying assumptions about how a site is connected. Instead of relying on complicated specifications like WSDL, consumers of REST-ful applications can use a lot more of a web application based simple guidelines.</p>
            
            <h1>The Design Analogy</h1>
            
            <p><img src="http://farm2.static.flickr.com/1065/543374965_8d8543c413_t.jpg" alt="design" class="left"/></p>
            
            <p>Let's try to put resources in terms of object-oriented design. A classic symptom of poor object design is where the functional aspects of the system are the first-class attributes of the system and any "objects" are simply data structures passed from function to function. Good object-oriented design puts the behavior right next to the data with a healthy sprinkling of encapsulation.</p>
            
            <p>You wouldn't have a single function that took an arbitrary number of parameters that could return an arbitrary type or amount of data or perform an arbitrary functions. The complexity would spiral out of control quickly and you would want to create some kind of taxonomy. In object-oriented design these emerge as objects that describe distinct domain entities. Why shouldn't this apply to web applications and services?</p>
            
            <h1>Your Checklist for Winning REST Arguments Around the Watercooler</h1>
            
            <p>So let's sum this up into something you can print out, cut down to pocket-size and laminate and keep in your wallet:</p>
            
            <ul>
            <li>REST is about resources. There are an infinite number of these, but a very finite number of things you can do with them.</li>
            <li>This simplifying assumption makes client consumption of data simpler.</li>
            <li>This simplifying assumption makes for cleaner resource design.</li>
            <li>Since you're using HTTP, you might as well use it correctly. Read <a href="http://www.faqs.org/rfcs/rfc2616.html">the spec</a>!</li>
            <li>REST promotes connectivity. Express the connections between your resources and make your application more discoverable and navigable.</li>
            <li>REST prefers a separation of an abstract resource from its physical representation. This is good design.</li>
            </ul>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>My first RubyGem!</title>
      <link>
        http://alexvollmer.com/posts/2007/06/09/my-first-rubygem/
      </link>
      <comments>
        http://alexvollmer.com/posts/2007/06/09/my-first-rubygem/#comments
      </comments>
      <pubDate>
        Sat, 09 Jun 2007 17:41:58 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2007/06/09/my-first-rubygem/
      </guid>
      <description>
        <![CDATA[
            With a little help from Ryan Davis' Hoe and reading the actual documentation of the <code>rubyforge</code> command, I finally got a gem released for my little <a href="http://redhanded.hobix.com/bits/campingAMicroframework.html">Camping</a> app, <a href="http://chikkenbukket.rubyforge.org/">Chikkin In A Bukket</a>. Yaaayyy!
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>With a little help from Ryan Davis' Hoe and reading the actual documentation of the <code>rubyforge</code> command, I finally got a gem released for my little <a href="http://redhanded.hobix.com/bits/campingAMicroframework.html">Camping</a> app, <a href="http://chikkenbukket.rubyforge.org/">Chikkin In A Bukket</a>. Yaaayyy!</p>
            
            <p>Chikkin In A Bukket is a simple, lightweight web application for accessing your Amazon S3 account. There are a lot of interfaces to S3, I hope that other people will find this one easy to use.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Getting Leverage</title>
      <link>
        http://alexvollmer.com/posts/2007/06/01/getting-leverage/
      </link>
      <comments>
        http://alexvollmer.com/posts/2007/06/01/getting-leverage/#comments
      </comments>
      <pubDate>
        Fri, 01 Jun 2007 05:03:02 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            philosophy
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2007/06/01/getting-leverage/
      </guid>
      <description>
        <![CDATA[
            Right after getting back from <a href="http://conferences.oreillynet.com/rails/">RailsConf</a> I headed off to a family trip to Southern California. I only just got back this weekend and I feel like I haven't quite had the time to fully digest the conference. As a result I've had a lot of odd thoughts rattling around in my head, most of which have failed to form into anything cogent. Well, except for one..
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>Right after getting back from <a href="http://conferences.oreillynet.com/rails/">RailsConf</a> I headed off to a family trip to Southern California. I only just got back this weekend and I feel like I haven't quite had the time to fully digest the conference. As a result I've had a lot of odd thoughts rattling around in my head, most of which have failed to form into anything cogent. Well, except for one..</p>
            
            <p>One of the important takeaways I got from the conference wasn't specific to Ruby or Rails, but rather a larger concept of leverage. It occurred to me that the people and organizations that are really shaping our technological lives are doing so by a judicious application of force. Our world is simply to complex to blindly apply raw force to problems. Instead it seems to me that the thought-leaders are finding and using force-mulitpliers of various types to build the future. Three examples of this pop into my head. Whether or not they constitute the best examples is debatable, but I believe that they all share a common underlying principle.</p>
            
            <p>The first is Ruby on Rails. I'm not here to sing the praises of Rails and tell you that you should do all your web dev in it. I happen to like Rails and think it's got a lot going for it. But for the purposes of this post I want to talk about the choice by its creator in using the Ruby programming language as the foundation for Rails. For the Rails team, the choice to use Ruby was crucial to the success of the framework. Not everyone likes Ruby and that's fine. But it <em>is</em> a high-leverage language. I'm not aware of anyone who has adopted its use and <em>not</em> become more productive.</p>
            
            <p>The Rails team and <a href="http://www.loudthinking.com/">David Heinemeier Hansson</a> deserve a lot of credit for developing Rails, but a lot of that credit should go towards the decisions they made about the tools they built and their choice of technologies to rely on. Rails isn't worth looking at because it's an ingenious piece of engineering, Rails is worth looking at because it applies maximum force with minimal effort.</p>
            
            <p>The second example is Google. It's hard to imagine web-life without Google. But what makes Google so special is not simply that they apply sheer mass of brain-power to sheer mass of computing-power (though it doesn't hurt). Instead it seems to me that Google has made the major effort up-front to build the world's must powerful clustered system (i.e. their core search engine), then they have leveraged both the data <em>and</em> the infrastructure from that core for their other services. The things they are doing with <a href="http://docs.google.com/">Docs &amp; Spreasheets</a> or <a href="http://code.google.com/apis/gears/">Google Gears</a> wouldn't be possible without leveraging that first asset and these types of products are clearly part of the next generation of services.</p>
            
            <p>My final example is similar to Google, and that is <a href="http://aws.amazon.com/">Amazon Web Services</a>. Much like Google, Amazon has built an impressive infrastructure to power their core retail business. At first glance AWS seems totally unrelated to that core business. But a second look reveals some pretty smart thinking.</p>
            
            <p>What Amazon has on its hands is a massive amount of computing power setup across the globe. Amazon's chief asset is the very infrastructure that its retail business is built on. AWS is simply the commercially-available version of that infrastructure. Whether Amazon has excess computing power that it's selling or it has figured out how to grow that power is largely irrelevant to this discussion. I'm assuming that Bezos and his minions are smart enough to figure out a way to sell this service and not make it a loss-leader. The important point here is that AWS could be a very disruptive technology. The flexibility and scalability that AWS provides is something that simply doesn't exist at a price-point where <em>anyone</em> can use it.</p>
            
            <p>I don't think Amazon's retail business is going to shrivel and blow away, but I think the creation of AWS reveals some shrewd long-term planning for the overall health of the company. Clearly Amazon is attempting to diversify and it is doing so by taking advantage of its greatest assets. The server infrastructure Amazon is the force-multiplier behind AWS.</p>
            
            <p>So while these three examples may seem a little disparate and scatter-brained I think they reflect an overall emerging trend in high-level thinking. Think of job interviews you may have had where someone wanted you to implement a merge-sort algorithm or write a database connection pool. I understand the mechanical intent behind these questions (does the candidate have basic technical skills?), but the answers that are typically sought miss a more important point. If I were to give that question to a prospective candidate I would hope they would answer with one line of code that calls a built-in sorting routine that comes with whatever platform we're using. I simply <em>don't care</em> that they can write a merge-sort. I can't imagine working on a project where its success was dependent on us writing sorting algorithms. So I'm much more concerned with whether or not they know how to use it and what it means in context.</p>
            
            <p>The sheer amount of information to master in our technical fields is daunting. Frederick Brooks acknowledges this in the 20th anniversary edition of "The Mythical Man Month" by lamenting the fact that he has simply had to stop following a number of developments in the field. We can't keep worrying about bit-shifting and low-level algorithms and hope to make any progress. While there has to be <em>somebody</em> who worries about this stuff, we need a much larger population that leverages these tools to build something truly wonderful. In short, let's find those things that let us get maximum force with minimal effort. For me right now, those things are tools like Ruby and my beloved MacBook Pro. As time goes on I'm sure I'll new tools to supplant these. The important thing is to keep evolving and adapting to find that leverage.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Day Three RailsConf '07 -- Summary</title>
      <link>
        http://alexvollmer.com/posts/2007/05/20/day-three-railsconf-07-summary/
      </link>
      <comments>
        http://alexvollmer.com/posts/2007/05/20/day-three-railsconf-07-summary/#comments
      </comments>
      <pubDate>
        Sun, 20 May 2007 19:05:28 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <category>
        <![CDATA[
            rails
        ]]>
      </category>
      <category>
        <![CDATA[
            railsconf
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2007/05/20/day-three-railsconf-07-summary/
      </guid>
      <description>
        <![CDATA[
            The day opened with a disappointing keynote speech from Tim Bray. He was upfront about the fact that Sun wrote a big check to big a sponsor and proceeded to launch, unapologetically, into a forty-five minute advertisement of Sun and just how great their servers and JRuby are.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>The day opened with a disappointing keynote speech from Tim Bray. He was upfront about the fact that Sun wrote a big check to big a sponsor and proceeded to launch, unapologetically, into a forty-five minute advertisement of Sun and just how great their servers and JRuby are.</p>
            
            <p>In contrast the opening act, Cyndi Mitchell of ThoughtWorks, had a much better presentation that called out to the Ruby and Rails community <a href="http://conferences.oreillynet.com/cs/rails2007/view/e_sess/14493">to "take back the enterprise"</a>. Her slide deck and presentation were fantastic. Anyone who crosses John Travolta&#8217;s awful &#8220;Battlefied Earth&#8221; with Dick Cheney gets big points in my book. I hope they post them somewhere.</p>
            
            <h1>Memcache</h1>
            
            <p>This feels like the year the conference has gotten more &#8220;serious&#8221;. To that end, a consistent underlying theme of the whole conference has been scalability and performance. I&#8217;ll walk away from this conference with a new and, hopefully, enlightened view of the whole software/hardware ecosystem. It ain&#8217;t just about Rails folks. A real production app has a lot more moving parts than what&#8217;s on your dev box.</p>
            
            <p>My first morning session was Chris Wanstrath&#8217;s presentation about Rails and memcache. I wasn&#8217;t real close so I didn&#8217;t get a good look at the dude, but from where I was sitting I&#8217;d swear he looked an awful lot like Shaun White (aka &#8220;the Flying Tomato&#8221;).</p>
            
            <p>I loved the fact that his first slide in his presentation was titled <a href="http://c2.com/xp/YouArentGonnaNeedIt.html">"YAGNI"</a>. Memcache is such a neat tool with such a clear, appealing design that it&#8217;s easy to be wooed by it. Chris made an excellent point that you shouldn&#8217;t even <em>think</em> about using memcache until you have some real numbers backing up your need.</p>
            
            <p>Chris showed off the <a href="http://require.errtheblog.com/plugins/browser/cache_fu"><code>cache_fu</code></a> plugin which looked like it did everything short of make you breakfast. I&#8217;ll definitely have to spend some quality time with the docs on that. He also hipped us to <a href="http://www.last.fm/user/RJ/journal/2007/04/10/392555/">libketama</a> which is a cool replacement for memcache&#8217;s default host-hashing algorithm. It allows you to dynamically add and remove memcache nodes without invalidating your entire memcache cluster.</p>
            
            <p>What&#8217;s brilliant about memcache is just how dumb it is. That dumbness keeps it simple to manage and deploy. That means though, that there is a back-pressure in your system to use the tool wisely. However it seems like a fair trade-off, especially when you consider that any decent performance-enhancing tool should force you to think about what the hell you&#8217;re doing.</p>
            
            <h1>Getting Real Numbers</h1>
            
            <p>The next session was put on by Julian Boot of ThoughtWorks, a hyper-active guy who was clearly more enthusiastic about his talk than about 90% of the presenters at the conference. He started his presentation of with a bang by making the claim that no off-the-shelf testing framework or product would be able to do proper performance testing for your application. Because setting up a test harness is (relatively) cheap and each team/environment/application is different, this is the one case where rolling-your-own makes more sense.</p>
            
            <p>He had a couple of interesting concepts that I hadn&#8217;t thought of. He suggested including a 90th-percentile measurement in your summary stats (y&#8217;know, min, max, avg) since max response times can jack your average. Seems like a helpful data-point to me.</p>
            
            <p>He also didn&#8217;t want to fool around with configuring several test nodes so that reporting stats would land in a single place. His answer was to simply have each test node send its stats out via UDP broadcast. A single stat-gathering &#8220;master&#8221; would listen on the broadcast address and simply write the packet contents (serialized samples) to a flat file for processing. In a switched network the likelihood of losing UDP packets is pretty low. I loved it the solution&#8212;it was brilliantly dumb and took about five lines of code.</p>
            
            <p>His last simple suggestion was to have each test client node output a single character indicating its state. I&#8217;ve seen a lot of processes that do this and the output is just noise. What makes this different though, and why I like it, is that you are actually interested in what&#8217;s going on at that moment. Often this kind of output is misapplied because it isn&#8217;t helpful until the process finishes and the output is too terse to be of much help.</p>
            
            <p>One of his key points is that writing a test harness is relatively simple. That hard part is the analysis and decision making made after the fact. While writing the harness might be fun, it&#8217;s a fraction of the time you will spend writing the ugly little test actions that will simulate what your users are doing. Julian stated that you know your test harness is correct when your test logs look like your production logs.</p>
            
            <h1>Nice answers to dumb questions</h1>
            
            <p>In between the morning keynote and the first session, I had a thirty minute window to get caffeinated and catch up on email. I only had to do the first, didn&#8217;t want to bother with second, and needed to find something to entertain myself until the next session. In the main foyer I noticed that DHH was chatting with a few folks. After a drive-by skulk realized that they were talking about one of my favorite topics, REST.</p>
            
            <p>During the conference I&#8217;ve been noodling around with the resources and URLs in a little side project I&#8217;m working on and I was getting really stuck on a satisfying way to apply REST-ful modeling to sessions and credentials. Since sessions are really managed by cookies, it&#8217;s kind of a round-peg-in-a-square-hole problem when applied to REST. So I walked up and posed this question to DHH directly. He hipped me to the <a href="http://api.rubyonrails.org/classes/ActionController/Resources.html#M000177"><code>map.resource</code></a> call in the routing configuration and the concept of &#8216;singleton resources&#8217;. We chatted a little bit more, I thanked him and headed off to my next session.</p>
            
            <p>Later, I started going over the Rails docs to get hip to this singleton resource concept. I realized that my question was on the edge of warranting a RTFM answer, but David was gracious enough to take the time to describe his solution and what the mechanism was. I have to say that I was really impressed with how helpful and gracious he was. I know that people sometimes want to treat DHH as a rockstar, but I think part of what has made Rails successful is the lack of a deeply-nested hierarchy that separates the ivory-tower types from the great unwashed masses. It&#8217;s comforting to know that you can pose a n00b question to someone like David and get a polite, helpful answer. I have great hope for this platform and this community.</p>
            
            <h1>RejectConf 2007</h1>
            
            <p>Living in Seattle I&#8217;m really fortunate to have access to such an <a href="http://www.zenspider.com/Languages/Ruby/Seattle/index.html">active group of Rubyists</a>. One fellow Seattle-ite, Ryan Davis, put on <a href="http://blog.zenspider.com/archives/2007/05/rejectconf_2007_final_details.html">RejectConf</a> which was described as a &#8220;gong show&#8221; style presentation forum where people get up and give very short (&lt; 5min) presentations on things they&#8217;ve been working on.</p>
            
            <p>The near-anarchic atmosphere (fueled by beer and snacks graciously donated by Addison-Wesley) gave a great energy to the gathering. Most of the ideas were pretty interesting, some were fringey, but it was a great forum to see what the rest of the community is up to. Oh yeah, a big shout-out to <a href="http://freegeek.org/">Free Geek</a> for providing the space.</p>
            
            <h1>Day Three Takeaway</h1>
            
            <p>By the end of the day I came away feeling like I have some serious wood-shedding to do. I&#8217;ve only been doing Ruby for about nine months so I don&#8217;t have a problem with this. For years I&#8217;ve relied on being an experienced Java programmer to give me that nice warm feeling of comfort and confidence. However that has also led to a sneaking feeling of gathering dust. I jumped onto Ruby because it reached a good head of steam right as I was looking for something to push myself in new ways. Right now, I&#8217;m okay with being a total n00b dork to learn something new.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Day Two Rails Conf '07 -- Summary</title>
      <link>
        http://alexvollmer.com/posts/2007/05/19/day-two-rails-conf-07-summary/
      </link>
      <comments>
        http://alexvollmer.com/posts/2007/05/19/day-two-rails-conf-07-summary/#comments
      </comments>
      <pubDate>
        Sat, 19 May 2007 16:55:26 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <category>
        <![CDATA[
            rails
        ]]>
      </category>
      <category>
        <![CDATA[
            railsconf
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2007/05/19/day-two-rails-conf-07-summary/
      </guid>
      <description>
        <![CDATA[
            After the keynote speech by David Heinemeier Hansson, I attended several session that felt disparate at the time, but magically coalesced into a meaningful takeaway by day's end. In short the day was about testing, REST and Amazon's suite of Web Services.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>After the keynote speech by David Heinemeier Hansson, I attended several session that felt disparate at the time, but magically coalesced into a meaningful takeaway by day's end. In short the day was about testing, REST and Amazon's suite of Web Services.</p>
            
            <h1>Getting Religion With Uncle Bob</h1>
            
            <p>Update 2007-05-19: Robert posted a PDF of his slides <a href="http://www.objectmentor.com/resources/articles/Clean_Code_Args.pdf">here</a>.</p>
            
            <p>The first session I attended was one titled "Clean Code" presented by Robert C. Martin. I will admit that I'm only familiar with his name as being the "signature" endorsement on some of my favorite texts by the likes of Kent Beck and Martin Fowler. After hearing him speak I can see why he has endorsed their writing and would include him into the small team of pragmatic agile bandits consisting of the aforementioned Kent and Martin as well as Ward Cunningham.</p>
            
            <p>The point of the talk was essentially about constant vigilance. Putting off fixing smelly code today only leads to much more expensive changes later&mdash;either in the form of grand redesigns (a bad idea) or in incrementally paying down the debt you've incurred. The answer is to use the tried-and-true  weapons at your disposal are a refined nose, testing and refactoring.</p>
            
            <p>He walked through a pretty good test-driven development (TDD) example that I suspect surprised a good bit of the crowd. I say 'surprising' because when someone who <em>doesn't</em> use TDD sees it for the first time, it looks very dogmatic and slow. People who don't do TDD get really hung up on this, but fail to understand what underpins the approach: humility. TDD is really based on the notion that we <em>all</em> write legacy code&mdash;<em>no one</em> is brilliant enough to get it right the first time.</p>
            
            <p>Another hang-up point for first-timers is the use of the term "test-first". I'm surprised at how often people think that means your write a <em>comprehensive</em> set of tests before you write any code. This, of course, is nonsense. Just like writing a detailed specification with pseudo-code up front is not a practical pre-cursor to a healthy system realization. I've always preferred the term "test-driven" as I think it sheds a little more light on another of unit-testing's benefits which is another form of system feedback.</p>
            
            <p>Sometimes you have a test that is so ugly that it alone will make you refactor code even though your production consumers may not really be suffering in the same way. Why would you do this? Test-code isn't production code, why let it force change? My answer is that you change the code because of the test for the same reason you change code used by the production system: if it hurts to understand or modify you have found a smell. If you let that smell linger, you will start incurring "debt" on your project.</p>
            
            <p>While I didn't get any real juicy new bits from Uncle Bob's talk, he was an extremely entertaining speaker who made and excellent presentation. If you ever get a chance to hear him speak, it's worth it.</p>
            
            <h1>Pushing the Boundaries of Rails Testing</h1>
            
            <p>The next session was put on by Jay Fields with ThoughtWorks. I think he probably had some very good points to make but without a few visual examples a lot of his points were lost on the audience. However he did make some points that brought questions for me about testing.</p>
            
            <p>When writing Java I've found that my tests and classes are quite fine-grained compared to the Rails code I test. In part a lot of that is to enforce a stronger separation between domain-level code and external resources like RDBMS persistence. When you use <code>ActiveRecord</code> in Rails, your model is welded directly to the database. When I first saw this as a Java programmer my nose wrinkled up and I was extremely suspicious about this approach.</p>
            
            <p>To my surprise testing <code>ActiveRecord</code> classes directly against a database has been much less painful than I suspected. I think this probably has to do with supporting structures such as rake tasks and fixtures that don't require you to drop down to SQL just to test your model. On the other hand, if your code is going to evolve to anything moderately sophisticated I think it's possible to exceed the capacities of these facilities. It's something to pay attention to for sure.</p>
            
            <p>Jay also threw out a couple of other thoughts that caused unease in the audience:
            *     While your code under test is object-oriented, your tests don't have to be
            *  Repeating yourself in your production code it bad, but it's okay in your tests
            *  The value of <code>private</code> and <code>protected</code> methods in Ruby is debatable</p>
            
            <p>Like books that get banned or musicians that get called up in front of congressional committees, the very things that give you unease are probably worth addressing head-on rather than sweeping under the rug. At the end of the session, my final thoughts were that we have a long way to go in evolving the craft of testing.</p>
            
            <h1>REST</h1>
            
            <p>I had about ten minutes to kill between sessions and I was desperate for a free cup of coffee. In a stroke of marketing genius the conference organizers moved the coffee bar from breakfast area to the vendor's floor show. Alright, I'm game. I'll look at your ads to get a free cuppa joe.</p>
            
            <p>Most of the booths held no interest until I stumbled across the Powell's Books stand. There before my eyes were stack of geek books. The night before I made the trek out to Powell's via Portland's delightful MAX transit system. If you've never been to Powell's do yourself a favor and go. For geeks, the Technical Bookstore simply can't be beat.</p>
            
            <p>Anyway, I figured there wouldn't be anything new there that I hadn't seen the night before at the Mothership. Of course, I was wrong. There before my eyes was shiny new O'Reilly book titled "REST Web Services". I had been aware that Leonard Richardson and Sam Ruby were working on this book, but had lost track of when they were actually going to publish. I spent a little quality time with the chapter on S3 and my hopes were confirmed&mdash;this is going to be a good book.</p>
            
            <p>REST has gotten a lot of press lately, but I think a lot of people really don't have a good grasp of the concepts. To this point the only literature has either been Roy Fielding's original dissertation and a few derivatives of that paper. I think Fielding's paper is definitely with reading. It's very dense and repays the reader with each read. However, for REST to truly take hold the community really needs a thoughtful source that boils down the essence of the architectural style in a consumable format. Given the eagerness with which REST is being embraced and the dearth of practical resources on this topic, I would expect this book to be a well-loved, dog-eared volume on a lot of desks.</p>
            
            <h1>Amazon Web Services</h1>
            
            <p>We took a long break from the geeks and had a fine dinner at <a href="http://www.mcmenamins.com/index.php?loc=57&amp;category=Location%20Homepage">McMenamins Kennedy School</a>. We returned to the conference and headed off to a late-night BOF session about Amazon's S3 Service. The turnout seemed pretty good. After a brief dog-and-pony show from an Amazon rep the floor was opened to the attendees. Despite the size of the session, only a handful or participants were actually using S3 in production&mdash;everyone else was curious to see how it was being used.</p>
            
            <p>While Amazon won't expressly claim that S3 can replace content distribution networks (e.g. Akamai), they aren't scaring people away from it. I would venture to guess that %50% of the attendees wanted to know if they could store assets in S3 that they could serve back up directly to their client. My impression is that Amazon is trying to evolve S3 to this kind of platform. This could be really big and is definitely going to be something I'm going to keep my eyes on.</p>
            
            <h1>Post-Game Show</h1>
            
            <p>I needed a little ramp-down time before I turned off the light for night-night time. I cracked open the "REST Web Services" book and started reading the chapter about S3 until my eyes drooped and I slipped into deep relaxation. As I slipped into unconsciousness, a few final thoughts on the day coalesced:
            *     If Rails is a big simplifying assumption around web apps, REST is a big simplifying assumption around web services
            *     While the Rails community is pretty good at embracing testing, the craft has a long way to go (this is true of <em>all</em> software platforms)
            *     AWS has the potential to make a fundamental <em>economic</em> and <em>technical</em> shift in the way large web applications are deployed</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Day Two RailsConf '07 -- DHH's Keynote Address</title>
      <link>
        http://alexvollmer.com/posts/2007/05/18/day-two-railsconf-07-dhhs-keynote-address/
      </link>
      <comments>
        http://alexvollmer.com/posts/2007/05/18/day-two-railsconf-07-dhhs-keynote-address/#comments
      </comments>
      <pubDate>
        Fri, 18 May 2007 21:43:06 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <category>
        <![CDATA[
            rails
        ]]>
      </category>
      <category>
        <![CDATA[
            railsconf
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2007/05/18/day-two-railsconf-07-dhhs-keynote-address/
      </guid>
      <description>
        <![CDATA[
            This is my first RailsConf so I can only say this given second-hand information. But it seems that I've arrive just as the interest and popularity in the framework has hit the bend in the hockey-stick. The enthusiasm at the morning keynote address by David Heinemeir Hanssson was equals parts technology preview, religious revival and idol worship.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>This is my first RailsConf so I can only say this given second-hand information. But it seems that I've arrive just as the interest and popularity in the framework has hit the bend in the hockey-stick. The enthusiasm at the morning keynote address by David Heinemeir Hanssson was equals parts technology preview, religious revival and idol worship.</p>
            
            <p>I&#8217;ve always found the use of &#8216;DHH&#8217; to be a little off-putting as it always smacked of a strange geek cult-of-personality. However given the length of the guy&#8217;s name, I can see why he went with the initials. At least it&#8217;s better than some obtuse l33t h4x0r handle.</p>
            
            <p>Anyway&#8230;</p>
            
            <p>Let me give you a quick 30,000 foot overview of things coming in Rails 2.0.</p>
            
            <h1>REST-ful Resources are the New World Order</h1>
            
            <blockquote><p> "The world of resources is a better, greener place" &mdash; DHH</p></blockquote>
            
            <p>REST will be the default way things are done in 2.0, meaning that there will be more default notions built-in to support the style.</p>
            
            <h2>Controllers</h2>
            
            <div class="highlight"><pre><span class="k">def</span> <span class="nf">create</span>&#x000A;      <span class="vi">@person</span> <span class="o">=</span> <span class="no">Person</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:person</span><span class="o">]</span><span class="p">)</span>&#x000A;      <span class="n">respond_to</span> <span class="k">do</span> <span class="o">|</span><span class="nb">format</span><span class="o">|</span>&#x000A;        <span class="nb">format</span><span class="o">.</span><span class="n">html</span> <span class="p">{</span> <span class="n">redirect_to</span><span class="p">(</span><span class="vi">@person</span><span class="p">)</span> <span class="p">}</span>  <span class="c1"># person_url(person)</span>&#x000A;    &#x000A;        <span class="nb">format</span><span class="o">.</span><span class="n">xml</span> <span class="k">do</span>&#x000A;          <span class="o">.</span><span class="n">.</span><span class="o">.</span>&#x000A;        <span class="k">end</span>&#x000A;      <span class="k">end</span>&#x000A;    <span class="k">end</span>&#x000A;    </pre>
            </div>
            
            
            <p>Look at how simple it is to render a the <code>@person</code> object. Isn&#8217;t that lovely?</p>
            
            <h2>Views</h2>
            
            <p>Views get cleaner by using a single <code>form_for</code> that understands updated vs. new creations (as well as the default URLs for that resource and actions). You no longer have to &#8220;drop-down&#8221; on the bare metal and expressly configure your <code>form_for</code> declarations with the correct URL and HTTP verbs.</p>
            
            <h2>Routes</h2>
            
            <p>REST-ful routing is getting a bit of a makeover, adding some more flexibility within the otherwise liberating constraints of REST.</p>
            
            <div class="highlight"><pre><span class="c1"># /admin/products/inventory</span>&#x000A;    <span class="c1"># /admin/products/5/tags</span>&#x000A;    <span class="c1"># /admin/products/5/seller</span>&#x000A;    <span class="n">map</span><span class="o">.</span><span class="n">namespace</span><span class="p">(</span><span class="ss">:admin</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">admin</span><span class="o">|</span>&#x000A;      <span class="n">admin</span><span class="o">.</span><span class="n">resources</span> <span class="ss">:products</span><span class="p">,</span>&#x000A;        <span class="ss">:collection</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="ss">:inventory</span> <span class="o">=&gt;</span> <span class="ss">:get</span> <span class="p">},</span>&#x000A;        <span class="ss">:member</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="ss">:duplicate</span> <span class="o">=&gt;</span> <span class="ss">:post</span> <span class="p">},</span>&#x000A;        <span class="ss">:has_many</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="ss">:tags</span><span class="p">,</span> <span class="ss">:images</span><span class="p">,</span> <span class="ss">:variants</span> <span class="p">},</span>&#x000A;        <span class="ss">:has_one</span> <span class="o">=&gt;</span> <span class="ss">:seller</span>&#x000A;    <span class="k">end</span>&#x000A;    </pre>
            </div>
            
            
            <h1>9 other things to like about Rails 2.0</h1>
            
            <p>The best part of the keynote was the preview of new features in Rails 2.0. There is enough good-looking stuff in here that I&#8217;m probably going to move my main Rails side-project to Edge Rails and feel the good lovin&#8217; on the bleeding edge.</p>
            
            <h2>Breakpoints are Back!</h2>
            
            <p>This was broken by a fix made in Ruby 1.8.5. The takeaway of course is not to implement features based on hacks.</p>
            
            <h2>ruby-debugger</h2>
            
            <p>One of the few embarrassing things about developing in Rails is the lack of a first-class debugger. So far I&#8217;ve been able to work with Rails quite well without having a debugger. In part this is because using a Test-driven approach means the debugger is usually the last tool you reach for rather than the first. But I will have to admit that I have had to &#8220;devolve&#8221; into using logging or <code>puts</code> statements to dump what was going on in the running app.</p>
            
            <p>Thanks to the inclusion of the <a href="http://rubyforge.org/forum/forum.php?forum_id=7778">ruby-debug</a> into the core you can actually do real debugging in Rails. With this new technology you can set a breakpoint in your code with a single <code>debugger</code> line in your code. When the code is hit your console will drop into a command-line interface where you can:
              * View your place in the stack
              * Modify objects in place
              * This looks like it&#8217;s gonna rock!</p>
            
            <h2>HTTP Performance</h2>
            
            <h3>JS/CSS Bundling</h3>
            
            <p>There is a tension between segmenting JS and CSS into separate files and HTTP performance. As a developer you want to separate our Javascript and CSS to keep your head straight. But the performance of downloading those assets suffers given the de-facto two-connections per site limitation in browsers. Rails 2.0 will have a way to send a single request to Rails for a collection of JS and CSS at once and it will returned as a single gzipped bundle.</p>
            
            <div class="highlight"><pre><span class="o">=</span> <span class="n">javascript_include_tag</span> <span class="ss">:all</span><span class="p">,</span> <span class="ss">:cache</span> <span class="o">=&gt;</span> <span class="kp">true</span> <span class="sx">%&gt;</span>&#x000A;    <span class="sx">= stylesheet_link_tag :all, :cache =&gt;</span> <span class="kp">true</span> <span class="o">%&gt;</span>&#x000A;    </pre>
            </div>
            
            
            <h3>Connection limits</h3>
            
            <p>JavaScript and CSS aren&#8217;t the only assets where you run into the two-connection limit. Any other static assets (particularly images) can kill you here. Rails 2.0 will have support for a type of name-based virtual hosting for your assets so that you can &#8221;trick&#8221; the browser into using more simultaneous connections to download content. You can set this in your config:</p>
            
            <div class="highlight"><pre><span class="n">config</span><span class="o">.</span><span class="n">action_controller</span><span class="o">.</span><span class="n">asset_host</span> <span class="o">=</span> <span class="s1">&#39;asset%d.example.com&#39;</span>&#x000A;    </pre>
            </div>
            
            
            <h2>Query cache for <code>ActiveRecord</code></h2>
            
            <p>This new feature sniffs the SQL being executed and looks between calls for the exact same SQL. If any UPDATEs or DELETEs were not executed between queries that affected those records AR will return a cached version of the objects.</p>
            
            <h2>MIME type handling</h2>
            
            <p>In the past the means to generate content and its format were mixed into the name (ex. <code>index_rss.rhtml</code>). Rails 2.0 has made some naming changes to better call out the separation between the type of the final content and the means by which it was generated.</p>
            
            <p>Now view files look like [template].[format].[rendering]. For example:</p>
            
            <pre><code>* people/index.html.erb  # Use ERB to generate HTML&#x000A;    * people/index.xml.builder  # Use builder to generate XML&#x000A;    * people/index.rss.erb  # Use ERB to generate RSS&#x000A;    * people/index.atom.builder  # Use builder to generate Atom&#x000A;    </code></pre>
            
            <h2>Configuration initializers</h2>
            
            <p>Application-specific configuration that used to go into <code>config/environment.rb</code> now goes into separate files in the <code>config/initializers</code> directory. I haven&#8217;t felt the pain of this, but I can certainly imagine how this might be helpful.</p>
            
            <h2>Sexier Migrations</h2>
            
            <p>A typical migration looks something like this:</p>
            
            <div class="highlight"><pre><span class="n">create_table</span> <span class="ss">:users</span> <span class="k">do</span> <span class="o">|</span><span class="n">u</span><span class="o">|</span>&#x000A;      <span class="n">u</span><span class="o">.</span><span class="n">user_name</span> <span class="ss">:type</span> <span class="o">=&gt;</span> <span class="ss">:text</span><span class="p">,</span> <span class="ss">:null</span> <span class="o">=&gt;</span> <span class="kp">false</span>&#x000A;      <span class="n">u</span><span class="o">.</span><span class="n">first_name</span> <span class="ss">:type</span> <span class="o">=&gt;</span> <span class="ss">:text</span>&#x000A;      <span class="n">u</span><span class="o">.</span><span class="n">last_name</span> <span class="ss">:type</span> <span class="o">=&gt;</span> <span class="ss">:text</span>&#x000A;      <span class="n">u</span><span class="o">.</span><span class="n">age</span> <span class="ss">:type</span> <span class="o">=</span> <span class="o">&gt;</span><span class="ss">:integer</span>&#x000A;    <span class="k">end</span>&#x000A;    </pre>
            </div>
            
            
            <p>See all the redundancies? Why keep specifying the <code>:type</code> parameter. Why not reverse it like this?</p>
            
            <div class="highlight"><pre><span class="n">create_table</span> <span class="ss">:people</span> <span class="k">do</span> <span class="o">|</span><span class="n">t</span><span class="o">|</span>&#x000A;      <span class="n">t</span><span class="o">.</span><span class="n">integer</span> <span class="ss">:account_id</span>&#x000A;      <span class="n">t</span><span class="o">.</span><span class="n">string</span> <span class="ss">:first_name</span><span class="p">,</span> <span class="ss">:last_name</span><span class="p">,</span> <span class="ss">:null</span> <span class="o">=&gt;</span> <span class="kp">false</span>&#x000A;    <span class="k">end</span>&#x000A;    </pre>
            </div>
            
            
            <h2>HTTP Authentication</h2>
            
            <p>Because REST-ful web services have become much more first-class in Rails, the need to support Basic HTTP Authentication has become important. HTTP Basic Auth for browsers and user-facing applications just plain sucks: there is no notion of &#8220;logging out&#8221; and there&#8217;s no control of the user interface. However for web services HTTP Basic Auth is fine. Rails 2.0 provides a new <code>authenticate_or_request_with_http_basic</code> method that can be used in controllers. This is very nice for computers interfacing to your APIs</p>
            
            <h2>Spring cleaning</h2>
            
            <p>Remember those deprecation warnings you see in the console when you run your app? Yeah, it&#8217;s time to address those and clean them up. Those deprecated features are going to be going away in Rails 2.0. Fix your code. Don&#8217;t say you weren&#8217;t warned.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Day One, Afternoon RailsConf 2007 -- Patterns to DRY Up Your Views</title>
      <link>
        http://alexvollmer.com/posts/2007/05/18/day-one-afternoon-railsconf-2007-patterns-to-dry-up-your-views/
      </link>
      <comments>
        http://alexvollmer.com/posts/2007/05/18/day-one-afternoon-railsconf-2007-patterns-to-dry-up-your-views/#comments
      </comments>
      <pubDate>
        Fri, 18 May 2007 04:20:35 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <category>
        <![CDATA[
            rails
        ]]>
      </category>
      <category>
        <![CDATA[
            railsconf
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2007/05/18/day-one-afternoon-railsconf-2007-patterns-to-dry-up-your-views/
      </guid>
      <description>
        <![CDATA[
            Update: 2007-05-19 &mdash; Bruce and Marcel posted a PDF of their talk <a href="http://codefluency.com/assets/2007/5/18/VisForVexing.pdf">here</a>.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>Update: 2007-05-19 &mdash; Bruce and Marcel posted a PDF of their talk <a href="http://codefluency.com/assets/2007/5/18/VisForVexing.pdf">here</a>.</p>
            
            <p>After knocking down lunch I was faced with a conundrum. I had signed up for a tutorial on view-layer patterns, but saw that Jamis Buck was doing a tutorial on <a href="http://www.capify.org/%20Capistrano">Capistrano</a>. When I registered originally I had no idea that Cap 2.0 was coming out and I didn't really care to get any more familiar with Cap 1.0. But between then and now Cap 2.0 came out and it looks like there is some good stuff to learn. But the view tutorial looked good too. What to do? What to do?</p>
            
            <p>The tutorial that Bruce Williams and Marcel Molina gave for refining the view layer was excellent. I think a lot of the content seemed to go over the heads of the audience--they seemed to get a little too wrapped up in the ugly details rather than the important over-arching points.</p>
            
            <p>The first point was that the view-layer deserved as much expressiveness and thoughtful design as we put in our models and controllers. In essence, we prefer expressing the <em>what</em> rather than the <em>how</em> in our code. Why not apply that to the view layer? This is general concept that I hope to explore in a later post.</p>
            
            <p>They started with a brief survey of other view layer technologies such as ERB, <a href="http://redhanded.hobix.com/inspect/markabyForRails.html%20Markaby">Markaby</a>,<a href="http://haml.hamptoncatlin.com/%20HAML"> HAML</a> and others. However the takeaway from this review was that most of these technologies were focused on cutting down on the amount of typing necessary to do the templating, but didn't really provide a means to increase domain-level expressiveness. The approach proposed by Bruce and Marcel was to embrace ERB/RHTML and use the power of Ruby to factor out complications within the view.</p>
            
            <p>One way to achieve this factoring is paying attention to the natural tensions that emerge as you evolve your application. When you find that certain names come up over and over again in describing the system with your customer, you probably need something with that name in your system.</p>
            
            <p>Another way to do this is to naturally evolve your view. Using regular ol' ERB in RHTML is probably a great way to start out. However once you start getting things in your templates like calls to finders, conditional logic, calls to Enumerable methods or temporary variables, it's time to push that work into a terse, but descriptive, method in your view helper.</p>
            
            <p>When you accumulate enough of these helper methods that share state or particular strategies, it's probably time to make it into an Object. Normally people think of Objects as belonging strictly to the domain layer which (wrongly) implies the use of ActiveRecord. Don't get hung up on this. Objects are powerful ways to encapsulate concepts. Don't hesitate to use them.</p>
            
            <p>You can use helpers as the "glue" between your view templates and these newly-emergent, sophisticated objects that are cleaning up your view. Again, we want to focus on the <em>what</em>, not the <em>how</em>.</p>
            
            <p>So, perhaps a way to consider the evolution of view layer encapsulation is like so:</p>
            
            <p>ERB in templates > Helper methods > first-class Objects</p>
            
            <p>You need to use your past experience and your sense of smell to figure out when to evolve. Picking some complicated Object strategy up front is probably doomed to not meet future needs and will require even more re-work to replace it with something else.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Day One, Rails Conf 2007 -- Morning Session</title>
      <link>
        http://alexvollmer.com/posts/2007/05/17/day-one-rails-conf-2007-morning-session/
      </link>
      <comments>
        http://alexvollmer.com/posts/2007/05/17/day-one-rails-conf-2007-morning-session/#comments
      </comments>
      <pubDate>
        Thu, 17 May 2007 20:22:16 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            ruby
        ]]>
      </category>
      <category>
        <![CDATA[
            rails
        ]]>
      </category>
      <category>
        <![CDATA[
            railsconf
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2007/05/17/day-one-rails-conf-2007-morning-session/
      </guid>
      <description>
        <![CDATA[
            Update: 2007-05-19 &mdash; Jason posted a PDF of his presentation <a href="http://media.joyent.com/JHoffmanRailsConf-May2007.pdf">here</a>.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>Update: 2007-05-19 &mdash; Jason posted a PDF of his presentation <a href="http://media.joyent.com/JHoffmanRailsConf-May2007.pdf">here</a>.</p>
            
            <p>Jason Hoffman, CTO of Joyent, gave a (mostly) fascinating session on scalability of Ruby on Rails. Here's the news-flash: Rails is only a tiny part of a large scale deployment. He started off the talk with a couple of points I really take to heart: large-scale apps are not achieved in their first iteration. They are realized in the fifteenth to twentieth iterations. Put another way, he said that "...the road to a top site on the internet is not from one iteration". A premature decision to mold the architecture without a real pain point <em>and</em> test data to back it up is as likely (if not more likely) to send you down the path to hell that solve any real problem.</p>
            
            <p>One of the first eye-openers in Jason's talk was the consideration of power consumption. I'll admit that this is a topic that I never considered, but according to the presenter it can fundamentally shape your physical deployment. In essence the intersection of cost and legal limits to power consumption in a physical space can limit what you can put in a rack more than anything else. In some cases co-location facilities may actually waste space because they simply can't power any more machines than are occupying a fraction of their total space! In short, the goal is to cram as much computational power into as small a space using as little power as possible.</p>
            
            <p>The middle section of the talk left my mind wandering as Jason rattled off performance metrics around the Solaris servers that they used. I got the sense that my mind wasn't the only one wandering at this point.</p>
            
            <p>After the break, the talk hit high-gear and finished with a bang. Jason began to get into the nitty-gritty deployment strategies that they used at Joyent. Now we were all sitting up straight in our chairs and paying attention.</p>
            
            <p>First, they do a <em>lot</em> of Big IP work. Lots of layer 7 (application) routing rules to partition the work in all sorts of interesting ways--even load-balancing to mongrel that handle a <em>single controller</em>. Instead of putting mongrel instances behind Apache (which Jason claims maxes out to about 150 reqs/second with Apache and mod_proxy), they load-balance <em>thousands</em> of mongrel instances directly behind Big IP.</p>
            
            <p>They don't use plain old vanilla mongrel either. Instead they are using the new <a href="http://brainspl.at/articles/2007/05/12/event-driven-mongrel-and-swiftiply-proxy%20a%20fancy%20new%20mongrel">event-driven, non-threaded mongrel</a>. In addition, they are running this inside of four virtual machines on a single box. A typical hardware configuration is a 16 GB RAM machine with 4 AMD CPUs. They run four virtual machines with ten mongrel instances each for a total of forty mongrel instances on a single box.</p>
            
            <p>Why virtualization? According to Jason, as you consolidate computing power into a single box, you may miss the "sweet spot" of smaller hardware that most software has been written for. You can recapture that with virtualization. It's a fascinating idea that I hadn't really considered. Of course you wouldn't dare do any of this without some real testing and numbers to back it up.</p>
            
            <p>Speaking of testing, Jason had an interesting approach. Start with a single mongrel instance and test it with some benchmarking tool like <a href="http://www.hpl.hp.com/research/linux/httperf/%20httperf%20--%20the%20benchmarking%20tool">httperf</a>. Find out what a <em>single</em> mongrel instance can do in terms of requests per second. Get an idea of how much CPU and memory that single instance uses. Now multiply it by some number such that the total resources consumed by your mongrel instances won't exceed your hardware. Now fire up that number of mongrel instances and benchmark each instance <em>separately but simultaneously</em> and monitor what your hardware is doing.</p>
            
            <p>You want to see that adding another mongrel instance provides a proportional level of capacity. Once you've hit the limits of that piece of hardware you know what a single box should do. As Jason stated several times during the session, you want things to "add up". That is, adding another 50 mongrel instances to the overall deployment should provide a commensurate level of capacity.</p>
            
            <p>Now that you know what a single node can do, you should be able to put any number of them behind a front-end load balancer and get a total throughput equal to the capacity of a single node times the number of nodes. If you are getting <em>significantly less</em> total throughput than you would expect, you probably need to revisit or refine your front-end load-balancing solution.</p>
            
            <p>This ties into another interesting point made during the presentation. Rails is just a small part of the overall application. Questions about whether the Rails stack will scale are somewhat silly since the Rails stack comprises only a small part of an overall application deployment.</p>
            
            <p>Of course, some of those other parts are things like persistent storage. He brought up several examples of how Joyent has moved some responsibilities off of the shoulders of the RDBMS and pushed them to other technologies including LDAP, Memcache, message buses and even the file system. I'm firmly convinced that it's pretty easy to max out the capabilities of RDBMS systems. They have a very hard job to do and adding lots of complicated clustering and scaling only make that job harder. Using a heterogeneous approach where certain kinds of data are stored via different kinds of mechanisms struck me as a very pragmatic approach.</p>
            
            <p>One final note was about DNS partitioning. If caching provides a way to avoid expensive operations over and over again, partitioning is way to spread operations across your hardware. Jason was a big fan of using DNS and Big IP routing to spread load across hardware.</p>
            
            <p>After a morning spent hopped up on cold-medicine and two cups of coffee (how else do you think I keep my girlish figure) I hope I can keep my stamina up for the afternoon session and BOFs tonight. Somehow I think the combination of readily-available caffeinated beverages and major geek convergence going on at the Portland Convention Center will carry me through.</p>
            
            <p>Next post: a review of the afternoon tutorial: "When V is for Vexing: Patterns to DRY Up Your Views"</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Leading Off</title>
      <link>
        http://alexvollmer.com/posts/2007/05/15/leading-off/
      </link>
      <comments>
        http://alexvollmer.com/posts/2007/05/15/leading-off/#comments
      </comments>
      <pubDate>
        Tue, 15 May 2007 19:45:33 PDT
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            blog
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2007/05/15/leading-off/
      </guid>
      <description>
        <![CDATA[
            Hi folks, and welcome to the new location of my blog. I've retired the old <a href="http://blog.feedbagnews.net%20The%20old%20feedbag%20blog">feedbag blog</a> and started this one since the topics I want to write about have grown beyond the scope of working on feedbag. Working on feedbag was a great experience, but I have since started new side-projects and a fantastic new job, thereby putting feedbag on the back-burner.
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p>Hi folks, and welcome to the new location of my blog. I've retired the old <a href="http://blog.feedbagnews.net%20The%20old%20feedbag%20blog">feedbag blog</a> and started this one since the topics I want to write about have grown beyond the scope of working on feedbag. Working on feedbag was a great experience, but I have since started new side-projects and a fantastic new job, thereby putting feedbag on the back-burner.</p>
            
            <p>I was finally motivated to setup a new blog because I'm heading off to <a href="http://conferences.oreillynet.com/rails/%20Official%20RailsConf%202007%20site">RailsConf 2007</a> and I wanted to have a clean slate to post about my experience there. This event seemed like a good time to make a break with the past and start over.</p>
            
            <p>So stay tuned. I'll be heading off to RailsConf this week and I'll begin posting starting from day one.</p>
        ]]>
      </content:encoded>
    </item>
    <item>
      <title>Using Closures To Support Object-Oriented AJAX</title>
      <link>
        http://alexvollmer.com/posts/2005/12/14/using-closures-to-support-object-oriented-ajax/
      </link>
      <comments>
        http://alexvollmer.com/posts/2005/12/14/using-closures-to-support-object-oriented-ajax/#comments
      </comments>
      <pubDate>
        Wed, 14 Dec 2005 16:27:47 PST
      </pubDate>
      <dc:creator>
        alex
      </dc:creator>
      <category>
        <![CDATA[
            javascript
        ]]>
      </category>
      <category>
        <![CDATA[
            AJAX
        ]]>
      </category>
      <guid isPermaLink='false'>
        http://alexvollmer.com/posts/2005/12/14/using-closures-to-support-object-oriented-ajax/
      </guid>
      <description>
        <![CDATA[
            <em>updated 1/23/2006</em>
        ]]>
      </description>
      <content:encoded>
        <![CDATA[
            <p><em>updated 1/23/2006</em></p>
            
            <p>Coming from a Java background, I can't resist turning any moderately involved JavaScript into a collection of objects that encapsulate data and behavior. A majority of the JavaScript that I have come across is decidedly non-object oriented and tends to be a library of functions that are attached to various event handlers. This style works well with AJAX, but loses the advantages of cohesive object-oriented design. I won't re-hash the OO vs. functional programming argument here, but it must be pointed out that if you want OO design in your JavaScript when using AJAX, you need to understand closures. This article describes why they are important and how they work.</p>
            
            <p>Just what the heck are closures? Closures are a way of creating function definitions with a particular scope chain attached to them. Closures allow <a href="http://en.wikipedia.org/wiki/Lexical_environment#Static_scoping">lexical scoping</a> to work correctly with nested functions. For a more refined (and perhaps accurate) definition, see this <a href="http://jibbering.com/faq/faq_notes/closures.html">exhaustive discussion of closures in JavaScript</a>.</p>
            
            <p>If you are creating JavaScript classes and object instances of those classes, you know that liberal use of the <code>'this'</code> keyword is what binds your object together and gives it cohesion. However when using AJAX the <code>'this'</code> keyword starts to take on different meaning because it is no longer refers to the same object it referred when you setup your <code>XmlHttpRequest</code>.</p>
            
            <p>Let's explore some examples. These all use the <a href="http://prototype.conio.net/">prototype framework</a> developed by Sam Stephenson.</p>
            
            <p>In our examples below we have a <code>User</code> object that has a reference to its HTML element (the 'div' argument passed in the constructor). As we add more functionality to the <code>User</code> we like having that direct reference to the div corresponding with the JS object.</p>
            
            <p>The last line in the constructor calls the <code>retrieve()</code> method which fires off an AJAX request. When that request has returned, we want to populate our div element with the response text using the <code>load()</code> method.</p>
            
            <div class="highlight"><pre><span class="kd">function</span> <span class="nx">User</span><span class="p">(</span><span class="nx">username</span><span class="p">,</span> <span class="nx">password</span><span class="p">,</span> <span class="nx">div</span><span class="p">)</span> <span class="p">{</span>&#x000A;      <span class="k">this</span><span class="p">.</span><span class="nx">username</span> <span class="o">=</span> <span class="nx">username</span><span class="p">;</span>&#x000A;      <span class="k">this</span><span class="p">.</span><span class="nx">password</span> <span class="o">=</span> <span class="nx">password</span><span class="p">;</span>&#x000A;      <span class="k">this</span><span class="p">.</span><span class="nx">div</span> <span class="o">=</span> <span class="nx">div</span><span class="p">;</span>&#x000A;      <span class="k">this</span><span class="p">.</span><span class="nx">retrieve</span><span class="p">();</span>&#x000A;    <span class="p">}</span>&#x000A;    &#x000A;    <span class="nx">User</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">update</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">req</span><span class="p">)</span> <span class="p">{</span>&#x000A;      <span class="k">this</span><span class="p">.</span><span class="nx">div</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">+=</span> <span class="nx">req</span><span class="p">.</span><span class="nx">responseText</span><span class="p">;</span>&#x000A;    <span class="p">}</span>&#x000A;    &#x000A;    <span class="nx">User</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">retrieve</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>&#x000A;      <span class="kd">var</span> <span class="nx">myAjax</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Ajax</span><span class="p">.</span><span class="nx">Request</span><span class="p">(</span><span class="s1">&#39;/users/demo&#39;</span><span class="p">,</span>&#x000A;        <span class="p">{</span> <span class="nx">method</span><span class="o">:</span><span class="s1">&#39;get&#39;</span><span class="p">,</span> &#x000A;           <span class="nx">onComplete</span><span class="o">:</span><span class="k">this</span><span class="p">.</span><span class="nx">update</span>&#x000A;        <span class="p">});</span>&#x000A;    <span class="p">}</span>&#x000A;    </pre>
            </div>
            
            
            <p>Note the <code>'onComplete'</code> property of the anonymous object given as the last parameter to the <code>Ajax.Request</code>. This is a pointer to our function that will handle the response. Since this is all happening within our <code>User</code> instance, we should still have a reference to that all-important div element that ties our object to the UI.</p>
            
            <p>Unfortunately this doesn't work. When the <code>Ajax.Request</code> calls the function specified in the <code>'onComplete'</code> property, you get a JS error saying that 'this' doesn't have a property called 'update'. Why? Because in this context, 'this' is no longer your <code>User</code> object--it is the <code>XMLHttpRequest</code> object instead.</p>
            
            <p>How can that be? Because the response for the <code>Ajax.Request</code> is executing in a separate execution context in the browser, the meaning of <code>'this'</code> has changed. The <code>'this'</code> reference is no longer defined by its lexical scope (the <code>User</code> instance), but by its dynamic scope.</p>
            
            <p>OK, so if <code>'this'</code> changes meaning across contexts, perhaps we can fool the JS engine.</p>
            
            <div class="highlight"><pre><span class="nx">User</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">retrieve</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>&#x000A;      <span class="kd">var</span> <span class="nx">_this</span> <span class="o">=</span> <span class="k">this</span><span class="p">;</span> <span class="c1">// don&#39;t overload the meaning of &#39;this&#39;</span>&#x000A;      <span class="kd">var</span> <span class="nx">myAjax</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Ajax</span><span class="p">.</span><span class="nx">Request</span><span class="p">(</span><span class="s1">&#39;/users/demo&#39;</span><span class="p">,</span> &#x000A;        <span class="p">{</span> <span class="nx">method</span><span class="o">:</span><span class="s1">&#39;get&#39;</span><span class="p">,</span> &#x000A;           <span class="nx">onComplete</span><span class="o">:</span> <span class="nx">_this</span><span class="p">.</span><span class="nx">update</span>&#x000A;        <span class="p">});</span>&#x000A;    <span class="p">}</span>&#x000A;    </pre>
            </div>
            
            
            <p>Instead of referring to <code>'this'</code> in our <code>'onComplete'</code> property we refer to a new variable <code>'_this'</code>. The <code>'_this'</code> variable that points to <code>'this'</code> which, in this particular context, refers to our <code>User</code> instance.</p>
            
            <p>Close but no cigar. You will still get JS errors saying that the <code>'this'</code> reference inside the <code>update()</code> method does not have a reference to a div (as in <code>'this.div'</code>). Hmmm, we seem to be making progress--now at least we're calling the right method, but <code>'this'</code> still isn't referring to our instance.</p>
            
            <p>Now what's going on? Because of JavaScript's dynamic nature, even getting a reference to an object instance's method doesn't mean that <code>'this'</code> always refers to the instance object. Unlike Java where <code>'this'</code> <em>always</em> refers to the instance containing the method, <code>'this'</code> means different things in JavaScript. This is somewhat analagous to Python's <code>'self'</code> reference which essentially passes in the object instance reference to method.</p>
            
            <p>Back in JavaScript, <code>'this'</code> changes meaning depending on the context in which it is used. So how can we get enough context around the reference to <code>update()</code> to make the instance method run properly?</p>
            
            <div class="highlight"><pre><span class="nx">User</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">retrieve</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>&#x000A;      <span class="kd">var</span> <span class="nx">_this</span> <span class="o">=</span> <span class="k">this</span><span class="p">;</span> <span class="c1">// don&#39;t overload the meaning of &#39;this&#39;</span>&#x000A;      <span class="kd">var</span> <span class="nx">myAjax</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Ajax</span><span class="p">.</span><span class="nx">Request</span><span class="p">(</span><span class="s1">&#39;/users/demo&#39;</span><span class="p">,</span>&#x000A;        <span class="p">{</span> <span class="nx">method</span><span class="o">:</span><span class="s1">&#39;get&#39;</span><span class="p">,</span> &#x000A;           <span class="nx">onComplete</span><span class="o">:</span><span class="kd">function</span><span class="p">(</span><span class="nx">req</span><span class="p">)</span> <span class="p">{</span> <span class="nx">_this</span><span class="p">.</span><span class="nx">update</span><span class="p">(</span><span class="nx">req</span><span class="p">);</span> <span class="p">}</span> &#x000A;        <span class="p">});</span>&#x000A;    <span class="p">}</span>&#x000A;    </pre>
            </div>
            
            
            <p>Enter the Closure. Now the <code>'onComplete'</code> property refers to a little anonymous function that, when called by <code>Ajax.Request</code>, will have a reference to our <code>User</code> instance (<code>_this</code>) and will call the update method on it. Now we have built enough context around the entire <code>User</code> instance to get it's instance methods to run.</p>
            
            <p>Note that this is very different from doing something like this:</p>
            
            <div class="highlight"><pre><span class="nx">User</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">retrieve</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>&#x000A;      <span class="kd">var</span> <span class="nx">myAjax</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Ajax</span><span class="p">.</span><span class="nx">Request</span><span class="p">(</span><span class="s1">&#39;/users/demo&#39;</span><span class="p">,</span>&#x000A;        <span class="p">{</span> <span class="nx">method</span><span class="o">:</span><span class="s1">&#39;get&#39;</span><span class="p">,</span> &#x000A;           <span class="nx">onComplete</span><span class="o">:</span><span class="k">this</span><span class="p">.</span><span class="nx">update</span><span class="p">(</span><span class="nx">req</span><span class="p">)</span> &#x000A;        <span class="p">});</span>&#x000A;    <span class="p">}</span>&#x000A;    </pre>
            </div>
            
            
            <p>This won't work at all because the <code>update()</code> method will be called as JavaScript evaluates the <code>Ajax.Request</code> constructor.</p>
            
            <p>OK, now it's working, but having lots of little anonymous functions throughout your code is a little ugly. Fortunately, prototype provides an extension to the <code>Function</code> class in the form of the <code>bind()</code> method.The <code>bind()</code> returns a function that applies itself to the given parameter when invoked.</p>
            
            <div class="highlight"><pre><span class="nx">User</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">retrieve</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>&#x000A;      <span class="kd">var</span> <span class="nx">myAjax</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Ajax</span><span class="p">.</span><span class="nx">Request</span><span class="p">(</span><span class="s1">&#39;/users/demo&#39;</span><span class="p">,</span>&#x000A;        <span class="p">{</span> <span class="nx">method</span><span class="o">:</span><span class="s1">&#39;get&#39;</span><span class="p">,</span> &#x000A;           <span class="nx">onComplete</span><span class="o">:</span><span class="k">this</span><span class="p">.</span><span class="nx">update</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="k">this</span><span class="p">)</span>&#x000A;        <span class="p">});</span>&#x000A;    <span class="p">}</span>&#x000A;    </pre>
            </div>
            
            
            <p>Closures are nifty ways of getting around the fact that the object on the other end of the <code>this</code> keyword changes depending scope. If you want write JavaScript objects like you write Java objects, you need be careful with  the <code>this</code> keyword.</p>
        ]]>
      </content:encoded>
    </item>
  </channel>
</rss>

