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?













