Matt posted a really elegant piece of code today that generates Graphviz files (suitable for importing into OmniGraffle) of your Rails ActiveRecord relationships. It’s pretty neat, and certainly handy for getting to know foreign codebases.
There’s one neat trick in there, though, that I wanted to expand on, as Matt breifly chatted to me about the problem earlier today over IM – namely, how you get the actual class object so that you can call reflect_on_all_associations
on it.
In Ruby, it’s easy to dynamically call methods – you can put the name of the method into a string, and then simply run Object.send(methodname)
. Getting the actual Class Object for a particular object – that’s much trickier.
There’s the obvious solution of using eval
. So, to get all the methods on your classname:
classname = 'Integer' eval classname + '.methods'
but that, of course, is pretty nasty and kludgy. This is Ruby, after all; there’s got to be a better solution, right?
There is. If you look in the Pickaxe, you’ll find that Class Names Are Constants:
All the built-in classes, along with the classes you define, have a corresponding global constant with the same name as the class.
So this means that by passing the class name to the const_get
method on the Kernel
module, we’ll confirm if a class exists with that name (eg Integer
). Then, because that constant is really a reference to an object of the same name, by sending a message (the method calal) to the constant, it will be passed on to the object and run (which is the best way I’ve got of explaining this). Job done! To use the previous example:
classname = 'Integer' Kernel.const_get(classname).methods
Which is, you must admit, a bit more elegant and maintainable than the evil that is eval
.