ProcオブジェクトのMarshal(その2)

Marshalモジュールは特殊なオブジェクトや拡張ライブラリで定義したオブジェクトに対応するために、Marshalしたいオブジェクトのクラスにメソッドを定義することでMarshalの仕方をカスタマイズできます。それも、カスタマイズの方法は3通りあります。

  1. marshal_dump/marshal_load
  2. _dump/_load
  3. _dump_data/_load_data/_alloc

これらのうち、上の2つは正統派でちゃんとドキュメントもあります。 http://www.ruby-lang.org/ja/man/html/Marshal.html
なにも、問題がなければmarshal_dump/marshal_loadを使うべきでしょう。

でも、上2つはProcオブジェクト対応のカスタマイズでは使えません。上2つはload時にallocateオブジェクトで初期化していないオブジェクトを生成するのですが、Procオブジェクトはallocateメソッドがないので、エラーが出てどうすることもできません。

その点、3番目のは_allocでオブジェクトのアロケートもカスタマイズも出来てばっちりです。ドキュメントがどこにもなく、いつ削除されてもおかしくないという些細な欠点がありますが、これを使うことにしました。

_dump_data/_load_data/_allocを使ってカスタマイズする方法を説明します。
オブジェクトをDUMPするとき、Marshalのシステムはオブジェクトに_dump_dataメソッドがあるか調べます。もしあれば、_dump_dataを呼び出して、その結果を再帰的にDUMPします。ProcオブジェクトのMarshalではコードと環境の組でDUMPするので、こんな感じになります。

class Proc
  def _dump_data
    [コード, 環境]
  end
end

コードと環境は昨日説明した怪しげな拡張メソッドで作るのですが、もう一手間書けます。その詳細は次回説明します。

次にオブジェクトをLOADするときは、LOADしようとするオブジェクトのクラスに_allocメソッドが定義されていればそれを、そうでなければallocateメソッドを用いて、初期化していないオブジェクトを生成します。つぎに、その初期化していないオブジェクトのメソッドとして_load_dataを呼び出します。_load_dataの引数は_dump_dataでの戻り値です。この引数をもとにオブジェクトの初期化を行います。
こんな感じのコードになります。

class Proc
  def _alloc
    Proc.new {}  # とりあえず適当にオブジェクトを生成する
  end

  def _load_data(obj)
    objに相当するProcオブジェクトを作る 

    self.copy(作ったProcオブジェクト) 
  end
end

前回の拡張ライブラリの話しで、Proc#copyを忘れていました。これは、proc0.copy(proc1)とすると、proc0にproc1のコードと環境がコピーされるというものです。

続く