Grounding
$ 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
Calling an instance method from an upper class
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
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
|