Ruby Metaprogramming

Ruby is definitely one of the most widely used languages nowadays. And if you are also working on Ruby, you must have heard the word ‘metaprogramming’. Even if you have used metaprogramming, there are chances that you didn’t understand it completely. Metaprogramming is an important concept of Ruby, and you need to understand the usefulness of this concept. the-black-magic-of-ruby-metaprogramming-1-638

What is ‘metaprogramming’?

It is a technique in which the written code writes itself, you can say programming of programming. This might sound confusing, but it is pretty simple to understand and work with. It helps you to add, edit, or modify the code while it is running. It can be used to create new methods or delete the existing ones. One can also reopen or modify existing classes, catch methods that do not exist. Repetitive coding can also be avoided to keep your program DRY.
Also Read-Capybara Basics For Automated testing of Ruby on Rails Application

How ‘Ruby‘ calls methods?

To understand the concept and scope of metaprogramming, it is important to understand how Ruby find a method when it is called. When a method is called in Ruby, it located that method within the code including the inheritance chain.
  1. class Employee
  2.  def test
  3.    “hello”
  4.  end
  5. end
  • charlie_will = Employee.new
  • charlie_will.test # => “hello”
  The test method is called in the example, Ruby first looks for the parent of the charlie.will object because it is an instance of the Employee class, and it has available a method called test, so this method is called. Things get more complicated however when the object is an instance of a class which has inherited from another class:  
  1. class Animal
  2.  def eats
  3.    “food”
  4.  end
  5.  def lives_in
  6.    “the wild”
  7.  end
  8. end
  9. class Pig < Animal
  10.  def lives_in
  11.    “farm”
  12.  end
  13. end
  14. babe = Pig.new
  15. babe.lives_in # => “farm”
  16. babe.eats # => “food”
  17. babe.thisdoesnotexist # => NoMethodError: undefined method `thisdoesnotexist’ for #<Pig:0x16a53c8>
In the above example, mixed inheritance is introduced. Methods defined higher in the inheritance chain needs to be considered by Ruby. When babe.lives_in is called Ruby first check the Pig class for the method, and it is called because it exists in the Pig class.
But if babe.eats method is called the story would be different. Ruby first checks the Pig class for eats method, as it does not exist in it then ‘Animal’ class will be called. In our case, it will respond as eats method exists in Animal class so it will be called. The method babe.thisdoesnotexist will throw an exception NoMethodError because the method does not exist. It means method defined in the lowest level of inheritance chain will be called if the method doesn’t exist, and an exception will be raised.  

After going through the example, we have discovered that Ruby looks up each method as follows:

  1. Ask object’s parent class if it can respond to the method, call it if found.
  2. If it does not respond, call the next parent class, continue until you reach the top of the inheritance chain.
  3. If nothing in the inheritance chain responds to the method call, the method does not exist.
 

Introducing Singleton Method

Singleton class is designed to give you the hold on to Object Oriented programming. It allows you to create an object that inherits from other classes and call their methods. It specifically does it for a single object. It is also known as Eigenclass.
  1. greeting = “Hello World”
  2. def greeting.greet
  3.  self
  4. end
  • greeting.greet # => “Hello World”
On different thing you would notice here is ‘self’. The greeting.greet method has access to the entire object it has been attached to; in Ruby, it will be referred to that object as ‘self’. In this case, it refers to the String value we attached to it. If we had attached it to anything else, it would have returned that object.
greeting = “I like cheese”
  1. class << greeting
  2.  def greet
  3.    “hello! ” + self
  4.  end
  5. end
  • greeting.greet # => “hello! I like cheese”
Singleton class method allows you to add many methods at once without prefixing all the method names. This syntax also allows you to add anything you would add while declaring the class including attr_writer, attr_reader, and attr_accessor methods.

How does it work?

When you work on the singleton class, Ruby needs a way to add methods to the object we are adding to, something that language doesn’t allow. To do so it creates a new class which is called ‘singleton class’. This class is, made the parent of the object we are working on, given the method and changes instead. It is also made an instance of the previous parent of our object so that the inheritance chain remains unchanged:

some object instance > singleton class > parent class > … > Object/BasicObject

Singleton class add one more step to the Ruby lookup method. Now Ruby looks up in the following way-
  1. Ask the object if its singleton class can respond to the method, calling it if found.
  2. Ask object’s parent class if it can respond to the method, call it if found.
  3. If it does not respond, call the next parent class, continue until you reach the top of the inheritance chain.
  4. If nothing in the inheritance chain responds to the method call, the method does not exist.
Not only can objects gain methods from their inherited classes, but now they can also gain individually unique methods as the program is running.

Putting metaprogramming to work with instance_eval and class_eval

Singleton classes are definitely helpful, but if you want to work with objects dynamically you need to be able to re-open them at runtime within other functions. Ruby does not allow you to have any class statements within a function syntactically, but instance-eval helps to do this. This method is defined in standard Kernel module of Ruby and allows you to add instance methods to an object just like our singleton class syntax.
  1. foo = “bar”
  2. foo.instance_eval do
  3.  def hi
  4.    “you smell”
  5.  end
  6. end
  • foo.hi # => “you smell”
The instance_eval method can take a block to evaluate, which can be used to define new methods as if you are writing a class. These methods will be added to the singleton class of the object.
The methods defined by instance_eval will be insurance methods. You cannot add attr_accessor to an instance method; to do so you need to operate on the class of the object instead of using class_eval:  
  1. bar = “foo”
  2. bar.class.class_eval do
  3.  def hello
  4.    “I can smell you from here”
  5.  end
  6. end
  • bar.hello # => “I can smell you from here”
Although instance_eval and class_eval are quite similar, their scope and application are slightly different. But you can remember which one to use in each situation by keeping in mind that instance methods are made by instance_eval while class methods are made by class_eval.  

Conclusion:

After reading all this, you must be still wondering what and where is metaprogramming? Coming back to the point, metaprogramming allows you to create more flexible code, be it through beautiful APIs or easily testable code. Moreover, it also allows you to do that using powerful programming techniques and syntax. IT allows you to create DRY, highly reusable and extremely concise code.