|
Red Squirrel Reflections
Dave Hoover explores the psychology of software development
|
|
Thu, 23 Oct 2008Rails Rumble 2008: Developing Run.Track.Run. Leah Welty-Rieger joined Obtiva a couple months ago. She is an apprentice in our Software Studio. (She also recently earned her PhD in Particle Physics from Indiana University.) About a month or so ago, Leah and I were walking back from lunch and started talking about our shared interest in running. Then she told me about an idea she had for making simpler, easier, more informative running software for planning and tracking your training. A couple weeks later I found a link to Rails Rumble 2008. I'd wanted to rumble in the past, because I'm a fairly speedy programmer, and I love competition. But having very little CSS-fu and lacking any inspired 48-hour-sized ideas, I'd never rumbled. Until this year. I immediately thought of Leah's idea and realized that with the addition of CSS-ninja Dan Nawara to our Studio team, we had sufficient skills to rumble. And just because I had the luxury of doing so, I asked Fred Polgardy to join us as well, because, well, Fred's a genius, particularly with JavaScript. We got together 3 times before the competition to formulate our plan of attack. This helped us start on the same page. We understood the domain model (Users have Runs have Splits), and we knew we were going to integrate with the Garmin Forerunner, and we knew we were going to use Flotr to build the HTML-canvas-based graphs that Leah had specified. We decided to use HAML and SASS, which I had never used, but found perfectly intuitive and productive (and now prefer it). I decided to stick with good old test/unit for TDD. I'm still much more productive in test/unit than any other Ruby testing framework and I needed something dead simple since I was going to be running low on energy. I also planned to use TDD strategically. Basically, I used it to test my models, but developed my controllers without tests. Ironically, this helped keep my controllers really skinny, since I ended up extracting logic into models (such as the Garmin XML file processing and matching scheduled runs to actual runs) and wrapping them in tests. We also decided on the name for the app: Run.Track.Run. We were both excited and disappointed to find out a day later that Relevance had created Run Code Run and would be offering free continuous integration for rumblers, and thereby leading everyone to assume that we copied their name. The competition started on Friday at 7pm Chicago time. My wife, kids, and I with friends in their house. I rudely, and suddenly grabbed my laptop and bluetooth-enabled modem/mobile-phone and started the process of setting up our github repository with Bort, installed Debian on Linode (with Apache+Passenger, MySQL, Postfix), hooked our build into Run Code Run, and pushed our first Capistrano deployment up to the server. The next morning the 4 of us met at the office and started rumbling as a team. The rest of the story is actually pretty mundane. I got really pissed off, swearing loudly at the Garmin JavaScript libraries for a while until Fred came over and solved the problem. We did a lot of pair programming. And mostly just kicked ass consistently for 48 hours. I got to pair with Leah, Dan and Fred (sorted in order of amount of pairing time). I think everyone paired with everyone. And everyone got enough sleep. And with an hour to go, we were kicking back with beer and making final tweaks. In the end, we launched Run.Track.Run., which I'm particularly excited about, since I'm one of the (currently) 75 runners who are already using the application. [/projects/runtrackrun] permanent link Fri, 25 Apr 2008I ripped the WordNet server-side integration out of The Lightweight Visual Thesaurus and shoved in the Twitter API to create Tweet Nodes. It's still at total hack and only works on Firefox, but it's a fun little Twitter conversation browser.
Here's how olabini's Tweet Nodes look today... [/projects/tweet_nodes] permanent link Wed, 10 Jan 2007I'm not sure why anyone would want to run Watir scripts on a handheld device ... but since the iPhone runs on OS X and ships with Safari, I'm wondering how difficult it would be to get SafariWatir setup ... just because we can.[/projects/watir] permanent link Wed, 03 Jan 2007Inspired by Jamis' post on assert_select I've just committed an update to my Watir on Rails plugin that adds support for both assert_select and assert_tag. Give it a try!
require File.dirname(__FILE__) + '/../test_helper'
class GettingStartedTest < Test::Unit::TestCase
include WatirOnRails
def test_getting_started
browser = open_browser("/")
assert_select "div#getting-started h1", "Getting started"
assert_tag :tag => "h1", :content => "Getting started",
:parent => { :tag => "div", :attributes => { :id => "getting-started"} }
end
end
Rather than asserting against the body of the response from the controller, I'm grabbing the HTML from the most recently opened browser.
[/projects/watir] permanent link Reviving the Lightweight Visual Thesaurus I've brought my Lightweight Visual Thesaurus back to life. I'm hoping to make some improvements to it this year, like supporting more browsers than just Firefox.[/projects/lvt] permanent link Wed, 06 Dec 2006Watch the screencast of SafariWatir running its test suite.[/projects/watir] permanent link Wed, 22 Nov 2006Hamish released the first rb-appscript gem, paving the way for the new and improved SafariWatir. Now with non-sluggish performance! Seriously, my regression test script runs ridiculously fast on my MacBook Pro compared to previous versions of SafariWatir.
This performance boost comes as no surprise. Up until now I was doing the
`osascript <<SCRIPT
tell application "Safari"
do JavaScript "#{script}" in document 1
end tell
SCRIPT`.chomp
This got me started, but it turns out that using an Apple Event bridge like rb-appscript is more natural, and so much faster...
app = AS.app("Safari")
document = app.documents[1]
app.do_JavaScript(script, :in => document)
In unrelated news, my fellow Obtivian Tyler Jennings has submitted his first Rails patch.
[/projects/watir] permanent link Mon, 16 Oct 2006Bye-Bye AppleScript, Hello Apple Events, Thanks Hamish So I lied. In my last post I said that I would be taking a break from Open Source in the short-term. How wrong I was. When I released SafariWatir 0.1.2 I whined about the lack stable Ruby/AppleScript support, which has been my excuse for SafariWater's sluggish performance. On Saturday, Hamish Sanderson, the author of appscript (Python + Apple Events) and rb-appscript (Ruby + Apple Events), contacted me to point me toward his recent port of appscript to rb-appscript on RubyForge. I just committed to the svn repository after spending about 3 hours tonight refactoring SafariWatir to use rb-appscript. Wow! Now I have a different performace problem. The pages actually go by too quickly!A release should be out soon, right after Hamish and I sort out some annoying warnings being spewed out somewhere in the depths of his magical modules. [/projects/watir] permanent link Fri, 13 Oct 2006On the plane to San Jose last week I polished off another SafariWatir release. This will likely be the last release for a while. Due to the flooding that we experienced last week, I have become very focused on revenue-generating activities in the short-term. There's nothing like losing 1/3 of your living space and slow-to-respond insurance companies and a family of 5 to support to keep you focused on the bottom line! This release adds table and frame support, along with improving the click events. Here are some examples of what you can do with SafariWatir at this point...
require 'rubygems'
require 'safariwatir'
require 'test/unit/assertions'
include Test::Unit::Assertions
browser = Watir::Safari.new
browser.goto("http://www.geraldmweinberg.com/")
browser.frame("menu").link(:text, "Books").click
browser.frame("menu").link(:text, /psychology/i).click
assert_match /Silver Anniversary/, browser.frame("content").text
browser.goto("http://basecamphq.com/")
assert_match /What is Basecamp\?/, browser.table(:index, 1)[1][2].text
# First table
browser.close # First row
# Second cell
# Yes, Watir uses 1-based indexes.
[/projects/watir] permanent link Wed, 04 Oct 2006Rails Seminar Watir on Rails talk cancelled due to Flood I arrived in San Jose late yesterday because of a thunderstorm delay in Chicago. When I landed I called my wife to let her know I had arrived. I was looking forward to presenting Watir on Rails at AjaxWorld's Ruby on Rails Seminar. Unfortunately, the thunderstorms that delayed my flight also flooded our home. Thankfully my wife and children are OK, but there's some serious damage to our house and I'm catching the next flight back. My talk won't be happening today.Update: We've received an amazing amount of help and support over the last 24 hours. When I arrived at home today, it was depressing to see a mudpit and a dumpster where our front yard used to be, but it was inspirational to see so many friends and family hard at work getting things cleaned up and dried out. This experience has certainly given Staci and me an opportunity to see things, particularly material things, from a new perspective. At times like this I'm frequently reminded of Jerry's Sage Advice: "It may look like a crisis, but it's only the end of an illusion." [/projects/watir] permanent link Thu, 21 Sep 2006Watir on Rails Update: It's all Relative I made a backward compatible update to the Watir on Rails plugin to allow you to get rid of those nasty fully-qualified URLs in your Watir on Rails tests. To allow for this, I added some config-like class methods to specify the server and port (defaults to http://localhost:3000) in your TestCases. Here's a simple example, relying on the defaults:
class WelcomeTest < Test::Unit::TestCase
include WatirOnRails
def test_salutation
browser = open_browser
browser.goto("/")
assert_match /Welcome Guest/, browser.div(:id, "banner").text
end
end
A slightly more complex example, testing against a non-standard test server configuration:
class MyAccountTest < Test::Unit::TestCase
include WatirOnRails
server "10.2.4.143"
port 3001
def test_update_preference
browser = open_browser("/squirrels/")
browser.link(:text, /preferences/i).click
browser.text_field(:name, "user").set("dave")
browser.text_field(:name, "pass").set("test")
browser.button(:name, "commit").click
assert_match /<h2>Welcome Dave<\/h2>/, browser.html
browser.checkbox(:id, "largeFont").set
browser.button(:name, "commit").click
assert_match /<h1>Welcome Dave<\/h1>/, browser.html
end
end
If you're serious about building up a good Watir acceptance test suite, consider developing a site-specific lanaguage (example here).
[/projects/watir] permanent link Thu, 07 Sep 2006After showing some hacked up examples of Watir and Rails integration at RailsConf and last Friday's Ajax on Rails addon, I decided that I was way overdue to venture into the exciting world of Rails Plugins. And just a few hours later, Watir on Rails was born. Here's a quickstart... gem install [safari]watir ./script/plugin install svn://rubyforge.org/var/svn/watir-on-rails ./script/generator watir SuccessfulLogin ./script/server -e test rake test:watir One of my goals for this first release was to make Watir on Rails have a similar feel to Rails Integration Tests, which provide a handy open_session method you can use to decorate your session object with a site-specific language. Well, if you know anything about Watir, it should not come as a surprise that Watir on Rails provides an open_browser method that does something similar. You can use open_browser to simply open a browser and use the excellent Watir API, or you can provide an optional block and write Watir on Rails tests methods like this...
def test_login
browser = open_browser
browser.login_with :username => "dave", :password => "test"
assert_match /Welcome Dave/, browser.div(:id, "banner").text
end
def open_browser
super do |browser|
def browser.login_with(args)
goto("http://localhost:3000/login")
text_field(:name, "user[name]").set(args[:username])
password(:name, "user[password]").set(args[:password])
button(:name, "login").click
end
end
end
[/projects/watir] permanent link Mon, 24 Jul 2006SafariWatir now offers feedback when you try to access an element that doesn't exist...
browser = Watir::Safari.start("http://google.com/")
browser.button(:name, "foo").click
Watir::Exception::UnknownObjectException: Unable to locate Button element with name of foo.
from /usr/local/lib/ruby/gems/1.8/gems/safariwatir-0.1.1/./safariwatir/scripter.rb:243:in `execute'
from /usr/local/lib/ruby/gems/1.8/gems/safariwatir-0.1.1/./safariwatir/scripter.rb:68:in `highlight'
from /usr/local/lib/ruby/gems/1.8/gems/safariwatir-0.1.1/./safariwatir.rb:10:in `click'
from (irb):4
That's an important step forward, but I added something else this weekend just for fun.
Tell me, Mr. Anderson, what good is a phone call if you're unable to speak? You can now interrogate any SafariWatir element ... and it will speak. After you gem install safariwatir, turn up your speakers and try this...
browser = Watir::Safari.start("http://redsquirrel.com/cgi-bin/dave/projects/watir/safari.watir.0.1.1.html")
browser.span(:id, "matrix_quote").speak
I'm not sure what a feature like this is good for, but I couldn't resist.
[/projects/watir] permanent link Sun, 23 Jul 2006Rails Acceptance Testing eBook I am writing an eBook for Addison-Wesley (they call them shortcuts) on using Selenium and Watir to test Rails apps. Look for it in the October-November timeframe. If you have experience reports that you would be willing to share, let me know.[/projects/watir] permanent link Fri, 21 Jul 2006If you're a Mac-user who has been coveting the power and simplicity of (Windows/IE-only) Watir, your wait is over! It's your turn now! A pre-pre-pre-alpha of SafariWatir has been released. Go ahead and gem install safariwatir, fire up irb and go nuts...
require 'rubygems'
require 'safariwatir'
browser = Watir::Safari.new
browser.goto("http://google.com")
browser.text_field(:name, "q").set("safariwatir")
browser.button(:name, "btnI").click
puts "FAILURE" unless browser.contains_text("Watir on Safari")
There are tons of things missing from this release, but many of the basic Watir actions are in place. The critical missing piece is error messages: If you try to access elements that don't exist on the page, SafariWatir won't complain (yet).
[/projects/watir] permanent link Thu, 20 Jul 2006I will be showing how to use Watir to test Rails+Ajax apps at the Real-World Ruby on Rails Seminar in Silicon Valley on October 3. This time I shouldn't need to use Windows on Parallels to show off Watir on my MacBook Pro thanks to SafariWatir. :-)[/projects/watir] permanent link Thu, 13 Jul 2006If you haven't noticed, I'm a big fan of web testing tools like Selenium and Watir. As I prepared for my RailsConf talk (at RailsConf), surrounded by herds of Macs, I realized that Watir adoption in the Rails community was seriously not going to happen anytime soon without some cross-browser and cross-platform support. Without that support, I continue to recommend Selenium RC or Selenium on Rails to most Rails developers. And yet I still find myself infatuated with Watir's dead-simple setup (gem install watir and you're done) and intuitive API. So ... <deep-breath/> ... inspired by the Mac-centric tendencies of most of the Rails frontrunners, I have launched the SafariWatir project on RubyForge. There's not very much there yet. You can open and close Safari, fill out text fields, click buttons and links, ask whether the page contains text ... oh, and you can click alert boxes. I just discovered Apple's GUI Scripting integration with AppleScript, which makes it wonderfully easy to handle things that Windows makes incredibly difficult. (Apple provides GUI Scripting to facilitate universal access.) Watir (for IE) developers, you may want to look away, the following code could hurt... tell application "System Events" to tell process "Safari" tell window 1 if button named "OK" exists then click button named "OK" end if end tell end tellThat's all it takes to click an alert button in AppleScript. I wrap that in Ruby-goodness and stick it behind the Watir API, and man, I think this project is going to just about write itself. [/projects/watir] permanent link Sat, 08 Jul 2006As I mentioned in my talk at RailsConf, I needed to update the Selenium RubyGem to get it up-to-date. The gem is now available: gem install selenium. This will give you the latest release (0.7) of Selenium Core. In previous Selenium gems there were a few crufty Ruby files in there, but these have now been extracted to Selenium Remote Control. This update is mostly for Jonas and Bret.
[/projects/selenium] permanent link Thu, 15 Jun 2006My Canvas/Ajax articles in Russian It appears that my articles on Canvas and Ajax (originals here and here) have been translated into Russian: the Supertrain article and the Interactive Canvas on IE article. Although they probably don't have permission from O'Reilly to reprint them, it's an exciting moment to realize someone else has taken the time to make my work available to a new population.Thu, 11 May 2006The Interactive Canvas on XML.com XML.com has published my Interactive Canvas and Internet Explorer article.[/projects/interactivecanvas] permanent link Tue, 02 May 2006The Selenium RubyGem on RubyForge was woefully out of date. Until today. It now contains the current Selenium release (0.6). Thanks to Jon for holding my hand as I created my first gemspec and to Jonas for his excellent Selenium on Rails plugin, which was the impetus for this update.[/projects/selenium] permanent link Tue, 25 Apr 2006Watir, the Lazy Programmer's Friend My current client was in need of a functional test suite for their web site to raise our confidence that we wouldn't break anything when we upgraded to Hibernate 3. Despite being a Selenium fan, I wanted to give Watir a try (for various reasons, it felt like a good fit). People were hesitant about the introduction of a new language (Watir is a web testing DSL in Ruby), but after I spent a day getting a bunch of test scenarios scripted, Watir proved its worth. Every developer I paired with said the same thing: "I'm never going to type into a browser again." There were things I could do with Watir that I would be hard pressed to do with Selenium, like outsmarting CAPTCHA, abstracting common code by opening Watir::IE, and switching between dev, test, and staging by changing a single variable. That last point is one of the big wins of Watir over Selenium, you never touch the server, it's dead simple to setup. That said, the achilles heel of Watir is that it currently only runs on Windows and IE, while Selenium runs virtually anywhere on any browser. So I'll stick with Selenium for my Rails project on my MacBook, while Watir seems to be a good fit for our Windows workstations at my client site. Abstracting common code into Watir::IE has worked beautifully so far. We created a WatirHelper module that includes code like this:
module Watir
class IE
def login_admin
text_field(:name, "name").set("jerryQ")
text_field(:name, "pass").set("bletch")
button(:name, "Sumbit").click
end
def fill_out_problem_form(last_name, email)
select_list(:name , "dateMonth").select("December")
select_list(:name , "dateDay").select("25")
select_list(:name , "dateYear").select("2008")
text_field(:name, "firstName").set("F")
text_field(:name, "lastName").set(last_name)
text_field(:name, "email").set(email)
text_field(:name, "confirmEmail").set(email)
end
end
end
Which allows our Watir scripts to look like this.
require 'watir'
require 'watir_helper'
include Watir
# update this to point to any server you want
server_name = "localhost"
ie = IE.new
ie.goto("http://#{server_name}/admin")
ie.login_admin
ie.link(:text, "Tell us your problem").click
ie.fill_out_problem_form("Foo", "foo@bar.org")
...
assert ...
Over time, we had become so lazy (thanks to Watir) that we couldn't tolerate filling out the lengthy forms on the site when we needed to try something out but didn't want to run the tests. So we developed some smaller scripts that didn't test anything, but would fill out forms and click through a series of pages so we could get on with the more interesting stuff. This worked beautifully, mostly because we had already removed enough duplication to give us coarse-grained operations with which we could construct short, powerful scripts to do our bidding.
But then I got tired of running scripts. I wanted a Watir dashboard. Ruby/Tk to the rescue! Here's my quick spike of how to drive Watir from a GUI.
require 'tk'
require 'watir_helper'
root = TkRoot.new { title "Dashboard" }
button = TkButton.new(root) do
text "Start Google"
command proc do
ie = IE.new
ie.goto("http://google.com/")
end
end
button.pack
Tk.mainloop
It took me a couple more minutes to get a real dashboard up and running and now I can drive around my client's webapp with a push of a button!
[/projects/watir] permanent link Mon, 17 Apr 2006The Interactive Canvas and Internet Explorer I wrote up a simple example of how to create interactive canvas applications. I used ExplorerCanvas to ensure compatibility on all major browsers.[/projects/interactivecanvas] permanent link Thu, 30 Mar 2006Google puts Canvas on IE: The Game Changer Update: ExplorerCanvas is the official project name. Master Canvas hacker Benjamin Joffe announced something on the canvas developers group yesterday that, in my humble opinion, is a game changer. Google is putting canvas on Internet Explorer. Benjamin received a pre-release version of a Javascript library (soon to be officially released on http://code.google.com/) that, when included on a page with canvas, allows the downtrodden Internet Explorer users of the world to step into the wonderful world of canvas. Emil started this work and it looks like Google is finishing it. It doesn't take a genius to figure out the implications of Google's work on this ... look out world! Here are a few examples that are using Google's canvas library:[/projects/ajaxtrains] permanent link Fri, 24 Mar 2006Using Rails as Javascript Scaffolding I'm working on something that I'm not talking about yet, but I can say it's a Rails app. I'm likely a bit behind the times, but I thought I'd mention a habit emerging in my Rails/Ajax development that I haven't read about in any of the blogs I follow. After I started playing with Behaviour, I grew to appreciate keeping my markup free of Javascript. At the same time, I enjoy using Rails JavaScriptHelper methods to do the dirty work for me. So what I end up doing is writing the Rails code and get everything working the way I want it. Then I view source in my browser, copy the Rails-generated HTML and Javascript and paste over the Rails code in my template. Then I'm free to separate everything into the appropriate places. Styles can get pulled up into a CSS file, onFoo methods can get extracted into Behaviours, and HTML elements can be formatted and identified the way I want. I just went through this routine with the marvelous script.aculo.us autocomplete and it worked like a charm. It feels so right, I wonder if it's wrong. I suppose I'm making my code more susceptible to breaking with future Rails/Scriptaculous upgrades. But it also feels like a smaller incarnation of the Rails scaffolding philosophy. So I'm wondering if I'm just doing something many others have silently practiced before me or if I'm violating an unspoken rule of Rails development.[/projects/beratings] permanent link Fri, 17 Mar 2006My Selenium on Rails proposal was accepted for RailsConf 2006. Attendees can look forward to practical examples of all types of Selenium tests extracted from Real Life Project(s). You can also look forward to no slides, just code. See you in June![/projects/selenium] permanent link Mon, 13 Feb 2006Canvas Directed Graph Library: webdep Update: Aslak's sites are temporarily unavailable. In the meantime, visit this working example if you want to play with webdep. I mentioned in my previous post that Aslak and I were collaborating on a new DamageControl feature that uses canvas to setup build dependencies. We're beginning to extract our code into it's own project. We're calling it webdep and it is currently hosted at buildpatterns. Browse the source or use subversion and svn co http://buildpatterns.com/svn/repos/webdep/trunk/. Currently we have the just extracted the layout behavior without any user interaction or Ajax. These will be coming soon, along with (hopefully) IE support (thanks to Emil).[/projects/webdep] permanent link Thu, 19 Jan 2006Canvas, Ajax, and the Supertrain: Reloaded After I wrote my Supertrains article last month, O'Reilly's XML.com decided to publish it. I reworked the Javascript into a more object-oriented style, replaced the homegrown wire protocol with JSON, and added a "hotspots" feature. I can't resist quoting Brooks...To only a fraction of the human race does God give the privilege of earning one's bread doing what one would have gladly pursued free, for passion. I am very thankful. --The Mythical Man Month, p. 291 [/projects/ajaxtrains] permanent link Sat, 14 Jan 2006Lightweight Visual Thesaurus v0.2 I joined the canvas-developers Yahoo! group a couple weeks ago. On Thursday, Benjamin Joffe posted an example of writing text to the canvas element. This had been my biggest frustration with canvas up to now, but Benjamin blew that problem away. I plugged hisCanvasRenderingContext2D.prototype.drawString function into The Lightweight Visual Thesaurus this morning, and bam! v0.2 was born.
[/projects/lvt] permanent link Fri, 23 Dec 2005Canvas, Ajax, and the Supertrain I've written a tutorial on how to combine canvas and Ajax. In this article I use canvas to graphically represent the real-time state of a fictional railway system (live example). Send me feedback if you have suggestions for improvements. Merry Christmas! |