Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does Object know about the const_get method?

Tags:

ruby

I was reading another question with an answer that mentions using the Module#const_get instance method to find a class in a module. For example:

module M
  class C
  end
end

p M.const_get 'C'
#=> M::C

I was curious about the const_get method so I used ri and found:

ri Module#const_get
...
This method will recursively look up constant names if a namespaced
class name is provided.  For example:

  module Foo; class Bar; end end
  Object.const_get 'Foo::Bar'
...

It seems like Object::const_get is a singleton method. Using it in our context works:

module M
  class C
  end
end

p Object.const_get 'M::C'
#=> M::C

But there's nothing documented about that singleton method:

ri Object::const_get
Nothing known about Object::const_get
ri Object.const_get
Nothing known about Object.const_get

This confused me because I know a Module is an Object but an Object is not a Module:

Module.ancestors
#=> [Module, Object, Kernel, BasicObject]
Object.ancestors
#=> [Object, Kernel, BasicObject]

Except then I used the Object#is_a? instance method to check and saw that I was wrong about that:

Module.is_a? Object
#=> true
Object.is_a? Module
#=> true

What started as an innocent ri query has led me to be confused about the entire Ruby object model.

  • Why does Object.is_a? Module #=> true if Module is not in Objects ancestor chain?
  • How does Object know about the const_get method?
like image 992
mbigras Avatar asked Mar 06 '23 09:03

mbigras


1 Answers

This is an artifact of the ill-understood separation between an object's class and an object's singleton class, a sort of shadow class that each class uses for things like this.

You can access this easily in Ruby 2.5+ with the singleton_class method:

Object.singleton_class.ancestors
# => [#<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]

Where Module makes an appearance here, so that's how those methods get mixed in and are callable via Object.

Object itself has a comparatively dull inheritance chain:

Object.ancestors
#=> [Object, Kernel, BasicObject]

Every object in Ruby has a class, even Class is an Object, which also has an associated Class.

like image 96
tadman Avatar answered Mar 09 '23 01:03

tadman