This is a multi-part message in MIME format.

------extPart_000_001F_01C32050.04CF41B0
Content-Type: text/plain;
	charsetso-8859-1"
Content-Transfer-Encoding: 7bit


"Hal E. Fulton" <hal9000 / hypermetrics.com> schrieb im Newsbeitrag
news:0d1f01c31fed$2b5978e0$0300a8c0 / austin.rr.com...
> ----- Original Message ----- 
> From: "Robert" <bob.news / gmx.net>
> Newsgroups: comp.lang.ruby
> To: "ruby-talk ML" <ruby-talk / ruby-lang.org>
> Sent: Wednesday, May 21, 2003 5:39 PM
> Subject: Re: How I'd like method-wrapping to work...
>
>
> > That's no fantasy.  Dreams come true...
>
> The plane, the plane! (Sorry, showing my age...)
> (For non-Americans and/or people under 35: A reference
> to the TV show _Fantasy Island_.)

I just know a hit by, I think it was "Tight Fit" with the same name.  The
80's...

> Thanks for this fascinating reply...

You're welcome!  I'm always amazed what can be done with Ruby, it's really
a jewel.  Sometimes it's even scaring...

> > > 3. I've probably overlooked a lot of issues. Please
> > > educate me (Matz, Guy, Dave, David, others...)
>
> > Although not listed this bugged me.  See what I came up with
(attached).
>
> Not listed? :)
>
>   others.include?(Robert)     # true

Oooops, my implementation of include? seems to be broken.  Thank's for the
fix.  :-)

> > Cons:
> >   - 'this' had to be introduced since I found no way to
> >     rebind the proc (even if there were a way we had
> >     to deal with multiple concurrent invocations).  The
> >     deeper reason for this problem is, that the block
> >     comes into existence in class context but should be
> >     evaluated in instance context later.
>
> I was having a hard time with that, too, and I wasn't
> even worrying about threads.

That's the single biggest problem for the hook approach with blocks.  If
that could be solved, maybe by a method Proc#rebind( binding ) Proc
returning a new proc with the given binding we had the perfect solution.
I think there were discussions about rebinding somethread else.

> >   - Argument lists have to be doubled for the block
>
> Hmm, not sure what you mean here. But I am going to
> play with this a little.

If you want to access method arguments in the block you have to include
them like I did in the second test script (attached).

> > Pros:
> >  - It nearly looks like what you asked for.
> >  - Return values work reasonably.
>
> By "reasonably" do you mean that the return value of
> prior will be used even if that call is not the last
> one inside the hook block? Ideally we shouldn't have
> to do the "val" stuff that I do. I'll test.

No, I meant, that you can decide whether you want to propagate them or
override them.  But it can be fixed to always return the original.  Lemme
see...  Done.  If I find some time I might even include a test for
duplicate prior calls from the same block.  Oops, that was easy.  I'll
have to stop that...  Please don't come up with further feature requests.
:-))

> >  - It even works for nested invocations, i.e. recursion
> >  - It works with several instances in the call chain
>
> This is essential if I understand you rightly. It's also
> one reason we need this -- methods are sometimes aliased
> more than once, e.g., in a library and in your own code.

Yep, that was what I suspected was the typical case.  You don't want to
worry whether someone else hooked a method already.

> >  - You can exactly do what you put in <fantasy>
>
> So I can! I see that the code in hook_test is line for line
> the same as mine.

Si!

> However, on 1.7.3 on Windows I got a syntax error on
> line 40 of hooks.rb (had to remove the "proc &"). Not
> sure what the issue is here -- assume it worked for you
> that way?

Strange, I got no error with 1.7.3 on Win.  Need to investigate this.  The
version I have here at work complains, too.

> Thanks for a quick reply...

Oh, I took some detours in between... (I dropped a version with a String
instead a block.)

> this is another bit of code
> that I'll certainly save.

That's very kind of you.

> I do hope the discussion continues, though...

Of course!  There's always room for improvement (see attached :-))

Kind regards

    robert

------extPart_000_001F_01C32050.04CF41B0
Content-Type: application/octet-stream;
	nameooks.rb"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
	filenameooks.rb"


class Object
  private
    def prior
      thr = Thread.current
      raise "Just one invocation of prior allowed!" if thr[ :__hook_prior ]
      thr[ :__hook_prior ] = true
      ths = thr[ :__hook_self ]
      sym = thr[ :__hook_next ]
      raise "No more methods in chain" unless sym

      begin
        puts '>>> nest' if $VERBOSE
        result = ths.send( sym, *(thr[ :__hook_args ]))
        puts '<<< unnest' if $VERBOSE
        thr[ :__hook_result ] = result
        return result
      ensure
        thr[ :__hook_next ]= sym
        thr[ :__hook_self ]= ths
      end
    end

    def this
      Thread.current[ :__hook_self ]
    end
end

class Module
  private
    def set_hook_proc( key, pr )
      (@hook_procs ||= {})[ key ]= pr
    end

  public
    def get_hook_proc( key )
      (@hook_procs ||= {})[ key ]
    end

    def define_hook( symbol, &block )
      al = generate_hook_alias symbol

      set_hook_proc( al, proc )

      x = <<-"EO_HOOK"
        alias #{al} #{symbol}
        def #{symbol}(*args)
          thr = Thread.current
          thr[ :__hook_self ]= self
          thr[ :__hook_args ]= args
          thr[ :__hook_next ]= :#{al}
          thr[ :__hook_prior ]= false
          result = #{self}.get_hook_proc( "#{al}" ).call( *args )
          thr.key?( :__hook_result ) ? thr[ :__hook_result ] : result
        end
      EO_HOOK

      # puts x
      self.class_eval x

      # p @hook_procs
    end

  private
    def generate_hook_alias( symbol )
      im = instance_methods

      count = 0

      begin
        s = "__hook__#{symbol}_#{count}"
        count += 1
      end while im.include? s

      s
    end

end


------extPart_000_001F_01C32050.04CF41B0
Content-Type: application/octet-stream;
	name
nother_test.rb"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filename
nother_test.rb"


require 'hooks.rb'

class Foo
  attr :len, true

  def initialize; @len  ; end

  def bar
    puts "bar: #{self.len}"
    "bar!"
  end

  define_hook :bar do
    puts "hook 1: #{this.len}"
    this.len + 
    prior
  end

  define_hook :bar do
    puts "hook 2: #{this.len}"
    this.len + 

    puts "prior 1"
    prior
    puts "prior 2"
    prior

    "gotcha!"
  end

  def foo(x)
    p this
    puts "foo #{x}"
    Foo.new.foo nil if x
    "foo!"
  end

  define_hook :foo do |x|
    puts "hook foo 1: #{this.len}"
    puts "hook foo 1: xx}"
    this.len + 
    prior
  end

end

f  oo.new

["__hook__bar_0", "__hook__bar_1", "bar"].each do |m|
  puts "calling #{m}"
  p f.send( m )
  puts ""
end

p f.foo("xxx")


------extPart_000_001F_01C32050.04CF41B0
Content-Type: application/octet-stream;
	nameook_test.rb"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filenameook_test.rb"


require 'hooks.rb'

class String

  define_hook(:length) do
    puts "I'm calculating the length"
    prior
  end

  define_hook(:upcase) do
    prior
    puts "I just did an upcase operation"
  end

  define_hook(:reverse) do
    puts "I'm about to do a reverse operation"
    prior
    puts "I just did a reverse"
  end

end

str  abcde"

x  tr.length
puts x

y  tr.upcase
puts y

z  tr.reverse
puts z


------extPart_000_001F_01C32050.04CF41B0--