rubymetaprogrammingobject-model

Ruby Kernel module methods used as Object class methods


Say I want to patch the Kernel module with one method I just came up with:

module Kernel
  def say_hello
    puts "hello world"
  end
end

I can now certainly do this:

Object.new.say_hello # => hello world

but I can also do the following which I normally shouldn't be able to do:

Object.say_hello # => hello world

Since Object includes Kernel it takes its instance methods and thus all Object instances should respond to say_hello. So far so good.

However Object.say_hello seems to be a class method which could only be justified if we had done something similar to this:

class << Object
  def say_hello
    puts "hello world"
  end  
end

Storing say_hello in Object's singleton class would allow us to use it as a class method but instead Kernel in just included in Object which shouldn't allow this behavior. But it does. Does anybody have a clue why?

Thanks


Solution

  • Kernel is just included in Object [...]

    That is correct.

    [...] which shouldn't allow this behavior.

    You overlook that classes are objects, too.


    Let's see where the say_hello method comes from if obj is an instance of Object:

    obj = Object.new
    
    obj.method(:say_hello)
    #=> #<Method: Object(Kernel)#say_hello>
    

    Just as expected. obj is an instance of Object and Object includes Kernel:

    obj.class.include? Kernel
    #=> true
    
    obj.class.ancestors
    #=> [Object, Kernel, BasicObject]
    

    Now let's see what happens if obj is the class Object:

    obj = Object
    
    obj.method(:say_hello)
    #=> #<Method: Class(Kernel)#say_hello>
    

    This time obj is an instance of Class and Class also includes Kernel:

    obj.class.include? Kernel
    #=> true
    
    obj.class.ancestors
    #=> [Class, Module, Object, Kernel, BasicObject]
    

    Ruby's documentation notes that class methods are in fact just instance methods defined on the class object: (emphasis added)

    class C
      def self.my_method
        # ...
      end
    end
    

    However, this is simply a special case of a greater syntactical power in Ruby, the ability to add methods to any object. Classes are objects, so adding class methods is simply adding methods to the Class object.