如何使模块常量在特征类中也可见?
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
里面,self
是 B
,所以 self::NAME
也可以工作
self::NAME
也适用于 self.class_scope
self::NAME
在 instance_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
self
在class_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
我创建了一个包含常量 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
里面,self
是B
,所以self::NAME
也可以工作 self::NAME
也适用于self.class_scope
self::NAME
在instance_scope
中不起作用:B
实例不是 class/module.
为什么 NAME
不起作用?
Here解释得很好。
constant lookup searches for constants that are defined in
Module.nesting
,Module.nesting.first.ancestors
, andObject.ancestors
ifModule.nesting.first
is nil or a module
self
在class_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