Search code examples
rubyjruby

Missing constant and "const_missing" inside "class << self" definition


I'm greatly confused by Ruby's behavior when defining const_missing and other class methods inside a class << self definition as opposed to using the def self.foo syntax.

I was trying to do something like this:

class Foo
  class << self
    def foo
      puts MISSING
    end

    def const_missing(name)
      puts "#{name} missing"
    end
  end
end

Foo.foo

I mostly use the the class << self syntax to define class methods. However, it did not work as expected. const_missing is never called. The above results in a NameError.

Defining both methods like this works as expected:

def self.foo
  puts MISSING
end

def self.const_missing(name)
  puts "#{name} missing"
end

I thought that the class << self syntax is just another way to define class methods, but completely equivalent to def self.foo? I've tested the above with MRI 1.8.7, 1.9.2 and JRuby 1.5.6. So obviously I'm missing something here?

Any hint is greatly appreciated.

Thanks, Martin


Solution

  • class << self is not a shortcut to define class methods. This syntax (I don't know the exact naming) opens the eigenclass from a object (in your case, a class). With that you can define methods to the object (not instance methods). But when you call a constant into the eigenclass, you are calling a constant from the eigenclass, not from the class. In this case you have to define a class method on the eigenclass to the const_missing, two ways to do that:

    class Test
      class << self
        def foo
          p MISSING
        end
    
        # First way: (syntax sugar for the second way)
        def self.const_missing(name)
          name
        end
    
        # Second way:
        class << self # eigenclass of the eigenclass of the class
          def const_missing(name)
            name
          end
        end
    
      end
    end
    
    Test.foo #=> :MISSING