From: Hideto ISHIBASHI Date: 2000-06-22T02:20:30+09:00 Subject: [ruby-list:23431] persistent framework (Re: dump a single object) 石橋"rubyholic"秀仁です。いろいろとゴタクが長いです (^^; 「質問」という見出しの部分だけ読んでくだされば。 目的は「Marshalと同じ機能で、バイトストリングでなく、 表形式で出力する」ことです。 # Subjectを「永続化フレームワーク」に変えました。なんて大層な・・・ まつもとさん、お返事ありがとうございます。 From: matz@netlab.co.jp (Yukihiro Matsumoto) Subject: [ruby-list:23412] Re: dump a single object > まつもと ゆきひろです > |Marshal.dump(obj) などで、インスタンス変数をたどらず、 > |obj そのものだけを serialize する方法はありませんか。 > > 良く分からないのですが、インスタンス変数をたどらなければ、ご > く一部のオブジェクトを除いては「あるクラスのインスタンス」と > いう情報しかないので無意味でないですか? # ぼくがなんか誤解しているかもしれません (^^; 「たどらない」というのは言葉の選択ミスで、おわびします。 「重複しないで identity も失わない」という意図です。 そのためには、「あるクラスのインスタンス」を ID で参照します。 この文章では、"Object#id" と "ID" を使い分けています。 "ID" は、この仕組で独自に定義する、オブジェクト固有の ID です。 # MS の COM だかでは GUID (Global Unique Identifier) と呼ぶやつ? # mail の Message-ID などを想像してもらえば。 コード例: class Foo def initialize(foo) @foo = foo end end class BarBaz def initialize(bar, baz) @bar = bar @baz = baz end end fooa = Foo.new("a") foob = Foo.new(fooa) fooc = Foo.new(foob) bara = BarBaz.new(fooa, foob) barb = BarBaz.new(fooc, bara) (いいかげんな) オブジェクト表: class Foo name : ID : @foo ---------------------- fooa : foo1 : "a" foob : foo2 : fooa fooc : foo3 : foob class BarBaz name : ID : @bar : @baz --------------------------- bara : bar1 : fooa : foob barb : bar2 : fooc : bara class String name : ID : value ----------------------- (none) : str1 : "a" 上表の '@foo' や 'value' の項目に ID を使い、 クラスを持たせて全オブジェクトを一括表記: ID : class : attr/value (Hash?) -------------------------------------------- foo1 : Foo : @foo => str1 foo2 : Foo : @foo => foo1 foo3 : Foo : @foo => foo2 bar1 : BarBaz : @bar => fooa, @baz => foob bar2 : BarBaz : @bar => fooc, @baz => bara str1 : String : value => "a" この表から元のオブジェクト群が復元できます (よね?)。 復元時に無いオブジェクトは Marshal と同様のイモヅル式。 復元済みのオブジェクトはキャッシュから参照を返します。 同じ "ID" を持つオブジェクトは存在しない。 同じ "Object#id" を持つオブジェクトも存在しない。 だから identity も保存/復元される、というつもりです。 <質問 1> String は identity があり、Integer は immutable で identity がないんですよね。区別が必要かどうか・・・ オブジェクトに Object#id のほうの ID を設定する方法ないですよね。 最初にイモヅルの「とっかかり」になるオブジェクトが必要です。 これは、なんらかの条件にヒットする最初のオブジェクトです。 つまり Enumerable#find {...} の返り値と同じ考えかたです。 もっとも単純な key として、アプリケーションが "ID" か "Object#id" でアクセスすることを考えています (Hash#[key] みたいに)。 <質問 2> 組み込みオブジェクト 上表の str1(String) に value => "a" と書きました。こういう、 インスタンス変数に値がなく、「そのもの」が値のクラスは どうしましょうか?組込みクラスなど、値の設定 (状態の復元) 法を 個別に把握するとかかなぁ。 そういうクラス (組込みだけ?) をサポートすれば、 それですべて解決だと思うのですが・・・。 <質問 3> オブジェクトのクラスは Object#type か? 特異オブジェクトの Object#type は、最初に new したクラスを 返すと思っています。特異メソッドを定義したり、特異クラス としてインスタンス変数を定義したりしたオブジェクトとかは? copy = obj.type.new してから特異メソッドの定義を復元する方法は ありませんよね (Method クラスとかよくわかっていないので ^^;)。 Object#instance_eval("...")でインスタンス変数は復元できますね。 # まあ特異なオブジェクトなんてふつー永続化しないでしょう・・・ # Marshal.dump も拒否するのですから。退行はしてないはず。 というわけで、中心課題は、 「インスタンス変数を個別に直列化するには?」 です。で、たぶん苦しんで Marshal を再利用するより、 XMarshal 方式 (hoge_dump, hoge_load メソッドを用意する/させる) のが楽かも。なにしろ自分の管理下に置けるので。 よし、そうしよう。書いてるうちに結論が出てしまった (^^; <質問 4> 矛盾点とか どこかおかしいところを指摘して下さい > みなさま 間違いがなければ 80% くらいできたようなもんで、 あとは最後までぼくが飽きないことですね (^^; > marshalは「壊れたオブジェクトをdump/loadしない」というポリシー > から、「limitを越えた時点でエラー無しで中断」という挙動を提 > 供していません。 それは尊重します。現状は安全で便利ですので。 セキュリティの問題もありますしね。 > |# 最近話題の xmarshal.rb で、「複数のオブジェクトのインスタンス > |# 変数として参照されているオブジェクトは、重複してダンプされる」 > |# という問題も、これが原因でしょうか。 > > たぶん、違うと思います。marshalは一度dumpしたオブジェクトを > ぜんぶテーブルにいれて「これはもう出力したな」というチェック > を行ってますが、xmarshalはそれを行っていないのでしょう。とい > うか、しないのが普通で、してるmarshalが普通でないような気が > しますが。 あ、そうですね。ちょっと勘違いしていました。失礼しました (^^; そもそも Marshal や Object#_dump/_load は使ってませんもんね。 で、ぼくのやりたいのは、チェック*する*ほうです。ざっくり言えば、 Marshal の出力形式が Hash になるというやつです。 P.S. こんなことやってるから blade clone は中断されてます。 やめてはいません (^^;。それのための persistent framework です。 -- Hideto "rubyholic" ISHIBASHI http://www.rr.iij4u.or.jp/~hideto-i/ blade clone: http://www.rr.iij4u.or.jp/~hideto-i/rb/yaiba/index.html