Meta World Peace (Metaprogramming!)
Welcome dear reader! Today we tackle metaprogramming in Ruby, what it is, why it's useful in your everyday programming life, and what the basic concepts, techniques and resources available to you are.
In brief, metaprogramming is writing code that will generate additional code as your program runs based on what external data is provided. Ruby provides various tools allowing you to examine and alter existing modules, classes and methods or to add brand new functionality programmatically, rather than by hand.
So, why should you learn this stuff? Well one of the primary uses of metaprogramming is to DRY out your code, as it is excellent for eliminating the repetition of highly similar, but not identical, functionality from your codebase. This process typically involves some logic that is repeated throughout your program with slight variations each time it is called. Relying on user input, a database schema (think of how ActiveRecord defines methods for you that provide access a parent model's child objets, or that allow you to search by specific properties of a model), or other external data source, you can automatically have your code define unique variations of these chunks of logic during runtime.
DRY'ing out your code is well and good, and is an important skill for any developer. But a more compelling reason for familiarizing yourself with metaprogramming is to increase your Ruby literacy. I believe this is one of the most effective ways of improving your understanding of other peoples' Ruby code, be it Rails or one of any number of popular Gems. When debugging these third-party libraries you'll find that metaprogramming is commonplace, and therefore understanding it is a major part of understanding what is actually happening in the code.
Below are some examples of essential metaprogramming techniques, with images courtesy of the wonderful RubyMonk.com. Ruby Monk has an excellent guide on this topic which I can't recommend highly enough. Code School's "Ruby Bits 2" course also has great material on the subject that I found extremely helpful.
1) Code inspection, send and method_missing:
Oftentimes, in order to be able to generate code on-the-fly you'll need to perform some checks on the existing code you're dealing with. For example, what methods does a certain object respond to, what self currently is (the scope or context), or what variables are currently defined within a given scope. This is code inspection.
These tools are very handy, and while they don't dynamically generate any code for you, they are invaluable in situations where you want to do so, because they allow your program to examine any piece of code, without knowing its contents ahead of time, and determine what course of action to take when adding or modifying functionality on-the-fly.
Fairly self-explanatory are Object#methods and Object#public_methods, which return arrays of all an object's methods or public methods, including those it inherits from parent classes. If you pass false to public_methods it will return the object's public methods, but none of its ancestors'. You can also call Object.method(:some_method_name_goes_here) to return an instance of class Method, which allows you to examine the properties of the method, for example by calling Method#parameters.
If you've used the Gem pry then you've already seen another very useful code inspection technique: Binding objects. Kernel#binding (you can simply call binding nakedly, without the reference to Kernel), creates an instance of the Binding class which captures, among other things, the scope and state of the program on whatever line you place it. You can define getter methods around bindings and place them at various points in your code to reference multiple contexts in this way.
Ruby also gives us some quick ways to access basic info about the program itself. The __FILE__ method returns the current file name, __LINE__ returns the current line of the program, and the global variable $0 returns the name of the program that was initially executed to arrive at that point in the code.
Get giddy, beloved reader, because you're about to add two of the most useful methods in Ruby to your toolkit: send and method_missing. Sticking with the theme of being able to deal with code without knowing its exact nature in advance, send is your friend, it allows you to call any method, passed in as a string or symbol, along with any arguments or block it may require.
This functionality is great for delegating methods to other objects when a particular object doesn't know how to respond or if you simply want to extend another class's functionality. But how does this work exactly? As you may know, when you call a method in Ruby a search is made through the method lookup chain (check out the image below) for that method. First the class of the receiving object is searched, followed by any modules included in that class. If the method isn't found the search is repeated in that class's parent class and its modules, with this process repeating all the way up Ruby's class hierarchy.
The Ruby method look up chain. From the allenlsy blog <3
However, Ruby also checks within each class to see if method_missing is defined. If it is, the logic in that method will be executed in lieu of the original method that was called. This is amazingly helpful as you can define custom behavior for when an unknown method is called on one of your objects. Inside of a particular class's method_missing you'll often see calls to other classes via send, effectively saying "if my class doesn't know how to deal with this particular method, send it to another class I know can handle it". This might seem to be a strange use case, but imagine it in the context of metaprogramming. You will have methods generated dynamically based on specific conditions in your codebase, and it will not always be clear exactly what methods are defined for a given class.
2) eval is evil
To get the value of some arbitrary piece of code you can pass the eval method a string and a Binding object to specify the scope (eg, is this a variable from the global scope, or the one with the same name that you've defined within a method?), and it will fire up the interpreter and parse that string as code. This means that, as with some of the code inspection techniques mentioned above, you don't need to know ahead of time what code will be passed in in order to evaluate it... pretty sweet!
But when you encounter eval in your meta-studies more than likely the only thing you'll hear is how you should never use it and how eval is evil. So why all the hate? Take heed! eval is computationally very expensive because you need to fire up the interpreter, and it's also highly insecure, as you're allowing someone to pass in potentially malicious code to be blindly executed.
So, what's a dev to do in this situation, where you need to examine a dynamically generated bit of code, but don't want to expose yourself to the pitfalls of eval? Best practice here is to pass a string to const_get (called on a class or module) or instance_variable_get (called on a specific instance) to return the actual Ruby object of whatever class, module or instance variable you're looking for.
3) Lifecycle callbacks, instance_eval and class_eval:
Check out the code example above from Ruby Monk's Metaprogramming book, it uses a pattern commonly found in Gems and one that we'll break down for you here.
Rearing its head again is the concept of context: what is the scope for a given object? The instance_eval, class_eval, and module_eval methods give you safer (although still expensive) alternatives to eval, and are great for modifying existing objects. Where with eval you pass in the context you want to evaluate via a binding, these methods set their context to the object they are called on, and then define some functionality for that context via a passed in string or block of code.
instance_eval defines methods particular to a specific instance, so class methods when called on a class, and singleton methods when called on instances of a class. class_eval can only be called on a class or module, and defines instance methods on that class or module. module_eval is simply an alias for class_eval, and obviously is more readable/semantic for defining instance methods within modules.
You can see that in this example, we're extending the ClassMethods module and including the InstanceMethods module into an object, using instance_eval and class_eval to add those modules within the correct context. instance_eval, you'll recall defines methods exclusive to a single instance, so that means class methods when dealing with a class object, and conversely, class_eval defines instance methods.
But the real clever part to this code is the use of included. included is an object life-cycle callback, or life-cycle hook. These are methods that Ruby will call automatically after certain events occur in your code. There are callbacks for when a module is included or extended, when a class is inherited from, for when a method is added or removed, and even for less common situations such as when a method that was defined becomes undefined. So, when you define include as was done in the example, you're saying run this code whenever the Gym module is included in an object, then take that object as a parameter and perform some operations on it.
Putting all of that together, the above code says that when you include the Gym module in a given object, take that object (a class presumably) and extend the ClassMethods module as a set of class methods (so, in the context of a specific instance of a class), and include the InstanceMethods module as a set of instance method (so, in the context of that instance's class). This provides a succinct way for third-party library authors to add both class methods and instance methods into a class with only one call to include.
4) define_method and instance_variable_set:
Finally, we have this scrumtrulescent beauty of a code snippet. Here, define_method is called, passing in the name of the method to be defined: initialize. In this case the method name is a hard coded value, but as mentioned above, names are often passed in from some dynamically generated data source. You can pass define_method a block, where you have access to any arguments that will be passed to the method and can define the method body.
In this Ruby Monk example we're creating a module that can be included in any class to provide a hash initializer (a common design pattern allowing you to supply arguments to initialize in any number and order). This allows us to write our initializer in only one place, as opposed to in each class, DRY'ing out our code. Let's see how they go about doing it.
First, the hash_initialize method takes a splat argument, allowing you to pass in as many arguments as needed. That array is then compared to the array keys for the hash that will be passed in to the dynamically defined initialize method (that hash is accessed as the variable h within the block passed to define_method). This is done to prevent initializing instance variables that have not been specified in the call to hash_initialize. Now if any arguments are not passed to hash_initialize, but are passed to initialize, the missing array will contain those extra arguments and an error will be thrown.
If no extra arguments are passed to initialize then each of the key-value pairs of the hash passed in to initialize will be made into instance variables for the object being initialized. This is done via instance_variable_set, which as the name implies, accepts a variable name and value as arguments. Note the use of string interpolation here to add the "@" to our instance variable name.
The end result is very terse, but allows every class to have a hash initializer, with control over what instance variables are allowed to be defined, in only two lines of code: including the module, and calling hash_initialize.
WHEW. Did you make it all the way through? Kudos to you, intrepid reader, if you did. Hopefully this has been a good high-level intro into metaprogramming, and one that has you all set to dive deeper into this powerful part of the Ruby language.
More from On and On coming ....soon!


















