[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.

Thanks for the quiz summary. I always wonder where you take the time from.

I wanted to comment on how to write my name. If you don't have, or don't want to use the german umlauts "ö, ä, ü Ö, Ä, Ü" they can be written as "oe, ae, ue, Oe, Ae, Ue". In fact the points are shortcuts for a following e that traversed up above the vocals in the history of german writing.

So my name would be spelled: Brian Schroeder.

cheers,

Brian

···

On Fri, 26 Nov 2004 01:02:47 +0900 Ruby Quiz <james@grayproductions.net> wrote:

Brian Schroder said:

--
Brian Schröder
http://ruby.brian-schroeder.de/