如何使模块常量在特征类中也可见?

How to make a module constant also visible within an eigenclass?

我创建了一个包含常量 NAME 和方法 hello 的模块。如果 class 包含模块,则两个定义应该在不同范围内可见。

module A
  NAME = 'Otto'
  def self.included(base)
    base.extend(ClassMethods)
  end

  def hello(name = 'world')
    self.class.hello(name)
  end

  module ClassMethods
    def hello(name = 'world')
      "Hello #{name}!"
    end
  end
end

class B
  include A

  def instance_scope
    p [__method__, hello(NAME)]
  end

  def self.class_scope
    p [__method__, hello(NAME)]
  end

  class << self
    def eigen_scope
      p [__method__, hello(NAME)]
    end
  end
end

B.new.instance_scope
B.class_scope
B.eigen_scope

#=> script.rb:34:in `eigen_scope': uninitialized constant Class::NAME (NameError)
    from script.rb:41

但是常量在 eigen 的实例方法范围内不可见class,class << self

有没有办法让模块更健壮并在上面的错误范围内提供常量?

解决方案

class << self
  def eigen_scope
    p [__method__, hello(self::NAME)]
    #=> [:eigen_scope, "Hello Otto!"]
  end
end

为什么 self::NAME 有效?

  • A::NAME 是最简单的,hard-coded 版本。
  • B::NAME 也可以,因为 B 包括 A
  • eigen_scope 里面,selfB,所以 self::NAME 也可以工作
  • self::NAME 也适用于 self.class_scope
  • self::NAMEinstance_scope 中不起作用:B 实例不是 class/module.

为什么 NAME 不起作用?

Here解释得很好。

constant lookup searches for constants that are defined in Module.nesting, Module.nesting.first.ancestors, and Object.ancestors if Module.nesting.first is nil or a module

selfclass_scopeeigen_scope中是一样的。

Module.nesting 虽然不同 :

  • [B] 对于 class_scope
  • [#<Class:B>, B] 对于 eigen_scope

所以 Module.nesting.first.ancestors 是 :

  • [B, A, Object, Kernel, BasicObject] 对于 class_scope
  • [#<Class:B>, A::ClassMethods, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject] 对于 eigen_scope

A 未被搜索,但 A::ClassMethods!

所以你可以定义:

module A
  module ClassMethods
    NAME = 'Bob'
  end
end