On Thu, 27 Apr 2006, Geoff Stanley wrote:

> Hello all.
>
> I'm working on a plugin system for a IRC client I'm currently making.
> The current plan is for plugins to extend the base classes of the
> program. Often a plugin will need to add a little bit of functionality
> to an already existing method in an already existing class.
>
> For example, I've got a logging plugin. I want it to extend the Channel
> class to allow for logging of conversations. It needs to redefine
> Channel's initialize method to open a log file. But it can't eliminate
> all the important things the base initialize method did.
>
> I've thought about doing this with alias, but I haven't succeeded. My
> thoughts were to rename 'initialize' to 'old_initialize', then have the
> plugin redefine 'initialize'. In code:
>
> class Channel
>    alias old_initialize initialize
>    def initialize
>        old_initialize
>        # Then write custom code. (To open a log file, for example.)
>    end
> end
>
> This doesn't work, however, because it leads to a stack overflow. I
> think the reason for the overflow is because, when this is done twice,
> old_initialize will call itself recursively with no end.
>
> Anyone have any ideas for how this could be done? It doesn't have to use
> alias, but the general intention is thus: I want to redefine a method
> and 'copy and paste' the old code from the method into the new method at
> a given point. Sort of like using 'super', but no inheritence required.
>
> Thanks! Responses are greatly appreciated.

     harp:~ > cat a.rb
     class Module
       def def__ m, &b
         __m__, m = %W( __#{ m }__ #{ m } )

         unless(instance_method(__m__) rescue nil)
           alias_method __m__, m

           module_eval <<-code
             def #{ m }(*__a__, &__b__)
               ret = nil
               #{ __m__ }(*__a__, &__b__)
               __mblocks__ = self.class.__mblocks__['#{ m }']
               __mblocks__.each{|b| ret = instance_eval(&b)}
               ret
             end
           code
         end

         __mblocks__[m] << b

         self
       end
       def __mblocks__
         @__mblocks__ ||= Hash::new{|h,k| h[k] = []}
       end
     end

     class C
       def foo
         p 0
       end

       def__ 'foo' do
         p 1
       end
     end

     c = C.new
     c.foo
     puts

     C.def__(:foo){ p 2 }
     c.foo
     puts

     C.def__(:foo){ p 3 }.def__(:foo){ p self => 42 }
     c.foo
     puts



     harp:~ > ruby a.rb
     0
     1

     0
     1
     2

     0
     1
     2
     3
     {#<C:0xb75d05f8>=>42}


regards.

-a
-- 
be kind whenever possible... it is always possible.
- h.h. the 14th dali lama