Why are constants from extended module not available in class methods declared with self.?

后端 未结 1 1370
感动是毒
感动是毒 2020-12-14 02:51

I thought there were no differences between methods declared within a class << self block and those declared with a self. prefix, but there a

相关标签:
1条回答
  • 2020-12-14 03:30

    In Ruby, constant lookup is not the same as method lookup. For method lookup, calling foo is always the same as calling self.foo (assuming it isn't private). Calling a constant FOO is very different from self::FOO or singleton_class::FOO.

    Using an unqualified constant (e.g. FOO) will do a lookup in the currently opened modules. A module is opened with module Mod, class Klass, class << obj, or module_eval and variants. When defining m1, these are B, and then B.singleton_class. When defining m2, only B is opened.

    module Foo
      X = 42
      class Bar
        def self.hello
          X
        end
      end
    end
    

    In this code, Foo::Bar.hello will return 42, even though X is not a constant of Bar, its singleton class or ancestor. Also, if you later add a constant X to Bar, then that value will be returned. Finally, the following definition is not equivalent:

    module Foo
      X = 42
    end
    
    class Foo::Bar
      def self.hello
        X
      end
    end
    
    Foo::Bar.hello # => uninitialized constant Foo::Bar::X
    

    Indeed, when hello is defined, only the class Foo::Bar is opened, while in the previous example, both Foo and Foo::Bar where opened.

    A last example, to show the difference an explicit scope can have with inheritance:

    class Base
      X = 42
      def self.foo
        X
      end
      def self.bar
        self::X
      end
    end
    
    class Parent < Base
      X = :other
    end
    
    Parent.foo # => 42
    Parent.bar # => :other
    

    In your case, you probably want to include your module, instead of extending it, no?

    Otherwise, you could use singleton_class::VAR, your code will work as you expect it.

    module A
      VAR = 'some_constant'
    end
    
    class B
      extend A
    
      class << self
        def m1
          puts singleton_class::VAR # not necessary here, as singleton_class is opened
        end
      end
    
      def self.m2
        puts singleton_class::VAR # necessary here!
      end
    end
    
    B.m1 # => OK
    B.m2 # => OK
    
    0 讨论(0)
提交回复
热议问题