Agile 2007 - Metaprogramming in Ruby

This morning's I went to a session called 'Ruby's Secret Sauce:  Metaprogramming'. It was interactive and so I had my first opportunity to try out some of the neat things you can do in Ruby. Here are a few things that stuck out for me:

  • method_missing is pretty cool. You get a handler that's called whenever you try to send an object a message it doesn't understand. Inside the handler, you can then define what should happen, or you can even dynamically add the missing method to the class itself.
  • Method aliasing allows you to do AOP-type stuff without a framework. For instance, if you've got class A with method DoThis(), you can alias DoThis() to DoThis2(). You then write a new definition of DoThis() in which you could wrap the call to DoThis2() with logging statements.
  • The 'eigenclass' is an interesting concept. Not only can you modify the class itself, but you can scope your changes so that you're only modifying a specific instance of the class. This is good if you have multiple libraries that all want to change 'object' or 'string' or some commonly used class. If they only modify the instances they need, then it's possible for all the libraries to play nicely together.

We also got to see the ActiveRecord class from Rails which does a ton of very cool things for you behind the scenes. Instead of having to (a) write SQL to create a table, (b) create a class to wrap the database, and (c) write find methods for various critiera, you can just derive your class, say Person, from ActiveRecord, and immediately get CRUD and find functionality for free. The sample class (taken directly from the presentation) looks like this:

class Person << ActiveRecord::Base  has_one :address  validates_length_of :first_name,    :last_name,    :on => :create, :minimum => 1  validates_length_of :ssn,    :on => :create,    :minimum => 9, :maximum => 9  validates_presence_of :first_name,    :last_name, :ssn, :on => :create  validates_uniqueness_of :ssnend

and you can then write code like this and it just works:

a_person = Person.find_by_ssn “123456789”smiths = Person.find_by_last_name “Smith”john_smiths = Person.find_by_first_name_and_last_name(“John”, “Smith”)

Obviously there's a lot going on behind the scenes there, and I'm not saying you want to do this for everything, but it became obvious quite quickly why Ruby is a prime candidate for writing DSLs.

Very enjoyable talk and some good hands-on experience as well.