In Ruby, getting the eigenclass of a class Foo
is a simple as
eigenclass = class << Foo; self; end
#=> #<Class:Foo>
eigenclass = Foo.singleton_class #2.1.0
#=> #<Class:Foo>
I'm interested in the inverse operation: getting the owner of the eigenclass from the eigenclass itself:
klass = eigenclass.owner
#=> Foo
I'm not sure if this is possible, given that the eigenclass is an anonymous subclass of Class
, so Foo
appears nowhere in its inheritance hierarchy. Inspecting the method list of the eigenclass isn't encouraging either. eigenclass.name
returns nil
. The only thing that gives me hope this is possible:
Class.new # normal anon class
#=> #<Class:0x007fbdc499a050>
Foo.singleton_class
#=> #<Class:Foo>
Clearly, the eigenclass's to_s
method knows something about the owner, even if this information is hardcoded when the eigenclass is instantiated. Therefore the only method I'm aware of is some hacky Object.const_getting
from that like
Object.const_get eigenclass.to_s[/^#\<Class\:(?<owner>.+)\>$/, :owner]
#=> Foo
Use ObjectSpace.each_object
passing it the singleton class to find all classes that match the given singleton class:
Klass = Class.new
ObjectSpace.each_object(Klass.singleton_class).to_a #=> [Klass]
However, since a class’s singleton class inherits from its superclass’s singleton class, you’ll get multiple results if the class you’re trying to find has subclasses:
A = Class.new
B = Class.new(A)
B.singleton_class.ancestors.include?(A.singleton_class) #=> true
candidates = ObjectSpace.each_object(A.singleton_class)
candidates.to_a #=> [A, B]
Fortunately, classes/modules are sortable by their place in the inheritance tree (same order ancestors
gives). Since we know all the results must be part of the same inheritance tree, we can take the max
to get the correct class:
candidates.sort.last #=> A
ObjectSpace.each_object(B.singleton_class).max #=> B
Refining @BroiSatse's answer in a ruby-implementation-agnostic way,
class A; end
class B < A; end
class C < A; end
eigenclass = A.singleton_class
ObjectSpace.each_object(eigenclass).find do |klass|
klass.singleton_class == eigenclass
end
#=> A
This is also reliable when handling branches in subclass trees, the only reason why @Andrew Marshall's elegant answer doesn't work.
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