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