在 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_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)

【讨论】:

以上是关于在 Ruby 中动态定义命名类的主要内容,如果未能解决你的问题,请参考以下文章

Ruby模块中的动态覆盖类方法

类命名空间与对象实例的命名空间

类命名空间与对象实例的命名空间和下面向对象的组合用法

python基础 13 类命名空间于对象实例的命名空间,组合方法

ruby rails动态css类

类命名空间与对象实例的命名空间 and 面向对象的组合用法