You often hear that metaprogramming is something that only Ruby ninjas use, and that it simply isn’t for common mortals. But the truth is that metaprogramming isn’t something scary at all. This blog post will serve to challenge this type of thinking and to get metaprogramming closer to the average Ruby developer so that they can also reap its benefits.

Ruby Metaprogramming: Code Writing Code

Ruby Metaprogramming: Code Writing Code

It should be noted that metaprogramming could mean a lot and it can often be very misused and go to the extreme when it comes to usage so I will try to throw in some real world examples that everyone could use in everyday programming.

Metaprogramming

Metaprogramming is a technique by which you can write code that writes code by itself dynamically at runtime. This means you can define methods and classes during runtime. Crazy, right? In a nutshell, using metaprogramming you can reopen and modify classes, catch methods that don’t exist and create them on the fly, create code that is DRY by avoiding repetitions, and more.

The Basics

Before we dive into serious metaprogramming we must explore the basics. And the best way to do that is by example. Let’s start with one and understand Ruby metaprogramming step-by-step. You can probably guess what this code is doing:

class Developer

  def self.backend
    "I am backend developer"
  end
  
  def frontend
    "I am frontend developer"
  end

end

We have defined a class with two methods. The first method in this class is a class method and the second one is an instance method. This is basic stuff in Ruby, but there is much more happening behind this code which we need to understand before we proceed further. It is worth pointing out that the class Developer itself is actually an object. In Ruby everything is an object, including classes. Since Developer is an instance, it is an instance of class Class. Here is how the Ruby object model looks like:

Ruby object model

p Developer.class # Class
p Class.superclass # Module
p Module.superclass # Object
p Object.superclass # BasicObject

One important thing to understand here is the meaning of self. The frontend method is a regular method that is available on instances of class Developer, but why is backend method a class method? Every piece of code executed in Ruby is executed against a particular self. When the Ruby interpreter executes any code it always keeps track of the value self for any given line. self is always referring to some object but that object can change based on the code executed. For example, inside a class definition, the self refers to the class itself which is an instance of class Class.

class Developer
  p self 
end
# Developer

Inside instance methods, self refers to an instance of the class.

class Developer
  def frontend
    self
  end
end
 
p Developer.new.frontend
# #<Developer:0x2c8a148>

Inside class methods, self refers to the class itself in a way (which will be discussed in more detail later in this article):

class Developer
  def self.backend
    self
  end
end

p Developer.backend
# Developer

This is fine, but what is a class method after all? Before answering that question we need to mention the existence of something called metaclass, also known as singleton class and eigenclass. Class method frontend that we defined earlier is nothing but an instance method defined in the metaclass for the object Developer! A metaclass is essentially a class that Ruby creates and inserts into the inheritance hierarchy to hold class methods, thus not interfering with instances that are created from the class.

Metaclasses

Every object in Ruby has its own metaclass. It is somehow invisible to a developer, but it is there and you can use it very easily. Since our class Developer is essentially an object, it has its own metaclass. As an example let’s create an object of a class String and manipulate its metaclass:

example = "I'm a string object"

def example.something
  self.upcase
end

p example.something
# I'M A STRING OBJECT

What we did here is we added a singleton method something to an object. The difference between class methods and singleton methods is that class methods are available to all instances of a class object while singleton methods are available only to that single instance. Class methods are widely used while singleton methods not so much, but both types of methods are added to a metaclass of that object.

The previous example could be re-written like this:

example = "I'm a string object"

class << example
  def example.something
    self.upcase
  end
end

The syntax is different but it effectively does the same thing. Now let’s go back to the previous example where we created Developer class and explore some other syntaxes to define a class method:

class Developer
  def self.backend
    "I am backend developer"
  end
end

This is a basic definition that almost everybody uses.

def Developer.backend
  "I am backend developer"
end

This is the same thing, we are defining the backend class method for Developer. We didn’t use self but defining a method like this effectively makes it a class method.

class Developer
  class << self
    def backend
      "I am backend developer"
    end
  end
end

Again, we are defining a class method, but using syntax similar to one we used to define a singleton method for a String object. You may notice that we used self here which refers to a Developer object itself. First we opened Developer class, making self equal to the Developer class. Next, we do class << self, making self equal to Developer’s metaclass. Then we define a method backend on Developer’s metaclass.

class << Developer
  def backend
    "I am backend developer"
  end
end

By defining a block like this, we are setting self to Developer’s metaclass for the duration of the block. As a result, the backend method is added to Developer’s metaclass, rather than the class itself.

Let’s see how this metaclass behaves in the inheritance tree:

Metaclass in inheritence tree

As you saw in previous examples, there’s no real proof that metaclass even exists. But we can use a little hack that can show us the existence of this invisible class:

class Object
  def metaclass_example
    class << self
      self
    end
  end
end

If we define an instance method in Object class (yes, we can reopen any class anytime, that’s yet another beauty of metaprogramming), we will have a self referring to the Object object inside it. We can then use class << self syntax to change the current self to point to the metaclass of the current object. Since the current object is Object class itself this would be the instance’s metaclass. The method returns self which is at this point a metaclass itself. So by calling this instance method on any object we can get a metaclass of that object. Let’s define our Developer class again and start exploring a little:

class Developer

  def frontend
    p "inside instance method, self is: " + self.to_s
  end

  class << self
    def backend
      p "inside class method, self is: " + self.to_s
    end
  end
  
end

developer = Developer.new
developer.frontend
# "inside instance method, self is: #<Developer:0x2ced3b8>"

Developer.backend
# "inside class method, self is: Developer"

p "inside metaclass, self is: " + developer.metaclass_example.to_s
# "inside metaclass, self is: #<Class:#<Developer:0x2ced3b8>>"

And for the crescendo, let’s see the proof that frontend is an instance method of a class and backend is an instance method of a metaclass:

p developer.class.instance_methods false
# [:frontend]

p developer.class.metaclass_example.instance_methods false
# [:backend]

Although, to get the metaclass you don’t need to actually reopen Object and add this hack. You can use singleton_class that Ruby provides. It is the same as metaclass_example we added but with this hack you can actually see how Ruby works under the hood:

p developer.class.singleton_class.instance_methods false
# [:backend]

Defining Methods Using “class_eval” and “instance_eval”

There’s one more way to create a class method, and that is by using instance_eval:

class Developer
end

Developer.instance_eval do
  p "instance_eval - self is: " + self.to_s
  def backend
    p "inside a method self is: " + self.to_s
  end
end
# "instance_eval - self is: Developer"

Developer.backend
# "inside a method self is: Developer"

This piece of code Ruby interpreter evaluates in the context of an instance, which is in this case a Developer object. And when you are defining a method on an object you are creating either a class method or a singleton method. In this case it is a class method - to be exact, class methods are singleton methods but singleton methods of a class, while the others are singleton methods of an object.

On the other hand, class_eval evaluates the code in the context of a class instead of an instance. It practically reopens the class. Here is how class_eval can be used to create an instance method:

Developer.class_eval do
  p "class_eval - self is: " + self.to_s
  def frontend
    p "inside a method self is: " + self.to_s
  end
end
# "class_eval - self is: Developer"

p developer = Developer.new
# #<Developer:0x2c5d640>

developer.frontend
# "inside a method self is: #<Developer:0x2c5d640>"

To summarize, when you call class_eval method, you change self to refer to the original class and when you call instance_eval, self changes to refer to original class’ metaclass.

Defining Missing Methods on the Fly

One more piece of metaprogramming puzzle is method_missing. When you call a method on an object, Ruby first goes into the class and browses its instance methods. If it doesn’t find the method there, it continues search up the ancestors chain. If Ruby still doesn’t find the method, it calls another method named method_missing which is an instance method of Kernel that every object inherits. Since we are sure that Ruby is going to call this method eventually for missing methods, we can use this to implement some tricks.

define_method is a method defined in Module class which you can use to create methods dynamically. To use define_method, you call it with the name of the new method and a block where the parameters of the block become the parameters of the new method. What’s the difference between using def to create a method and define_method? There’s not much difference except you can use define_method in combination with method_missing to write DRY code. To be exact, you can use define_method instead of def to manipulate scopes when defining a class, but that’s a whole other story. Let’s take a look at a simple example:

class Developer
  define_method :frontend do |*my_arg|
    my_arg.inject(1, :*)
  end

  class << self
    def create_backend
      singleton_class.send(:define_method, "backend") do
        "Born from the ashes!"
      end
    end
  end
end

developer = Developer.new
p developer.frontend(2, 5, 10)
# => 100

p Developer.backend
# undefined method 'backend' for Developer:Class (NoMethodError)

Developer.create_backend
p Developer.backend
# "Born from the ashes!"

This shows how define_method was used to create an instance method without using a def. However, there’s much more we can do with them. Let’s take a look at this code snippet:

class Developer

  def coding_frontend
    p "writing frontend"
  end

  def coding_backend
    p "writing backend"
  end

end

developer = Developer.new

developer.coding_frontend
# "writing frontend"

developer.coding_backend
# "writing backend"

This code isn’t DRY, but using define_method we can make it DRY:

class Developer

  ["frontend", "backend"].each do |method|
    define_method "coding_#{method}" do
      p "writing " + method.to_s
    end
  end

end

developer = Developer.new

developer.coding_frontend
# "writing frontend"

developer.coding_backend
# "writing backend"

That’s much better, but still not perfect. Why? If we want to add a new method coding_debug for example, we need to put this "debug" into the array. But using method_missing we can fix this:

class Developer

  def method_missing method, *args, &block
    return super method, *args, &block unless method.to_s =~ /^coding_\w+/
    self.class.send(:define_method, method) do
      p "writing " + method.to_s.gsub(/^coding_/, '').to_s
    end
    self.send method, *args, &block
  end

end

developer = Developer.new

developer.coding_frontend
developer.coding_backend
developer.coding_debug

This piece of code is a little complicated so let’s break it down. Calling a method that doesn’t exist will fire up method_missing. Here, we want to create a new method only when the method name starts with "coding_". Otherwise we just call super to do the work of reporting a method that is actually missing. And we are simply using define_method to create that new method. That’s it! With this piece of code we can create literally thousands of new methods starting with "coding_", and that fact is what makes our code DRY. Since define_method happens to be private to Module, we need to use send to invoke it.

Wrapping up

This is just the tip of the iceberg. To become a Ruby Jedi, this is the starting point. After you master these building blocks of metaprogramming and truly understand its essence, you can proceed to something more complex, for example create your own Domain-specific Language (DSL). DSL is a topic in itself but these basic concepts are a prerequisite to understanding advanced topics. Some of the most used gems in Rails were built in this way and you probably used its DSL without even knowing it, such as RSpec and ActiveRecord.

Hopefully this article can get you one step closer to understanding metaprogramming and maybe even building your own DSL, which you can use to code more efficiently.

About the author

Nikola Todorovic, Serbia
member since May 7, 2015
Nikola has more than 6 years of experience in software development. He graduated from the best computer science faculty in Serbia, and since then, he's been working on a variety of technologies and companies on projects for the domestic and US markets. He even started his own startups and learned a lot about what is needed for a business to succeed. Currently, his passion is Ruby on Rails and startups. [click to continue...]
Hiring? Meet the Top 10 Freelance Ruby Developers for Hire in September 2016

Comments

Petr Mlčoch
Nice article. Little more light to my dark corners of Ruby. Is not hard to debug such dynamically created methods?
Nikola Todorovic
Thanks :) I'll try to continue and write something more about this topic in the future. As for debugging - it's not harder than usual debugging :)
silverhammermba
Ruby has had the Object#singleton_class method for over 5 years. If I see that class << self; self; end; hack one more time I'm going to scream.
Fred@Bootstrap
Great article, thorough explanation of a complex subject. Nice summary of class_eval vs instance_eval and the role of self. Well-written!
Yaniv Preiss
It's worth mentioning that when using the `missing_method` technique, one would usually change the `respond_to?` as well.
dhalai
Thx for the article. There is 1 mistake: class << example def example.something self.upcase end end In this case you shouldn't use `example` pointer inside metaclass.
cao
you are wonderful
cao
i like you website
ipowazi
I was just about to make the same comment, the code should look like this: <pre><code class="ruby"> class << example def something self.upcase end end </code></pre>
ipowazi
Thanks for the great article :) I don't think this portion is accurate: "The difference between class methods and singleton methods is that class methods are available to all instances of a class object while singleton methods are available only to that single instance. Class methods are widely used while singleton methods not so much, but both types of methods are added to a metaclass of that object." I don't like using the term "class methods" with Ruby, because class methods don't exist in Ruby. Singleton methods in Ruby can behave like class methods, but they're singleton methods are just regular instance methods that are defined in the singleton class. Here is an excerpt from the book Metaprogramming Ruby: That’s really what class methods are: they’re Singleton Methods of a class. In fact, if you compare the definition of a Singleton Method and the definition of a class method, you’ll see that they’re the same: <pre><code class="ruby"> def obj.a_singleton_method; end def MyClass.another_class_method; end </code></pre> Your Developer class demonstrates this well: <pre><code class="ruby"> Developer.singleton_class.instance_methods(false) # => [:backend] Developer.singleton_methods # => [:backend] </code></pre> Thanks again for the great article!
KOTP
Missing information on the method_missing hook and how respond_to_missing? should be utilized. Good article overall!
Bikram Suwal
Worth reading.
comments powered by Disqus
Subscribe
The #1 Blog for Engineers
Get the latest content first.
No spam. Just great engineering and design posts.
The #1 Blog for Engineers
Get the latest content first.
Thank you for subscribing!
You can edit your subscription preferences here.
Trending articles
Relevant technologies
About the author
Nikola Todorovic
Oracle PL/SQL Developer
Nikola has more than 6 years of experience in software development. He graduated from the best computer science faculty in Serbia, and since then, he's been working on a variety of technologies and companies on projects for the domestic and US markets. He even started his own startups and learned a lot about what is needed for a business to succeed. Currently, his passion is Ruby on Rails and startups.