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

ssc*_*eck 5 ruby metaprogramming eigenclass

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

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
Run Code Online (Sandbox Code Playgroud)

但是常量在本征类的实例方法范围中是不可见的class << self.

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

Eri*_*nil 4

解决方案

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

为什么self::NAME有效?

  • A::NAME将是最简单的硬编码版本。
  • B::NAME也会起作用,因为B包括A
  • 里面eigen_scope,selfB, 所以self::NAME也有效
  • self::NAME也会工作在self.class_scope
  • self::NAME不起作用instance_scopeB实例不是类/模块。

为什么不起作用NAME

这里有一个很好的解释。

常量查找搜索在 Module.nestingModule.nesting.first.ancestorsObject.ancestorsif Module.nesting.firstis nil 或模块中定义的常量

selfclass_scope在和中是相同的eigen_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
Run Code Online (Sandbox Code Playgroud)