December 2008 - Posts

In ruby, when you call a method that doesn't actually exist on that object, the object always invokes the method_missing method instead.  This is one of ruby's cool metaprogramming features: it lets you decide yourself how to handle what would have otherwise been an error.  This feature is used in lots of interesting ways, such as dynamically finding and generating methods, dispatching method or system calls, and generating custom XML with method calls.  But let's start with something simple: using method_missing to implement basic tracing functionality.

 

 

class MyClass
  def foo(bar)
    17
  end
  def do_something(what, ntimes)
    puts "Something happened!"
  end
end

 

That's my class.  It has some methods!

 

class Tracer 
    def initialize(obj) 
        @obj = obj 
   end 
    def method_missing(method_name, *args) 
        puts "Called #{method_name} with args: #{args * ', '}"
        @obj.send(method_name,*args)
   end 
end

 

This is a wrapper class: it will be initialized with my class as a parameter.  Notice it has no methods defined except method_missing.  That means any method calls will just invoke method_missing.  method_missing outputs a trace message and then invokes the method (with the given parameters) on the wrapped object.

 

To use the Tracer, we create a new MyClass object and pass it to a new Tracer, and we're good to go!

myclass = MyClass.new
wrapped_class = Tracer.new(myclass)
wrapped_class.do_something("blah", 5)
wrapped_class.foo(89)

 
Running this code yields the output:

Called do_something with args: blah, 5
Something happened!

Called foo with args: 89
17

To keep it simple, this example doesn't get the return value of the invoked method, or account for stacked method calls. 

Posted by mfedner | 739 comment(s)
Filed under: