Metaprogramming in Ruby

Jesse McDermott
5 min readMay 10, 2021

What is Metaprogramming?

Metaprogramming allows programmers to create code that programs other programs. Let's break this down a little more. The term ‘meta’ means to refer to itself or to the conventions of its genre. Metaprogramming can then be defined as programs that refer to programs with the use of a computer program. Hmm that’s just too confusing and too repetitive. We can do better, let's say… simply put metaprogramming is code that writes code.

What is Metaprogramming in Ruby then?

In Ruby, metaprogramming refers to code that writes code for you but dynamically (during and after runtime) as opposed to statically (before runtime). Essentially, we can break problems into sub-problems and use the solution of that problem to solve other sub-problems again. It allows us to create methods and classes in Ruby spontaneously instead of having to define them in the program itself. Metaprogramming in Ruby enables programmers to define and redefine methods and classes dynamically during runtime, enhancing the visibility of a hidden issue or bug within an application.

Ruby’s interface is one of the most metaprogramming applicable languages available and is quite suggestive compared to other languages. This is because Ruby’s creator Yukihiro Matsumoto wanted to design a more comprehensible language for programmers to enhance productivity in programming.

With reference to the code snippet below, imagine you wanted to create a string or sentence with very similar details to describe a particular subject, action, or entity. We could do this by creating multiple methods and reprinting the full string each time in each method. However, that just means we are repeating long unnecessary code. Instead, we could just settle for a single method that takes in a single less descriptive string that accounts for each argument. But that description might be incompatible or mismatched with the arguments that we are attempting to describe.

What if we could just define that main string once and insert the different arguments into that one string?

Ruby allows us to do this by using a metaprogramming technique called define_method.

In this example we have created a class called Car, that takes in a number of define _ method strings with similar (but different) strings passed into them to be invoked. As discussed before this creates unnecessary code and its use is limited. Also, this isn’t very helpful as we could just as easily do this with a regular def method.

A more useful approach in implementing define_method is by incorporating it inside of an each loop.

Mmmm much tastier code don’t you think? If we need to add in a new car_item to test, we can just simply add it to the array.

So what does this have to do with metaprogramming? Remember metaprogramming can be defined as code that writes code. In the above example, we are using the define_method to insert or write the car_item into the string for us without having to create a brand new string ourselves.

Another aspect of metaprogramming involves an interesting term called Monkey Patching.

Monkey Patching

Ruby gives you the ability to re-open any class and enhance, change or override its methods. This includes any built-in class in the Ruby language including String, Integer, Array, or Hash. This ability is referred to as Monkey Patching.

Let's first look at an example where Monkey Patching can go wrong...

Here we have re-opened the built-in Integer class and added a built-in method to the class. We have manipulated the expected output of the ‘integer?’ method. Normally we would expect the above output ‘5.integer?’ to result as true. However, we now receive the output ‘Yes’.

So what? Isn’t it obvious that the built-in output for this method has been overridden?

I have purposely made this particular example obvious for clarity. However, there could be a much more subtle example of its use. Even if it is obvious to the developer who changed the definition of the method, it may not be so clear for other developers. What if this project had dozens of files and another developer wanted to use the inbuilt ‘integer?’ method? The output would not be what they would expect and cause unnecessary bugs further down the line. This could cause even more issues if that built-in method needed to be called on multiple times.

So why Monkey Patch at all?

Convenience. Often Monkey Patching can be used to make a built-in method more usable for a developer. The built-in method could maintain its original functionality and be enhanced for more useful compatibility in a project. Monkeypatching is considered metaprogramming as it allows us to create or alter methods on the fly for us to leverage.

That being said Monkey Patching isn’t considered best practice due to its dangers. But it does provide us with a powerful example of a Ruby metaprogramming feature.

I have only referred to two examples here as I wanted to keep it light and help provide a basic understanding. Ultimately metaprogramming enables a programmer to create code that can be utilized to create and manipulate other code in the program for us. There is a lot more to uncover in metaprogramming and it can prove very useful if implemented scrupulously.

Jesse McDermott is a Full-Stack Web Developer from Auckland, New Zealand residing in Victoria, BC, Canada who is obsessed with building web applications, problem-solving, and learning various languages, frameworks, libraries, environments, and databases.

Github | Linkedin

--

--