CSC/ECE 517 Fall 2007/wiki1b 2 22: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
 
(25 intermediate revisions by 2 users not shown)
Line 3: Line 3:
= Introduction - method_missing =
= Introduction - method_missing =


The method_missing is a method that called whenever someone tries to call a method in your object that doesn't exist. It is a Ruby kernel method.  It used as a method of last resort. When you send a message to a Ruby object, Ruby looks for a method to invoke with the same name as the message you sentFirst it looks in the current self object’s own instance methods. Then it looks in the list of instance methods that all objects of that class share, and then in each of the included modules of that class, in reverse order of inclusion. Then it looks in that class’s superclass, and 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 Kernel module, included in the class Object.
The method - method_missing - is invoked by Ruby, when an object sent a message that it cannot handle. symbol is the symbol for the method called, and args are any arguments that were passed to it. By default, the interpreter raises an error when this method is called. However, it is possible to override the method to provide more dynamic behavior.  
 
  obj.method_missing(symbol [, *args] ) => result
 
When a method is called, Ruby looks for a same name methodIt searches the method in the following sequence - self object’s methods, instance methods of all objects of this class, included modules in this class, superclass, superclass’s included modules, all the way till class Object. If it still can’t find a method, it looks in the Kernel module, included in the class Object. When a method does not exist, Ruby invokes method_missing.
[http://www.thirdbit.net/articles/2007/08/01/10-things-you-should-know-about-method_missing/]


= Examples =
= Examples =


== 1. Object composition ==
== 1. Object composition ==
The below implementation of method_missing can be used to debug some code without littering it with print statements. This object "intercepts" all method calls made to it, prints out a message and forwards on the method call to an internal object using the 'send' method, without knowing anything about the object passed to it.  [http://blog.mauricecodik.com/2005/12/more-ruby-methodmissing.html]


  class SimpleCallLogger
  class SimpleCallLogger
Line 20: Line 26:
   end
   end
  end
  end
This object "intercepts" all method calls made to it, prints out a message and forwards on the method call to an internal object using the 'send' method, without knowing anything about the object passed to it. It can be used to debug some code without littering it with print statements. [http://blog.mauricecodik.com/2005/12/more-ruby-methodmissing.html]


== 2. Factorial ==
== 2. Factorial ==


Let's create a Computer class that contains a factorial method (you know the famous n! thing).
Below is the - Computer class which contains a factorial method. (n!). If we want to use the special notation (_<digits>!), the method_missing implementation extracts the number, from the method name, and calls the factorial method to get the result.
[http://www.alef1.org/ruby/method_missing/index.html]


class Computer
  class Computer
  def factorial n
    def factorial n
       raise ArgumentError if n < 0
       raise ArgumentError if n < 0
       f = 1
       f = 1
Line 36: Line 40:
       end
       end
       f
       f
    end
    def method_missing(meth, *args)
      meth.to_s =~ /_([0-9]*)!/
      return super if ! $1
      factorial($1.to_i)
     end
     end
   end
   end
So instead of using traditional method call, we can use some thing like _<digits>!
   computer = Computer.new
   computer = Computer.new
   puts computer.factorial(4)
   puts computer.factorial(4) /*Traditional*/
  puts computer._4! /*method_missing will return correct result.*/
 
This way, we won't have to create methods for every integer, by using method_missing.


We would like to use some notation close to the usual notation:
== 3. Enumerator ==
  computer = Computer.new
We can enhance the functionalities of existing classes with the use of method_missing. Below is the implementation of class Enumerable. It shows how simply and in a user friendly manner, we can add an integer to all the elements of map, select, reject and find specifying criteria. [http://www.ruby-forum.com/topic/112986]
puts computer._4!
   
  class Enumerable::Enumerator
    def method_missing(sym,*args,&blk)
      each{ |x| x.send(sym,*args,&blk) }
    end
  end


Obviously, we cannot create methods for every integer, to do it we use the method_missing.
The benefit is a number of nice conveniences, eg.
  def method_missing(meth, *args)
  [1,2,3].map + 3  => [4,5,6]
  meth.to_s =~ /_([0-9]*)!/
  [1,2,3].select > 2  => [3]
  return super if ! $1
  [1,2,3].reject > 1  => [1]
   factorial($1.to_i)
  [1,2,3].find > 1    => 2
 
== 4. Ruby Torrent ==
RubyTorrent uses method_missing to make all the bencoded fields in the .torrent file easy to access. (bencoding being the BitTorrent encoding scheme.). It also helps ensuring type-checking.  
 
  class MetaInfoInfoFile
  def initialize(dict=nil)
    @s = TypedStruct.new do |s|
      s.field :length => Integer, :md5sum => String, :sha1 => String,
              :path => String
      s.array :path
      s.required :length, :path
    end
  end
  def method_missing(meth, *args)
    @s.send(meth, *args)
   end
  end
  end


If we use the special notation (_<digits>!) the method_missing implementation extracts the number, from the method name, and calls the factorial method to get the result. Each time and for any method the same processing happens.
== 5. Ruby moment of zen ==
[http://www.alef1.org/ruby/method_missing/index.html]
 
Below is the model class for a User. If the user has a ton of profile data and we want to abstract that data, we will make a Profile class.
 
However, we don’t want to call user.profile.street_address every time we need to access an attribute in a user’s profile. We also don’t want to define a bunch of reader methods. [http://cleanair.highgroove.com/articles/2006/07/04/ruby-moment-of-zen-method_missing]
 
class User < ActiveRecord::Base
  has_one :profile
end
class Profile < ActiveRecord::Base
  belongs_to :user
end


== 3. Foo_Bar ==
class User < ActiveRecord::Base
class Foz
  ...
   def bar
  # Attempts to pass any missing methods to
     puts "Foz-bar"
  # the associated +profile+.
   def method_missing(meth, *args, &block)
    if profile.respond_to?(meth)
      profile.send(meth)
    else
      super
     end
   end
   end
  end
  end
So, we can now have this:
@user.street_address == @user.profile.street_address
== 6. Generic Handler ==
  class NoBar
  class NoBar
   def method_missing(methodname, *args)
   def method_missing(methodname, *args)
       puts "NoBar" if "bar" == methodname.to_s
       define_method(:bar) if "bar" == methodname.to_s
      define_method(:nobar) if "nobar" == methodname.to_s
   end
   end
  end
  end
class MakeBar
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.
  define_method(:bar) {puts "madebar"}
end
bu= Baruser.new
bu.baruse(Foz.new) # another type altogether
bu.baruse(NoBar.new) # a class that doesn't have bar method
bu.baruse(MakeBar.new) # a class where the bar method is created programatically
We can see from the examples that what ruby does is searching for a handler for the bar message. The handler can be a method (symbol) called bar or a generic handle like Missing_Method -- Ruby doesn't care as long as the message get handled. [http://www.ddj.com/blog/architectblog/archives/2007/08/dependency_inje.html]


= Advantages =
= Advantages =
Allow you to catch problem at runtime.
#Allows to catch problem at runtime.
Allow you define a generic method_missing and handle any undefined method.
#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.
The use of method_missing falls under the general technique of metaprogramming.
#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.


= References =
= References =
10 things you should know about method_missing.[http://www.thirdbit.net/articles/2007/08/01/10-things-you-should-know-about-method_missing/]
#10 things you should know about method_missing.http://www.thirdbit.net/articles/2007/08/01/10-things-you-should-know-about-method_missing/
#More Ruby: method_missing http://blog.mauricecodik.com/2005/12/more-ruby-methodmissing.html
#Hey Ruby, how much for the method_missing? http://www.alef1.org/ruby/method_missing/index.html


= Further reading =
= Further reading =
#Evaluation Options in Ruby. [http://www.infoq.com/articles/eval-options-in-ruby]
#Evaluation Options in Ruby. http://www.infoq.com/articles/eval-options-in-ruby
#IF YOU BUILD IT .. will they come [http://www.ddj.com/blog/architectblog/archives/2007/08/dependency_inje.html]
#IF YOU BUILD IT .. will they come. http://www.ddj.com/blog/architectblog/archives/2007/08/dependency_inje.html


= External links =
= External links =
#http://www.thirdbit.net/articles/2007/08/01/10-things-you-should-know-about-method_missing/
#http://blog.mauricecodik.com/2005/12/more-ruby-methodmissing.html
#http://www.alef1.org/ruby/method_missing/index.html
#http://www.ruby-forum.com/topic/112986
#http://redhanded.hobix.com/inspect/theBestOfMethod_missing.html
#http://cleanair.highgroove.com/articles/2006/07/04/ruby-moment-of-zen-method_missing

Latest revision as of 18:58, 10 October 2007

Introduction - method_missing

The method - method_missing - is invoked by Ruby, when an object sent a message that it cannot handle. symbol is the symbol for the method called, and args are any arguments that were passed to it. By default, the interpreter raises an error when this method is called. However, it is possible to override the method to provide more dynamic behavior.

obj.method_missing(symbol [, *args] ) => result

When a method is called, Ruby looks for a same name method. It searches the method in the following sequence - self object’s methods, instance methods of all objects of this class, included modules in this class, superclass, superclass’s included modules, all the way till class Object. If it still can’t find a method, it looks in the Kernel module, included in the class Object. When a method does not exist, Ruby invokes method_missing. [1]

Examples

1. Object composition

The below implementation of method_missing can be used to debug some code without littering it with print statements. This object "intercepts" all method calls made to it, prints out a message and forwards on the method call to an internal object using the 'send' method, without knowing anything about the object passed to it. [2]

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

2. Factorial

Below is the - Computer class which contains a factorial method. (n!). If we want to use the special notation (_<digits>!), the method_missing implementation extracts the number, from the method name, and calls the factorial method to get the result. [3]

 class Computer
   def factorial n
     raise ArgumentError if n < 0
     f = 1
     n.downto(1) do |i|
       f = f * i
     end
     f
   end
   def method_missing(meth, *args)
     meth.to_s =~ /_([0-9]*)!/
     return super if ! $1
     factorial($1.to_i)
   end
 end

So instead of using traditional method call, we can use some thing like _<digits>!

 computer = Computer.new
 puts computer.factorial(4) /*Traditional*/
 puts computer._4! /*method_missing will return correct result.*/

This way, we won't have to create methods for every integer, by using method_missing.

3. Enumerator

We can enhance the functionalities of existing classes with the use of method_missing. Below is the implementation of class Enumerable. It shows how simply and in a user friendly manner, we can add an integer to all the elements of map, select, reject and find specifying criteria. [4]

 class Enumerable::Enumerator
   def method_missing(sym,*args,&blk)
     each{ |x| x.send(sym,*args,&blk) }
   end
 end

The benefit is a number of nice conveniences, eg.

 [1,2,3].map + 3   => [4,5,6]
 [1,2,3].select > 2   => [3]
 [1,2,3].reject > 1   => [1]
 [1,2,3].find > 1     => 2

4. Ruby Torrent

RubyTorrent uses method_missing to make all the bencoded fields in the .torrent file easy to access. (bencoding being the BitTorrent encoding scheme.). It also helps ensuring type-checking.

class MetaInfoInfoFile
 def initialize(dict=nil)
   @s = TypedStruct.new do |s|
     s.field :length => Integer, :md5sum => String, :sha1 => String,
             :path => String
     s.array :path
     s.required :length, :path
   end
 end
 def method_missing(meth, *args)
   @s.send(meth, *args)
 end
end

5. Ruby moment of zen

Below is the model class for a User. If the user has a ton of profile data and we want to abstract that data, we will make a Profile class.

However, we don’t want to call user.profile.street_address every time we need to access an attribute in a user’s profile. We also don’t want to define a bunch of reader methods. [5]

class User < ActiveRecord::Base
 has_one :profile
end
class Profile < ActiveRecord::Base
 belongs_to :user
end
class User < ActiveRecord::Base
 ...
 # Attempts to pass any missing methods to
 # the associated +profile+.
 def method_missing(meth, *args, &block)
   if profile.respond_to?(meth)
     profile.send(meth)
   else
     super
   end
 end
end

So, we can now have this:

@user.street_address == @user.profile.street_address

6. 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

  1. Allows to catch problem at runtime.
  2. 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.
  3. 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.

References

  1. 10 things you should know about method_missing.http://www.thirdbit.net/articles/2007/08/01/10-things-you-should-know-about-method_missing/
  2. More Ruby: method_missing http://blog.mauricecodik.com/2005/12/more-ruby-methodmissing.html
  3. Hey Ruby, how much for the method_missing? http://www.alef1.org/ruby/method_missing/index.html

Further reading

  1. Evaluation Options in Ruby. http://www.infoq.com/articles/eval-options-in-ruby
  2. IF YOU BUILD IT .. will they come. http://www.ddj.com/blog/architectblog/archives/2007/08/dependency_inje.html

External links

  1. http://www.thirdbit.net/articles/2007/08/01/10-things-you-should-know-about-method_missing/
  2. http://blog.mauricecodik.com/2005/12/more-ruby-methodmissing.html
  3. http://www.alef1.org/ruby/method_missing/index.html
  4. http://www.ruby-forum.com/topic/112986
  5. http://redhanded.hobix.com/inspect/theBestOfMethod_missing.html
  6. http://cleanair.highgroove.com/articles/2006/07/04/ruby-moment-of-zen-method_missing