在 Ruby 中动态定义命名类
Posted
技术标签:
【中文标题】在 Ruby 中动态定义命名类【英文标题】:Dynamically define named classes in Ruby 【发布时间】:2011-09-28 19:54:52 【问题描述】:我正在用 Ruby 编写内部 DSL。为此,我需要以编程方式创建命名类和嵌套类。最好的方法是什么?我认为有两种方法可以做到这一点:
-
使用
Class.new
创建一个匿名类,然后使用define_method
为其添加方法,最后调用const_set
将它们作为命名常量添加到某个命名空间。
使用某种eval
我已经测试了第一种方法并且它有效,但是对于 Ruby 来说是新手,我不确定将类作为常量是正确的方法。
还有其他更好的方法吗?如果不是,以上哪个更可取?
【问题讨论】:
eval
最好避免。 ***.com/questions/637421/is-eval-supposed-to-be-nasty
【参考方案1】:
如果您想创建一个具有动态名称的类,您几乎必须完全按照您所说的去做。但是,您不需要使用define_method
。您只需将一个块传递给您初始化类的Class.new
。这在语义上与class
/end
的内容相同。
记住const_set
,在该范围内认真对待接收者(self
)。如果您希望全局定义该类,则需要在 TopLevel 模块上调用 const_set
(名称和细节因 Ruby 而异)。
a_new_class = Class.new(Object) do
attr_accessor :x
def initialize(x)
print #self.class initialized with #x"
@x = x
end
end
SomeModule.const_set("ClassName", a_new_class)
c = ClassName.new(10)
...
【讨论】:
我还应该提到,类名本质上是常量。它们被定义为它们所在模块的常量。 您能否更具体地了解顶层模块的名称?【参考方案2】:您实际上不需要使用const_set
。 Class.new
的返回值可以赋值给
一个常量,Class.new
的块是class_eval
。
class Ancestor; end
SomeClass = Class.new(Ancestor) do
def initialize(var)
print "#self.class initialized with #var"
end
end
=> SomeClass
SomeClass.new("foo")
# SomeClass initialized with foo=> #<SomeClass:0x668b68>
【讨论】:
这不会创建具有动态名称的类。 SomeClass 是静态确定的。 并非如此。当类被用于某事时,将推断出常量名称。 gist.github.com/1064909 在您的示例中,您将新类定义为“SomeClass”。您刚刚粘贴的示例与您“您实际上不需要使用 const_set”的陈述相矛盾。您确实需要使用它将某些内容绑定到模块常量。 从这个意义上说你是对的,我认为你的意思是“具有动态名称的类”是 == anon 类。【参考方案3】:应该是这样的
a_new_class = Class.new(Object) do
attr_accessor :x
def initialize(x)
@x = x
end
end
SomeModule = Module.new
SomeModule.const_set("ClassName", a_new_class)
c = SomeModule::ClassName.new(10)
【讨论】:
以上是关于在 Ruby 中动态定义命名类的主要内容,如果未能解决你的问题,请参考以下文章