2013-01-16 28 views
7

このように、プログラム的にクラスを定義したいという状況があります。私がここで継承しているサードパーティのクラスは、そのクラスが匿名で定義されているという事実を好まないという事実を除いて、私が使用しているメソッドはうまくいきます(基本的に、必要な情報を持っていません。匿名クラスを定数に設定する前に、 '継承された'フック内のクラス名)。ブロックを使った動的クラス定義

['one', 'two', 'three'].each do |model| 
    cls = Class.new(ThirdPartyClass) do 
     define_method :model do 
      model 
     end 
    end 
    ThirdPartyClass.const_set(model.capitalize, cls) 
end 

私は単純にevalを使用するなど、クラスの何かを定義することができます。

['one', 'two', 'three'].each do |model| 
    eval "class ThirdPartyClass::#{model.capitalize} < ThirdPartyClass; ...; end" 
end 

をしかし、それは厄介な文字列補間だので、私はそれを好きではありません。ブロックベースの方法は、美的に言えば「より良い」と思われます。

ブロックベースの構文を使用して、そのクラスを非匿名で(つまり、最初から名前を付けて)定義できる方法はありますか、またはevalの乱雑な文字列入力の土地にとどまるのですか?

+0

クラスが同じクラスの名前空間内にあり、同じクラスのサブクラスになることは奇妙です。 – sawa

+0

Trueですが、名前空間の問題はこの特定の問題に直面しています(少なくとも私はそう考えています)。好奇心が強いなら、私はclassy_enum宝石を使用しています。その宝石の慣例により、enum値はメインenumクラスの名前空間内になければなりません。それは、他の言語のそれと幾分類似した列挙構文を生成する。 – elsurudo

答えて

0

Class#inheritedコールバックは、クラスの作成時に呼び出されます。匿名クラスをインスタンス化するときは、常にクラスが定数に代入されて名前を割り当てる前になります。私はこれを回避する方法は見当たりません。 evalを使用して文字列evalのコード量を最小化し、inheritedの前に名前が表示される特別なクラス構文を使用して空のクラスを作成し、ブロックフォームのclass_evalをフォローアップしてメソッドを定義します。

class Super 
    def self.inherited(child) 
    puts "#{self.name} inherited by #{child.inspect} named '#{child.name}'" 
    end 
end 

# Your way, inherited can't see name 
['one', 'two', 'three'].each do |model| 
    klassname = model.capitalize 
    klass = Class.new(Super) do 
    puts "defining #{model} inheriting from Super" 
    define_method :model do 
     model 
    end 
    end 
    Super.const_set(klassname, klass) 
end 

# this way inherited sees name because we use special class definition syntax in minimal string eval 
['four', 'five', 'six'].each do |model| 
    klassname = model.capitalize 

    eval %Q{ 
    class Super::#{klassname} < Super 
     puts %Q[defining Super::#{klassname} inheriting from Super] 
    end 
    } 

    Super.const_get(klassname).class_eval do 
    puts "defining methods for Super::#{klassname} inheriting from Super" 
    define_method :model do 
     model 
    end 
    end 
end 

# produces: 
Super inherited by #<Class:0x0000010084c988> named '' 
defining one inheriting from Super 
Super inherited by #<Class:0x0000010084c640> named '' 
defining two inheriting from Super 
Super inherited by #<Class:0x0000010084c320> named '' 
defining three inheriting from Super 
Super inherited by Super::Four named 'Super::Four' 
defining Super::Four inheriting from Super 
defining methods for Super::Four inheriting from Super 
Super inherited by Super::Five named 'Super::Five' 
defining Super::Five inheriting from Super 
defining methods for Super::Five inheriting from Super 
Super inherited by Super::Six named 'Super::Six' 
defining Super::Six inheriting from Super 
defining methods for Super::Six inheriting from Super 
+0

ありがとう、これはまあまあの十分な回避策です。 – elsurudo

関連する問題