Last Wednesday (08.27.2008), I attended a presentation given by local ruby "hero" Joe O'Brien. He was presenting techniques for meta-programming in Ruby. Meta-programming is a general topic which applies to many languages, however Ruby provides some very basic methods which result in powerful meta-programming tools for creating dynamic run-time functionality and for enhancing the base language.
Joe started the presentation with a run-down of some of the meta-programming methods exposed by the base object in Ruby.
- send--By using the send method with a string value (method name) and a list of parameters you can dynamically choose a method to call. This method is provided in conjunction with the message-passing back-bone used by the Ruby interpreter.
- method_missing--Implementations of the method_missing message handler can be used to handle messages which are currently not understood by the receiving object. This is an expensive method to use, but can be used to add new functionality in response to an unknown message. ActiveRecord in Rails uses this method to create finder methods when a search is performed.
- eval--Evaluate a string value as arbitrary Ruby code. User beware Since this method takes a string argument as the code to be evaluated, any code in that string is not checked by the interpreter until the eval statement is executed by the interpreter.
- define_method--Pass a string value (method name) and a block in order to define a new method defined by the given block. This method takes a block as a parameter and is safer to use than the eval method.
- methods--Returns an array of the methods currently defined on an object. By calling methods on an object and subtracting the result of calling methods on that object's supertype, you can easily get a list of the methods defined only within that object.
- attr_reader--The attr_reader method is used to make a private field in an object accessible to other objects.
- attr_writer--The attr_writer method is used to make a private field in an object settable by other objects.
- attr_accessor--The attr_accessor method is used to make a private field in an object accessible and settable by other objects.
Example: Declarative Tests and Home-Grown Behavior Driven Development
Joe, being a "test-first bigot" (his words, not mine), was uncomfortable with the idea that it took him nearly 10 lines of code to test one ActiveRecord has_many association. He also expressed his dislike for creating test methods whose names look like test_a_description_of_the_test. His iterative approach to creating a declarative version of this this test method started with the original 10 lines of code in his test method. He extracted that method into a method on the Test::Unit::TestCase class, test_that, which took three arguments, the model under test, the expected has_many association class, and a description of the test as a string. These parameters were used in conjunction with the define_method method to create a new method at run-time which executed the original code. Using this technique, Joe stated that he was able to reduce the tests for many of the ActiveRecord capabilites to one-liners.