Handshake 0.2.1 Released
I’ve pushed out a new version of Handshake. The big-ticket item in 0.2 is that method argument contracts now support contracts on blocks. When a block is passed to a method that’s protected by a block contract, the block will be modified to check the arguments and return values for that block in much the same way that a normal contract is checked. Note that this will probably not work in Ruby 1.9, or at least will have to be modified for recursive contract checking.
Here’s the syntax:
class StringArray < Array
contract :each, Block(String => anything) => self
endBlock checking in 0.2.0 involved reopening the Proc class; release 0.2.1 fixes that.
The new release also contains a new method, checked_self. Because of the way Handshake is implemented (with a proxy object), calls to private methods aren’t checked (because they’re called on the real object instead of the proxy object). checked_self returns the proxy object instance and calls made to that object will be contract checked.
I’ve also improved the documentation. You can find the rdoc at http://handshake.rubyforge.org, and the project page at http://rubyforge.org/projects/handshake/.
Posted in Professional | no comments |
Handshake vs. ruby-contract
Florian Groß (cool!) asks:
Interesting. Any chance of getting a comparison to ruby-contract? :)
ruby-contract is quite nice, and if I’d realized just how nice beforehand I probably wouldn’t have bothered with Handshake; imagine my chagrin. Its wiki is defunct, and I couldn’t gem install ruby-contract, so I figured the project was dead or unmaintained and never took a look. Lucky for me, there are enough big differences between the two in interface and approach that it’s worth spending some time to examine them. I can’t claim to understand everything that’s going on it it yet, so Florian, please correct me if I get it wrong.
Update: I misread one of Florian’s signature examples. Fixed.
Big Picture
See my previous post for the big picture on Handshake. The idea is that you define contracts directly in classes, whose behaviors are applied to subclasses. In contrast, ruby-contract’s contracts reside in their own class and are applied to other classes through the fulfills method. The code below comes from ruby-contract’s test cases (tc_contract.rb) and gives a good feel for how you would interact with the code to define contracts.
class EnumerableContract < Contract
provides :each
end
module List
module NonEmpty; end
end
class ListContract < Contract
implies List
def self.check_count() 3 end
provides :each
provides :[]
provides :size do
assert(@object.size >= 0)
end
end
class NonEmptyListContract < ListContract
implies List::NonEmpty
provides :size do
assert(@object.size > 0)
end
end
class EmptyList
def each() end
def [](idx) end
def size() 0 end
fulfills ListContract
end
class NonEmptyList < EmptyList
def size() 1 end
fulfills NonEmptyListContract
endThis code does a fair number of things that Handshake doesn’t attempt. Because Handshake agreements are defined either directly within a class or within its superclass there’s no way or reason to verify that a particular class provides or doesn’t provide a method. I also really, really like the syntax for the provides precondition checks. On the other hand, ruby-contract doesn’t appear to allow you to check class invariants, or to check postconditions in the context of all of a method’s arguments. Florian, please correct me if I’m mistaken.
Method Signature Checking
Both libraries offer method signature checking. I happen to prefer my syntax, but it’s mostly an aesthetics thing. I’m dismayed to discover that my idea of using the === operator for these clauses isn’t half as clever as I thought it was, or perhaps we are simply both clever. :)
Handshake:# accepts a string, return value doesn't matter
contract :name=, String => anything
# accepts a block, returns an array
contract :each, Block => Array
# Matches something that is an Enumerable and that responds to
# either :to_ary or :to_a.
contract :x, all?(
Enumerable,
any?(
responds_to?(:to_a),
responds_to?(:to_ary) ) ) => anything# accepts a string, return value doesn't matter
signature(:name=, String)
# accepts a block, returns an array
signature(:each, :block => true, :result => Array)
# Matches something that is an Enumerable and that responds to
# either :to_ary or :to_a.
# From ruby-contract docs.
signature :x, Contract::Check::All[
Enumerable,
Contract::Check::Any[
Contract::Check::Quack[:to_a],
Contract::Check::Quack[:to_ary]
]
]We both provided remarkably similar combinators for signature clauses, although mine are defined methods and his are classes. Mine are quite expressive in terms of the error messages they generate, but I haven’t checked ruby-contract in that regard.
Implementation
Handshake is implemented as a proxy object that resides on top of an object and intercepts all method calls, running them through invariant, signature, and general pre- and post-condition checks. It uses this to ascertain blame as necessary, although the exception messages are generated by the checks themselves. The proxy object looks, acts, and feels like the real object.
In contrast, if I understand things correctly, ruby-contract uses eval to redefine methods that the programmer wants to check. For end users, the difference lies in the way calls to methods within the same class are evaluated. In Handshake, the proxy object establishes a barrier but cannot monitor anything that occurs inside that barrier. This means that calls to private methods are effectively uncheckable. The upside is that it allows you to define invariants in terms of methods as well as instance variables without getting stuck in an infinite loop. This is particularly useful for cases where you want to extend the behavior of a core Ruby class but don’t have access to its internals; see the NonEmptyArray example from my last post.
ruby-contract also hooks into Test::Unit to allow access to its assert methods within provides blocks and utilizes its inheritance properties to automatically check those clauses as soon as a class is defined as fulfilling a contract. This is an insanely good idea and I may stealborrow some portion of it.
Conclusion
Both libraries provide some unique and complementary contract mechanisms. ruby-contract is more mature, judging by the code, although it hasn’t been updated for a while. Not to be all wishy-washy, but it looks like there are projects that could benefit from either library, or both. I’m glad I finally took the time to dig through it.