faded picture of luke
a semi-random photo | click for the full photo gallery
click to browse photos
homepage navigation

Luke Melia

Agile “cheat” Sheet

Fellow New Yorker Agilist Dave Laribee made a present for us. A cheat sheet for Agile as a PDF:

Sure you’ve got the ReSharper 3.0 cheat sheet taped to your monitor, but what do you have that’s reminding you of values and principles? You need an Agile cheat sheet!

Luckily I’ve got one for you right here! Print it out and put it in your team room, tack it to your task board, and tape it to your monitor.

We’ve had the agilemanifesto.org homepage taped up to the wall of our team room for a while now, too, and it’s a nice reminder.

The point of the post isn’t just to encourage you to follow Dave’s lead, though. You can figure that out on your own…

I checked to see if there was a version of this already for “cheat”, the command line ruby wiki cheat sheet thing. There wasn’t.

Now, there is!

$ gem install cheat
$ cheat agile

Happy software crafting…

A rake task to apply a patch from a Trac ticket

Tracks uses a Trac for a bug tracker, and the awesome Tracks community helpfully attaches patches to tickets for a committer (me) to review and apply. I wanted an easy way to do this, so I whipped up this rake task that let’s you select a ticket and attachment to apply to the app as a patch:

namespace :trac do
  desc 'Apply a patch from a trac ticket.' 
  task :patch => :environment do
    require 'hpricot'
    require 'open-uri'
    require 'rubygems'
    require 'action_view/helpers/text_helper'
    include ActionView::Helpers::TextHelper
    trac_url = "http://dev.rousette.org.uk"
    unless ENV['TICKET']
      print "Enter the ticket number: "
      ENV['TICKET'] = STDIN.gets.chomp
    end
    doc = Hpricot(open("#{trac_url}/ticket/#{ENV['TICKET']}"))
    attachments_dts = doc/"#attachments"/"dt"
    attachment_index = 0
    if attachments_dts.length > 1
      print "\nThere are multiple attachments for this ticket.\n"
      attachments_dds = doc/"#attachments"/"dd"
      attachments_dts.each_with_index do |dt, i|
        print "\n[#{i + 1}] " + strip_tags(dt.inner_html) + " - " + attachments_dds[i].inner_html
      end
      print "\n\nEnter the number of the attachment you want to apply as a patch: "
      attachment_index = STDIN.gets.to_i - 1
    end
    filename = attachments_dts[attachment_index].search("a").inner_html
    patch_url = "#{trac_url}/attachment/ticket/#{ENV['TICKET']}/#{filename}?format=raw"
    
    patch = open(patch_url).read

    File.open('patch.diff', 'w+') do |f|
      f.puts patch
    end

    `patch -p0 < patch.diff && rm patch.diff`

    print "\nPatched with #{filename} from Trac ticket #{ENV['TICKET']}.\n"
  end
end

Here’s a sample shell session demonstrating the case when there is more than one patch attached:

$ rake trac:patch
Enter the ticket number: 560

There are multiple attachments for this ticket.

[1] cleanup_stats_controler.diff (25.9 kB) - added by foo@barcom on 09/16/07 19:32:35. - cleanup of stats_controller
[2] cleanup_stats_controler.2.diff (25.9 kB) - added by foo@barcom on 09/16/07 19:46:51. - whoeps old patch introduced an error. fixed with this patch

Which number attachment do you want to use as the patch to apply: 2

Patched with cleanup_stats_controler.2.diff from Trac ticket 560.

If there is only one attachment, it will be applied without you having to make a selection. Also, you can specify the ticket number on the command line (i.e. rake trac:patch TICKET=155).

That’s it. Hope this is useful!

Thanks to Chris Wansrath of err.the_blog for the inspiration via pastie:patch.

Upcoming Conferences

I had a great time at Agile 2007 in August with the rest of the Oxygen team. In May, Kris and I went to RailsConf 2007. And of course, in April, I helped organize the first Gotham Ruby Conf. I also presented at Microsoft’s Code Camp NYC in March.

Coming up soon, I’ll be attending the first RubyEast near Philadelphia on September 28. I’ll spend some time with the Oxygen team promoting Ript at the DigitalLife Expo in NYC September 27th-30th, and will be taking my first trip to Austin to participate in the ALT.net Conference October 5th-7th.

Unfortunately, it looks like I’m going to be out of town for XP Day Manhattan on October 13th.

Aside from these special events, I do my best to make the nyc.rb and Extreme Tuesday events.

Hope to see you at one or more of these!

P.S. If you considering attending the Better Software’s Agile Development Practices Conference in December in Orlando, you’ll be graced with the presence and presentations of my colleagues Wendy & Oksana. Definitely worth a trip to Disney World. Same goes for Ken with two engagements this fall: in London at the Scrum Gathering and at HICCS in Hawaii.

Expressing Yourself With Selenium and Rails

Selenium, the web testing framework, is powerful tool for your testing toolbox. I use it extensively on Tracks to nail down pesky AJAX bugs. Yesterday, I hit a wall that took a few hours to overcome. Once I did, though, I had a new level of appreciation for how expressive I can be with Selenium and Selenium on Rails.

This was the situation: in Tracks, you can add a new “to-do” specifying a “context” that it is associated with. The to-do is added to the page within the corresponding context container via AJAX. If a brand new context is specified, the app should create a new context container to hold the new to-do. That was the behavior I needed to test.

After much trial and error, here is what I came up with:

setup :fixtures => :all
login :as => 'admin'
open "/"
store_eval "this.browserbot.getCurrentWindow().$$('.context').length", 'initial_context_count'
type "todo_description", "a new action"
type "todo_context_name", "Brand new context"
click "css=#todo-form-new-action .submit_box button"
store_eval "${initial_context_count} + 1", 'expected_context_count'
wait_for_eval "this.browserbot.getCurrentWindow().$$('.context').length", "${expected_context_count}"

We’re looking at the contents of a .rsel file, called RSelenese, which is translated to a Selenium test by the Selenium on Rails plugin. The lines I had trouble with were 4, 8 and 9. I had never used this feature of Selenium before, and had trouble figuring out that to execute javascript in the context of the page under test, I had to use the this.browserbot.getCurrentWindow() reference.

In a nutshell, the test simulates a login, loads the homepage, gets the current number of context containers on the page and stores it in a javascript variable, stores that number plus 1 in another variable, adds a new to-do to a non-existent context, and then waits to make sure that the new number of contexts is equal to one more than the original number.

I was satisfied that I got this working, but disappointed with how ugly the syntax was. There had to be a better way…

It turned out that Selenium had an extensions model that could help. Selenium looks for a file called user-extensions.js and loads it automatically. I created one like this:

// All get* methods on the Selenium prototype result in
// store, assert, assertNot, verify, verifyNot, waitFor, & waitForNot commands.
// Will result in support for storeContextCount, assertContextCount, etc.
Selenium.prototype.getContextCount = function() {
    return this.browserbot.getCurrentWindow().$$('.context').length;
};

To get at it elegantly from Selenium on Rails, I followed the pattern the plugin uses in defining methods to call the built-in commands. To avoid modifying the plugin code directly, I reopened the module SeleniumOnRails::TestBuilderAccessors in my own selenium_helper.rb in my app’s lib directory. (I “require” this file in my config/environments/test.rb).

module SeleniumOnRails::TestBuilderAccessors

   # How many elements with the class "context" are present on the page? 
   #
   # Related Assertions, automatically generated:
   # * +assert_context_count+
   # * +assert_not_context_count+
   # * +verify_context_count+
   # * +verify_not_context_count+
   # * +wait_for_context_count+
   # * +wait_for_not_context_count+
   def store_context_count variable_name
     command 'storeContextCount', variable_name
   end
  
   each_assertion 'store_context_count' do |assertion_method, command_name|
     define_method assertion_method do |expected_count|
        command command_name, expected_count
     end
   end
 end

This let me modify my test case to the more pleasing:

setup :fixtures => :all
login :as => 'admin'
open "/"
store_context_count 'initial_context_count'
type "todo_description", "a new action"
type "todo_context_name", "Brand new context"
click "css=#todo-form-new-action .submit_box button"
store_eval "${initial_context_count} + 1", 'expected_context_count'
wait_for_context_count "${expected_context_count}"

I was a lot happier with this, but I thought of the way that Marcel Molina’s assert_difference reads so nicely, and added this:

module SeleniumOnRails::TestBuilderAccessors
 
  def assert_context_count_incremented(&block)
    store_context_count 'initial_context_count'
    store_eval "${initial_context_count} + 1", 'expected_context_count'
    yield
    wait_for_context_count "${expected_context_count}"
  end

end

Sure enough, this let me improve my test case yet again to:

1
setup :fixtures => :all
login :as => 'admin'
open "/"
assert_context_count_incremented do
  type "todo_description", "a new action"
  type "todo_context_name", "Brand new context"
  click "css=#todo-form-new-action .submit_box button"
end

The test case now gives me hope that my Selenium test case can be not only pragmatic, but also expressive!

P.S. If you want to dig further, all this code can be found and studied in the Tracks repository.

LukeMelia.com created 1999. ··· Luke Melia created 1976. ··· Live With Passion!
Luke Melia on software development freelance web development how to contact me Luke Melia, Software Developer letters and more from my travels photo gallery personal philosophy