Last updated on Sat Jun 12 23:07:41 CEST 2004
Back to Ruby stuff

Grounding TOP

 $ ri-rpa -T UnboundMethod
 --------------------------------------------------- Class: UnboundMethod
     Ruby supports two forms of objectified methods. Class +Method+ is
     used to represent methods that are associated with a particular
     object: these method objects are bound to that object. Bound method
     objects for an object can be created using +Object#method+.

     Ruby also supports unbound methods; methods objects that are not
     associated with a particular object. These can be created either by
     calling +Module#instance_method+ or by calling +unbind+ on a bound
     method object. The result of both of these is an +UnboundMethod+
     object.
 [...]
     Unbound methods are a reference to the method at the time it was
     objectified: subsequent changes to the underlying class will not
     affect the unbound method.

Use cases TOP

Calling an instance method from an upper class TOP

 batsman@tux-chan:/tmp$ cat fdgdisyhgkjgdhk.rb
 class Foo
     def foo
         puts "Foo#foo"
     end
 end
 class Bar < Foo
     def foo
         puts "Bar#foo"
     end
     def bar
         # calls Foo#foo instead of Bar#foo
         Foo.instance_method(:foo).bind(self).call
     end
 end
 Bar.new.bar
 batsman@tux-chan:/tmp$ ruby fdgdisyhgkjgdhk.rb
 Foo#foo

AOP stuff TOP

This is more powerful than plain alias_method because you’re working with closures; it feels much cleaner too. OTOH, as of 1.8 it won’t allow you to propagate blocks :-( However

 define_method(:meth){|*args, &block|   .... }

has been added to 1.9 CVS.

 batsman@tux-chan:/tmp$ cat aop2.rb

 class Module
     def def_advice(meth, &block)
         prev = self.instance_method(meth)
         define_method(meth) do |*a|
             block.call prev.bind(self), *a
         end
     end

     def def_preadvice(meth, &block)
         prev = self.instance_method(meth)
         define_method(meth) do |*a|
             block.call(*a)
             prev.bind(self).call(*a)
         end
     end

     def def_postadvice(meth, &block)
         prev = self.instance_method(meth)
         define_method(meth) do |*a|
             prev.bind(self).call(*a)
             block.call(*a)
         end
     end
 end

 class A
     def foo(*a)
         puts "A#foo #{a.inspect}"
     end
 end
 a = A.new
 a.foo

 class A
     def_advice(:foo) do |prev, *a|
         puts "before"
         prev[*a]
         puts "after"
     end
 end

 puts "--"
 a.foo

 class A
     def_preadvice(:foo) { puts "pre 1" }
     def_postadvice(:foo) { puts "post 1"}
     def_preadvice(:foo) { puts "pre 2" }
 end

 puts "--"
 a.foo
 batsman@tux-chan:/tmp$ ruby aop2.rb
 A#foo []
 --
 before
 A#foo []
 after
 --
 pre 2
 pre 1
 before
 A#foo []
 after
 post 1
 

Copyright © MJFP
batsman dot geo at yahoo dot com