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

时间:2017-01-13 09:03:06

标签: 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

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

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

1 个答案:

答案 0 :(得分:4)

解决方案

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

为什么self::NAME有效?

  • A::NAME将是最简单的硬编码版本。
  • B::NAME也可以,因为B包含A
  • eigen_scope内,selfB,因此self::NAME也适用
  • self::NAME也适用于self.class_scope
  • self::NAMEinstance_scope中不起作用:B实例不是类/模块。

为什么NAME不起作用?

Here是一个非常好的解释。

  

常量查找搜索中定义的常量   Module.nestingModule.nesting.first.ancestors和。{   如果Object.ancestors为零或模块

,则Module.nesting.first

selfclass_scopeeigen_scope相同。

Module.nesting虽然有所不同:

    {li> [B] class_scope {li> [#<Class:B>, B] eigen_scope

所以Module.nesting.first.ancestors是:

    {li> [B, A, Object, Kernel, BasicObject] class_scope {li> [#<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