One of my favorite computer games when I was growing up was Robot Odyssey; I imagine it will come as a surprise to no one that I was a nerdy kid. This article is a little bit of a tribute to that game, and the coolness of solving complex problems with a handful of simple concepts combined in clever ways.
Imagine you want to write a web-based game that involves robots. The robots in your game are a bit like the robots in Robot Odyssey: you program them with a list of simple instructions and when you turn them on they follow those instructions faithfully. Let's say, for the sake of argument, that your robots can Walk Forward, Turn Left, Turn Right, Jump, and Beep.
The project that I'm working on is using Screw.Unit for Javascript testing. We recently ran into a case where we found ourselves copying and pasting some code. We wanted to DRY up our specs and found a neat way to do it that I figured I'd share with everyone. Here's a really simple example to demonstrate how we did it.
Given a cat model that keeps track of the number of lives the cat has left:
function Cat() {
var lives = 9;
this.die = function(num) {
lives = lives - 1
};
this.lives = function(){
return lives;
};
this.isDead = function() {
return lives <= 0
};
}
Lets make up a spec that checks that isDead works for some values of lives:
Screw.Unit(function() {
describe('Cat', function() {
var cat;
describe('isDead', function(){
var shouldNotBeDeadBehavior = function(num){
describe("when the cat has " + num + " lives left", function(){
it("it should not be dead", function(){
cat = new Cat({lives: num});
expect(cat.isDead()).to(be_false);
});
});
}
for (i=3;i>0;i--){
shouldNotBeDeadBehavior.call(Screw.Specification, i);
}
describe("when the cat has 0 lives left", function(){
it("it should be dead", function(){
cat = new Cat({lives: 0});
expect(cat.isDead()).to(be_true);
});
});
});
});
});
The neat part in here is the line:
shouldNotBeDeadBehavior.call(Screw.Specification, i);
If you're not familiar with it, the call function in javascript allows you to define what "this" is for that function call. By calling our shared behavior with Screw.Specification, we're saying that we want to execute this function within the context of the Screw.Unit Specifications. This lets us execute our specs as though they were written in various places. The test results from this spec look like this

This is one way to DRY up some of your Screw.Unit specs. If you find yourself copying and pasting code, consider refactoring the spec out into a shared behavior instead.
Have other good ways to clean up Srew.Unit specs? Share them in the commments!
Interesting Things
- Be careful when extending classes from structs. Their superclasses are essentially anonymous classes, so reopening them can be difficult. If you attempt to reopen them by extending them from "the same" struct, it will actually be a different anonymous class.
Ever had an STI model but wanted the views and controllers to pretend like it all extended the base class? You can have rails change the params[] namespace that it uses like so:
form_for :user, @admin_user, :url => user_path(@admin_user)
Or you can be super-cool and use polymorphic routes:
form_for @admin_user.becomes(User)
The becomes method is part of ActiveRecord, and it actually creates a 2nd copy of the object with the same attributes and a different class (shallow copy). Due to this implementation, it has limitations so use it carefully.
- Upgrading to rails 2.3.3 breaks HopToad. This is related to filter_parameter_logging, and it's technically rails' fault. It has been fixed in 2.3.3 stable (which I assume will be released as 2.3.4). You can also fix it yourself with a one-line-patch. Personally, I'd wait to upgrade till 2.3.4 comes out.
Desert 0.5.2 focuses on speed improvements and bug bixes.
Pivotal Tracker Stories:
- "Fix mailer templates -- very slow with Desert": ActionMailer::Base.view_paths are now cached. This speeds up ActionMailer tests/specs.
- "Plugin schema migrations should successfully convert from the 'old' scheme to the 'new' scheme": Pulled cauta/desert at master and fixed specs.
- "Add sweepers to Desert's load path": Pulled willcodeforfoo/desert at master and fixed specs.
- "Specs should handle Rails version incompatibilities"
This fixes several bugs that people have complained about for quite a while. Please let me know if anything is broken.
GemInstaller 0.5.3 has been released!
GemInstaller
CHANGES
- 0.5.3 / 2009-08-25
- Many long overdue bugfixes and patches, see http://tinyurl.com/geminstaller-0-5-3-release for details.
- Thanks to Greg Fitzgerald, Britt Crawford, John Trupiano, Gabriel Gironda, and Eric Hodel for patches and assistance.
- Issues with case statement under Ruby 1.9
- GemInstaller cannot distinguish between gems that have the ame name but capitalized differently.
- add ./ci as default location for config file
- Disable GemInstaller install in default rails preinitializer.rb, but fork if it is used
- autogem() fails when run for newly-installed gem
- Sometimes installing fails due to RubyGems cache not being cleared between multiple API calls
DESCRIPTION
Automated Gem installation, activation, and much more!
FEATURES
GemInstaller provides automated installation, loading and activation of RubyGems. It uses a simple YAML config file to:
- Automatically install the correct versions of all required gems wherever your app runs.
- Automatically ensure installed gems and versions are consistent across multiple applications, machines, platforms, and environments
- Automatically activate correct versions of gems on the ruby load path when your app runs ('require_gem'/'gem')
- Automatically reinstall missing dependency gems (built in to RubyGems > 1.0)
- Automatically detect correct platform to install for multi-platform gems (built in to RubyGems > 1.0)
- Print YAML for "rogue gems" which are not specified in the current config, to easily bootstrap your config file, or find gems that were manually installed without GemInstaller.
- Allow for common configs to be reused across projects or environments by supporting multiple config files, including common config file snippets, and defaults with overrides.
- Allow for dynamic selection of gems, versions, and platforms to be used based on environment vars or any other logic.
- Avoid the "works on demo, breaks on production" syndrome
- Find lost socks.
Quick Start
See http://geminstaller.rubyforge.org/documentation/index.html
INSTALL
- [sudo] gem install geminstaller
0.9.14 of Tweed is now available in the App Catalog.
Bugs:
- timeout didn't actually timeout
- re-launch app created new card
Features/Change:
- loading spinner/scrim no longer full screen -- allows for user interaction/cancel if Twitter is slow
- option to disable links in tweet list (still available from tweet popup)
- ability to re-tweet own tweets
- Nearby timeline (can search within nearby, configure Nearby radius)
- Photo View
- Support for French (for Canadian users)
- preference to open multiple cards per account
Photo Viewing
- if tweet contains photo link, icon indicator will be present
- can view photos within Tweed, no need for external browser load
- twitpic.com, tweetphoto.com (and pic.gd), yFrog.com and twitgoo.com supported
Multiple Cards
If enabled, you can tap Open from App Menu to open another card for an account. (This allows multiple timelines to be simultaneously open, similar to TweetDeck)

Interesting Things
- One of our projects is using mechanical turk to match and normalize their ugly data. After building some automatic matching with little success, the group is simply using the template web interface. The task can be trivially decomposed into tiny tasks, and accuracy does not need to be 100% perfect, so it's a great fit. They only spent a few hours and have results already!
When creating associations to classes that use Single Table Inheritance (STI), rails is smarter about the hierarchy than I would have expected. A call to find on a base class yields a query that does not filter on type. But what about a call to find on a subclass? Instead of making a single filter on type, rails finds all the subclasses of that subclass and creates an "OR" expression in the "WHERE" clause.
Generated SQL respects class heirarchy
# ActiveRecord::Base < User < Admin < SuperAdmin something.admin # has_one :admin
Generates the following SQL:
# SELECT * FROM users WHERE (type = 'Admin' OR type = 'SuperAdmin')
When it doesn't work
Since it relies on the class hierarchy, the query is only accurate if every subclass has been loaded. If class preloading is off, for example, very weird things can happen. The query will depend non-deterministically on which subclasses have been loaded.
I just spent a wonderful weekend with 75 of the brightest folks I know in the Ruby community. My hat's off to Obie and the Hashrocket crew for putting together a really great, intimate conference in a beautiful location. It's refreshing to really have to struggle to choose which talk to attend from so many choices at each session. I know too many choices are a Bad Thing™, but the format made for great small sessions, and a wonderful thing happened: Everyone got to really meet and get to know everyone.
Among many others, I had the pleasure of meeting CJ Kihlbom, who nails a lot of why these conferences are so important in his post, The Business Value of Conferences.
It was really pleasant to present to a community of business leaders who understand the value of agile, and who are serious practitioners in their own practices.
A lot of you have asked for the slides from my talk, Agile, Rails and the Cloud, so I've posted them here.
Those of you who thought about coming but didn't really missed out. Come next year. You'll be glad you did.
Ask for Help
"How do I redefine a CSS class in javascript"
You can create a new <style> element and append it to the head. This should probably be avoided if you can help it.
If you simply want to toggle between 2 states, consider putting both sets of rules in the CSS and toggling a class on the body or other container.
*"Should I use BOSH for XMPP on the iPhone?"
Probably not. If you have a long-running low latency XMPP connection, you'll probably want to use a socket from the CFNetwork package. That's the most we know about iPhone development right now.
Interesting Things
- New tracker updates with better burndown charting!

