Let's make a deal: Handshake, a contract system for Ruby
Handshake is an informal design by contract system written in pure Ruby. It’s designed to offer simple, flexible semantics for improving the clarity and robustness of your code. It supports three basic kinds of checking: invariants, method pre/post conditions, and method arguments. This is a unit-tests-pass-but-you-probably-shouldn’t-use-it-for-anything-important 0.1 release.
A couple of clarifications: Pre- and post-conditions, and argument contracts, all accept the name of the method you’d like them to apply to as an optional first argument. If you omit it, they’ll be applied to the next method you define.
Also, anything is a clause that always returns true.
Updated 5/9/07: I’ve modified some of the content below to reflect changes to Handshake’s interface. Handshake is also now obtainable as a gem (gem install handshake).
Argument Contracts
Argument contracts are insanely handy. They allow you to specify conditions for the arguments and return values of methods simply and easily. Here’s an example:
class AcceptsString
include Handshake
contract [ String, Fixnum ] => anything
def initialize(str, num); ...; end
endWait a second, I hear you say. That’s not exciting at all! I just left Java behind forever. Quasi-statically-typed languages are for suckers.
Here’s the rub: Handshake accepts as an argument clause any object that implements the === method. Why? Because it’s an incredibly convenient “is equivalent to” form of comparison. It’s what makes Ruby’s case statement so handy. Try this awkwardly contrived example on for size:
# Rate a website on a scale from 1 to 5.
class WebsiteRatingSystem
include Handshake
contract [ String, /^http:\/\// ] => anything
def initialize(name, url)
@name, @url, @ratings = name, url, []
end
contract 1..5 => anything
def add_rating(n); @ratings << n; end
contract nil => 1..5
def average_rating
ratings_sum = @ratings.inject(0.0) { |sum, n| sum + n }
ratings_sum / @ratings.length
end
contract [ String, [ Integer ], Block([String, Integer] => anything) ] => Hash
def generate_report(name, *metrics, &block)
..
end
endBut Brian, you say, in the imaginary conversation I am conducting with you in my head. Brian, I like being able to give it a range, or a regexp. That seems downright neato. But man, what a hassle to have to craft my own ===-implementing object for every single clause.
As it turns out, I have made this process entirely hassle-free for you.
contract assert("equals foo") { |arg| arg == "foo" } => anything
contract any?(String, Integer) => anything
contract [ all?( hash_of?(Symbol, String),
hash_with_keys(:foo, :bar, :baz) ] => anything
contract nonzero? => hash_contract(:count => Integer, :description => String)
contract responds_to?(:each, :map, :length) => anything
contract nil => StringAnd they said that duck typing and contracts didn’t mix! Good day, sir and/or madam.
Except for also
I’ve defined some accessors, just for you.
class HoldsSomeThings
include Handshake
contract_accessor :foo => String, :bar => Integer
contract_reader ...
contract_writer ... # You get the idea.
endInvariants
Also handy are invariants, which are conditions that should always hold. They’re checked after your object is constructed and before and after every method invocation. They have access to local methods and instance variables. They are all up in your class, policing your object.
class NonEmptyArray < Array
include Handshake
invariant { not empty? }
endPre- and post-conditions
Sometimes you need a broader, more holistic approach to method checking. These conditions, if specified, are executed before and after (respectively) the named method and have access to its arguments, return value(s), and any local methods and instance variables. These work very much like you’d expect them to:
class HoldOnJustAMinute
include Handshake
before("halt in the name of the law") do |arg|
assert @justice_dispensable
assert arg.law_abiding?
end; def do_something_potentially_damaging(arg)
...
end
endCaveats
- It isn’t underpinned by a formal calculus (although it’s modeled very loosely on the PLT Scheme contract library and on the Eiffel contract system). I’m more or less cool with that; Ruby’s appeal to me comes not least from its loose informalism.
But the professor under whose grudging tutelage I developed this would like you to know that until it does it’s not good for anything whatsoever, and whatever benefits you think you might be deriving from such a seemingly useful library are just a figment of your diseased imagination.He informs me that he’s okay with that bit, but the half-bakedness of the implementation bothers him more. See below. - It doesn’t yet have a proper blame system. See
abovebelow re: professor. - It handles inheritance only trivially: a subclass will inherit the contracts of its superclass but will not prevent you from overriding them. This means that it allows you to write contracts that fail the Liskov substitution principle. I hope to fix this soon, but in the meantime, don’t let it keep you awake at night.
- There’s a lot of metaclass magic going on under the covers, and you shouldn’t use it in contexts with a lot of other metaclass magic (myrailsapp/lib is probably fine; myrailsapp/app is not.)
There’s no rdoc yet (although the code itself is fairly well-documented).Fixed. See handshake.rubyforge.org.- You can’t define contracts in modules.
- And it will probably impact performance, although I haven’t conducted any performance testing.
Update: The inimitable Matthias Felleisen responds via email:
1. I never insist on a formal calculus. That’s what researchers are for. It’s a tool for exploring the ideas, communicating them in concise form, and proving basic fundamental theorems about such frameworks.
2. I do think that half-baked or quarter-baked implementations of good ideas are bad for the construction of quality software in the long term. They just propagate the silly idea that programming is shallow and anyone can do it, trained or not. One day we’ll have the equivalent of bridges come down on us in software and then the regulators will be worse than anyone can imagine now.
3. I also believe that without a properly design blame system, a contract framework provides only a 1/3 of the value that it can. That’s sad. It’s like buying a model T when we know how to build Porsches.
In my defense, this is at least two-thirds more-baked than any other Ruby contract system (5/9/07: except flgr’s ruby-contract. But I still like mine better). Since no Ruby contract system that I’ve seen is more than 15% baked, I guess that would make this… yes, about a quarter baked. It’s also a lot of fun to work on.
Finally
Find the project online at rubyforge.org/projects/handshake (MIT license), the RDoc at handshake.rubyforge.org, and the gem handshake, and please do let me know if you end up using it, and how.
Meet RubySearch!
Update: Apparently there’s a bug in which the scrollbar doesn’t work properly the first time you run a query. You can resolve the issue by either resizing the widget or running a second query. Thanks to Kent for pointing this out; I’ll post a fix as soon as I have one. Widgets are weird.
I’ve finally gotten around to polishing RubySearch and releasing it into the wild. RubySearch is a MacOS (10.4) Dashboard widget for Ruby developers. It allows you to query local Ruby documentation (generated and stored locally by rdoc/ri) and pulls it all into a handy dandy dashboard widget, complete with hyperlinks and what have you. Which is only useful if you’re into that sort of thing, and baby, I am.
I am arbitrarily calling the first version of RubySearch version 0.6. It requires that you have Ruby installed correctly, along with its accessory ri command. Here’s the official site, here’s a direct download link, and here’s a screenshot:

Ain’t it purdy?
Sick of Courier New?
I was working with the TA for our Software Development class the other day and I noticed that he’d remapped his browser to display fixed-width fonts as Monaco instead of Courier New. It’s a good idea and I thought I’d repost it here in the service of others who were, like me, too dumb to come up with it on their own but still unsatisfied with their fixed-width font experience. The setting is in Firefox under Preferences -> Content -> Fonts & Colors -> Advanced.
Running Windows? This is similarly old news, but the new ClearType-enabled fonts that Microsoft released for Windows Vista work just fine in XP. I discovered this a couple of months ago when I installed Windows on my shiny new Mac via BootCamp and was suddenly acutely aware, in a way I never had been before, how ugly everything looked. Their new fixed-width font is called Consolas, and it’s lovely. You can grab them all here.
It’s not that Courier New is such a terrible font. Well actually, I guess it is.
Waitin' for the spam to come rolling in
Aaron says, in the comments:
You’d better put a captcha or something on here soon. I predict that you can survive about 15 days without it before each and every post is overrun with spam comments.
Call it an experiment. I started this blog last Thursday. If by next Friday I don’t have bot spam then… well, I guess I’ll continue to be lazy. If I start to see some then I’ll know that the world has discovered my existence and put up a CAPTCHA.
I’m using Typo as my blog software, which is written in Ruby on Rails, which I know well, so theoretically I could go in and hack together my own solution. I wrote a math CAPTCHA some time ago as an experiment; maybe it’s time to dust it off.
Three hundred morons
I saw The 300 today, which I link to not because I expect you to visit the site but because this is apparently the sort of thing one does in a blog. I didn’t care for it. It’s possible that I’m more forgiving of historical reinterpretations when they come in more flexible comic book or animation forms; this one felt actively dishonest as a movie. It also might have been the utterly one-dimensionality of the characters that threw me, although the movie certainly was beautiful.
Historical reinterpretation is most frustrating to me when the real history is just as, if not more, interesting. I know that sounds a little pretentious, and I’m cool with that.
Scheree Schrager has a kitty!
I am reservedly pro-Scheree but unconditionally pro-kitty. Link
Aaron Leans On PISA
Air reports that work continues apace on PISA, a web application designed to ease short-term financial planning and money management for struggling, irresponsible twentysomethings like ourselves. For a couple of months we were working furiously on it but I found I had to drop the commitment due to insane amounts of schoolwork.
I still really like the idea of the project, but I was surprised by how difficult it is to make financial calculations that are both helpful and reasonably accurate. Our thesis was essentially that there should be a metric, The Number, which accurately reflects the amount of “spending money” in one’s pocket on any given day. A brief quote from our project wiki:
The Number
The amount of money you have available in your pocket, on any given day, to spend on whatever you like. Calculated after bills and budget have been factored in.
Our second thesis is that when you are building an application geared towards irresponsible twentysomethings you cannot count on conscientious and reliable data input.
It turns out that you have to resolve all sorts of very difficult things before this becomes feasible. You need an accurate budget, calibrated reliably from week to week. You need a reasonably good description of the transactions that have occured in your day-to-day bank account. You need to figure out credit card charges and debts. And so forth.
We think we have a reasonable solution and it looks like Air’s churning away on it. I look forward to seeing what he comes up with and I’ll let you know if he releases a public prototype.
Funditry with Puppies
This post is for Carolyn, who apparently prefers puppies to programming languages. It would take a colder heart than mine to disagree, although I’m more of a kitten man myself.

Photo credit: Brando the Black Labrador, from The Daily Puppy.
Funditry with Punditry
The Economist’s economics blog, Free Exchange, explains why punditry veers into the bizarre. Read the rest of the post for the justification, but here’s the key bit:
If you want to be on television, you need to be the chap saying something that is
- Unequivocal
- Something no one else is saying
- Testable
There’s no money in sensible, predictable advice. Anyone can give you THAT. People latch on to sensationally successful, novel predictions and tend to ignore or write off the failures. To become a pundit, predict something remarkable that no one else is predicting and be right about it once. Then make sure everyone hears about it.
That having been said, it is possible to lose your credibility eventually. Just look at John C Dvorak.
Shocker: FBI abused PATRIOT act
Gonzales, Mueller Admit FBI Broke Law
Transparency and accountability will never stop being a bad idea. It’s good news that their internal audit process apparently functions correctly. But their stunning arrogance at every juncture prior to the current one is dangerous and disheartening.
Secrecy is dangerous. It’s not enough to trust the current administration (not that anyone does anymore) or its chosen administrators. If you accept the precedents they set of secrecy and of the newly-expanded powers of the executive branch they you must also be prepared to trust anyone who might ever follow them, Democrat or Republican. And I’m not.