Tuesday, February 21, 2006
The Way of Meta - Part III
The Way of Meta
Writing Method Generators
I am going to write a simple example that illustrates how you can create a method generator: a construct used within a class definition as a shortcut to generate richer method semantics, in the same way that attr_accessor generates enrich class behaviour by adding new methods.
This example concerns the development of a 'synonym' generator. A synonym is similar to an alias because it provides different calls to achieve the same result. It is different from an alias in that an alias generates a copy of a method, rather than a different name to call the same method.
If an aliased method gets overridden, its aliases will keep calling the old code. Conversely, a synonym of a method is a simple light code layer that keeps calling a certain method name. If the aliased method gets overwritten, then all its synonyms will start calling the new version of the method.
Let's start from our intent; let's visualise how we would like to use synonyms.
Here we are declaring that both the method 'say' and the method 'walk' have several synonyms.
We also expect the following statements to be true
They should become false when we override the old methods:
Enough talking, we have all the intent we need for a first stab at the problem. Let's dive straight into the code.
First of all we need a convenient way to process the arguments passed to the 'synonym' generator.
Consider how we will need to elaborate the following hash:
This is just a convenient idiom that we use to express the following relationships:
Let's write some code that can take us from the first compact format to the second explicit representation. We will call the relationships 'associations' and we will provide a method that allows us to iterate over a hash one association at a time.
Now that we have a way to visit our associations, we will generate a new synonym method for each association:
For each association specified in the synonym generator we inject a new method in our class. The new method has the name specified on the right hand side of the association, and all it does is to invoke the original method of the class.
The synonym generator has been defined within the Module class, so that it becomes automatically available within the context of the classes 'Module' and 'Class'.
We can now use the generator even to expand existing classes with new developer friendly names.
Here we tweak the Array class:
And here we provide a way to talk to a Person, rather than giving him orders!
It is worth pointing out a small syntactic trick that we used to handle both cases of single ( :calculate => :how_much ?) and multiple synonyms (:size => [ :count , :n_elements ]).
We get the right hand side of the hash element and we apply 'to_a' to it, turning it into an array.
If the element is not an array, it gets turned into one. If it is already an array, it is left unchanged. Whatever the case, we are left with a simple array to operate on in the end.
Sunday, February 19, 2006
The Way of Meta - Part II
The Way of Meta
Exploring Ruby Metaprogramming Capabilities
Getting our Feet Wet
Tired of all this talk? Then it is time to dip our feet in the deep waters of rubesque metaprogramming. We are going to start our journey exploring the metaprogramming features that are illustrated in the pragmatic programmers' PickAxe book, while at the same time we will go through the less known libraries present in the ruby distribution. At the same time we will ask for some help to the wonderfully brief and clear 'Seeing Metaclasses Clearly' by Why the Lucky Stiff.
Out-of-the-Box Metaprogramming Mechanisms
Ruby provides several straight out of the box metaprogramming mechanisms with its core standard libraries. Although using these mechanisms can take you very quickly to unmanageable levels of complexity it is worth looking at them to have a feeling of what is possible. Later on we will encapsulate these mechanisms within our own metaprogramming protocol.
In many languages, the mechanism that allows your source code to refer to a library or to another piece of code is handled by the language in an opaque way. Not so in Ruby.
When using Ruby you are in charge of the code loading.
The 'load' command loads some ruby code, right where you are calling it.
The 'require' command adds a little check that prevents the loading of the same file twice from different points in the code.
Load and require are not special commands for the compiler, they are dynamically executed and can be embedded within blocks, control statements, etc. The name of the file that they load can also be constructed dynamically.
The load construct in particular allows you to reload code definitions any number of times if they get changed inside the file. It's now easy to see how your code could generate code files, maybe using a templating mechanism, and then load them back in the runtime.
There are two environment variables that can turn out to be helpful when dealing with dynamic source code loading.
$LOAD_PATH is an array of directories that are searched for source code by 'load' and 'require'.
$LOADED_FEATURES is an array containing the filenames of the files that have already been loaded.