Saturday, June 23, 2012

Metaprogramming in Ruby

Ruby is a dynamically typed language. You can define methods and classes at run time. Ruby has several metaprogramming styles.

One way is to use "define_method":

#defining new class
c = Class.new
c.class_eval do
    define_method :hi do
        puts "hello say hi"
    end

    define_method :get_price  do |productname, location|
      puts "4$ for product #{productname} #{location}"
    end

    #method with default location
    define_method :get_price_2  do |productname, defaultlocation="rr"|
      puts "4$ for product #{productname} #{defaultlocation}"
    end


end

c.new.hi
c.new.get_price("bike","charlotte")
c.new.get_price_2("bike" )
c.new.get_price_2("bike","not rr" )
Prints:
hello say hi
4$ for product bike charlotte
4$ for product bike rr
4$ for product bike not rr

Inside this code block, we are creating a new class with three methods. First method is not taking any parameter. Second method is  taking two parameters. Third method is taking two parameters and last parameter is with the default value.

Another method for metaprogramming is using eval keyword. You can compile any string into a code. That is scary and somewhat crazy to me. It might be good for Artificial Intelligence projects, but not much useful for production application that you need to maintain and troubleshoot.


class MyClass
   eval %{def hi
              puts "Eval code string at runtime hello world"
          end
        }

end

d = MyClass.new
d.hi
Prints:
Eval code string at runtime hello world

This code block is taking a string and running that. You can do similar code eval syntax in javascript and php as well.

You can also define classes inside the loop and use that class outside or inside the loop.


2.times do
  class Classtimes
    puts "hello world from Class time objectid #{self.object_id} classid #{self.class.object_id} "
  end
  class Classtime2
    def printit
      puts "hello world from Classtime2 printit objectid #{self.object_id} classid #{self.class.object_id} "
    end
  end
  Classtime2.new.printit
end


Inside this code block, first class uses same object to print. Second class will create new objects for each iteration, but class will be defined once. Here is the output:

hey world from Class time objectid 18939288 classid 15445296 
hey world from Classtime2 printit objectid 18939156 classid 18939192 
hey world from Class time objectid 18939288 classid 15445296 
hey world from Classtime2 printit objectid 18938988 classid 18939192 
If you define the same class again outside of this loop, you may be wondering about the outcome. Ruby lets you extend the class,so it will use the same class to add method. It is similar to using partial keyword in C#, but you can have dynamic extensions with Ruby.

#right after 2.times block code
class Classtime2
  def printitagain
    puts "hey world from Classtime2 printitagain objectid #{self.object_id} classid #{self.class.object_id} "
  end
end
Classtime2.new.printitagain
Output:
hey world from Classtime2 printitagain objectid 18938904 classid 18939192


puts "you can look at instance methods and variables easily"
p MyClass.new.instance_variables
p MyClass.instance_methods(false)
p Classtime2.instance_methods(false)

Output:
you can look at instance methods and variables easily
[]
[:hi]
[:printit, :printitagain]



TODO for this subject:
Module extension
class << self
extending methods for single object