From: Ruby Quiz Date: 2004-11-26T01:02:47+09:00 Subject: [SUMMARY] Object Browser (#8) Brian Schroder said: I always wanted to learn more about the reflection capabilites of ruby, and indeed there is quite a lot to learn. This quiz was not too complicated, but the design of a good gui takes a lot of time. (Especially if you're not accustomed to the toolkit). I agree and I think that about summarizes this quiz. The main point of the quiz is using "reflection". What is reflection? To quote the Pragmatic Programmers: ...to examine aspects of the program from within the program itself. Obviously, to write an Object Browser, we would need to be able to learn things about objects we didn't design and create. This technique has a lot of practical applications though and odds are even a new Ruby programmer has seen it in action. Used tab completion in irb? Built test cases with Test::Unit? Both are great examples of tools that figure out your intentions by looking at Ruby code. That's reflection. So, how do we do all that? I don't want to recreate a bunch of documentation here, but here are some methods to look up if you're new to Ruby's reflection capabilities: ObjectSpace.each_object Object.class Object.methods Object.instance_variables # and many more in Object Module.ancestors Module.constants Module.class_variables # and many more in Module Putting such methods to use is pretty trivial. Here's Brian's (original) code for building an object tree: # Class Tree class ClassTreeNode attr_accessor :klass, :subclasses, :objects def initialize(klass) @klass = klass @subclasses = {} @objects = [] end def add_class(klass) @subclasses[klass] ||= ClassTreeNode.new(klass) end def add_object(object) @objects << object self end end # Creates or updates a klass_tree. # When updating no classes or objects are removed def object_browser(classtree = ClassTreeNode.new(Kernel)) ObjectSpace.each_object do | x | classnode = classtree x.class.ancestors.reverse[1..-1] \ .inject(classtree){ | classnode, klass | classnode.add_class(klass) }.add_object(x) end classtree end object_browser() just assembles a "Class Tree" by walking ObjectSpace.each_object() and using ancestors() to find parent Classes/Modules. (Note the clever use of inject(). Isn't that iterator cool?!) The rest of Brian's solution (not shown) hands that Class Tree off to GUI routines, to build suitable displays. These routines use more reflection methods to fetch method and variable lists. Both solutions use gtk2 to manage their GUIs. That involves no small amount of code and is a little beyond the scope of this summary, so I'll simply refer you to the solutions if you want to dig deeper. I owe a big thanks to Jamis and Brian for pushing the quiz along this weekend while I was horribly busy. All teasers for the next quiz have been censored out of this summary.