From: Robert Klemme Date: 2007-05-01T00:30:04+09:00 Subject: Re: Ways to change the behavior of a block On 30.04.2007 17:10, Brian Guthrie wrote: > On 4/30/07, Robert Klemme wrote: >> On 30.04.2007 07:31, Brian Guthrie wrote: >> > I'd like to be able to change the behavior of a call to a block (I'm >> > working on a design-by-contract system and would like to be able to >> > check the block's signature, filtering each call through some contract >> > check) and I was wondering if anyone had any advice. It's easy enough >> > to override Proc#call, but doing so doesn't appear to affect calls to >> > implicit blocks with using the yield keyword. Is it even possible to >> > do this? >> >> If I understand you properly you want to be checking the argument list >> in the *calling* method. >> >> irb(main):003:0> def f(&b) >> irb(main):004:1> raise ArgumentError unless b.arity == 4 >> irb(main):005:1> b[0,1,2,3] >> irb(main):006:1> end >> => nil >> irb(main):007:0> f {|a,b| p a,b} >> ArgumentError: ArgumentError >> from (irb):4:in `f' >> from (irb):7 >> from :0 >> irb(main):008:0> f {|a,b,c,d| p a,b} >> 0 >> 1 >> => nil >> >> Kind regards >> >> robert >> >> > > It's a bit more complicated than that, but that's the idea. If you're > curious, the library is called Handshake (handshake.rubyforge.org; > haven't made an announcement as it's not quite mature yet). It > supports (among other things) argument contracts of the form: > > contract String => Integer > def to_i ... > > contract "foo" => 1..3 > def foo ... > > contract any?( String, hash_of?(Symbol, Fixnum) ) => String > def accepts_string_or_hash ... > > The goal is to extend it to support block contracts: > > contract [ String, Block(String => Integer) ] => Integer > > The Handshake library surrounds an object that includes the > appropriate module with a proxy object and checks everything that > passes across that barrier. That means that I can easily manipulate > any incoming Proc objects, extending the instance so that Proc#call is > required to check an argument list. The problem is that changing the > behavior of Proc#call does _not_ affect the behavior of yield: Then just create another block that will invoke the original and do the checks. def f(&b) bb = lambda {|*a| raise ArgumentError unless a.size == 4 result = b[*a[0...4]] raise "Whatever" unless Array === result result } other_method(&bb) end Kind regards robert