2011-07-05 47 views
22

我寫在Ruby的內部DSL。爲此,我需要以編程方式創建命名類和嵌套類。最好的辦法是什麼?我偵察,有兩種方法可以做到這一點:動態定義命名的類在Ruby中

  1. 使用Class.new創建一個匿名類,然後使用define_method的方法添加到它,最後調用const_set將其添加爲命名常量一些命名空間。
  2. 使用某種eval

我測試過的第一種方式,它的工作,但作爲新的Ruby,我不知道該把類作爲常量纔是正道。

是否有其他更好的方法?如果不是,上述哪一項更可取?

+0

'eval'是最好避免。 http://stackoverflow.com/questions/637421/is-eval-supposed-to-be-nasty –

回答

20

如果你想創建一個具有動態名稱的類,你將不得不做幾乎完全你所說的。但是,您不需要使用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

我還應該提到,類名固有地是常量。它們被定義爲它們包含的模塊的常量。 –

+3

您可以更具體地瞭解頂級模塊的名稱嗎? –

4

你並不真的需要使用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> 
+3

這不創建一個類具有動態名稱。 SomeClass是靜態確定的。 –

+0

不是。當這個類被用於某些事物時,常量名將被刪除。在你的例子中https://gist.github.com/1064909 – Julik

+2

,你正在將你的新類定義爲「SomeClass」。你剛剛粘貼的例子與你的陳述相矛盾:「你並不需要使用const_set」。您確實需要使用它來將某些東西綁定到模塊常量。 –

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)