CSC/ECE 517 Fall 2007/wiki1b 4 19

From Expertiza_Wiki
Jump to navigation Jump to search

Metaprogramming in Ruby

Introduction to metaprogramming

Metaprogramming,just as the name suggests, means “Programming a program”. This means that one can define classes and methods at runtime. It is possible only because of the dynamic nature of the language.

For instance one might take the example of taking input from a CSV file into a program. In such a case, if we are using static languages, we generally make a class or structure of the different fields in the file. We then make instances using the values. In another approach we simply use associative arrays, however this seems too unnatural and cumbersome.

However in a dynamic language such as ruby, since we are now equipped with the powers of meta programming, we can create the class depending on the structure of the CSV file i.e. if the attribute names are stored in the first line of the file, we can create a class with these as variables, at runtime, on the fly and then instantiate.

For possible meta programming implementations of the CSV problem in Ruby, I would recommend you to an excellent DEV SOURCE article

Under the hood

So you think how does ruby achieve this great feat. Well its not rocket science. Every object in ruby has a associated class that is accessible to only that object. Now recall that everything in ruby is an object. Put two and two together and you realize that classes and modules are also objects and so they too have classes, all for themselves.

These classes are called Singleton classes(accessible to only a single object). So when you send a message to an object, its singleton class is first checked to see if it has methods defined that can handle this message.

Metaprogramming is made possible because of singleton classes. Whenever we dynamically define a method on an object, it is added as a method of the singleton class for this object.

For an excellent discussion about singleton classes and their properties, this blog is a must read.

Classification of meta programming features of Ruby

  • Dynamism

Because of its dynamic nature ruby supports creating classes and methods at runtime

c = Class.new
c.class_eval do
	define_method :foo do
		puts "Hello World"
	end
end
c.new.foo    # => "Hello World"
  • Directly defining methods on Singleton classes

We can use class_eval to define methods on singleton classes. This will then modify the behaviour of the sub classes(if any) and not the base class. An excellent example of this, borrowed from Ola Bini's Blog entry Point 2

class Product < ActiveRecord::Base
set_table_name 'produce'
end

module ActiveRecord
class Base
def self.set_table_name name
	define_attr_method :table_name, name
end

def self.define_attr_method(name, value)
	singleton_class.send :alias_method, "original_#{name}", name
	singleton_class.class_eval do
 		define_method(name) do
   			value
 			end
		end
        end
end
end

Here he wants to modify the behavior of the Product class or any other class that extends the active record module but at the same time not change the functionality of the base class. So we basically use class eval to send messages to the singleton class. Now the :table_name is aliased to product

  • Extending Specific Objects

Ruby allows us to define methods or alter the behavior of specific objects It is also possible for an object alone to extend a module, thus altering only its behavior.

  • Method missing

Ruby allows us to define actions in case a method is missing, one of these actions can also be to define a method at runtime. A very interesting application of method missing is for converting roman numerals to decimals

  • Method Aliasing

We can alias a method to another name and then redefine the method to do some extra work and then call the old method or whatever the case be. This can be used for meta programming.

  • Blocks and proc objects

Materializing a Proc and saving this in variables and sending it around is a good way to use meta programming to do interesting things. Lambda function is used to define proc objects

p = lambda { puts "hello"; return 1 }
define_method(:a, &p)
  • The different evals: There are 4 different evals in ruby
    • Eval: This call can take a string or binding to evaluate but not a block
    • Class_Eval: It is just a alias for module_eval
    • Instance_eval: It will evaluate the string or the block in the context of the receiver. Thus it sets self to be the receiver.
    • Module_Eval: It will evaluate the string or the block in the context of the module it is called on. Thus it is used to define new methods on singleton classes. This is the most commonly used eval call.

A good way to think about the difference is that instance eval associates the new method with the class as an instance and thus it is availaible to all its objects. While Class_eval takes you into a class definition block for C, where def is an instance method of C. Thus you require an instance to call this method.

Applications

I credit Ola Bini for the first 3 applications, in his blog

  • Domain Specific Languge:

We can define methods on singleton classes, as discussed above, to store information about classes in such a way that it can be used by other parts of the framework. For the product example above, the fact that we can refer to table_name as product for the Product class is very useful for other parts of the system.

  • Introspect on instance variables:

This is a facility Rails uses to pass values between the controller and the view. This is made possible because of meta programming.

from.instance_variables.each do |v|
to.instance_variable_set v, from.instance_variable_get(v)
end
  • Null Object refactoring

This refactoring basically states that an object could either contain an object, or null, and if it's null it will have a predefined value. This is very easy to bring about in Ruby because of meta programming.

def nil.name; "default name"; end
x # => nil
name = x.name # => "default name"
  • CSV files example we talked about earlier
  • Roman numerals example (because of method missing)

Some other useful links

A presentation about Meta programming in Ruby made at the 2005 O’Reilly Open Source Convention by Glenn Vanderburg

About Metaclasses(different take on meta programming)

References

  1. http://www.devsource.com/article2/0,1895,1928561,00.asp
  2. http://practicalruby.blogspot.com/2007/02/ruby-metaprogramming-introduction.html
  3. http://ola-bini.blogspot.com/2006/09/ruby-metaprogramming-techniques.html
  4. http://rubyquiz.com/quiz22.html
  5. http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/192665