2011-07-05 12 views
22

Rubyで内部DSLを作成しています。このためには、名前付きクラスと入れ子クラスをプログラムで作成する必要があります。そうする最善の方法は何ですか?Rubyで名前付きクラスを動的に定義する

  1. 使用Class.newがそれにメソッドを追加するdefine_methodを使用し、その後、匿名クラスを作成し、最終的にいくつかの名前空間への名前付き定数としてそれらを追加するconst_setを呼び出すために:私はそうするには2つの方法があることを偵察します。
  2. eval

のいくつかの並べ替えを使用して、私は最初の方法をテストしてみた、それが働いたが、ルビーに新しいもの、私は定数としてクラスを置くことは正しい方法であることを確認していません。

他にも良い方法がありますか?そうでない場合は、上記のどちらが望ましいですか?

+0

'のようにする必要があります。 http://stackoverflow.com/questions/637421/is-eval-supposed-to-be-nasty –

答えて

20

ダイナミックな名前のクラスを作成する場合は、あなたが言ったことをほぼ正確に行う必要があります。ただし、define_methodを使用する必要はありません。クラスを初期化するClass.newにブロックを渡すことができます。これは、意味的にはclass/endの内容と同じです。

const_setと覚えておいて、その範囲内の受信者(self)を慎重にすること。クラスをグローバルに定義する場合は、TopLevelモジュール(Rubyで名前と詳細が異なります)でconst_setを呼び出す必要があります。

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

が最高の回避されeval`この

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)