0

私はRuby(1.9.3)で小さなコードを書いています。そして、 const_setでいくつかの定数を定義し、これらの定数のいくつかの動作を定義する、単純な「列挙型」クラスのペアを使用します(たとえば、クラスDaysは定数MONTUE ...およびDays:: MON.succはTUEに評価される必要があります)。

私はこれらのクラスに本当に満足しています。ただし、コードを成長させている間、それらをさらに追加する必要がある場合があり、ソースコードの99%を共有する5つ以上のクラスを作成するというアイデアは好きではありません。

class Days
  NAMES = %w( MON TUE ... )
  INSTANCES = []

  def initialize(num)
    @num = num
  end

  # An example operation
  def +(n)
    INSTANCES[(@num + n) % INSTANCES.length]
  end

  # Another example operation
  def succ
    self + 1
  end

  def to_s
    NAMES[@num]
  end

  NAMES.each_with_index do |name, idx|
    instance = new(idx)
    INSTANCES[idx] = instance
    const_set name, instance
  end
end

class Months
  NAMES = %w( JAN FEB ... )
  ...
end

Rubyのメタプログラミング機能を使用してこれらのクラスを生成できるかどうか疑問に思いました。ただし、NAMESINSTANCES、および「列挙型」定数(MONTUEなど)を作成するのに苦労しています。const_setであるClassのクラスメソッドであるため、このコードでは、そのコンテキスト(selfの値)はそれぞれDaysMonthsです。

ファクトリメソッドを作成するとき、私は次のようなことを強いられます。

def enum_new(names_array)
  Class.new do
    const_set "NAMES" []
    names_array.each_with_index do |name, idx|
      NAMES[idx] = name
    end
     ...
  end
end
Days = enum_new(%w| MON TUE ... |)
Months = enum_new(%w| JAN FEB ... |)

しかし、これは機能しません(少なくとも、私が望んでいたようには)。const_setは、名前が魔法のように設定されているクラス(つまり、 DaysMonths)のコンテキストでは呼び出されないため、明らかにClassのコンテキストで呼び出されます。 ; したがって、インスタンスメソッドからアクセスできないだけでなく、引数として名前の新しい配列を使用してenum_newが呼び出されるたびに上書きされます。クラス変数を使用する場合にも同様の問題が発生します。これは、メソッドで生成されたすべてのクラス間で共有されるためです(Classのクラス変数になるためです)。

Class.newで生成されたクラスに定数を作成し、コードをほとんど同じクラスで汚染することなく、元のDaysおよびMonthsクラスとすべて同じクラスを取得する方法はありますか?

ご清聴ありがとうございました!:)

4

1 に答える 1

1

はい。ブロックで初期化を行います。class_execこのブロックに名前データを渡すことができself、適切なクラスを参照します。

theClass=Class.new
theClass.class_exec(names) do |names|
  #initialize constants here...
end
于 2012-04-30T15:44:49.363 に答える