Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where are the Ruby Class Singleton Constants Stored?

Let's assume we have this Ruby class:

class MyClass
  class << self
    MC_CONST = 30
  end
end 

Then, let's instantiate MyClass and add another constant to the object's metaclass:

m = MyClass.new
class << m
  OBJ_MC_CONST = 50
end

There are no issues with the object singleton constant:

m.singleton_class::OBJ_MC_CONST # => 50 <-- [OK]
m.singleton_class.constants.include? :OBJ_MC_CONST # => true <- [OK]

But not exactly what I'd expect with the class singleton constant:

MyClass.singleton_class::MC_CONST # => 30 <-- [OK]
MyClass.singleton_class.const_get :MC_CONST # => 30 <-- [OK]
MyClass.singleton_class.constants.include? :MC_CONST # => false <-- [Why???]

Why on earth the array returned by .constants method on metaclass of MyClass class doesn't contain :MC_CONST? What am I missing here?

Thank you.


EDIT 1: This does appear to be a bug in MRI 2.x after all. I've filed a new issue with the Ruby core team: https://bugs.ruby-lang.org/issues/9413 to address this.

EDIT 2: This bug has apparently been fixed in https://bugs.ruby-lang.org/projects/ruby-trunk/repository/revisions/44628/diff/

like image 933
demisx Avatar asked Jan 11 '14 00:01

demisx


1 Answers

I've explored a bit on this question and I think it is caused by a bug / inconsistency / idiosyncrasy of MRI.

On MRI 2.1.0, this code:

class MyClass
  class << self
    MC_CONST = 30
  end
end 

p MyClass.singleton_class.const_defined? :ABBA_CONST, false
p MyClass.singleton_class.const_defined? :MC_CONST, false
p MyClass.singleton_class.constants(false)

yields

false
true
[]

So the MC_CONST constant is defined, but it is not available as a local constant of the class (I'm passing false to the various methods to disable the constant resolution and just keep it local to that class), which it should be indeed. If we check on the documentation of Module#constants it says:

Returns an array of the names of the constants accessible in mod. This includes the names of constants in any included modules (example at start of section), unless the all parameter is set to false.

Also see Module::const_defined?.

So it tells us to check const_defined? to better understand the behaviour of constants, but the two methods give different results!


Furthermore, on other Ruby implementation this code works as expected.

On JRuby 1.7.9, it yields:

false
true
[:MC_CONST]

On Rubinius 2.2.1, it yields:

false
true
[:MC_CONST]

Which is the expected behaviour :)

like image 61
Ju Liu Avatar answered Nov 13 '22 02:11

Ju Liu