matz / zetabits.com (Yukihiro Matsumoto) writes:

> |The first parameter to the block is a proc object that wraps the
> |original routine, and the remaining parameters are those supplied by
> |the caller.
> 
> How did you do that.  Show me your (imcomplete?) code.

This is the version that makes no attempt to execute the function in
the object's context. I had to recreate this using about 1,000 emacs
undo's, so it may well be buggy, but I hope it gets the idea across.


  ###
  # This is the stuff behind the curtain
  #

     module Hook
       @@n = 0
       @@hook_function = Array.new

       def Hook.create(block)
         @@n += 1
         @@hook_function[@@n] = block
         @@n
       end

       def Hook.call(from, n, block, *args, &orig_fn)
         b.call(orig_fn, *args, &block)
       end

       def Hook.getHook(n)
         b = @@hook_function[n]
       end
     end


     class Module
       def hook(sym, &block)
         if block.nil?
           raise "Missing block to hook function"
         end

         n = Hook.create(block)
         fn = sym.to_s
         class_eval %{
            alias hook_#{n} #{fn}
            def #{fn}(*args, &block)
              b = Hook.getHook(#{n})
              orig_fn = proc  { hook_#{n} }
              b.call(orig_fn, *args, &block)
            end
         }
       end
     end

  ###
  #  and this is the stuff the user sees
  #

     class String
       hook :upcase do |_upcase|
         _upcase.call.tr('_', '~')
       end
     end

     p "foo_bar".upcase    #=>  FOO~BAR