Although we successfully modified the Range class by adding a new method to use instead of each, we could have modified each itself, and I will show that later. But first let’s define metaprogramming.
Metaprogramming
Here is an extract from the wikipedia definition of metaprogramming:
The language in which the metaprogram is written is called the metalanguage. The language of the programs that are manipulated is called the object-language. The ability of a programming language to be its own metalanguage is called reflexivity.
A simple example of a metaprogram is this bash script, which is an example of generative programming:
Classes and Objects
Back to our example, where we added the method myTest to the class Range. We could instead have added the method to only a single instance of Range:
Take a look at ‘Programming Ruby’ for more examples.
Mixins
Ruby does not support C++ style multiple inheritance for the same reasons Java does not. It is seen as too easy for the programmer to hang themselves. However Ruby supports a concept that lies somewhere between java’s interfaces and C++’s multiple inheritance. Here is an example from Bruce Tates ‘Beyond Java’:
We have created a module called ‘Greetable’ that provides an implemented method ‘greet’. If Greetable is ‘mixed-in’ to a class that has a defined attribute @name, then that class will have a working greet method. If the class does not have @name, it is created dynamically by greet, refers to nil, which in turn displays as an empty string in the greet method. As a Java programmer you can almost hear the ‘NullPointerException’ that would happen in Java could get anywhere close to this capability.
Let’s try a mixin that modifies a pre-exiting method. Ruby defines ‘inspect’ on every Object, and it returns a description of the Object. Let’s redefine it:
Wow. In the same bit of running code we called the normal inspect, redefined it and called it again. Dynamic languages rule!
Interceptors
Another example by Bruce Tate, available both online and in ‘Beyond Java’, demonstrates the possibly dangerous power you have when redefining methods, in this case to emulate the java concept of ‘method interception’:
Strangely enough this example did not work in the normal ruby interpreter, only in irb, the interactive interpreter, and I never did discover why.
Full circle
I promised I’d show how the original example can be changed to rather redefine the Range.each method. Well, here it is:
We use the Ruby method ‘alias_method’ to rename the each method to original_each, and then we redefine each to call original_each, but with some small changes. Obviously this example does not do much, but the possibilities are limitless.
This 'snippet' was a little longer than planned, and took nearly 30 minutes to present, including questions. So next week I'll present only a single example of Threads in Ruby, and keep it short :-)