为什么扩展模块中的常量在使用self声明的类方法中不可用。

时间:2013-05-07 13:24:47

标签: ruby

我认为class << self块中声明的方法与使用self.前缀声明的方法之间没有区别,但有:

module A
  VAR = 'some_constant'
end

class B
  extend A

  class << self
    def m1
      puts VAR
    end
  end

  def self.m2
    puts VAR
  end
end

B.m1 # => OK
B.m2 # => uninitialized constant B::VAR

为什么A中的m1常量可用m2而不是{{1}}?

1 个答案:

答案 0 :(得分:22)

在Ruby中,常量查找与方法查找不同。对于方法查找,调用foo始终与调用self.foo相同(假设它不是私有的)。调用常量FOOself::FOOsingleton_class::FOO非常不同。

使用非限定常量(例如FOO)将在当前打开的模块中进行查找。将使用module Modclass Klassclass << objmodule_eval和变体打开一个模块。定义m1时,这些是B,然后是B.singleton_class。定义m2时,只会打开B

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

在此代码中,Foo::Bar.hello将返回42,即使X不是Bar的常量,也是其单例类或祖先。此外,如果您稍后向X添加常量Bar,则会返回该值。最后,以下定义不相同:

module Foo
  X = 42
end

class Foo::Bar
  def self.hello
    X
  end
end

Foo::Bar.hello # => uninitialized constant Foo::Bar::X

确实,在定义hello时,只会打开课程Foo::Bar,而在上一个示例中,FooFoo::Bar都会打开。

最后一个示例,显示显式范围与继承的区别:

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

在您的情况下,您可能希望include您的模块,而不是extending ,不是吗?

否则,您可以使用singleton_class::VAR,您的代码将按预期运行。

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