Search code examples
rubysingletonconstants

Constants in singleton classes


I have this code:

class A
  def print
    puts CONSTANT
  end
end

module B
  CONSTANT = "Don't Panic!"
end

Suppose a is an instance of class A.

So I need a definition of CONSTANT that should be able to be found to make a.print available.

I considered to include the module B into a's singleton class like:

a.singleton_class.send :include, B

a.print   # error: CONSTANT isn't found

I assumed it should be okay now to call the method but actually not.

The constant should be successfully imported as the following code works as expectation:

a.singleton_class.constants  # => [.., :CONSTANT, ..]

However, by including the constant into the class instead of singleton class, it works:

a.class.send :include, B

a.print   # prints: Don't Panic!

I thought it is strange that I can't refer a constant that is defined in a singleton class. If this is reasonable, I want to know why.


Solution

  • The class returned by singleton_class is an anonymous class which was inherited from class of the object a.

    So a.class and a.singleton_class refer to different objects.

    puts a.class.object_id           # => 79495930
    puts a.singleton_class.object_id # => 79495830
    

    And also different classes: a.singleton_class is a child class of A.

    a.singleton_class.superclass # => A 
    
    a.singleton_class.ancestors  # => [B, A, Object, Kernel, BasicObject] 
    a.class.ancestors            # => [A, Object, Kernel, BasicObject] 
    

    And because of this, a.singleton_class doesn't share its own constants to parent just like any other child class do.

    puts a.class.constants             # => []
    puts a.singleton_class.constants   # => [:CONSTANT]