<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">
 
 <title>JohnWilger.com</title>
 
 <link href="http://johnwilger.com/" />
 <updated>2009-02-07T08:04:00+00:00</updated>
 <id>http://johnwilger.com/</id>
 <author>
   <name>John Wilger</name>
   <email>johnwilger@gmail.com</email>
 </author>
 
 
 <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/johnwilger" /><feedburner:info uri="johnwilger" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><entry>
   <title>Retrospective Facilitation</title>
   <link href="http://feedproxy.google.com/~r/johnwilger/~3/0i1dBbyyYgQ/retro-facilitation.html" />
   <updated>2009-01-13T00:00:00+00:00</updated>
   <id>http://johnwilger.com/2009/01/13/retro-facilitation</id>
   <content type="html">&lt;p&gt;I facilitated my second release retrospective with the ProjectDX team yesterday. The first, back in October, went well enough given that I was asked to facilitate at the start of the retrospective and had no time to prepare; but yesterday’s retrospective went great since I knew ahead of time that I would be facilitating.&lt;/p&gt;

&lt;p&gt;The activities we used during the retrospective mainly came from Diana Larsen and Esther Derby’s book, Agile Retrospectives. We started off with the “ESVP” (Explorer, Shopper, Vacationer, Prisoner) activity to find out what the group’s interest level was in relation to the work we were doing in the retrospective. Nearly everyone in the group said they were an Explorer via anonymous vote. With this group, I basically trust that outcome, although I did wonder if anyone may have given the answer they thought they were “supposed” to give.&lt;/p&gt;

&lt;p&gt;Next, I had the team build a time line of events from the past release. Prior to the start of the retrospective, I drew the time line on our white board and divided it into sections for each iteration (our cycle is 2 3-week sprints followed by a 2-week, internal Alpha testing period and a 2-week customer Beta testing period). The result looked something like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  |------------------|------------------|--------------|--------------|
  | Sprint 1         | Sprint 2         | Alpha        | Beta         |
  |                  |                  |              |              |
  |                  |                  |              |              |
  |                  |                  |              |              |
  |                  |                  |              |              |
  |                  |                  |              |              |
  |                  |                  |              |              |
11/03 ------------ 11/24 ------------ 12/15 -------- 12/29 -------- 01/09
  |                  |                  |              |              |
  |                  |                  |              |              |
  |                  |                  |              |              |
  |------------------|------------------|--------------|--------------|&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;During the retrospective, I had everyone take some time to think about any events during that time period that were meaningful to them in any way, write the event on a sticky note then stick the note on the time line in roughly the right spot. We spent about 45 minutes generating events, during which time we were free to look back at calendars and email as well as the commit logs from our SCM. Having set the time box to 45 minutes, I said it was fine for people to take a break if they felt like they couldn’t remember any more events and asked everyone to simply be back on time for the next step.&lt;/p&gt;

&lt;p&gt;Rather than specifying that people work in pairs, I opted to have the team self-organize here. It was interesting to see how—at first—everyone seemed to work on their own, but as the more obvious events were posted to the board, people started working in groups to come up with more data. At the end of the allotted time, we had a white board full of events.&lt;/p&gt;

&lt;p&gt;The area below the time line was intended for a graph of energy/emotion levels, which I’ll talk about in a moment, but JD Huntington had a great idea to also add to that area a rough bar graph of our commit volume as reported by GitHub’s activity graphs.&lt;/p&gt;

&lt;p&gt;After everyone had a last opportunity to look at the board and add any last-minute stickies, I asked everyone to take a turn at the board and use markers to place a blue dot next to any event which they felt positive about and a red dot next to any event that they felt negative about. Then, I asked them to think about their positive and negative energy/emotion levels throughout the release and draw a line graph in the bottom section to signify their ups and downs throughout the release cycle. At this point, we looked at each iteration in the cycle and I asked the team to make observations about the data. Using bullet lists at the bottom of the white board, we captured the observations as further data points for analysis.&lt;/p&gt;

&lt;p&gt;After a break for lunch, we used the “Patterns &amp; Shifts” activity to generate insights about the data on the time line. We gathered around the white board where I asked each person to mark on the time line any point at which they felt there was a shift or transition of some sort and to draw lines between any events, graph points and observations that they felt were somehow related.&lt;/p&gt;

&lt;p&gt;After no one was able to see any more connections, I asked the team to look for any patterns among the connections and the shifts. The first thing we noticed was that we felt like the real shift between iterations was constantly happening about 3 days after the actual time box ended. Rather than get into a long discussion about it when it was noticed, I asked the team to simply keep it in mind as a possible concern during the next phase of the retrospective.&lt;/p&gt;

&lt;p&gt;The next observation was that we had a number of connections with long lines spanning two or more iterations. I asked everyone to focus on these connections and look for those where we may have noticed an issue early on but it wasn’t addressed until much later. In some cases it turned out that while that was the issue, the length of the line just signified that either the work took that long or there was a conscious decision not to address the issue right away.&lt;/p&gt;

&lt;p&gt;Reflecting on the other cases led to an interesting discovery, however. Chris noticed that where the lines crossed the bounds of the iterations it was likely signifying a parallel cycle which we were attempting to force into our release cycle: specifically, we have both an explicit product release cycle and an inexplicit customer deployment cycle occurring at the same time.&lt;/p&gt;

&lt;p&gt;After we were finished recognizing patterns in the data, it was time to discuss these patterns (and anything else on our minds) and come up with a few action items for the next release cycle. Here I ran the team through the “Circle of Questions” activity. In this activity, the group sits in a circle, and each person takes turns asking a question to the person on their immediate left. The question can be about anything they like (barring anything offensive or attacking), but it’s helpful to focus on the insights gained during the previous stage of the retrospective. The person to the left answers the question to the best of their ability, and then they ask the person to &lt;em&gt;their&lt;/em&gt; left any other question (or the same question if they feel they’d like a better answer). This continues until the alloted time is up or you have gone around the entire circle twice, whichever comes &lt;em&gt;last&lt;/em&gt;. Make sure you go around the complete circle: if some people in the group get more turns to ask or answer a question than others, it can send the wrong message.&lt;/p&gt;

&lt;p&gt;The thing that I really like about the Circle of Questions activity is that when you have a mixed group of people—some of whom tend to dominate the conversation while others tend to stay quiet—it gives &lt;em&gt;everyone&lt;/em&gt; a chance to ask their questions and have their opinions heard without feeling like the most boisterous people have the only valuable input. We certainly have a mix like that in our case, and this activity worked wonderfully.&lt;/p&gt;

&lt;p&gt;In the end, we came away with a number of things to change for the next release cycle. It’s interesting to note that none of our action items were directly related to the insights generated from the time line. I think the insights are still valuable (and are contained in the notes which we post to our internal wiki), so we may still come back to them in the near future; we were running over on time and had to wrap up even though the discussion could have continued longer.&lt;/p&gt;

&lt;p&gt;Before we held this retrospective, I think some of the team members felt that we would not gain much from doing a full-day retrospective this time around. We had made good progress on our action items from the previous retrospective, and the general feeling was that things were going basically well. What could we possibly need to change?&lt;/p&gt;

&lt;p&gt;People, please don’t hold retrospectives only when your team feels like there’s a problem that needs to be dealt with. If you hold retrospectives on a regular basis, whether you feel like you “need” to or not, you will uncover issues &lt;em&gt;before&lt;/em&gt; the become problems. In our case, we managed to uncover a number of issues during our retrospective that could easily have grown into much larger problems if we didn’t bother to think about them until the effect was obvious.&lt;/p&gt;

&lt;p&gt;If you &lt;em&gt;do&lt;/em&gt; have obvious issues, then you might choose a different set of activities than what we did for this retrospective. The time line activity is great for discovering potential improvements even when everything seems to be going well, so I suggest giving it a try the next time your team is tempted to skip a retrospective because no one thinks there’s any huge problems.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/johnwilger/~4/0i1dBbyyYgQ" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://johnwilger.com/2009/01/13/retro-facilitation.html</feedburner:origLink></entry>
 
 <entry>
   <title>2008 Year-End Review</title>
   <link href="http://feedproxy.google.com/~r/johnwilger/~3/Jj5LpITavKA/2008-year-end-review.html" />
   <updated>2008-12-31T00:00:00+00:00</updated>
   <id>http://johnwilger.com/2008/12/31/2008-year-end-review</id>
   <content type="html">&lt;p&gt;Wow! Just…wow!&lt;/p&gt;

&lt;p&gt;I can’t believe 2008 is already over. The older I get, the faster the years seem to fly by; I’m not sure I like the trend. The effect mainly tells me that I need to concentrate more on living my life &lt;em&gt;intentionally&lt;/em&gt;, so that I have a better memory of my everyday experiences.&lt;/p&gt;

&lt;p&gt;That’s not to say that I don’t remember 2008—this has been a year of big changes and lots of neat experiences. The year started off with a plan to take a job in Dublin, Ireland and move the whole family over there with me. For various reasons, that didn’t work out. We stayed in Portland where I opened up shop as an independent consultant. Finally, after working with a number of wonderful clients, I decided to accept a job offer from one of them; and I’m once again an employee.&lt;/p&gt;

&lt;p&gt;Probably my most important achievement of 2008 was to completely, 100% quit smoking cigarettes. I had actually quit in the Fall of 2007, but I was using the nicotine patch, and it took me longer than it should have to wean myself off of it. But I did, and I’m now living completely nicotine-free.&lt;/p&gt;

&lt;p&gt;I did not, unfortunately, stick to my exercise goals for the year. The new bike I bought this summer with a plan to ride several time per week has only been out of the garage a handful of times. In the Fall, I started the 100 Push-ups program and added on sit-ups, leg curls and calf extensions. I was doing pretty good for a few weeks, but then my schedule got thrown off when Misty went out of town, and I never got back on track. Boo.&lt;/p&gt;

&lt;p&gt;Of course, no 2008 year-end review would be complete without a record of the Snowpocalypse which descended upon us during the last half of December. It almost never snows on the valley floor in Hillsboro or Portland. This year it not only snowed, but it stuck. We had well over a foot of accumulation by the house along with a half inch of ice. Since we’ve no infrastructure for clearing the roads, everything basically shut down. Of course, I enjoyed the hell out of it, because it gave me a chance to put the Jeep in 4-lo once or twice.&lt;/p&gt;

&lt;p&gt;This was definitely a prosperous year for our family, and we’ve been able to do a number of fun extra things such as pay for my friend, &lt;a href='http://quentinbaker.com'&gt;Quentin Baker&lt;/a&gt;, to fly out for a few days this fall and for my dad and his wife to come out for a week during Christmas. We’re debt free except for two car payments, and we’re hopefully in a good position to weather the current problems with our economy.&lt;/p&gt;

&lt;p&gt;2009 looks like it could be another year of changes. It’s definitely going to be a big change for our country as our new president takes office. Hopefully I’ll be able to bring some more positive change to my individual life as well. I plan to spend some time tomorrow thinking about my goals and resolutions for the next year.&lt;/p&gt;

&lt;p&gt;For now, good-bye 2008 and happy new-year!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/johnwilger/~4/Jj5LpITavKA" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://johnwilger.com/2008/12/31/2008-year-end-review.html</feedburner:origLink></entry>
 
 <entry>
   <title>Switched to Jekyll</title>
   <link href="http://feedproxy.google.com/~r/johnwilger/~3/7wxan8Aswtg/switched-to-jekyll.html" />
   <updated>2008-12-20T00:00:00+00:00</updated>
   <id>http://johnwilger.com/2008/12/20/switched-to-jekyll</id>
   <content type="html">&lt;p&gt;If you’re seeing this, then you see that I’ve stopped hosting my blog on &lt;a href='http://blogger.com'&gt;Blogger&lt;/a&gt; and am now using a &lt;a href='http://github.com/jwilger/jekyll'&gt;slightly modified Jekyll&lt;/a&gt;. Jekyll is the engine that powers GitHub’s recently announced ”&lt;a href='http://github.com/blog/272-github-pages'&gt;pages&lt;/a&gt;” feature.&lt;/p&gt;

&lt;p&gt;I’m not hosting on GitHub, though. I simply installed Jekyll on the cheapest VPS from &lt;a href='http://slicehost.com'&gt;Slicehost&lt;/a&gt;, set up a git repository on that server to hold the contents of the site and then slapped together a simple post-update hook script for git that automatically publishes the site when I commit changes.&lt;/p&gt;

&lt;p&gt;The changes also come with a new design.&lt;/p&gt;

&lt;p&gt;Dragons are cool, so I am cool.&lt;/p&gt;

&lt;p&gt;The stylesheets obviously aren’t fleshed out yet, so everything pretty much looks like crap. I plan to have it fixed up real soon.&lt;/p&gt;

&lt;p&gt;Also need to slap together a new Atom feed for the posts.&lt;/p&gt;

&lt;p&gt;All worth it, since I can now easily compose my posts in Vim using markdown syntax and have them stay editable in that format. MAybe now I’ll actually post more than once every couple of months. :-)&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/johnwilger/~4/7wxan8Aswtg" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://johnwilger.com/2008/12/20/switched-to-jekyll.html</feedburner:origLink></entry>
 
 <entry>
   <title>Doing the Employee Thing Again</title>
   <link href="http://feedproxy.google.com/~r/johnwilger/~3/tZoLWmtVrdk/doing-the-employee-thing-again.html" />
   <updated>2008-11-27T00:00:00+00:00</updated>
   <id>http://johnwilger.com/2008/11/27/doing-the-employee-thing-again</id>
   <content type="html">
&lt;p&gt;After working for myself as an independent consultant for the last 9 months,
I've decided to accept an offer of employment from one of my clients. As of
December 1st I will be a full time employee of TSSI and working with the team
building &lt;a href="http://projectdx.com"&gt;ProjectDX&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This was not an easy decision to make, and I definitely went back and forth on
it over the several days after receiving a formal offer. On one hand, I
really enjoy working as an independent consultant and felt that it might be a
mistake to leave that role after less than a year. On the other hand, I had a
decent offer from a company that I really enjoy working with. In the end, the
decision came down to this: If I turned down the offer, I couldn't expect it to
still be there in a couple of months if I regretted the decision; but if I
accepted the offer then later decide it's not where I belong, I can always
return to independent consulting in the future.&lt;/p&gt;

&lt;p&gt;My role on the ProjectDX team won't change a whole lot from what I've been doing
the last several months—I'll mostly just be doing more of it. I will officially
be taking over the role of scrum master and continue to serve as "technical
lead" when it comes to helping the team learn and use agile engineering
practices.&lt;/p&gt;

&lt;p&gt;I'm excited to deepen my relationship with the ProjectDX team, because I've been
impressed from day one with their adoption of the agile philosophy—both from the
developers &lt;em&gt;and&lt;/em&gt; the product owners and executive management. It's rare
to find a team that works together so effectively, and I'm proud to be on
board.&lt;/p&gt;

&lt;p&gt;I don't plan to leave the consulting world altogether. I won't have time to get
involved in any in-depth programming work, but I will still be available for
occasional retrospective facilitation, technical review or other short-term
engagements.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/johnwilger/~4/tZoLWmtVrdk" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://johnwilger.com/2008/11/27/doing-the-employee-thing-again.html</feedburner:origLink></entry>
 
 <entry>
   <title>OpenID, Finally</title>
   <link href="http://feedproxy.google.com/~r/johnwilger/~3/2aBBn0nUFn4/openid--finally.html" />
   <updated>2008-08-08T00:00:00+00:00</updated>
   <id>http://johnwilger.com/2008/08/08/openid--finally</id>
   <content type="html">
&lt;p&gt;I’m finally getting a chance to play around with implementing &lt;a href="http://openid.net/"&gt;OpenID&lt;/a&gt; on one of my current projects. Yeah, I realize I’m like the last one to the party: but most of the applications I’ve worked on the last few years were internal business apps where it wouldn’t make sense to use something like OpenID.&lt;/p&gt;

&lt;p&gt;Looks like it’s fairly trivial to add OpenID support to a &lt;a href="http://rubyonrails.com"&gt;Rails&lt;/a&gt;-based application; I’m mainly pointing out these directions because the info I found on the web was a bit old. The basic directions are still the same as they were a year ago, but the old timestamps on the blog posts I found left me unsure at first as to whether the info was appropriate for the latest version of Rails (2.1.0 as of this writing).&lt;/p&gt;

&lt;p&gt;Essentially, you install the &lt;a href="http://www.openidenabled.com/ruby-openid/"&gt;&lt;code&gt;ruby-openid&lt;/code&gt;&lt;/a&gt; library which is available as a rubygem. Then add the &lt;a href="http://github.com/rails/open_id_authentication/tree/master"&gt;&lt;code&gt;open_id_authentication&lt;/code&gt;&lt;/a&gt; plugin to your Rails application. Thankfully, this plugin limits itself to providing an API to make calls against an OpenID server and a little bit of behind-the-scenes ActionController magic that seems to stay out of your way—I &lt;em&gt;hate&lt;/em&gt; plugins that want to add a bunch of views and models and crap to an application, so this was a blessing.&lt;/p&gt;

&lt;p&gt;While the plugin supplies the API for authenticating against an OpenID server, it is your responsibility to add the correct logic to your controllers to determine when the request should be made and how to handle the results. The example in the &lt;a href="http://github.com/rails/open_id_authentication/tree/master%2FREADME?raw=true"&gt;README for &lt;code&gt;open_id_authentication&lt;/code&gt;&lt;/a&gt; are pretty clear.&lt;/p&gt;

&lt;p&gt;I was worried that adding OpenID support could be a bit of a time-sink, but in reality it’s very little work at all. If you have a public Rails application that allows users to sign in, there’s really not much excuse for &lt;em&gt;not&lt;/em&gt; adding OpenID support.&lt;/p&gt;

&lt;img src="http://feeds.feedburner.com/~r/johnwilger/~4/2aBBn0nUFn4" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://johnwilger.com/2008/08/08/openid--finally.html</feedburner:origLink></entry>
 
 <entry>
   <title>Why am I Doing This?</title>
   <link href="http://feedproxy.google.com/~r/johnwilger/~3/gJs6BcwhzXE/why-am-i-doing-this-.html" />
   <updated>2008-08-03T00:00:00+00:00</updated>
   <id>http://johnwilger.com/2008/08/03/why-am-i-doing-this-</id>
   <content type="html">
&lt;p&gt;Admittedly, one of the reasons I was happy to leave my previous employer, hang up my own shingle and start working for myself was so that I could work from home and spend more time at home with my family; however last week saw me out of the house on a client site nearly every day. Not a bad thing though. I spent most of the week helping a client with release planning. This is the same client who just decided to make the jump to low-tech planning using a bulletin board and index cards.&lt;/p&gt;

&lt;p&gt;The client has spent a good deal of time working with a user-experience firm to develop the look and feel, information architecture and features of the next version of their application, and our release planning process has centered mainly around reviewing the mockups produced by the UX team and translating into user stories for development. I’m finding this to be a bit of a mixed-bag. On one hand, it does make it easier to talk about much of the application with the stakeholders, because we have something visual to reference while discussing various features. On the other hand, I think there’s too much tendency to get hung-up on the specifics of the interface mock-ups even when the release-planning discussion begins to reveal that the mocked-up solution may not be the best approach.&lt;/p&gt;

&lt;p&gt;One nice thing about this client is that, even though the product owner hasn’t had any prior experience with agile software development processes, she is making a huge effort to understand the process and give the development team what it needs. I can’t take the credit for that—the manager of the development team did a good job of selling agile before I was ever involved.&lt;/p&gt;

&lt;p&gt;During the release planning, one of the things that the team has found both useful and difficult is quantifying the value of each user story. We’ve adopted the newish style of user story that started floating around the Internet a couple of months ago where the stories are phrased, “So that {VALUE}, as a {ROLE}, I want {FEATURE}.”&lt;/p&gt;

&lt;p&gt;The benefit of putting the value proposition first in the story is that it underscores the fact that this is really the most important part. While simply a change in phrasing, it seems to give developers a feeling of more freedom to suggest alternate implementations that provide the same value.&lt;/p&gt;

&lt;p&gt;The hard part about writing this way is that we’ve often found the first go at a user story simply repeats the value phrase as the feature phase. For instance, this first version might look like, “so that I can see what widgets I have added to my account, as a customer, I want to see a list of my favorite widgets.”&lt;/p&gt;

&lt;p&gt;When the value statement and the feature are basically the same thing, it’s easy to think that writing the value statement is a waste of time—and if this is where you stopped, you’d be correct. When this happens, it’s a sign that you don’t understand the value of the story you are writing. At this point, you need to stop and ask &lt;em&gt;why&lt;/em&gt; the user needs this thing. Here’s a hint: a good value statement should almost never contain any reference to the application itself. The user doesn’t care about your application nearly as much as you do; he is using it in order to complete some task which provides value to him. The value statements in your user stories should reflect some aspect of the overall benefit your user derives from using the application. Doing so might lead to a user story that reads more like, “so that I can quickly re-order widgets which I commonly purchase, as a customer, I want to see a list of my favorite widgets.”&lt;/p&gt;

&lt;p&gt;This new version of the story has a clear benefit that isn’t solely about the application—the value could conceivably be satisfied by means outside the application as well. It’s now easier think about a group of user stories that could all be part of providing this value to the user. During release planning, these values become a tool for negotiating the scope of the release; should the release have a narrow focus and attempt to satisfy only one or two of the most important values, or should the release have a broad focus and attempt to touch on all of the values without perfectly addressing any one of them. Perhaps it’s somewhere in-between—the point is that the product owner has an explicit axis around which to prioritize.&lt;/p&gt;

&lt;p&gt;Perhaps most importantly, having a clear and appropriate value statement at the beginning of each user story helps the development team understand why they are doing what they are doing. This is extremely important in an agile process, because we intentionally leave gaps in our planning to accommodate a frequently changing list of priorities. If the developers don’t understand &lt;em&gt;why&lt;/em&gt; a particular feature is being implemented, the gaps in the project plan could lead to an implementation that, while meeting the letter of the user story, does not actually satisfy the stakeholders’ requirements. However, if the developers understand the value that the stakeholders hope to gain from the feature outlined in the user story, they have more ability to complete the “logical leaps” across the gaps in the project plan in a way that will keep the whole application providing a cohesive benefit to its users.&lt;/p&gt;

&lt;img src="http://feeds.feedburner.com/~r/johnwilger/~4/gJs6BcwhzXE" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://johnwilger.com/2008/08/03/why-am-i-doing-this-.html</feedburner:origLink></entry>
 
 <entry>
   <title>Omnifocus, Index Cards and Anonymous Classes</title>
   <link href="http://feedproxy.google.com/~r/johnwilger/~3/w4WbXaMwyTA/omnifocus--index-cards-and-anonymous-classes.html" />
   <updated>2008-07-29T00:00:00+00:00</updated>
   <id>http://johnwilger.com/2008/07/29/omnifocus--index-cards-and-anonymous-classes</id>
   <content type="html">
&lt;p&gt;Already I’m way behind on the posting schedule I wanted to keep up. If it’s any consolation, I’m way behind on a number of other things as well. :-) Here’s a quick round-up of what’s been going on over the last week:&lt;/p&gt;

&lt;h2 id="personal_productivity"&gt;Personal Productivity&lt;/h2&gt;

&lt;p&gt;I’m still using a combination of the Omnifocus desktop and iPhone applications to manage both personal and work-related tasks. The iPhone application was updated since the last time I posted and syncing is now &lt;em&gt;much&lt;/em&gt; faster and more reliable. I still get occasional crashes when adding a lot of items via the phone, but it’s certainly more usable than it was with the first release. The one thing I’ve really been wishing the application had over the last week is the ability to either add tags to tasks and filter by them (like the Things application) or at the very least put multiple contexts on a task.&lt;/p&gt;

&lt;h2 id="lo_tech_project_management"&gt;Lo-Tech Project Management&lt;/h2&gt;

&lt;p&gt;One of my clients is using an agile project management process that more or less resembles Scrum, but most of the team is inexperienced with the process. For most of the time that I’ve been working with them, they’ve been trying to use &lt;a href="http://rallydev.com"&gt;Rally&lt;/a&gt; to track the process; but last week I had a big hand in converting them over to using physical notecards and a task-board.&lt;/p&gt;

&lt;p&gt;The upset started a couple of weeks ago when, during a sprint-planning meeting, one of the project stakeholders (the “Gold Owner”, in fact) came into the room and wanted the team to roll back the last week’s production deployment due to some errors that they were seeing in the new version of the product. Without getting too far into details, this lead to the realization that what was in the current sprint plan (and possibly the last one as well) did not accurately reflect the expectations of the project’s stakeholders.&lt;/p&gt;

&lt;p&gt;Tools like Rally can be problematic for a number of reasons. For one thing, they’re just not visible enough. This particular team is co-located, so there’s no real need to make story cards available to remote team members; and when you don’t have that need, I think that these kind of tools do more to restrict access to the project information than they do to make it accessible. No matter how easy to use you make the software, it’s never (well, not with current, commonplace technology) going to be as easy as getting up and looking at a wall, moving things around with your hands. Because of this, it just doesn’t serve well as an information radiator, because most of the project stakeholders will not be looking at it regularly. No, sorry, they won’t. Seriously, only us geeks actually &lt;em&gt;enjoy&lt;/em&gt; looking at that kind of stuff.&lt;/p&gt;

&lt;p&gt;Also, the nature of the tools tends to put the control too firmly in one person’s hands. With physical cards, a group of people can stand at the board or around a table and everyone can easily participate in planning and tracking simply by reaching out and moving a card around. With software tools—even when there are no built-in permission restrictions—we tend to get the feeling that manipulating the data (at least beyond simple status updates) is the job of one or two people. Even if the planning is done in a meeting, the person who is updating the tool has perhaps an unconscious power to direct the planning toward there own needs and desires and limit the participation of the rest of the team.&lt;/p&gt;

&lt;p&gt;The bigger problem with using a software tool to manage an agile process is that the software tends to dictate the process to you. Here’s the thing about agile software development: There’s no way I can outline steps a, b and c for you and say “do these and you’ll be doing Agile!”&lt;/p&gt;

&lt;p&gt;The agility actually has very little to do with the steps in the process itself; agility comes from the fact that when the process needs to change so that we can be successful, we are able to quickly change the process and adapt to the situation at hand. If you’re using a software tool to manage your process, you are almost assuredly &lt;em&gt;not&lt;/em&gt; agile. If you discover that your process needs to change in some manner, and this discovery leads to someone either needing to modify the code of your tool or sit down and figure out which settings in the software to manipulate—well, that’s a lot of friction that will cause resistance to the process change. On the other hand, if the only thing required to make the change is saying “OK, from now on we put this type of card on the board over here instead of there,” then you have relatively low friction to make that change.&lt;/p&gt;

&lt;h2 id="rubycocoa"&gt;RubyCocoa&lt;/h2&gt;

&lt;p&gt;Darrin and I haven’t gotten too far yet on the RubyCocoa app we’re working on together. We’ve had a few remote pairing sessions so far. The one hurdle we still need to jump is coming up with a way to do function testing of the GUI (like what you would do with Selenium in the web-app world). However, I was talking to John Labovitz during FOSCON last week, and he gave me a few ideas and offered to follow up via email with some pointers. Hopefully his help will allow us to come up with a good way to mix RSpec’s Story Runner test format with full Cocoa GUI interaction.&lt;/p&gt;

&lt;p&gt;If anyone else reading this has some thoughts on functional testing Cocoa GUI apps, please either leave a comment or &lt;a href="mailto:johnwilger@gmail.com"&gt;email me directly&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="anonymous_classes_as_local_data_structures"&gt;Anonymous Classes as Local Data Structures&lt;/h2&gt;

&lt;p&gt;I have a project where I needed to assemble a summarized report. Let’s make this a simplified example and say that I have a bunch of store locations and, for each location, and for a given date range, I need to output the number of hours worked each day and also the number of widgets sold on that day. The report might look like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;        |         | Day One       | Day Two       | Day Three
Region  | Store   | Hrs | # Sold  | Hrs | # Sold  | Hrs | # Sold
--------|---------|-----|---------|-----|---------|-----|---------
TOTAL   | TOTAL   | 20  | 10      | 15  | 30      | 25  | 5
NW      | TOTAL   | 10  |  7      |  0  |  0      | 10  | 1
NW      | A       |  5  |  4      |  0  |  0      |  5  | 0
NW      | B       |  5  |  3      |  0  |  0      |  5  | 1
SW      | TOTAL   | 10  |  3      | 15  | 30      | 15  | 4
SW      | C       |  6  |  2      | 10  | 20      |  7  | 2
SW      | D       |  4  |  1      |  5  | 10      |  8  | 2
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;When representing the number of hours worked and the number of widgets sold at a particular store on a particular day, a simple Hash will suffice for storing the data:&lt;/p&gt;

&lt;pre class="textmate-source blackboard"&gt;&lt;span class='linenum'&gt;    1&lt;/span&gt; &lt;span class="source source_ruby"&gt;&lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby"&gt;{&lt;/span&gt;&lt;span class="meta meta_syntax meta_syntax_ruby meta_syntax_ruby_start-block"&gt; &lt;/span&gt;&lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;hours&lt;/span&gt; &lt;span class="punctuation punctuation_separator punctuation_separator_key-value"&gt;=&gt;&lt;/span&gt; &lt;span class="constant constant_numeric constant_numeric_ruby"&gt;6&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;widgets&lt;/span&gt; &lt;span class="punctuation punctuation_separator punctuation_separator_key-value"&gt;=&gt;&lt;/span&gt; &lt;span class="constant constant_numeric constant_numeric_ruby"&gt;2&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;If I have a Hash like that for each store in a region, it’s not too difficult to get the total hours and widgets sold for the whole region on a particular day:&lt;/p&gt;

&lt;pre class="textmate-source blackboard"&gt;&lt;span class='linenum'&gt;    1&lt;/span&gt; &lt;span class="source source_ruby"&gt;region_data &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby"&gt;[&lt;/span&gt;
&lt;span class='linenum'&gt;    2&lt;/span&gt;   &lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby"&gt;{&lt;/span&gt;&lt;span class="meta meta_syntax meta_syntax_ruby meta_syntax_ruby_start-block"&gt; &lt;/span&gt;&lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;hours&lt;/span&gt; &lt;span class="punctuation punctuation_separator punctuation_separator_key-value"&gt;=&gt;&lt;/span&gt; &lt;span class="constant constant_numeric constant_numeric_ruby"&gt;6&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;widgets&lt;/span&gt; &lt;span class="punctuation punctuation_separator punctuation_separator_key-value"&gt;=&gt;&lt;/span&gt; &lt;span class="constant constant_numeric constant_numeric_ruby"&gt;2&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby"&gt;}&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt;
&lt;span class='linenum'&gt;    3&lt;/span&gt;   &lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby"&gt;{&lt;/span&gt;&lt;span class="meta meta_syntax meta_syntax_ruby meta_syntax_ruby_start-block"&gt; &lt;/span&gt;&lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;hours&lt;/span&gt; &lt;span class="punctuation punctuation_separator punctuation_separator_key-value"&gt;=&gt;&lt;/span&gt; &lt;span class="constant constant_numeric constant_numeric_ruby"&gt;4&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;widgets&lt;/span&gt; &lt;span class="punctuation punctuation_separator punctuation_separator_key-value"&gt;=&gt;&lt;/span&gt; &lt;span class="constant constant_numeric constant_numeric_ruby"&gt;1&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby"&gt;}&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby"&gt;]&lt;/span&gt;
&lt;span class='linenum'&gt;    4&lt;/span&gt; 
&lt;span class='linenum'&gt;    5&lt;/span&gt; region_summary &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; region_data&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;inject&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;(&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby"&gt;{}&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;)&lt;/span&gt; &lt;span class="keyword keyword_control keyword_control_start-block keyword_control_start-block_ruby"&gt;do &lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_variable punctuation_separator_variable_ruby"&gt;|&lt;/span&gt;&lt;span class="variable variable_other variable_other_block variable_other_block_ruby"&gt;m&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_variable punctuation_separator_variable_ruby"&gt;,&lt;/span&gt;&lt;span class="variable variable_other variable_other_block variable_other_block_ruby"&gt;d&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_variable punctuation_separator_variable_ruby"&gt;|&lt;/span&gt;
&lt;span class='linenum'&gt;    6&lt;/span&gt;   &lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby"&gt;{&lt;/span&gt;&lt;span class="meta meta_syntax meta_syntax_ruby meta_syntax_ruby_start-block"&gt;
&lt;/span&gt;&lt;span class='linenum'&gt;    7&lt;/span&gt;     &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;hours&lt;/span&gt; &lt;span class="punctuation punctuation_separator punctuation_separator_key-value"&gt;=&gt;&lt;/span&gt; m&lt;span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby"&gt;[&lt;/span&gt; &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;hours&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby"&gt;]&lt;/span&gt; &lt;span class="keyword keyword_operator keyword_operator_arithmetic keyword_operator_arithmetic_ruby"&gt;+&lt;/span&gt; d&lt;span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby"&gt;[&lt;/span&gt; &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;hours&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby"&gt;]&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt;
&lt;span class='linenum'&gt;    8&lt;/span&gt;     &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;widgets&lt;/span&gt; &lt;span class="punctuation punctuation_separator punctuation_separator_key-value"&gt;=&gt;&lt;/span&gt; m&lt;span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby"&gt;[&lt;/span&gt; &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;widgets&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby"&gt;]&lt;/span&gt; &lt;span class="keyword keyword_operator keyword_operator_arithmetic keyword_operator_arithmetic_ruby"&gt;+&lt;/span&gt; d&lt;span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby"&gt;[&lt;/span&gt; &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;widgets&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby"&gt;]&lt;/span&gt;
&lt;span class='linenum'&gt;    9&lt;/span&gt;   &lt;span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_ruby"&gt;}&lt;/span&gt;
&lt;span class='linenum'&gt;   10&lt;/span&gt; &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;
&lt;span class='linenum'&gt;   11&lt;/span&gt; 
&lt;span class='linenum'&gt;   12&lt;/span&gt; region_summary&lt;span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby"&gt;[&lt;/span&gt; &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;hours&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby"&gt;]&lt;/span&gt;    &lt;span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_ruby"&gt;#&lt;/span&gt;=&gt; 10
&lt;/span&gt;&lt;span class='linenum'&gt;   13&lt;/span&gt; region_summary&lt;span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby"&gt;[&lt;/span&gt; &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;widgets&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby"&gt;]&lt;/span&gt;  &lt;span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_ruby"&gt;#&lt;/span&gt;=&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;The problem with that is…well…yuck. It gets the job done, but it’s not very expressive. And I can see now some junior developer going in later and copying and pasting that crap all over the place without bothering to refactor, and then all of the sudden you have ugly ball of crap * 241 gumming up your whole code base. No thank you.&lt;/p&gt;

&lt;p&gt;If I have several data points for stores in a region, what I’d really like to be able to do is something like:&lt;/p&gt;

&lt;pre class="textmate-source blackboard"&gt;&lt;span class='linenum'&gt;    1&lt;/span&gt; &lt;span class="source source_ruby"&gt;store_a&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;hours   &lt;span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_ruby"&gt;#&lt;/span&gt;=&gt; 6
&lt;/span&gt;&lt;span class='linenum'&gt;    2&lt;/span&gt; store_a&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;widgets &lt;span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_ruby"&gt;#&lt;/span&gt;=&gt; 2
&lt;/span&gt;&lt;span class='linenum'&gt;    3&lt;/span&gt; 
&lt;span class='linenum'&gt;    4&lt;/span&gt; store_b&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;hours   &lt;span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_ruby"&gt;#&lt;/span&gt;=&gt; 4
&lt;/span&gt;&lt;span class='linenum'&gt;    5&lt;/span&gt; store_b&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;widgets &lt;span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_ruby"&gt;#&lt;/span&gt;=&gt; 1
&lt;/span&gt;&lt;span class='linenum'&gt;    6&lt;/span&gt; 
&lt;span class='linenum'&gt;    7&lt;/span&gt; region_data &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby"&gt;[&lt;/span&gt; store_a&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; store_b &lt;span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby"&gt;]&lt;/span&gt;
&lt;span class='linenum'&gt;    8&lt;/span&gt; 
&lt;span class='linenum'&gt;    9&lt;/span&gt; region_summary &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; region_data&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;sum
&lt;span class='linenum'&gt;   10&lt;/span&gt; 
&lt;span class='linenum'&gt;   11&lt;/span&gt; region_summary&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;hours      &lt;span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_ruby"&gt;#&lt;/span&gt;=&gt; 10
&lt;/span&gt;&lt;span class='linenum'&gt;   12&lt;/span&gt; region_summary&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;widgets    &lt;span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_ruby"&gt;#&lt;/span&gt;=&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;(Note: the above example relies on the ActiveSupport library from Rails for Enumerable#sum and Symbol#to_proc)&lt;/p&gt;

&lt;p&gt;In order to make that code example work, both &lt;code&gt;store_a&lt;/code&gt; and &lt;code&gt;store_b&lt;/code&gt; need to be objects that respond to #hours and #widgets by returning the respective numbers and also respond to #+ by returning a similar object with properties that represent the sum of the respective properties of the operands. Nothing earth-shattering there to be sure.&lt;/p&gt;

&lt;p&gt;But let’s think about where to put the definition for this object that represents our data point. Assuming for the moment that this one method I’m running to generate the report is currently the only place in the code that needs this data structure, I might not want to make it a full-fledged citizen of my application’s domain.&lt;/p&gt;

&lt;p&gt;Often, programmers will use a Struct defined within the scope of the current class to represent a data structure that’s more formal than a simple Hash yet not quite so formal as to need a completely separate class definition. This seems like a good fit for our needs except for one problem: we need to define special behavior for the #+ method on each instance, and Struct only allows us to define accessors for instance variables.&lt;/p&gt;

&lt;p&gt;There are a number of ways we could define something with the (in)formality of a Struct but add the needed behavior to it. In this case, I opted for the anonymous Class instance; thus the full example becomes:&lt;/p&gt;

&lt;pre class="textmate-source blackboard"&gt;&lt;span class='linenum'&gt;    1&lt;/span&gt; &lt;span class="source source_ruby"&gt;data_point &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; &lt;span class="support support_class support_class_ruby"&gt;Class&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;&lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;new&lt;/span&gt; &lt;span class="keyword keyword_control keyword_control_start-block keyword_control_start-block_ruby"&gt;do
&lt;/span&gt;&lt;span class='linenum'&gt;    2&lt;/span&gt;   &lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;attr_accessor&lt;/span&gt; &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;hours&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; &lt;span class="constant constant_other constant_other_symbol constant_other_symbol_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_constant punctuation_definition_constant_ruby"&gt;:&lt;/span&gt;widgets&lt;/span&gt;
&lt;span class='linenum'&gt;    3&lt;/span&gt; 
&lt;span class='linenum'&gt;    4&lt;/span&gt;   &lt;span class="meta meta_function meta_function_method meta_function_method_with-arguments meta_function_method_with-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;initialize&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_ruby"&gt; hours &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; &lt;span class="constant constant_numeric constant_numeric_ruby"&gt;0&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; widgets &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; &lt;span class="constant constant_numeric constant_numeric_ruby"&gt;0&lt;/span&gt; &lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class='linenum'&gt;    5&lt;/span&gt;     &lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;hours&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; &lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;widgets&lt;/span&gt; &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; hours&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; widgets
&lt;span class='linenum'&gt;    6&lt;/span&gt;   &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;
&lt;span class='linenum'&gt;    7&lt;/span&gt; 
&lt;span class='linenum'&gt;    8&lt;/span&gt;   &lt;span class="meta meta_function meta_function_method meta_function_method_with-arguments meta_function_method_with-arguments_ruby"&gt;&lt;span class="keyword keyword_control keyword_control_def keyword_control_def_ruby"&gt;def&lt;/span&gt; &lt;span class="entity entity_name entity_name_function entity_name_function_ruby"&gt;+&lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;(&lt;/span&gt;&lt;span class="variable variable_parameter variable_parameter_function variable_parameter_function_ruby"&gt; other &lt;/span&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_parameters punctuation_definition_parameters_ruby"&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class='linenum'&gt;    9&lt;/span&gt;     &lt;span class="variable variable_language variable_language_ruby"&gt;self&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;class&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;&lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;new&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;(&lt;/span&gt; &lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;hours&lt;/span&gt; &lt;span class="keyword keyword_operator keyword_operator_arithmetic keyword_operator_arithmetic_ruby"&gt;+&lt;/span&gt; other&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;hours&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; &lt;span class="variable variable_other variable_other_readwrite variable_other_readwrite_instance variable_other_readwrite_instance_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_variable punctuation_definition_variable_ruby"&gt;@&lt;/span&gt;widgets&lt;/span&gt; &lt;span class="keyword keyword_operator keyword_operator_arithmetic keyword_operator_arithmetic_ruby"&gt;+&lt;/span&gt; other&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;widgets &lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;)&lt;/span&gt;
&lt;span class='linenum'&gt;   10&lt;/span&gt;   &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;
&lt;span class='linenum'&gt;   11&lt;/span&gt; &lt;span class="keyword keyword_control keyword_control_ruby"&gt;end&lt;/span&gt;
&lt;span class='linenum'&gt;   12&lt;/span&gt; 
&lt;span class='linenum'&gt;   13&lt;/span&gt; store_a &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; data_point&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;&lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;new&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;(&lt;/span&gt; &lt;span class="constant constant_numeric constant_numeric_ruby"&gt;6&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; &lt;span class="constant constant_numeric constant_numeric_ruby"&gt;2&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;)&lt;/span&gt;
&lt;span class='linenum'&gt;   14&lt;/span&gt; store_b &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; data_point&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;&lt;span class="keyword keyword_other keyword_other_special-method keyword_other_special-method_ruby"&gt;new&lt;/span&gt;&lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;(&lt;/span&gt; &lt;span class="constant constant_numeric constant_numeric_ruby"&gt;4&lt;/span&gt;&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; &lt;span class="constant constant_numeric constant_numeric_ruby"&gt;1&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_function punctuation_section_function_ruby"&gt;)&lt;/span&gt;
&lt;span class='linenum'&gt;   15&lt;/span&gt; 
&lt;span class='linenum'&gt;   16&lt;/span&gt; store_a&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;hours   &lt;span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_ruby"&gt;#&lt;/span&gt;=&gt; 6
&lt;/span&gt;&lt;span class='linenum'&gt;   17&lt;/span&gt; store_a&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;widgets &lt;span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_ruby"&gt;#&lt;/span&gt;=&gt; 2
&lt;/span&gt;&lt;span class='linenum'&gt;   18&lt;/span&gt; 
&lt;span class='linenum'&gt;   19&lt;/span&gt; store_b&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;hours   &lt;span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_ruby"&gt;#&lt;/span&gt;=&gt; 4
&lt;/span&gt;&lt;span class='linenum'&gt;   20&lt;/span&gt; store_b&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;widgets &lt;span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_ruby"&gt;#&lt;/span&gt;=&gt; 1
&lt;/span&gt;&lt;span class='linenum'&gt;   21&lt;/span&gt; 
&lt;span class='linenum'&gt;   22&lt;/span&gt; region_data &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; &lt;span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby"&gt;[&lt;/span&gt; store_a&lt;span class="punctuation punctuation_separator punctuation_separator_object punctuation_separator_object_ruby"&gt;,&lt;/span&gt; store_b &lt;span class="punctuation punctuation_section punctuation_section_array punctuation_section_array_ruby"&gt;]&lt;/span&gt;
&lt;span class='linenum'&gt;   23&lt;/span&gt; 
&lt;span class='linenum'&gt;   24&lt;/span&gt; region_summary &lt;span class="keyword keyword_operator keyword_operator_assignment keyword_operator_assignment_ruby"&gt;=&lt;/span&gt; region_data&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;sum
&lt;span class='linenum'&gt;   25&lt;/span&gt; 
&lt;span class='linenum'&gt;   26&lt;/span&gt; region_summary&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;hours      &lt;span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_ruby"&gt;#&lt;/span&gt;=&gt; 10
&lt;/span&gt;&lt;span class='linenum'&gt;   27&lt;/span&gt; region_summary&lt;span class="punctuation punctuation_separator punctuation_separator_method punctuation_separator_method_ruby"&gt;.&lt;/span&gt;widgets    &lt;span class="comment comment_line comment_line_number-sign comment_line_number-sign_ruby"&gt;&lt;span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_ruby"&gt;#&lt;/span&gt;=&gt; 3&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;img src="http://feeds.feedburner.com/~r/johnwilger/~4/w4WbXaMwyTA" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://johnwilger.com/2008/07/29/omnifocus--index-cards-and-anonymous-classes.html</feedburner:origLink></entry>
 
 <entry>
   <title>Jeep-Induced Sunburn, New Side-Project, iPhone 2.0</title>
   <link href="http://feedproxy.google.com/~r/johnwilger/~3/VnQn155Yedo/jeep-induced-sunburn--new-side-project--iphone-2-0.html" />
   <updated>2008-07-17T00:00:00+00:00</updated>
   <id>http://johnwilger.com/2008/07/17/jeep-induced-sunburn--new-side-project--iphone-2-0</id>
   <content type="html">
&lt;h2 id="the_jeep"&gt;The Jeep&lt;/h2&gt;

&lt;p&gt;I’ve been having fun the last week cruising around in the new Jeep. I took the hard top off almost right away and have been cruising around with the top down a lot, letting the wind blow through my hair while driving down the highway. Can’t beat it. Of course, on Monday I forgot to put on any sunscreen, had the top down, and ended up sitting in a traffic jam on 26 for nearly an hour. Gave myself a nice burn on the face and arms, complete with goggle-marks from my sunglasses.&lt;/p&gt;

&lt;h2 id="remote_pairing_on_a_new_rubycocoa_app"&gt;Remote Pairing on a New RubyCocoa App&lt;/h2&gt;

&lt;p&gt;&lt;a href="http://dje.typepad.com/"&gt;Darrin Eden&lt;/a&gt; and I are going to start working on a new project together that will give me a chance to explore some new territory. For one thing, we’ll be doing remote-pairing for a couple of hours at a time, twice a week. Although I’ve done a lot of pair-programming with people in the same room, I’ve not had a good remote-pairing experience yet. Darrin is convinced it can work, so I’m willing to give it a try.&lt;/p&gt;

&lt;p&gt;While I’m not ready to discuss exactly what the application we’re working on is, we plan (at least for now) to develop it using &lt;a href="http://www.rubycocoa.com/"&gt;RubyCocoa&lt;/a&gt;. I’ve made half-hearted attempts to do a bit of Cocoa development in the past, but I’ve never made it too far, because I always ended up getting lost in the huge set of available libraries; when I only had an hour or two per week to dedicate to it, it just wasn’t enough time to get over that hump. Darrin has more experience with Cocoa and OSX programming than I, so I’m hoping that I’ll get a lot of benefit from his experience as we pair up. As much as I love writing web-apps, I’d really like to have at least one desktop GUI app toolkit on my belt.&lt;/p&gt;

&lt;p&gt;When Darrin and I hatched this plan over drinks at &lt;a href="http://www.giltclub.com/"&gt;Gilt&lt;/a&gt; last night, one of the concerns we came up with was how to functional test a Cocoa GUI application. Dear lazy-web: are there any existing frameworks out there for testing Cocoa-based GUI applications similar the the way you can use &lt;a href="http://selenium.openqa.org/"&gt;Selenium&lt;/a&gt; to test a web application? If we can’t find something that works, we’ll probably have a go at developing our own; but we really don’t want to re-invent the wheel here.&lt;/p&gt;

&lt;p&gt;Darrin has also been using &lt;a href="http://rspec.info/documentation/stories.html"&gt;RSpec’s Story Runner&lt;/a&gt; on a number of projects lately, and we will probably try to incorporate that into this project. While I’ve used &lt;a href="http://rspec.info"&gt;RSpec&lt;/a&gt; and am somewhat familiar with what Story Runner is supposed to do, I haven’t really used it myself yet; so this should be fun.&lt;/p&gt;

&lt;p&gt;Aside from the fact that I think we’re going to come out of this with a really useful application that both of us have a need for, I’m really pumped about collaborating on this project with Darrin just for the number of new (to me) technologies I’ll be getting to play with. I feel like I’ve been concentrating so hard lately on delivering in my areas of expertise that I haven’t taken enough time to expand my horizons.&lt;/p&gt;

&lt;h2 id="iphone_20_and_the_appstore"&gt;iPhone 2.0 and the AppStore&lt;/h2&gt;

&lt;p&gt;I’m sticking to my guns at this point that I’m happy with my &lt;em&gt;vintage&lt;/em&gt; 1.0 iPhone. If I want GPS I’ll buy a &lt;a href="https://buy.garmin.com/shop/shop.do?cID=145&amp;pID=14904"&gt;Garmin&lt;/a&gt;, 80% of the time I’m on WiFi and—to be honest—I really prefer the look, feel and heft of the 1st-generation model. The new phone seems like a step in the wrong direction design-wise; kind of like those retarded &lt;a href="http://www.youtube.com/watch?v=eCHblz_LsKc"&gt;iMacs that looked like a desk lamp&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’ll admit to being a little disappointed with with the much anticipated launch of the AppStore for iPhone. Nothing like waiting 6 months for 12 todo list apps, some games I’m not interested in and a couple of news readers that aren’t any better than &lt;a href="http://reader.google.com"&gt;Google Reader&lt;/a&gt;’s iPhone interface. Where the hell is my SSH terminal app for heaven’s sake?!? I thought for sure someone had to have something like that in the works. Hopefully someone does, and hopefully Apple will approve it, because not being able to SSH form my iPhone is just wimpy.&lt;/p&gt;

&lt;p&gt;The one app I am &lt;em&gt;trying&lt;/em&gt; to find useful is &lt;a href="http://www.omnigroup.com/applications/omnifocus/"&gt;OmniFocus&lt;/a&gt;. I purchased the iPhone version and am currently using a trial of the desktop version. They seem to be the only company with a &lt;a href="http://www.davidco.com/what_is_gtd.php"&gt;GTD&lt;/a&gt;-ish task manager that is currently capable of syncing between the iPhone and the desktop version. The iPhone app is a bit limited compared to the desktop version (duh); I don’t think I could use it by itself, but I’m starting to feel comfortable with using the two in combination.&lt;/p&gt;

&lt;p&gt;OmniFocus’s syncing process is a bit slow on the phone, though. It looks like they way they handle syncing is to upload a copy of the database to iDisk (or your own WebDAV server) from the computer. The phone will then download a copy of that database and do the conflict resolution on the phone itself. Since the iPhone isn’t exactly a blazing computer, this comparison of the two databases takes a good while. (I’m guessing about the process, though—I could be completely wrong about how it’s done and why it’s slow.)&lt;/p&gt;

&lt;p&gt;It would be much cooler if OmniFocus provided a web service to which both the desktop and the iPhone client could upload information and then let the web service manage the conflict resolution and send back the results.&lt;/p&gt;

&lt;img src="http://feeds.feedburner.com/~r/johnwilger/~4/VnQn155Yedo" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://johnwilger.com/2008/07/17/jeep-induced-sunburn--new-side-project--iphone-2-0.html</feedburner:origLink></entry>
 
 <entry>
   <title>Shared Formulas, Technical Debt and a Jeep</title>
   <link href="http://feedproxy.google.com/~r/johnwilger/~3/J6e1DfSCaXU/shared-formulas--technical-debt-and-a-jeep.html" />
   <updated>2008-07-12T00:00:00+00:00</updated>
   <id>http://johnwilger.com/2008/07/12/shared-formulas--technical-debt-and-a-jeep</id>
   <content type="html">
&lt;p&gt;Already I’m failing to keep the update schedule I just announced. I am awesome.&lt;/p&gt;

&lt;p&gt;Truthfully, I haven’t had much interesting going on professionally the last couple of days. I’ve just been in crunch mode trying to clean up a project for my former employer. It’s mainly report formatting issues and such—busywork rather than anything worth writing about.&lt;/p&gt;

&lt;p&gt;I did just agree to ramp up my commitment with one of my clients. It started out as a training/mentoring gig, and now they need some extra hands doing some actual coding over their next few sprints. I’m not sure how much I can say about the project publicly, but I can at least mention that I hope to come away from the project with a bit of GIS knowledge—specifically PostGIS.&lt;/p&gt;

&lt;p&gt;The project is one where the initial spike was developed using PHP mainly by a developer who hasn’t previously had to support a long life-cycle project or work as part of a team of developers. Management decided to switch the project to Rails (more for the culture they want to adopt rather than purely technical reasons) and I came in to help with that transition. I will say that the lead developer on the team is really sharp and is picking up both Rails and Ruby itself quite well.&lt;/p&gt;

&lt;p&gt;The project involves a lot of formulas that need to be calculated both on the server for certain operations and in JavaScript on the client for others. One of the things we’ve been working on is how best to keep these formulas DRY between both places. Currently the formulas are simply expressed as strings inside a Hash-based data structure. On the Ruby side, these strings are eval’ed as needed to get the value for their key, and they are eval’d in a context where #method_missing is used to look up any variables used in the formula within the same data structure (which could—in turn—cause another formula to be evaluated.) The formula strings contain only basic math, so they can also be re-used on the javascript side by simply rendering them into the HTML output. Where it gets kind of gross is the fact that, because some formulas depend on the value of other formulas, the Javascript currently needs the formulas to appear in a certain order. I’d like to refactor this so that the Javascript side can use the same key-based lookup and lazy evaluation style that is used on the Ruby side. Shouldn’t bee too hard, except there is also a large Javascript library for this application which is probably pretty tightly coupled to the current implementation.&lt;/p&gt;

&lt;p&gt;As I was pointing out a number of places where refactoring like that I mentioned above would be beneficial, the developer I was pairing with on this project kept pushing back saying that he knows it should be done but that they just need to get this stuff out the door. That’s something I hear a lot in companies I’ve worked with: I’m hoping that this group will take get in the habit of taking the time to address their technical debt. So many places don’t, and I always see it come back to haunt them.&lt;/p&gt;

&lt;p&gt;In more personal news: I bought a brand-spankin’-new ‘08 Jeep Wrangler on Thursday. I just got the X model (with a number of extra options) rather than a Rubicon, because I’m not really an “off-roader”. I just wanted something that would allow me to access some of the more difficult to reach trailheads when I go backpacking; 90% of my driving is on pavement, but the Cavalier bottomed out on enough badly maintained gravel roads that it just wasn’t working for me. Now I should be able to tackle them no problem.&lt;/p&gt;

&lt;img src="http://feeds.feedburner.com/~r/johnwilger/~4/J6e1DfSCaXU" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://johnwilger.com/2008/07/12/shared-formulas--technical-debt-and-a-jeep.html</feedburner:origLink></entry>
 
 <entry>
   <title>Unofficial right_aws GitHub Repository Updated to 1.7.2</title>
   <link href="http://feedproxy.google.com/~r/johnwilger/~3/LBZxpwRFr6U/unofficial-right_aws-github-repository-updated-to-1-7-2.html" />
   <updated>2008-07-09T00:00:00+00:00</updated>
   <id>http://johnwilger.com/2008/07/09/unofficial-right_aws-github-repository-updated-to-1-7-2</id>
   <content type="html">
&lt;p&gt;Since there are now a number of people watching the &lt;a href="http://github.com/jwilger/right_aws/tree/master"&gt;unofficial GitHub repository for the right_aws gem&lt;/a&gt;, I guess I should actually maintain it a bit. I’ve updated the master branch to the &lt;a href="http://rubyforge.org/frs/?group_id=4176&amp;release_id=23697"&gt;official 1.7.2 release sources&lt;/a&gt;. Since the &lt;a href="http://www.rightscale.com"&gt;RightScale&lt;/a&gt; folks implemented COPY support, I removed the branch I made which adds that support; however, I added a &lt;a href="http://github.com/jwilger/right_aws/commits/jwilger"&gt;jwilger branch&lt;/a&gt; which includes some of the changes I’ve made around the tests.&lt;/p&gt;

&lt;p&gt;Unfortunately, RightScale doesn’t seem to have a publicly accessible repository for the gem’s source code available at all, so the update to 1.7.2 sources was accomplished by just unpacking the gem and moving it’s contents into my git repository. If anyone from RightScale is listening: even if you don’t want to move from Subversion to git, a &lt;em&gt;public&lt;/em&gt; Subversion repository would at least make it easier to keep the unofficial git repository up to date!&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/johnwilger/~4/LBZxpwRFr6U" height="1" width="1"/&gt;</content>
 <feedburner:origLink>http://johnwilger.com/2008/07/09/unofficial-right_aws-github-repository-updated-to-1-7-2.html</feedburner:origLink></entry>
 
</feed>
