1

Ruby マーシャリングを使用して 2 つのクライアント間でデータを送信しています。各クライアントには、マーシャリングされたデータのロードを支援するために使用する一連のクラス定義があります。定義は外部の ruby​​ ファイルに保存され、いつでもロードできます (通常は起動時)。

簡単な使用例は次のとおりです。

  • クライアント A マーシャルがデータをダンプし、クライアント B に送信します。
  • クライアント B マーシャルがデータをロードし、それをファイルに書き出す

ただし、1 つのクライアントが、他のクライアントの定義で定義されていないオブジェクトを含むデータを送信する場合があります。この場合、他のクライアントは定義を適宜更新する必要があります。

クラスの定義に追加する必要がある新しいインスタンス変数である場合もあれxyzば、完全に新しいクラスである場合もあります。

Marshal#Load現在、未定義の変数 (例: undefined class/method abc) に遭遇したときに例外をスローするだけです。

クライアントがデータを喜んで読み書きできるように、この例外を取り、それに応じて定義を更新する方法はありますか?

dumpすべてのクラスには、文字列、配列、ハッシュ、数値など、Marshal が既にエンコード/デコードする方法を知っているデータが含まれます。カスタム/loadメソッドを必要とするデータはありません。

4

1 に答える 1

2

私の解決策は、クラス(および一定の階層、つまりFoo::Bar::Baz)を自動的に作成し、クラスが属性アクセスの試行に自動応答するようにすることです。

class AutoObject
  def method_missing(*args,&b)
    if args.size == 1
      name = args[0]
      if instance_variable_defined? "@#{name}"
        self.class.send :attr_accessor, name
        send(*args)
      else
        super
      end
    elsif args.size == 2 && args[0].to_s[/=$/]
      name = args[0].to_s[0...-1]
      if instance_variable_defined? "@#{name}"
        self.class.send :attr_accessor, name
        send(*args)
      else
        super
      end
    end
  end
end

def Marshal.auto_load(data)
  Marshal.load(data)
rescue ArgumentError => e
  classname = e.message[%r(^undefined class/module (.+)$), 1]
  raise e unless classname

  classname.split("::").inject(Object) do |outer, inner|
    if !outer.const_defined? inner
      outer.const_set inner, Class.new(AutoObject)
    else
      outer.const_get inner
    end
  end
  retry
end

これを簡単に拡張して、作成されたすべてのクラスをログに記録し、それらが持つ可能性のあるインスタンス変数を判別することもできます。これは、おそらくプログラムでファイルを更新するのに役立ちます。

于 2012-12-27T10:20:21.270 に答える