CSC/ECE 517 Fall 2012/ch1 1w22 an: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
 
(102 intermediate revisions by 3 users not shown)
Line 1: Line 1:
=='''Introduction'''==
=='''Introduction<ref>http://rubylearning.com/satishtalim/ruby_method_missing.html</ref>'''==
When we define a method in a class and decide to call that method, how do we do it?
A method that belongs to a class is called by creating an object of the class and passing the method name to the object as a message. The object then looks up the [http://www.madebydna.com/all/code/2011/06/24/eigenclasses-demystified.html method lookup path] and tries to match the called method with the defined methods in the class. On success, the method is executed and the result is returned.


We simply create an object of the class and pass the method name to the object as a message. The object then looks up into its [http://blog.rubybestpractices.com/posts/gregory/031-issue-2-method-lookup.html method lookup path] and tries to match the called method (passed as a message to the object) with the defined methods in the class. When there is a match, the method is executed along with the parameters passed and the result is returned.  
If the object does not find a match in its method lookup, in normal circumstances the [http://www.ruby-doc.org/core-1.9.3/NoMethodError.html NoMethodError Exception] is raised .
=='''What is a Method Lookup Path?'''==
When the object receives a method name that is to be executed, these are the steps carried out for finding out that method called:


*It looks in the current self object’s own [http://docs.oracle.com/javase/tutorial/java/javaOO/classvars.html instance methods].
In cases where the user wants to handle the methods which are not defined but are still called, “method_missing” can be defined and the user can handle the methods as he/she sees fit.
*Then it looks in the list of instance methods that all objects of that class share.
*Then in each of the included [http://www.tutorialspoint.com/ruby/ruby_modules.html modules] of that class, in reverse order of inclusion.
*Then it looks in that class’s superclass.
*Then in the superclass’s included modules, all the way up until it reaches the class Object.
*If it still can’t find a method, the very last place it looks is in the [http://linux.die.net/lkmpg/x40.html Kernel module], included in the class Object.
*Finally, it calls method_missing (if defined in the class), else throws up the NOMethodError exception.


This entire tracing that the object does is the method lookup path.
=='''Format for Defining method_missing'''==


=='''What is method_missing?<ref>http://rubylearning.com/satishtalim/ruby_method_missing.html</ref>'''==
=> def method_missing(m,*args,&block)
Now suppose that the object does not find a matching method in its method lookup path i.e there is no such method defined in the class. Then what?
In normal circumstances the [http://www.ruby-doc.org/core-1.9.3/NoMethodError.html NoMethodError Exception] is raised .


Here is where the method_missing comes into picture. The name “method_missing” should be self explanatory that it is invoked when a method is not found. It is a method of last resort. This method accepts the name of the non-existing method, the array of arguments passed and also any associated block to the method.
(i) '''m->''' accepts the symbol/name of the undefined method (ii) '''*args->''' accepts the array of arguments passed in the method call  (iii) '''&block->'''accepts a block passed to the method


=='''The format for defining method_missing'''==
=='''Ruby Method Lookup Flow'''==
When the object of a class receives a method name to be executed, the following steps are carried out for matching and executing the method:


=> def method_missing(m,*args,&block)
*First, the object looks in its own [http://docs.oracle.com/javase/tutorial/java/javaOO/classvars.html instance methods].
*Second, it looks in the list of instance methods that all objects of that class share.
*Third, in each of the included [http://www.tutorialspoint.com/ruby/ruby_modules.html modules] of that class, in reverse order of inclusion.
*Fourth, it looks in that class’s superclass.
*Fifth, in the superclass’s included modules, all the way up until it reaches the class Object.
*Sixth, if it still can’t find a method, the very last place it looks is in the [http://linux.die.net/lkmpg/x40.html Kernel module], included in the class Object.
*Finally, it calls method_missing (if defined in the class), else throws up the NOMethodError exception.


There are 3 arguments: (i) '''m->''' Takes the symbol/name of the undefined method (ii) '''*args->''' Takes the array of arguments passed in the method call  (iii) '''&block->''' Takes any block passes to the method  
This entire tracing that the object does is called the method lookup path.


=='''Examples'''==
=='''Examples'''==
===simple illustration===
===Calling Defined and Undefined Methods===


  class A // creating a class A
  class A // creating a class 'A'
  def say // defining a method say
  def say // defining a method 'say'
  puts " say Hi "               // body of method say
  puts " say Hi " // body of method say
  end
  end
  end
  end


Now, creating the object of the class
Creating the object of the class
   
   
   a=A.new         // object of the class
   a=A.new       // object of the class
   => #<A:0x2a082e0>     //object id
   => #<A:0x2a082e0>     //object id


Calling the defined method
Calling the defined method


  a.say                  // defined method
  a.say                  // defined method
  => say Hi         // returned result
  => say Hi       // returned result


Calling the undefined method
Calling the undefined method
   
   
  a.sayhi                 // undefined method sayhi
  a.sayhi               // undefined method sayhi
  NoMethodError: undefined method `sayhi' for #<A:0x2a082e0>  // the NoMethodError is raised
  NoMethodError: undefined method `sayhi' for #<A:0x2a082e0>  // the NoMethodError is raised


===method_missing implementation<ref>http://www.thirdbit.net/articles/2007/08/01/10-things-you-should-know-about-method_missing/</ref>===
===method_missing Implementation<ref>http://www.thirdbit.net/articles/2007/08/01/10-things-you-should-know-about-method_missing/</ref>===


  class A
  class A
Line 59: Line 55:
  puts " say hi "
  puts " say hi "
  end
  end
  def method_missing(m,*args,&block) // defining method_missing
  def method_missing(m,*args,&block) // defining method_missing
  puts " This method does not exist" // body of method_missing
  puts " This method does not exist" // body of method_missing
  end
  end
  end
  end
Line 67: Line 63:
   
   
  a=A.new
  a=A.new
  a.sayhi                                 // calling the undefined method sayhi with no arguments
  a.sayhi                               // calling the undefined method sayhi with no arguments
  => This method does not exist // this result returned when method_missing is executed
  => This method does not exist // this result returned when method_missing is executed


'''Explanation:''' When the object 'a' traces its method lookup path for a matching method as 'sayhi', after a failure it resorts to method_missing and the body of method_missing is executed.
When the object 'a' traces its method lookup path for a matching method 'sayhi', upon failure it resorts to method_missing and the body of method_missing is executed.


'''Note:''' There is something interesting that programmers do. Sometimes when a class has many methods that do generally the same kinds of things, and the programmer is not sure in advance which methods will the user call since there are so many of them, and they are all so similar, implementing all of them by hand seems futile. In these situations method_missing makes a new method that was previously not defined and adds it to the class ; or it just does what needs to be done, this is in the hands of the programmer. You can look at the Generic Handler example for this.


Sometimes when a class has many methods that do generally the same kinds of functionality, and the programmer is not sure in advance which methods the user will call since there are so many of them, and all of them are similar, writing code for all of the methods seems futile. In these situations method_missing can be defined to take care of these cases. The below 'Generic Handler' example implements this.


Now, let us look into a few more examples to get the concept right.
===Passing Parameters to an Undefined Method Call===
 
===passing parameters in an undefined method call===


  class A
  class A
Line 89: Line 83:
  end
  end


the passed parameters are stored in the array 'args' and can be accessed like a normal array                 
The passed parameters are stored in the array 'args' and can be accessed like a normal array                 
                                                    
                                                    
   
   
Calling the defined method
Calling the defined method


  a.add(1,2) // calling the defined method add and passing the parameters (1,2)
  a.add(1,2)         // calling the defined method add and passing the parameters (1,2)
  => 3                          // result
  => 3                          // result


Line 102: Line 96:
  => You have typed the method name wrong and these were the parameters passed; 4, 2
  => You have typed the method name wrong and these were the parameters passed; 4, 2


'''Explanation:''' There is a genuine mistake that the user instead of add has typed in adds and this method is not defined. Here when the adds method with parameters is called, the object 'a' tries to look up the method in the method lookup path. When upon failure it invokes method_missing then the args passed in the 'adds' method are stored in the array “args”. It then executes the body of method_missing making use of the parameters.
The user made a genuine mistake by typing 'adds', but this method is not defined. When the 'adds' method with parameters is called, the object 'a' tries to match the method in the method lookup path. Upon failure it invokes method_missing, the args are passed, stored in the array 'args' and the body of method_missing is executed.


===converting numbers from roman representation to integer representation<ref>http://www.rubyquiz.com/quiz22.html</ref>===
===Converting Numbers from Roman Representation to Integer Representation<ref>http://courses.ncsu.edu/csc517/common/lectures/notes/wk2.pdf</ref>===
  class Roman
  class Roman
  @@Roman_to_Numeric = {'i' => 1, 'v' => 5, 'x' => 10, 'l' => 50, 'c' => 100, 'd' => 500, 'm' => 1000}  
  DIGITS = {'I'=>1,'V'=>5,'X'=>10,'L'=>50,'C'=>100,'D'=>500,'M'=>1000,}
  def method_missing(method_var,*args,&block)
  def roman_to_integer(roman_string)
  numeric_value = 0
  last = nil
  roman_string = method_var.to_s.downcase
  roman_string.to_s.upcase.split(//).reverse.inject(0) do
for i in 0...roman_string.length-1
  |memo, digit|
  if (@@Roman_to_Numeric[roman_string[i]]-@@Roman_to_Numeric[roman_string[i+1]] == 0 || @@Roman_to_Numeric[roman_string[i]]-    @@Roman_to_Numeric[roman_string[i+1]] == -9 || @@Roman_to_Numeric[roman_string[i]]-@@Roman_to_Numeric[roman_string[i+1]] == -4 || @@Roman_to_Numeric[roman_string[i]]-@@Roman_to_Numeric[roman_string[i+1]] >= 4) && (/.v.x/ =~ roman_string) == nil
if digit_value = DIGITS[digit]
if last && last > digit_value
memo -= digit_value
  else
  else
  puts "Roman string is invalid"
  memo += digit_value
return
  end
  end
last = digit_value
  end
  end
  while roman_string != ""
  memo
if roman_string[roman_string.length - 1] == 'x' && roman_string[roman_string.length - 2] == 'i'
numeric_value += 9
roman_string.chop!
roman_string.chop!
elsif
roman_string[roman_string.length - 1] == 'v' && roman_string[roman_string.length - 2] == 'i'
numeric_value += 4
roman_string.chop!
roman_string.chop!
else
numeric_value += @@Roman_to_Numeric[roman_string[(roman_string.length)-1]]
roman_string.chop!
  end
  end
  end
  end
  puts "Numeric Value for #{method_var} is: #{numeric_value}"
 
  def method_missing(method)
str=method.id2name
roman_to_integer(str)
  end
  end
  end
  end


calling the undefined methods
 
Calling the undefined methods


  r= Roman.new
  r= Roman.new
  r.vii
  r.vii
r.iClx
  r.xxix
  r.xxix
r.xxxi
  r.xxiv
  r.xxiv
  r.xxvi
  r.xxvi
  r.vx
   


The Output:
The Output


  Numeric Value for vii is: 7
  => 7
  Roman string is invalid
  => 29
Numeric Value for xxix is: 29
  => 24
  Numeric Value for xxxi is: 31
Numeric Value for xxiv is: 24
Numeric Value for xxvi is: 26
Roman string is invalid


===method_missing to log method calls<ref>http://expertiza.csc.ncsu.edu/wiki/index.php/CSC/ECE_517_Fall_2007/wiki1b_2_22</ref>===
===method_missing to Log Method Calls<ref>http://expertiza.csc.ncsu.edu/wiki/index.php/CSC/ECE_517_Fall_2007/wiki1b_2_22</ref>===
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:
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. A simple solution to this problem can be obtained using method_missing as:


   class SimpleCallLogger
   class SimpleCallLogger
Line 172: Line 154:
   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.
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.


===Generic Handler===
===Generic Handler===
Line 186: Line 168:
=='''Advantages of method_missing'''==
=='''Advantages of method_missing'''==


*In addition to specifying the error messages for the methods that are not defined, method_missing provides a more dynamic behavior in the programming environment.
*In addition to specifying the error messages for the undefined methods, method_missing provides a more dynamic behavior in the programming environment.
*If we are unfamiliar with the usage of the object we created but it must support unexpected method calls, then using method_missing is a good option.
*If we are unfamiliar with the usage of the objects we created, then using method_missing is a good technique.
*Allows to catch problem at runtime.
*Handles problems at runtime.
*Allows to define a generic method_missing and handle any undefined method. This is a big advantage over Java. In Java, when you call an undefined method, the program will not compile.
*Define's a generic method_missing and handle's any undefined method, a big advantage over Java. In Java, when you call an undefined method, the program will not compile.
*The use of method_missing falls under the general technique of meta-programming. You can employ meta-programming in missing_function to write a another function to handle the call.
*method_missing falls under the general technique of [http://en.wikipedia.org/wiki/Metaprogramming meta-programming]. Employ meta-programming in missing_function to write an another function to handle the call.


=='''Disadvantages of method_missing'''==
=='''Disadvantages of method_missing'''==
*It is slower than conventional method lookup. Simple tests indicate that method dispatch with method_missing is at least two to three times as expensive in time as conventional dispatch.
*Slower than conventional method lookup. Simple tests indicate that method dispatch with method_missing is at least two to three times as expensive in time as conventional dispatch.


*Since the methods being called never actually exist—they are just intercepted at the last step of the method lookup process—they cannot be documented or introspected as conventional methods can.
*Since the methods being called never actually exist—they are just intercepted at the last step of the method lookup process—they cannot be documented or introspected as conventional methods can.


*Because all dynamic methods must go through the method_missing method, the body of that method can become quite large if there are many different aspects of the code that need to add methods dynamically.
*method_missing restricts compatibility with future versions of an [http://en.wikipedia.org/wiki/Application_programming_interface API]. Introducing new methods in a future API version can break users' expectations.
 
=='''Key Points'''==
 
*In the following example, if within method_missing() we define an undefined method, we get a stack level too deep error message.
 
class A
@@i = 0
def method_missing(method_id)
puts "In Method Missing #{@@i}"
@@i += 1
self.fun
end
end
 
a = A.new
a.foo
 
Output


*Using method_missing restricts compatibility with future versions of an API. Once you rely on method_missing to do something interesting with undefined methods, introducing new methods in a future API version can break your users' expectations.
The result is a 'stack level too deep' error.  


=='''Things to remember'''==
When the 'foo' method is called, after no method match the method_missing is run and this block has a method 'self.fun' that is undefined.    Here when the program execution encounters 'self.fun' it once again calls method_missing. This goes on in an endless loop till the stack memory becomes full.
*Ruby knows that method_missing( ) is there, because it's a private instance method of BasicObject that every object inherits. The BasicObject#method_missing( ) responds by raising a NoMethodError. Overriding this method_missing( ) allows you to call methods that don't really exist. If your method_missing method is only looking for certain method names, don't forget to call super if you haven't found what you're looking for, so that the other superclass method_missing can handle it.


*obj.respond_to? function returns true if the obj responds to the given method. So if you want to know whether your class will respond to a function you can use respond_to? to know the answer. But if method_missing() is used the output may not be what you expected.
*Ruby knows method_missing( ) exists, because it's a private instance method of 'BasicObject' that every object inherits. The BasicObject#method_missing( ) responds by raising the NoMethodError. Overriding this method_missing( ) allows you to call methods that don't really exist.  


*Take the case of the below program. Class A defines only method method_missing() and no other method. Now when a new object is created for class A and when that tries to access a method say, foo, the respond_to? will return the value false. But when you try to actually implement it using “a.foo”, the code will get executed courtesy of method_missing(). So even though the respond_to? says that you cannot access foo function using class A object, if you have method_missing() defined you can access the method.
*If method_missing is only looking for certain method names, don't forget to call the [http://en.wikibooks.org/wiki/Java_Programming/Keywords/super super] keyword if you haven't found what you're looking for, so that the other superclass' method_missing can handle it.


class A
*[http://www.prateekdayal.net/2007/10/16/rubys-responds_to-for-checking-if-a-method-exists/ obj.respond_to?] function returns 'true' if the obj responds to the given method. So if you want to know whether your class will respond to a function you can use respond_to? to know the answer. But if method_missing() is used, the output may not be what you expect.
 
class A
  def method_missing(method_id)
  def method_missing(method_id)
  puts "In method_missing"
  puts "In method_missing"
Line 218: Line 220:
  a.foo
  a.foo


Output:
Output
  false
  false
  In method_missing
  In method_missing


*What if within the method_missing() we define an undefined method? Then what is the result expected? Try to run this code and see what is the result:
=='''Similar functionality in other languages'''==
Method missing, one of the dynamic features of Ruby, is not a feature that is unique to Ruby. It exists in Smalltalk, Python, Groovy, some Javascripts and most CLOS (Common Lisp Object System)extensions. In this section we look at the few such similar implementations in other languages. The table below gives different ways the functionality related to method_missing is handled in other languages.<ref>http://olabini.com/blog/2010/04/patterns-of-method-missing/</ref>
 
 
 
'''Construct'''                          '''Language '''
AUTOLOAD                          Perl
AUTOSCALAR, AUTOMETH, AUTOLOAD...  Perl6
__getattr__                        Python
method_missing                    Ruby
doesNotUnderstand                  Smalltalk
__noSuchMethod__(1)                CoffeeScript, JavaScript
unknown                            Tcl
no-applicable-method              Common Lisp
doesNotRecognizeSelector          Objective-C
TryInvokeMember(2)                C#
match [name, args] { ... }        E
the predicate fail                Prolog
forward                            Io
 
(1) supported by Firefox
 
(2) only for dynamic objects


class A
Table Reference<ref>http://stackoverflow.com/questions/2865865/are-there-equivalents-to-rubys-method-missing-in-other-languages</ref>
@@i = 0
 
def method_missing(method_id)
*'''_getattr_ implementation in Python'''<ref>http://docs.python.org/reference/datamodel.html?highlight=__getattr__#object.__getattr__</ref>
puts "In Method Missing #{@@i}"
 
@@i += 1
object.__getattr__(self,name)
self.fun
 
end
This method is called when an attribute lookup has not found the attribute in the usual places i.e. it is not an instance attribute nor is it found in the class tree for self. name is the attribute name. This method should return the (computed) attribute value or raise an AttributeError exception.
end
 
class Roman(object):
  def roman_to_int(self, roman):
    # implementation here
  end
 
  def __getattr__(self, name):
    return self.roman_to_int(name)
  end
 
>>> r = Roman()
 
>>> r.iv
 
4
 
 
*'''doesNotUnderstand in Smalltalk language'''<ref>http://c2.com/cgi/wiki?DoesNotUnderstand</ref>
 
In SmalltalkLanguage When a receiver is asked to perform a method that is unknown to it, then a run-time complaint is issued which is #doesNotUnderstand.
When a Smalltalk object is sent a message for a method it has not defined, the runtime system turns the message-send into an object and sends #doesNotUnderstand: to the original receiver with this message-send object as argument. By default the #doesNotUnderstand: method raises an exception, but the receiver can override it and implement it in a way that he sees fit.
 
*'''Other languages'''
JavaScript also has a method which has an implementation similar to that of method_missing and that is "noSuchMethod". The limitation of this method is that it is only supported by Firefox/Spidermonkey.
 
Similarly, Perl has an AUTOLOAD method which works on subroutines & class/object methods.
 
=='''Patterns of method missing<ref>http://olabini.com/blog/2010/04/patterns-of-method-missing/</ref>'''==
Now that we have covered all the important areas about method missing including the advantages, the disadvantages and key points related to its functionality, it would be appropriate to know about the different ways method_missing is used and what are the consequences of its use.
 
* Providing Debug information on Failure
Well as method missing is called when there is no object to handle the method being called, we can use method_missing to include more information about the reasons for it being called, i.e. to say that we can provide users with more information about the error messages and hence make the life of the programmers easy and provide a faster way to solve the bugs.
 
* Encode parameters in method name
Instead of sending method as explicit parameters, another method is to use the name to encode parameters. Find below a Rails-style find expression:
Person.find_by_name_and_age("ABC",30)
Another way of writing the same:
Person.find_by(:name => "ABC", :age => 30)
 
The disadvantage of this is that creating such kind of API's make it difficult to debug and maintain the application.
 
* Builders
The idea of a builder is that you use Ruby’s blocks and method_missing to make it easy to create any kind of output structure. You create a builder object and then send it messages and it responds to the messages by building up a data structure based on those messages. For example, the following code
 
  builder = Builder::XmlMarkup.new("", 2)
  puts builder.person {
    name("ABC")
    phone("12345", "local"=>"yes")
    address("Raleigh")
  }
 
will print
 
  <person>
    <name>ABC</name>
    <phone local="yes">12345</phone>
    <address>Raleigh</address>
  </person>
 
Here we have defined a method_missing method and it handles any undefined method and adds the name of the method to the XML markup that is being built. Code blocks are used to capture the nested nature of the XML. The result is a very natural way to programmatically generate XML markup. Also the cumbersome task of closing the tags and escaping rules are taken care of for us.


a = A.new
* Accessors
puts a.respond_to?(:foo) // just checking whether the receiver 'a' responds to the method 'foo'
The inversion of the builder pattern is to use a parser that goes through the XML document and then allow access to the elements by using method_missing.
  even though the result is false the execution will proceed normally
a.foo


output:
* Test Helpers
The expected result would be an "stack level too deep" error.  
Different kind of test helpers can be created using method_missing. Many of the open source Ruby projects implementations of method missing are found in the tests.


'''Explanation:''' when the “foo” method is called upon the object, the method_missing is run and in this block you have a method “self.fun” that is undefined (its a random name that we gave). Here when the program execution encounters “self.fun” it again calls upon method_missing and this goes on in an endless loop till the stack is full.
=='''Conclusion'''==
method_missing is a very powerful feature of Ruby and as is the way with powerful things it can help you a great deal if used properly but it can make things a lot harder for you if implemented incorrectly. It is a kind of a feature which should be used sparingly, if not at all. And there are things that needs to be taken into consideration, as mentioned in the Key Points section above, if method_missing is to be implemented.


=='''References'''==
=='''References'''==
Line 248: Line 330:
=='''Further Suggested Reading'''==
=='''Further Suggested Reading'''==
*http://www.rubyinside.com/  
*http://www.rubyinside.com/  
*https://www.youtube.com/watch?v=SAEiCkixrdE&feature=relmfu
*http://blog.jayfields.com/2008/02/ruby-replace-methodmissing-with-dynamic.html  
*http://blog.jayfields.com/2008/02/ruby-replace-methodmissing-with-dynamic.html  
*http://liquiddevelopment.blogspot.com/2006/04/twisting-and-shaping-dsls-using-ruby.html  
*http://liquiddevelopment.blogspot.com/2006/04/twisting-and-shaping-dsls-using-ruby.html  
*http://anders.janmyr.com/2009/06/ruby-method-lookup.html  
*http://blog.rubybestpractices.com/posts/gregory/031-issue-2-method-lookup.html
*http://phrogz.net/RubyLibs/RubyMethodLookupFlow.pdf
*http://phrogz.net/RubyLibs/RubyMethodLookupFlow.pdf(Diagram)
*http://expertiza.csc.ncsu.edu/wiki/index.php/CSC/ECE_517_Fall_2007/wiki1b_2_Method_Missing

Latest revision as of 03:59, 20 September 2012

Introduction<ref>http://rubylearning.com/satishtalim/ruby_method_missing.html</ref>

A method that belongs to a class is called by creating an object of the class and passing the method name to the object as a message. The object then looks up the method lookup path and tries to match the called method with the defined methods in the class. On success, the method is executed and the result is returned.

If the object does not find a match in its method lookup, in normal circumstances the NoMethodError Exception is raised .

In cases where the user wants to handle the methods which are not defined but are still called, “method_missing” can be defined and the user can handle the methods as he/she sees fit.

Format for Defining method_missing

=> def method_missing(m,*args,&block)

(i) m-> accepts the symbol/name of the undefined method (ii) *args-> accepts the array of arguments passed in the method call (iii) &block->accepts a block passed to the method

Ruby Method Lookup Flow

When the object of a class receives a method name to be executed, the following steps are carried out for matching and executing the method:

  • First, the object looks in its own instance methods.
  • Second, it looks in the list of instance methods that all objects of that class share.
  • Third, in each of the included modules of that class, in reverse order of inclusion.
  • Fourth, it looks in that class’s superclass.
  • Fifth, in the superclass’s included modules, all the way up until it reaches the class Object.
  • Sixth, if it still can’t find a method, the very last place it looks is in the Kernel module, included in the class Object.
  • Finally, it calls method_missing (if defined in the class), else throws up the NOMethodError exception.

This entire tracing that the object does is called the method lookup path.

Examples

Calling Defined and Undefined Methods

class A		// creating a class 'A'
def say		// defining a method 'say'
puts " say Hi " // body of method say
end
end

Creating the object of the class

 a=A.new	       // object of the class
 => #<A:0x2a082e0>     //object id

Calling the defined method

a.say                  // defined method
=> say Hi	       // returned result

Calling the undefined method

a.sayhi                // undefined method sayhi
NoMethodError: undefined method `sayhi' for #<A:0x2a082e0>   // the NoMethodError is raised

method_missing Implementation<ref>http://www.thirdbit.net/articles/2007/08/01/10-things-you-should-know-about-method_missing/</ref>

class A
def say
puts " say hi "
end
def method_missing(m,*args,&block)	// defining method_missing
puts " This method does not exist"	// body of method_missing
end
end

Calling a method that is not defined

a=A.new
a.sayhi                                // calling the undefined method sayhi with no arguments
=> This method does not exist		// this result returned when method_missing is executed

When the object 'a' traces its method lookup path for a matching method 'sayhi', upon failure it resorts to method_missing and the body of method_missing is executed.


Sometimes when a class has many methods that do generally the same kinds of functionality, and the programmer is not sure in advance which methods the user will call since there are so many of them, and all of them are similar, writing code for all of the methods seems futile. In these situations method_missing can be defined to take care of these cases. The below 'Generic Handler' example implements this.

Passing Parameters to an Undefined Method Call

class A
def add(a,b)
a+b
end
def method_missing(name,*args,&block)    // the method_missing is defined and the *args parameter accepts all the parameters passed during 								                                 
                                            the method call   
puts “You have typed the method name wrong and these were the parameters passed ; #{args[0]}, #{args[1]}”								
end                         			
end

The passed parameters are stored in the array 'args' and can be accessed like a normal array


Calling the defined method

a.add(1,2)		        // calling the defined method add and passing the parameters (1,2)
=> 3                           // result

Calling the undefined method

a.adds(4,2) 			// calling the undefined method adds and passing the parameter (4,2)
=> You have typed the method name wrong and these were the parameters passed; 4, 2

The user made a genuine mistake by typing 'adds', but this method is not defined. When the 'adds' method with parameters is called, the object 'a' tries to match the method in the method lookup path. Upon failure it invokes method_missing, the args are passed, stored in the array 'args' and the body of method_missing is executed.

Converting Numbers from Roman Representation to Integer Representation<ref>http://courses.ncsu.edu/csc517/common/lectures/notes/wk2.pdf</ref>

class Roman
DIGITS = {'I'=>1,'V'=>5,'X'=>10,'L'=>50,'C'=>100,'D'=>500,'M'=>1000,}
def roman_to_integer(roman_string)
last = nil
roman_string.to_s.upcase.split(//).reverse.inject(0) do
|memo, digit|
if digit_value = DIGITS[digit]
if last && last > digit_value
memo -= digit_value
else
memo += digit_value
end
last = digit_value
end
memo
end
end
def method_missing(method)
str=method.id2name
roman_to_integer(str)
end
end


Calling the undefined methods

r= Roman.new
r.vii
r.xxix
r.xxiv
r.xxvi

The Output

=> 7
=> 29
=> 24

method_missing to Log Method Calls<ref>http://expertiza.csc.ncsu.edu/wiki/index.php/CSC/ECE_517_Fall_2007/wiki1b_2_22</ref>

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. A simple solution to this problem can be obtained using method_missing as:

 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

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.

Generic Handler

class NoBar
def method_missing(methodname, *args)
define_method(:bar) if "bar" == methodname.to_s
define_method(:nobar) if "nobar" == methodname.to_s
end
end

This is an example of using method_missing as a generic handler to handle when a calling method is not exist. You can use missing_method to dynamically create a method at a runtime.

Advantages of method_missing

  • In addition to specifying the error messages for the undefined methods, method_missing provides a more dynamic behavior in the programming environment.
  • If we are unfamiliar with the usage of the objects we created, then using method_missing is a good technique.
  • Handles problems at runtime.
  • Define's a generic method_missing and handle's any undefined method, a big advantage over Java. In Java, when you call an undefined method, the program will not compile.
  • method_missing falls under the general technique of meta-programming. Employ meta-programming in missing_function to write an another function to handle the call.

Disadvantages of method_missing

  • Slower than conventional method lookup. Simple tests indicate that method dispatch with method_missing is at least two to three times as expensive in time as conventional dispatch.
  • Since the methods being called never actually exist—they are just intercepted at the last step of the method lookup process—they cannot be documented or introspected as conventional methods can.
  • method_missing restricts compatibility with future versions of an API. Introducing new methods in a future API version can break users' expectations.

Key Points

  • In the following example, if within method_missing() we define an undefined method, we get a stack level too deep error message.
class A
@@i = 0
def method_missing(method_id)
puts "In Method Missing #{@@i}"
@@i += 1
self.fun
end
end
a = A.new
a.foo 

Output

The result is a 'stack level too deep' error. 

When the 'foo' method is called, after no method match the method_missing is run and this block has a method 'self.fun' that is undefined. Here when the program execution encounters 'self.fun' it once again calls method_missing. This goes on in an endless loop till the stack memory becomes full.

  • Ruby knows method_missing( ) exists, because it's a private instance method of 'BasicObject' that every object inherits. The BasicObject#method_missing( ) responds by raising the NoMethodError. Overriding this method_missing( ) allows you to call methods that don't really exist.
  • If method_missing is only looking for certain method names, don't forget to call the super keyword if you haven't found what you're looking for, so that the other superclass' method_missing can handle it.
  • obj.respond_to? function returns 'true' if the obj responds to the given method. So if you want to know whether your class will respond to a function you can use respond_to? to know the answer. But if method_missing() is used, the output may not be what you expect.

class A

def method_missing(method_id)
puts "In method_missing"
end
end
a = A.new
puts a.respond_to?(:foo)
a.foo

Output

false
In method_missing

Similar functionality in other languages

Method missing, one of the dynamic features of Ruby, is not a feature that is unique to Ruby. It exists in Smalltalk, Python, Groovy, some Javascripts and most CLOS (Common Lisp Object System)extensions. In this section we look at the few such similar implementations in other languages. The table below gives different ways the functionality related to method_missing is handled in other languages.<ref>http://olabini.com/blog/2010/04/patterns-of-method-missing/</ref>


Construct                          Language 
AUTOLOAD                           Perl
AUTOSCALAR, AUTOMETH, AUTOLOAD...  Perl6
__getattr__                        Python
method_missing                     Ruby
doesNotUnderstand                  Smalltalk
__noSuchMethod__(1)                CoffeeScript, JavaScript
unknown                            Tcl
no-applicable-method               Common Lisp
doesNotRecognizeSelector           Objective-C
TryInvokeMember(2)                 C#
match [name, args] { ... }         E
the predicate fail                 Prolog
forward                            Io

(1) supported by Firefox

(2) only for dynamic objects

Table Reference<ref>http://stackoverflow.com/questions/2865865/are-there-equivalents-to-rubys-method-missing-in-other-languages</ref>

object.__getattr__(self,name)

This method is called when an attribute lookup has not found the attribute in the usual places i.e. it is not an instance attribute nor is it found in the class tree for self. name is the attribute name. This method should return the (computed) attribute value or raise an AttributeError exception.

class Roman(object):

 def roman_to_int(self, roman):
   # implementation here
 end
 def __getattr__(self, name):
   return self.roman_to_int(name)
 end

>>> r = Roman()

>>> r.iv

4


In SmalltalkLanguage When a receiver is asked to perform a method that is unknown to it, then a run-time complaint is issued which is #doesNotUnderstand. When a Smalltalk object is sent a message for a method it has not defined, the runtime system turns the message-send into an object and sends #doesNotUnderstand: to the original receiver with this message-send object as argument. By default the #doesNotUnderstand: method raises an exception, but the receiver can override it and implement it in a way that he sees fit.

  • Other languages

JavaScript also has a method which has an implementation similar to that of method_missing and that is "noSuchMethod". The limitation of this method is that it is only supported by Firefox/Spidermonkey.

Similarly, Perl has an AUTOLOAD method which works on subroutines & class/object methods.

Patterns of method missing<ref>http://olabini.com/blog/2010/04/patterns-of-method-missing/</ref>

Now that we have covered all the important areas about method missing including the advantages, the disadvantages and key points related to its functionality, it would be appropriate to know about the different ways method_missing is used and what are the consequences of its use.

  • Providing Debug information on Failure

Well as method missing is called when there is no object to handle the method being called, we can use method_missing to include more information about the reasons for it being called, i.e. to say that we can provide users with more information about the error messages and hence make the life of the programmers easy and provide a faster way to solve the bugs.

  • Encode parameters in method name

Instead of sending method as explicit parameters, another method is to use the name to encode parameters. Find below a Rails-style find expression: Person.find_by_name_and_age("ABC",30) Another way of writing the same: Person.find_by(:name => "ABC", :age => 30)

The disadvantage of this is that creating such kind of API's make it difficult to debug and maintain the application.

  • Builders

The idea of a builder is that you use Ruby’s blocks and method_missing to make it easy to create any kind of output structure. You create a builder object and then send it messages and it responds to the messages by building up a data structure based on those messages. For example, the following code

 builder = Builder::XmlMarkup.new("", 2)
 puts builder.person {
   name("ABC")
   phone("12345", "local"=>"yes")
   address("Raleigh")
 }

will print

 <person>
   <name>ABC</name>
   <phone local="yes">12345</phone>
   <address>Raleigh</address>
 </person>

Here we have defined a method_missing method and it handles any undefined method and adds the name of the method to the XML markup that is being built. Code blocks are used to capture the nested nature of the XML. The result is a very natural way to programmatically generate XML markup. Also the cumbersome task of closing the tags and escaping rules are taken care of for us.

  • Accessors

The inversion of the builder pattern is to use a parser that goes through the XML document and then allow access to the elements by using method_missing.

  • Test Helpers

Different kind of test helpers can be created using method_missing. Many of the open source Ruby projects implementations of method missing are found in the tests.

Conclusion

method_missing is a very powerful feature of Ruby and as is the way with powerful things it can help you a great deal if used properly but it can make things a lot harder for you if implemented incorrectly. It is a kind of a feature which should be used sparingly, if not at all. And there are things that needs to be taken into consideration, as mentioned in the Key Points section above, if method_missing is to be implemented.

References

<references/>

Further Suggested Reading