【问题标题】:Dynamically define named classes in Ruby在 Ruby 中动态定义命名类
【发布时间】:2011-09-28 19:54:52
【问题描述】:

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

  1. 使用Class.new创建一个匿名类,然后使用define_method为其添加方法,最后调用const_set将它们作为命名常量添加到某个命名空间。
  2. 使用某种eval

我已经测试了第一种方法并且它有效,但是对于 Ruby 来说是新手,我不确定将类作为常量是正确的方法。

还有其他更好的方法吗?如果不是,以上哪个更可取?

【问题讨论】:

标签: ruby metaprogramming dsl metaclass


【解决方案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_setClass.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)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-27
    • 2015-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-14
    相关资源
    最近更新 更多