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.
The following solution refers to the superclass of the eigenclass. As a consequence, it has the side effect of allocating the eigenclass (detectable by ObjectSpace.count_objects[:T_CLASS] in MRI). But since BasicObject#class is only invoked on blank slate objects (i.e. objects that are not kind-of Object, i.e. that are not Objects) the side effect also applies just for blank slate objects. For Objects, the standard Kernel#class is invoked.
class BasicObject def class (class << self; self end).superclass end end # tests: puts RUBY_VERSION # 1.9.2 class B < BasicObject; end class X; end p BasicObject.new.class # BasicObject p B .new.class # B p X .new.class # X p 6.class # Fixnum p B.instance_method(:class).owner # BasicObject p X.instance_method(:class).owner # Kernel p 6.method(:class).owner # Kernel Edit - Note: Indeed, there is an issue with ActiveSupport::Duration. This class uses interception (method_missing) for redirecting messages to the :value attribute. As a consequence, it provides false introspection for its instances. To preserve this falsity, it is necessary to use another name for the class map, e.g. the proposed __realclass__. Thus, the modified solution might look like this:
class BasicObject def __realclass__; (class << self; self end).superclass end end class Object; alias __realclass__ class end Another way of not invoking class << self on Objects is via Module#===, as suggested by Kelvin on this page.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With