Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

ruby

jruby

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

like image 690
martido Avatar asked Mar 14 '11 20:03

martido


1 Answers

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
like image 180
Guilherme Bernal Avatar answered Jan 04 '23 16:01

Guilherme Bernal