Posts Tagged ‘method_missing’

Beware of the Proxy design pattern -read method_missing-

You probably read about how easy it was to implement the Proxy design pattern in Ruby.

Thanks to the Ruby method_missing method, you can pass messages to underlying objects. See the previous article Local resource available in the wild, thanks to DRb for a fully described example.

But there’s one caveat, you have to be very careful when implementing your method_missing method.

Take this code for example:

def method_missing(name, *args, &block)
  # Get the first arg, which contain information about which underlying
  # object to call.
  id = arg[0]
 
  # Call the corresponding underlying object with the first argument removed
  my_underlying_objects[id].__send__(name, *args[1..-1], &block)
end

If you execute this code, you’ll be stuck in an infinite loop. Why ? There’s a typo, one typo which will cause a segmentation fault. I wrote arg[0] instead of args[0].

To detect this problem before it happens, we can take advantage of the Kernel#caller method. It generates the current execution stask. Here is how we can use it to detect that the current object is calling himself:

def method_missing(name, *args, &block)
  # Check that we're not calling 'method_missing' recursively
  if caller.first.include?(__FILE__)
    raise "#{self.class} is calling itself -method #{name}-. Verify that you do not call a non existing method !!"
  end
 
  # Get the first arg, which contain information about which underlying
  # object to call.
  id = args[0]
 
  # Call the corresponding underlying object with the first argument removed
  my_underlying_objects[id].__send__(name, *args[1..-1], &block)
end

That’s all, we just check that the caller method is not in the current file. If your method_missing code become more and more complex, especially if it includes some meta-programming tricks, you’ll feel A LOT safer!

One last thing: Kernel#caller is not what we could call a non-expensive method, you should only use it in development.