Meta-programming is often kind of mean. It has a tendency to be hard to understand and it can be quite destructive - causing methods to be modified or overwritten in non-obvious ways. I decided to write a post about a more courteous form of meta-programming. I picked up the technique from a blog post about method_missing and respond_to?. I realized that although I am comfortable with meta-programming, other developers might not feel the same way. So, here’s a simple pattern that can make your meta-programming more readable and less destructive.
I want to create a module named
Greetings so that when I extend my classes
with it I can declaratively add greetings to them.
There are a couple of ways to accomplish this with Ruby. But before we jump into code, let’s add these constraints to the system. After all, we are trying to write courteous code.
greetmethod should support the use of
superto delegate to parent definitions.
For instance, consider the following use of the
Greetings module. It supports
the use of
super to delegate to previous implementations of
greet and it
allows the base class to define it’s own
Let’s look at the most common meta-programming approaches to solving this problem.
class_evalmight be acceptable, but these options are generally destructive. They overwrite methods on the base class and this breaks our non-destructive requirement. If we were able to solve this problem by using alias method chains then we would definitely be entering the territory of un-readable code. Also method chains would not allow us to use
superto call previous definitions of
super, but it also greatly restricts our flexibility. We can no longer define an alternate
greetmethod on the base class.
Since these two approaches don’t fit our courteous meta-programming requirements, here’s the cool alternative that does.
By using the anonymous module
m, and including it we are actually creating a
storage object in the class hierarcy for the method, instead of writing it on
the original class. This approach is non-destructive and allows us to maintain
a clean method chain that supports the use of
super rescue nil
line in our
TriLingualWithEnglish class now calls into the anonymous module
and allows both greeting implementations to co-exist. The code is also very
readable since the programmer does not need to understand
instance_eval. I would advocate this approach whenever possible since it
preserves the class hierarchy and avoids some of the less readable