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 の乱雑な文字列入力の世界にとどまる運命にあるのでしょうか?

4

1 に答える 1

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
于 2013-01-16T19:52:38.900 に答える