Given the Ruby metaclass, how do I get the instance to which it is attached?

This is the opposite of the question " Given an instance of a Ruby object, how do I get its metaclass? "

You can see the representation of the object to which the metaclass or singleton class is attached in the default to_s output:

 s = "hello" s_meta = class << s; self; end s_meta.to_s # => "#<Class:#<String:0x15004dd>>" class C; end c_meta = class << C; self; end c_meta.to_s # => "#<Class:C>" 

Is it possible to implement the Class.attached method that returns this object (or nil if the receiver is a regular class)?

 s_meta.attached # => s c_meta.attached # => C C.attached # => nil 
+6
source share
4 answers

There is an ugly (still working) hack using ObjectSpace. For example, something you should never use other than a game and possibly debugging. You just need the first (and only) instance, so:

 ObjectSpace.each_object(self).first 

To determine if it is a singleton class, you can use the weird property that ancestors will not include its receiver if it is a singleton class (or eigenclass or magic class):

 ObjectSpace.each_object(self).first unless ancestors.include? self 

If you care about edgecases, there are three objects whose classes are also their singleton classes.

 [true, false, nil].each do |o| o.class.send(:define_method, :attached) { o } end 
+8
source

I do not know about MRI.

In JRuby, the following returns what you want:

 require 'java' class A def self.meta class << self; self; end end end A.meta.to_java.attached 
+3
source

You can define a metaclass to hold the attached object.

 class Class attr_accessor :attached end class Object def metaclass meta = class << self; self; end meta.attached = self meta end end class A; end a = A.new a_meta = a.metaclass pa #=> #<A:0xb74ed768> p a_meta #=> #<Class:#<A:0xb74ed768>> obj = a_meta.attached p obj #=> #<A:0xb74ed768> puts obj == a #=> true p A.attached #=> nil 
+1
source

You can get it from inspect (in the MRI implementation):

 class Class def attached # first, match the object reference from inspect o_ref = inspect.match /0x([0-9a-f]+)>>$/ # if not found, it not a metaclass return nil unless o_ref # calculate the object id from the object reference o_id = (o_ref[1].to_i(16) >> 1) - 0x80000000 # get the object from its id ObjectSpace._id2ref o_id end end # testing... class A; end a = A.new a_meta = class << a; self; end pa #=> #<A:0xb7507b00> p a_meta #=> #<Class:#<A:0xb7507b00>> p a_meta.attached #=> #<A:0xb7507b00> pa == a_meta.attached #=> true p A.attached #=> nil 

For the relationship between object id and inspect see this answer .

+1
source

Source: https://habr.com/ru/post/894992/


All Articles