Metaprogramming means to program a different or a higher level of abstraction. Ruby allows to change your program to inspect and change itself dynamically.Methods as eval allow your program to write new Ruby code, calling the parser and compiler at the run time.

What is the cost of metaprogramming?

Alternative ways to define methods.

Normal definition process

Class Quote
  Def display
     Puts “my quote”
  end
end

When Ruby executes the class keyword it creates a new lexical scope for the new Quote class. Ruby sets the nd_class pointer in lexical scope to point to an RClass structure for the new Quote class. RClass structure initially has an empty method table. Next Ruby executes the def keyword. It creates a separate snippet of YARV code for display method when compiling. Later when executing the def Ruby assigns the code to the target class Quote saving the name in the method table. When we execute this method Ruby looks up the method using the Method Lookup Algorithm.

There are 3 steps for defining normal methods using def

At compilation it saves a snippet of YARV instructions Uses current lexical scope to obtain a pointer to a class or module, on execution It saves methods name an integer if that maps to the name int the method table for that RClass

Defining class methods using an object prefix

Ruby saves class methods in the metaclass of that class.

class Quote
  def self.display
    puts "The quick brown fox jumped over the lazy dog."
  end
end

Using self prefix tells Ruby to add the method to the class of the object rather than using current lexical scope. Ruby uses a different algorithm to save this method. Evaluates the prefix expression, self is set to Quote class. Any objects or Ruby expression can be a prefix. Ruby finds the class of this object Ruby saves that new method in that class’s methods table. In this case places the display method in the metaclass for Quote

Defining class methods using a new lexical scope

class Quote
  class << self 
    def display
      puts "The quick brown fox jumped over the lazy dog."
    end
  end
end

class << self declares a new lexical scope just as class Quote does. Ruby assigns methods to self’s class.

Ruby evaluates the expression that appears after class << in this case self which evaluates to Quote class Ruby finds the class for the object, Ruby creates a new lexical scope for this class

Now we can use the new lexical scope to define new methods.

Defining methods using Singleton classes

class Quote
end

some_quote = Quote.new
def some_quote.display
puts "The quick brown fox jumped over the lazy dog."
end

Internally Ruby implements this using a hidden class called the singleton class which is like a metaclass for a single object.

A singleton class is a special hidden class that Ruby creates internally to hold methods defined for a particular object.

A metaclass is a singleton class in the case when that object itself is a class.

Defining methods using singleton classes in a lexical scope

class Quote
end

some_quote = Quote.new
  class << some_quote
    def display
      puts "The quick brown fox jumped over the lazy dog."
    end
  end
end

Difference is that we use some_quote which evaluates to a single object and not to the class object. Ruby creates a new singleton class for that object along with a new lexical scope. If the singleton class for that object already exist, Ruby will use the same class.

Creating refinements.

This gives us the possibility to define methods and add them later to a class.

module AllCaps
  refine Quote do
  
    def display
      puts "THE QUICK BROWN FOX JUMPED OVER THE LAZY DOG."
    end
  end
end

This defines new behaviour for Quote that we can activate later. Refine method creates a new lexical scope. Ruby creates a new refinement module and uses that as the class for this new scope. Ruby saves a pointer to the Quote class in refined_class inside the new refinement module.

Using refinements, activating a refined method. The using method attaches the refinements from the specified module to the current lexical scope. Ruby 2.0 allows this only in top level lexical scope, might change in future versions.

Quote.new.display
=> The quick brown...
v using AllCaps
w Quote.new.display
=> THE QUICK BROWN…

Metaprogramming and Closures: eval, instance_eval and binding.

Code that writes code

We pass a string to eval and Ruby immediately parses, compiles and executes the code, using the same Bison grammar rules and parse engine. Ruby evaluates the new code string in the same context from where you called eval. Calling eval invokes the parser and compiler on the text we pass it. When the compiler finishes Ruby creates a new stack frame for use in running the new compiled code, Ruby sets the EP in this new stack frame to point to the lower stack frame. Aside from parsing and compiling the code dynamically eval works the same way as if we had passed a block to a function. The eval method creates a closure, a combination of a function and the environment where that function was referred

Calling eval with binding

The eval method can take a second parameter a binding, a binding is a closure without a function, it is the referencing environment.

Ruby makes a persistent copy of this environment in the heap because we might call wval long after the current frame has been popped off the stack. The binding object is a n indirect way to access, save and pass around Ruby’s internal rb_env_t structure

instance_eval

The instance_eval method is similar to eval except that it evaluates the given string in the context of the receiver or the object we call it on.

class Quote
  def initialize
    @str = "The quick brown fox"
  end
end

str2 = "jumps over the lazy dog."
obj = Quote.new

obj.instance_eval do
  puts "#{@str} #{str2}"
end

The block passed to instance_eval has 2 environments. The self pointer records the current value of self, indicates which object is the owner of the method Ruby is currently executing. Each level in Ruby call stack can contain different values for self.

Instance_eval changes self to the receiver. When we call instance aval Ruby creates both a closure and a new lexical scope.

Using defined_method

Difference between def nd define_method is that with define_method we can dynamically construct the method name and we provide the method body as a block, blocks are actually closures, this means that the code inside the new method has access to the environment outside. This is not the case with the def keyword.