rubyruby-1.9

How do I get the class of a BasicObject instance?


I have a script that iterates using ObjectSpace#each_object with no args. Then it prints how many instances exist for each class.

I realized that some classes redefine the #class instance method, so I had to find another way to get the actual class; Let's say it's stored in variable "klass", and klass === object is true.

In Ruby 1.8 I could do this, assuming Object wasn't monkeypatched:

Object.instance_method(:class).bind(object).call

This worked for ActiveSupport::Duration instances:

# Ruby 1.8
# (tries to trick us)
20.seconds.class
=> Fixnum
# don't try to trick us, we can tell
Object.instance_method(:class).bind(20.seconds).call
=> ActiveSupport::Duration

But, in Ruby 1.9 this no longer works:

# Ruby 1.9
# we are not smart...
Object.instance_method(:class).bind(20.seconds).call
TypeError: bind argument must be an instance of Object
  from (irb):53:in `bind'
  from (irb):53
  from /Users/user/.rvm/rubies/ruby-1.9.2-p0/bin/irb:17:in `<main>'

It turns out that ActiveSupport::Duration subclasses ActiveSupport::BasicObject. The latter is made to subclass ::BasicObject in Ruby 1.9, so Object is excluded from the inheritance chain. This doesn't, and can't, happen in Ruby 1.8, so ActiveSupport::BasicObject is a subclass of Object.

I haven't found any way to detect the actual class of a Ruby 1.9 object that isn't an instance of Object. BasicObject in 1.9 is really bare-bones:

BasicObject.instance_methods
=> [:==, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__]

Ideas?

UPDATE:

Since ruby 1.9 reached end-of-life, I'm changing my accept to @indirect's answer. The mentions of ruby 1.9 above are merely for historical purposes, to show that the change from 1.8 to 1.9 was the original cause of my problem.


Solution

  • If you can upgrade to Ruby 2.0, you don't need to implement anything at all:

    >> Kernel.instance_method(:class).bind(BasicObject.new).call
    => BasicObject