On 4/30/07, Robert Klemme <shortcutter / googlemail.com> wrote:
> On 30.04.2007 17:10, Brian Guthrie wrote:
> > On 4/30/07, Robert Klemme <shortcutter / googlemail.com> 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

That would work, I think.  I'll do that.  Thanks for the advice.