Handshake, an informal contract system for Ruby


#1

Handshake is an informal contract system for Ruby. It allows
developers to apply clear, concise, and enforceable interfaces to
classes, in three forms:

  • method signatures
  • class invariants
  • pre- and post-conditions

Note that Handshake will only enforce checked contracts if the $DEBUG
flag is set to true (ruby -d).

Method signature contracts are applied in the following way:

class Foo
include Handshake

contract [ all, acceptable, arguments ] => return_value
def method(arg, ...) ...

end

Signature contracts accept clauses, which are defined as any object
that implements the === method. When specified with a symbol, they
will be applied the named method; when specified without one, they
will be applied to the next defined method. Handshake comes with a
number of useful clauses predefined:

contract :foo, any?(String, 0…5) => Symbol
contract :sum, many?(Integer) => Integer
contract :deposit, positive_n? => Integer

It can also apply contracts to varargs and anonymous blocks:

contract [ String, [ Integer ] ] => anything
def foo(str, *ints) …

contract :each, Block(String => anything) => many?(String)

In addition, users can define their own custom project-specific
clauses very easily. See the RDoc for Handshake::ClauseMethods for
more information.

Class invariants accept a message and a block, which is expected to
return a boolean value. Their value is checked before and after each
method invocation and are scoped to allow checking of instance
variables and private methods.

class NonEmptyArray < Array
include Handshake
invariant(“should never be empty”) { not empty? }
end

Pre- and post-conditions allow for grouping common sets of assertions,
typically those that require checking arguments against one another.
All Test::Unit assertions are available, although they raise a
Handshake::ContractViolation exception here upon failure.

before do |first_half, second_half|
assert_equal 10, first_half.length + second_half.length
end
def accepts_ten_items(first_half, second_half) …

Handshake is implemented by aliasing the new method of any class that
includes it. The modified new method returns a proxy object in place
of the original object that looks, acts, and feels like the real
thing. It acts as a barrier, and all method invocations that cross
the barrier are checked against the defined contracts. It differs in
this manner from other Ruby DBC-style systems, which generally alias
each contract-checked method as necessary. Because of both this and
its grave irrelevance to a Rails context, I wouldn’t recommend trying
to use Handshake with any Rails projects.

Handshake is written in pure Ruby, is available as open source under
the same license as Ruby, and is installable as a gem (latest
version=0.3.0). It lives at http://rubyforge.org/projects/handshake,
and the RDoc is available at http://handshake.rubyforge.org.

I’d love to hear any comments, criticisms, and suggestions. I’m also
presenting Handshake tonight at the Boston Rubygroup and figured,
well, I should probably announce the silly thing on ruby-talk first.

Brian Guthrie
removed_email_address@domain.invalid
http://blog.brianguthrie.com