Loccasions: Pair Programming

Glenn Goodrich
Share

On a day like any other, I was scanning through my Twitter feed searching for knowledge and inspiration. Although I didn’t know it yet, this day of Tweet Fishing would prove fruitful and change the direction of Loccasions forever. (You should imagine some dramatic music here, complete with a cutaway of a Loccasions screen shot fading to black.)

The tweet that would change everything was about rubypair.com. RubyPair.com is a “searchable directory of developers who are looking to pair, in-person or remotely, on topics that they’ve expressed an interest in through their profile.” In other words, it’s a way to find someone to pair program on your (or their or some open source) Ruby project. I read this tweet, and it struck me like a thunderbolt! I need to pair program on Loccasions! I mean, I had one (arguable) programmer, I just needed to find another. Where would I find such a soul? Well, I thought that the creator of RubyPair.com would surely be keen. I’d offer up free publicity on RubySource.com, THE (read: a) place to read about all things Ruby.

Looking on the About page, I saw Evan Light’s name. I’ve followed Evan on twitter (@elight) for awhile now, and I know that he holds “office hours” where programmers seeking pairing can sign up. I sprang at the opportunity and snagged Evan’s Office Hours on September 20th. Now, all I had was the crazy anticipation of waiting for that day, like it was my own Ruby Christmas.

Let There Be (Evan) Light

Evan Light has been doing software development a long time. You can tell by reading his highly informative blog and his bio here. Evan hasn’t always done Ruby, and in fact took a great risk to get to his current station in life Evan, in my opinion, exemplifies why the Ruby community is, how-you-say?, super-fantastic. He is smart, approachable, and passionate about helping others learn Ruby. If you need an awesome Ruby resource, he is available for hire (with all the blessings of Rubysource) at TripleDogDare. He is one cool cat (that loves cats).

Am I Worthy?

Being honest, I was a bit apprehensive. I’ve never really paired before. Especially not with a stranger. Especially not with a stranger that is more experienced/smarter/better at this Ruby stuff than yours truly. It was a distinct possibilty, in my brain, that the session would be highly frustrating for him and that I, quite possibly, would cry. (Continuing with the dramatic theme of the post…) I stood firm, though, knowing that sacrificing for your art is a sign of greatness.

The Day Arrives

As 9:50pm I start the stare-blankly-at-my-screen-worrying-about-my-impending-embarrassment trance at my desk. Ten minutes later, the Skype ring jolts me back to reality. It’s Evan. I answer, “Hi.”

“Hello, Glenn. Are you ready to go?” he asks.
“Um, sort of. I have to be honest, here, I’ve never done this before.”
“What? Programmed?” he chuckles. “It’s OK, I’ll get us going.”

I had previously told Evan about Loccasions, and he had cloned my github repository. He told me that all he needed from me was my SSH public key. I sent him the key, and he tells me to ssh to his box (he set me up a user and he has a cool DynDNS-type domain), which I do. My next instruction was to type tmux -S /tmp/glenn attach. I had not heard of tmux before, but I threw caution to the wind and typed the command.

Revelations

So, tmux is insanely fantastic. The command I entered connected me to a tmux session that Evan had started. The terminal window was split into a command line and an editor (vim) window with the Loccasions source in it. The kicker? We both can control/type/etc the session in real time.

TMUX Session Window

There is no handing control back and forth. It’s real time editing and hacking as if you were sitting next to each other. Actually, it’s better than that, because we don’t have to swap seats. You can split the window as much as needed, so we can run spork in one window, vim in another, and the command-line in another. I can’t relay how freaking cool this is for remote code-sharing/pairing.

To make it even cooler, Evan forwarded a port to his development rails server on his box, so when he ran rails s, I could open up a local browser and see our changes to the site, again, in real-time. I was blown away. Evan, who must be used to seeing mouth-agape, gaffawing dopes like me in a Skype video chat window, shrugs like it’s no big deal. The pairing session was already worth it, and we hadn’t touched any code. I told him that he needs to blog about this pairing approach. The masses must be informed.

Oh Yeah, We’re Supposed to Program

With considerable effort, Evan talked me down off my cloud and put us on task. He had, as I mentioned, cloned the repository, but had not run bundle install yet. Here is where we hit our first issue. I hadn’t, yet, upgraded Loccasions to Rails 3.1 final (it started life on rc5), so that was step one. Evan cranked open the Gemfile and complimented me for using “twiddle-waka” (~>) the gems in the file. Being honest, I’ve always done that, but couldn’t really explain why, but Evan can: Twiddle-waka is usually a safe choice in Gemfiles because tiny releases (The Z in X.Y.Z) are supposed to be safe updates that won’t cause breakage as I understand it. Apparently, that’s THE thing to do with your Gemfile, and my tension about being a moron eased a bit. Maybe that’s why Evan mentioned it, maybe not. Either way, I ceased to worry about the possibility of being told I was worthless and weak and started to get really excited about the session.

(I realize that the approach to gem versioning with Bundler is somewhat debatable. For another approach, read this.)

Evan changed the gem rails line in the Gemfile to gem rails ~>'3.1.0', which caused Bundler to complain about the sass-rails gem. To make Bundler happy again, Sass-rails and coffee-rails also had to point to their respective 3.1.0 versions. The final version change for Rails 3.1.0 was Devise, moving from 1.4.2 to 1.4.6.

Mongoid had recently released 2.2.0, so we changed that too. That last change brought on messages about bson_ext needing to be 1.4.0, not 1.3.1. You have to luuuuuv Bundler (and I do). A quick change of the bson_ext version and bundle install finished successfully. We’re at Rails 3.1.0 Final, progress in pair programming.

The next suggestion Evan had was to use the guard_rspec (based on guard) gem to have the tests automatically run as files are modified. I had been toying with the idea of using autotest, so this was good timing. Neither of us were sure if spork and guard would play nicely, and after our session I went and found guard_spork. Long story short, add:

gem 'guard-rspec', '~> 0.4.5'
gem 'guard-spork', '~> 0.2.1'

to the development group of your Gemfile. Finally, update spork to 0.9.0.rc9 (gem 'spork', '~> 0.9.0.rc9') or you'll get a C error when spork builds andbundle install`. [Note: I am not adding the Mac OSX specific stuff (the fsevents and growl_notify) gems to the Gemfile. Please read the README at the guard Github repository and install the file system events for your system.] With guard installed, we need to tell it about spork and rspec, and we do that with a Guardfile in the root of the app:

guard 'spork', :cucumber_env => { 'RAILS_ENV' => 'test' }, :rspec_env => { 'RAILS_ENV' => 'test' } do
  watch('config/application.rb')
  watch('config/environment.rb')
  watch(%r{^config/environments/.+.rb$})
  watch(%r{^config/initializers/.+.rb$})
  watch('spec/spec_helper.rb')
end

guard 'rspec', :version => 2 do
  watch(%r{^spec/.+_spec.rb$})
  watch(%r{^lib/(.+).rb$})     { |m| "spec/lib/#{m[1]}_spec.rb" }
  watch('spec/spec_helper.rb')  { "spec" }

# Rails example
  watch(%r{^spec/.+_spec.rb$})
  watch(%r{^app/(.+).rb$})                           { |m| "spec/#{m[1]}_spec.rb" }
  watch(%r{^lib/(.+).rb$})                           { |m| "spec/lib/#{m[1]}_spec.rb" }
  watch(%r{^app/controllers/(.+)_(controller).rb$})  { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
  watch(%r{^spec/support/(.+).rb$})                  { "spec" }
  watch('spec/spec_helper.rb')                   { "spec" }
  watch('config/routes.rb')                          { "spec/routing" }
  watch('app/controllers/application_controller.rb')  { "spec/controllers" }
  # Capybara request specs
  watch(%r{^app/views/(.+)/.*.(erb|haml)$})          { |m| "spec/requests/#{m[1]}_spec.rb" }
end

Opening up a new terminal and typing guard should fire up a guard (and spork) session.

On Guard!

Now, as we modify test and application files, the specs should run automatically. I like that feature – another notch on the stick for pair programming. (After our session, I noticed that my Selenium tests were unable to connect to Firefox 7, so I updated Capybara to 1.1.1 in the Gemfile as well. Do that too.)

Feature of the Day

Once we had the environment up and running with guard, Evan asked me what feature I wanted to add during our session. I had very ambitious ideas of what we could do, but we’d start with creating the page to show a singular Event. In Rails terms, I wanted to create the events#show sequence (here I am using “sequence” to refer to the controller => view needs to satisfy the show ing of an event ). So, I moved to the spec directory and created an acceptence/show_event_spec.rb. This was Evan’s first chance to see how I was, basically, relying entirely on acceptance tests to exercise the app. He told me how he was moving away from acceptance tests for performance and syntactical reasons. Evan wants something like Cucumber, but not Cucumber. He actually wrote a DSL, called Couda that emphasizes the Given-When-Then approach to testing, but doesn’t rely on regular expressions like Cucumber. Check it out.

Evan told me of experiences of having hundreds of specs to run, where he had to use things like parallel_tests to get the tests to run within 15 minutes. In fact, we talked about all kinds of stuff, which is probably the biggest takeaway of the session. Sure, we added a feature to Loccasions, but it was the chatter around what we were doing and around Ruby/Rails that I found fascinating.

OK, OK, the ACTUAL Code

I am not going to step through the actual sequence of events that led to creating the Show Event page. I’ll post the code below, which should all be in your wheelhouse if you’ve been following the Loccasions series. At a high level, Evan did some coding, I did some coding (he was really good about making me do some of the work, as I was content to sit back and watch) and we ended up with a basic page. Here is the spec for the page:

require 'spec_helper'

feature 'Show Event', %q{
  As a registered user
  I want to see an Event
  so I can see my Event Details
} do

background do
    @user = Factory(:user)
    @event = Factory(:event, :user => @user )
    login_user @user
  end

scenario "Show Event" do
    click_link "Show Details"
    page.current_path.should == event_path(@event)
    page.should have_content(@event.name)
    page.should have_content(@event.description)
  end
end

Add the “Show Details” link to **app/views/events/index.haml.html** (Thanks Alert Reader rsludge!) Here is the new ul and loop for events.

%ul#events
  - for event in @events
    %li{:class => @event == event ? :selected : nil}
      %span.del_form
        =button_to "X", event, :confirm => "Are you sure?", :method => :delete
      %span.event_name
        = link_to event.name, edit_event_path(event)
      %span.event_details
        = link_to "Show Details", event_path(event)
      %span.event_description= event.description
      %div.clear

If you read the last post, then you know what is coming. The spec fails, because there is no events_controller#show method:

def show
    @event = current_user.events.find(params[:id])
end

Next, it’ll ask for the view template (app/views/events/show.html.haml):

%h2= @event.name
.description= @event.description

Yes, I know it isn’t much, but it won’t be until we add Occasions (next post, I promise!). Creating this view makes the spec pass, and we had a new feature.

Time Flies

At the end of this spec, we had just about reached an hour. I mentioned adding Occasions, and Evan (understandably) said we were at a good place to stop. I agreed, and he sent me the code. You can see all the changes here which actually includes some minor changes from the comments of previous posts (thanks Alert Readers!)

Go and Pair

All in all, I really enjoyed the pairing session with Evan. I learned a ton in an hour, and Loccasions is better for it. Evan is passionate about helping the Ruby community, and he has found a manner (the tmux/Skype crazy-awesome) and a message (RubyPair). I encourage you to sign up at RubyPair, find another programmer, and do some work. Evan is always looking for pairers to work on RubyPair, so sign up on his Office Hours and help out. Finally, I can’t adequately express my appreciation to Evan for allowing me to document this expereince and for being gracious and understaning. Right, now pair up!

CSS Master, 3rd Edition