Ramblings

22 Aug, 2008

Ruby’s ObjectSpace: Subclasses

Posted by: Matt Williams In: ruby

One of the (imho) lesser used pieces of the Ruby language is ObjectSpace.  In this article I’ll show one of the things you can do with it — get all the subclasses of a class.

ObjectSpace is almost a red-headed stepchild.  In JRuby you can even turn it off.  However, it does have its uses.

ObjectSpace has the following methods (apart from those in Object):

1
2
>> ObjectSpace.my_methods
=> ["_id2ref", "add_finalizer", "call_finalizer", "define_finalizer", "each_object", "finalizers", "garbage_collect", "remove_finalizer", "undefine_finalizer"]

ObjectSpace does allow you to take an object’s id and change it to a reference:

1
2
3
4
5
6
7
8
9
10
>> ObjectSpace._id2ref(ObjectSpace.object_id)
=> ObjectSpace
>> String.object_id
=> -605676558
>> ObjectSpace._id2ref(String.object_id)
=> String
>> "a".object_id
=> -607204778
>> ObjectSpace._id2ref(-607204778)
=> "a"

The each_object method allows one to step through all the objects in memory, performing an operation with each. This is how we’re going to get a list of subclasses:

module Subclasses
  # return a list of the subclasses of a class
  def subclasses(direct = false)
    classes = []
    if direct
      ObjectSpace.each_object(Class) do |c|
        next unless c.superclass == self
        classes << c
      end
    else
      ObjectSpace.each_object(Class) do |c|
        next unless c.ancestors.include?(self) and (c != self)
        classes << c
      end
    end
    classes
  end
end
 
Object.send(:include, Subclasses)

The code steps through the objects in ObjectSpace and returns an array of either the direct descendents, that is, ones directly subclassed from our object, or the entire tree of children.  By default it returns all the children (and their children (and …)).

So we can do the following:

>> Enumerable.subclasses
=> [Struct::Tms, Dir, File, IO, Range, Struct, Hash, Array, String, StringIO, Socket, UNIXServer, UNIXSocket, UDPSocket, TCPServer, TCPSocket, IPSocket, BasicSocket, Struct::Group, Struct::Passwd, Zlib::GzipReader, Gem::SourceIndex]

So why would we want to do this? Well, for one, it’s a good learning tool. For another, imagine a case where we don’t know all of the classes which exist in an application, however, we do know that a good number of them are subclasses of the mysterious class X. We could pass a message to all of them — telling them it’s time for dinner, perhaps, by asking X about his children. Then we message the children, letting them know it’s dinner time:

>> X.subclasses.each {|klass| klass.send(:dinner_time)}

Ok, so that last bit was contrived and I’m tuning into _why (not quite there, it’s still a bit static around the edges).

Anyone else have any uses for ObjectSpace?

5 Responses to "Ruby’s ObjectSpace: Subclasses"

1 | Pit Capitain

August 22nd, 2008 at 5:14 pm

Avatar

Hi Matt, if you search the ruby-talk archives you’ll find other, more efficient implementations for the direct == false case.

4 | Tomte

February 21st, 2009 at 4:18 am

Avatar

Hi there,

I found your code very helpful for my work. I’m working on a modular tool and I want to create one instance of all classes that are subclasses of another class. Since I don’t know of the existence of all those classes (I would need a configuration file or something like that, where each single one is specified), I searched exactly for the solution you provided here :). It works very well!!!

Thanks for the good work :)

5 | Matt Williams

February 21st, 2009 at 3:09 pm

Avatar

I’m glad I was able to help!

Comment Form

Categories

DrakNet Web Hosting

Flickr PhotoStream

    layout_newm3headerTerrain Testa

About

Matt Williams is a geekly jack of all trades residing in Columbus, OH, USA.

Recommend Me


Adopt a Dragon from Dragcave
Adopt one today!Adopt one today!Adopt one today!Adopt one today! Adopt one today! Adopt one today!Adopt one today! Adopt one today! Adopt one today! Adopt one today!