在Ruby中动态定义命名类

时间:2011-07-05 13:26:14

标签: ruby metaprogramming dsl metaclass

我正在用Ruby编写内部DSL。为此,我需要以编程方式创建命名类和嵌套类。这样做的最佳方法是什么?我认为有两种方法可以做到这一点:

  1. 使用Class.new创建一个匿名类,然后使用define_method向其添加方法,最后调用const_set将它们作为命名常量添加到某个命名空间。
  2. 使用某种eval
  3. 我已经测试了第一种方式并且它有效,但是对Ruby来说是新手,我不确定将类作为常量是正确的方法。

    还有其他更好的方法吗?如果没有,上述哪一项更可取?

3 个答案:

答案 0 :(得分:26)

如果你想创建一个带有动态名称的类,你必须完全按照你所说的去做。但是,您不需要使用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)

...

答案 1 :(得分:5)

您实际上不需要使用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>

答案 2 :(得分:4)

应该是这样的

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)