通过Class.new
创建匿名类时,它们似乎没有自己的常量名称空间:
klass1 = Class.new do
FOO = "foo"
end
klass2 = Class.new do
FOO = "bar"
end
这给出了warning: already initialized constant FOO
,看起来是正确的:
> klass1.const_get(:FOO)
"bar"
> klass2.const_get(:FOO)
"bar"
> FOO
"bar"
我将在简单的DSL中使用这种方法来定义应用程序的插件,如下所示:
class App
class AddonBase
attr_reader :session
def initialize(session)
@session = session
end
end
def self.addons
@addons ||= {}
end
def self.addon(name, &block)
addons[name] = Class.new(AddonBase, &block)
end
end
这对于简单的加载项很好,但是如果定义常量,它们将位于Object::
下,而不是变成addons[name]::CONSTANT
:
App.addon "addon1" do
PATH="/var/run/foo"
def execute
File.touch(PATH)
end
end
App.addon "addon2" do
PATH="/etc/app/config"
def execute
File.unlink(PATH)
end
end
# warning: already initialized constant PATH
常量可以是任何东西,附加组件甚至可以定义自己的实用程序子类,因此,这不仅仅是将PATH
替换为函数。
有什么办法可以解决这个问题?
答案 0 :(得分:2)
通过Class.new创建匿名类时,它们似乎没有用于常量的命名空间
当然,根据“匿名”一词的定义。比较以下两个摘要:
class C1; puts "|#{name}|"; end
#⇒ |C1|
C2 = Class.new { puts "|#{name}|" }
#⇒ ||
除非分配给常量,否则该类没有名称,因此内部定义的所有常量都转到Object
命名空间。也就是说,这里的警告实际上是指出错误,并且Object::FOO = "bar"
会覆盖Object::FOO = "foo"
常量。
也就是说,在这种情况下不能使用常量。改为使用类级实例变量,或手动构造唯一的常量名称(尽管如此,我建议避免使用一堆无关的常量来污染Object
类。)
答案 1 :(得分:2)
通过
Class.new
创建匿名类时,它们似乎没有自己的常量名称空间
可以,您可以使用const_set
在匿名类中定义常量:
klass1 = Class.new do
const_set(:FOO, 'foo')
end
klass2 = Class.new do
const_set(:FOO, 'bar')
end
klass1::FOO #=> "foo"
klass2::FOO #=> "bar"
或通过self::
klass1 = Class.new do
self::FOO = 'foo'
end
klass2 = Class.new do
self::FOO = 'bar'
end
klass1::FOO #=> "foo"
klass2::FOO #=> "bar"
答案 2 :(得分:1)
实际上,问题是如何使用包含常量定义的proc来定义类。如前所述,这是不可能的,因为proc获得class_eval'd且不允许定义常量。
我建议另一种方法。您可以使用模块代替proc来定义将模块混合到类中的新插件吗?
示例:
module AddonModule
FOO = "foo"
end
klass = Class.new
klass.include AddonModule
klass::FOO # => "foo"
在您的DSL中的使用:
def self.addon(name, addon_module)
addon = Class.new(AddonBase)
addon.include addon_module
addons[name] = addon
end