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.