CSC/ECE 517 Fall 2007/wiki1b 2 p2
ASSIGNMENT # 1 b
Q. There are plenty of examples of method_missing on the Web. Unfortunately, I find most of them rather difficult to understand. One needs to look at quite a bit of source code and figure out what it does. Fix this by giving a plain-English description of several uses of method_missing not covered in our class, with Web links to the pages where you found them
What is method_missing?
Method_missing is called when someone tries to call a method that does not exist on the object. Obj.method_missing(symbol,[*,args]) This method is invoked by Ruby when object Obj is passed a message that it can’t handle. Symbol is the symbol for the method called and args are the parameters for the method.
When you send a message to a Ruby object, Ruby looks for a method to invoke with the same name as the message you sent.
Search Path Used By Ruby When a Method Is Invoked On an Object
CURRENT OBJECT’S OWN SELF INSTANCE METHODS
INSTANCE METHODS SHARED BY ALL OBJECTS OF THE CLASS
EACH INCLUDED MODULE OF THE CLASS
SUPERCLASS OF THE CLASS
MODULES INCLUDED IN SUPERCLASS
HAVE YOU REACHED CLASS=OBJECT?
CALL METHOD_MISSING
When is method_missing called?
When you send a message to an object, the object executes the first method it finds on its method lookup path with the same name as the message. If it fails to find any such method, it raises a NoMethodError exception - unless you have provided the object with a method called method_missing. The method_missing method is passed the symbol of the nonexistent method, and any arguments that were passed in.
EXAMPLES OF method_missing
WHY method_missing??
A CLASS THAT DOESN’T PROVIDE AN IMPLEMENTATION FOR METHOD_MISSING
Suppose we have a file SimpleClass.rb that contains the following class:
class SimpleClass def printMessage(msg) puts “The message is #{msg}” end end a=SimpleClass.new a.printMessage(“Hello World”); a.abc(“Helllloo”);
>ruby SimpleClass.rb
The output is: The message is Hello World NoMethodError: undefined method `abc' for #<SimpleClass:0x6688f34>
EXPLANATION
In the above example we create a new object of SimpleClass named a. We call method printMessage that accepts a message and pass a message “Hello World” to this method. The method outputs the message on the screen. Next we call method abc on the object. However, method abc is not defined on object a. By default, the interpreter raises an error if you call a method that hasn’t been defined for the object. So, in this case, the interpreter raises NoMethodError as it couldn’t find an appropriate definition for method abc.
However it is possible to override this default behavior.
A CLASS THAT PROVIDES AN IMPLEMENTATION FOR METHOD_MISSING
Suppose we have a file tmp.rb that contains the following class:
class Method_Missing_Example def method_missing(m, *args) puts "There's no method called #{m} here -- please try again." end end Method_Missing_Example.new.anything >ruby tmp.rb
The output is: There's no method called anything here -- please try again.
EXPLANATION
In the above example method we create an object of class Method_Missing_Example and invoke the method anything on this object. The method anything is not defined on this object. So the method method_missing is invoked on the object. In this simple example the method method_missing simply prints a message saying “There’s no method called (method name passed as argument) please try again”.
Apart from specifying error messages for undefined methods, method_missing can also be used to provide more dynamic behavior in programming environment.
EXAMPLE WHERE YOU CAN ADD BUT CANT SUBTRACT TWO NUMBERS
class MathWiz def add(a,b) return a+b end def method_missing(name, *args) puts "I don't know the method #{name}" end end mathwiz = MathWiz.new puts mathwiz.add(1,4) puts mathwiz.subtract(4,2)
The output is Mathwiz=#<MathWiz:0x6875018> 5 Nil I don't know the method subtract
EXPLANATION
MathWiz is a class where it is possible to add two numbers. But the class doesn’t provide a method to subtract two numbers. So when add method is called on Mathwiz object 5 is returned but when subtract method is called method_missing is invoked printing “I don’t know the method subtract”.
EXAMPLE THAT CONVERTS NUMBERS FROM ROMAN TO INTEGER REPRESENTATION
class Roman def romanToInt(str) # Code that converts a number from roman representation to int representation end
def method_missing(methId) str = methId.id2name romanToInt(str) end end r = Roman.new r.iv #=> 4 r.xxiii #=> 23 r.mm #=> 2000
The output is: 4 23 2000
EXPLANATION
Class Roman given above is an example of dynamic behavior method_missing can offer. Class Roman has a method ‘romanToInt(str)’ to convert a roman numeral to integer. The most natural way to covert a roman numeral to integer is to pass the Roman numeral itself to object (e.g., r.iv) rather than calling a special method on object. However, this call needs to be diverted to method that actually performs the conversion. This can be easily accomplished using method_missing. As Roman numeral is not a valid method, method_missing gets invoked. Inside method_missing, roman numeral is converted to string with id2name method and passed as parameter to romanToInt method. romanToInt performs the actual conversion and/or handles the error conditions(implementation not given).
Another application that makes use of method_missing could be a simple logger used for debugging purposes. Many times, it may be required to log the trace of called methods and provide information such as: called method-name, arguments, return type. It can be tedious to repeat this part of code in every method. So, a simple solution to this problem can be obtained using method_missing as:
EXAMPLE THAT USES method_missing TO LOG METHOD CALLS
class SimpleCallLogger def initialize(o) @obj = o end def method_missing(methodname, *args) puts "called: #{methodname}(#{args})" a = @obj.send(methodname, *args) puts "\t-> returned: #{a}" return a end end
EXPLANATION
This program makes use of method_missing in a way that it wraps around called method to output the logging information on entry and on exit, it logs the return type. Further, method_missing intercepts the method call and forward it to internal object with ‘send’ method of ruby. Hence, this use of method_missing acts as wrapper.
CONCLUSION
In a nutshell, method_missing not only allows interception of method calls but also saves the programmer’s time and typing by allowing him to use more user friendly method names.
REFERENCES:
1) http://blog.mauricecodik.com/2005/12/more-ruby-methodmissing.html
2) http://www.thirdbit.net/articles/2007/08/01/10-things-you-should-know-about-method_missing/