「Hello, World.」の続きです。印字サーバー側(puts00)は終わったので、本の指示に従い起動したままほったらかしておきます。次はクライアント側、本の「2.1.2 irbからの利用」あたりからです。クライアント側はirbでやるんですね。irb(interactive ruby)は、Rubyと逐次対話しながら遊べる道具です。
there = DRbObject.new_with_uri('druby://localhost:12345')
と、irbでタイプします。
DRbObject.new_with_uri()は、URIを指定して分散オブジェクトへの参照を返します。 URIはputs00.rbを起動したときに与えたものを指定します。生成された参照(DRbObject)をthereという変数に覚えておきます。2.1.2 irbからの利用
ふむふむ、ちゃんと理解するためにDRbObject.new_with_uriの中を見てみると、
self.new(nil, uri)
してるだけのようです。selfってのは自分を指すのでしょう。が、selfの厳密な意味や仕組みを深く突っ込んでいくとハマりそうな気配がするのでちょっとうやむやにしておます。ここでは、DRbObjectのインスタンスが生成されている、という程度の理解で留めます。つまり、どんなオブジェクトがサーバーで共有されていようが、リモートオブジェクトの参照は、必ずDRbObjectだということですね。クライアント側からリモートオブジェクトにアクセスするには必ずDRbObjectを介するので、DRbObjectさえ理解すりゃアクセスの仕組みはわかると。そういうことと理解しておきます。
じゃあ、次にみるべきところはDRbObjectのinitializeかなあ。中を見ると、、、まず、第1引数のobjがnilかどうかで分岐しています。でもどちらも@uriと@refに設定していることに違いは無いようです。で、この場合は必ずnilなので、そっちのブロックを見ることにします。
@uri,option=DRbProtocol.uri_option(uri,DRb.config)
うごご、「@uri,option = ・・・」なんじゃこりゃ、全然わからん。一体@uriには何がセットされるんだ、、、で、30分以上悩みました。。が、これは多重代入と言うんですね。右オペランドと左オペランドの要素数が複数個でも入れれるだけ入れてくれると。ふむふむ。例えば
foo, bar = 1,2;p foo;p bar
とすると、
1
2
と出ます。実際には[1,2]と配列になって返却されているようですね。こんなことも知らないとは、、と言われそうですが知りませんでした。Ruby(というかスクリプト言語)の通信簿1ですからね。はい。またちょっと賢くなりました、さて、ここでは、DRbProtocol.uri_optionを呼び出し、uriとoptionを返してもらっています。引数になっているDRb.configあたりを追いかけると出てくるcurrent_serverは、いたるところで使われていてかなり重要そうな気がしますが、とりあえずこれは今回は置いておきます。なので、次は素直にDRbProtocol.uri_optionを見ていきます。(多分元のuri文字列を加工してるんだろうなという想像をしつつ。)
def uri_option(uri, config, first=true)
@protocol.each do |prot|
begin
uri, opt = prot.uri_option(uri, config)
# opt = nil if opt == ''
return uri, opt
・・・
まず、@protocolは配列なので複数のプロトコルをサポートしているようです。デフォルトではDRbTCPSocketの一要素のみが設定されています。で、これが「druby://ホスト:ポート番号」のやつだと。でも、正確には「druby://ホスト:ポート番号?オプション」と言う形式で指定される仕様のようです。@protocolのuri_optionメソッドはDRbTCPSocketの同名メソッドに委譲しています。この中で指定されたURI文字列をパースして、ホストやポート番号、オプションに分解しているようですね(多分同じことはDRbServerの方でもやってるんでしょうが、main_loopの中まで見ていなかったので遭遇しなかったようです)。
@uri周りはこれ位で満足しました。次は@refです。もう一度DRbObjectのinitializeに戻って
@ref = DRbURIOption.new(option) unless option.nil?
これは、unlessだから、「optionがnullでなければoptionを@refに代入しろ、optionがnullならDRbURIOption.newして@refに代入しろ」ってことです。理由はわかんないけどオプションを単なる文字列じゃなくてDRbURIOptionオブジェクトとして持っておきたいってことなのだろうという程度の理解で次。
ってあれれーー??もう終わった。設定のみか。何か簡単すぎるような気もするが気にせず進めていきます。これで、irbでさっきタイプした
there = DRbObject.new_with_uri('druby://localhost:12345')
のthereに、uriやoptionが設定されたDRbObjectのインスタンスが代入されるんだよ、ってところまでは理解しました。で、引き続き次はirbで、
there.puts('Hello, World.')
と打つとサーバー側のputs00を動かしているターミナルに表示される。うん、すごい!!でもどうやってんだろ、DRbObjectには当然ながらputsなんてメソッドはありません。でも呼べてるんだから何かある。。と、これはどうやらmethod_missingメソッドの中でやっています。RubyのObjectクラスのリファレンスを見ると
method_missing(name, args, ... )呼びだされたメソッドが定義されていなかった時、Ruby がこのメソッドを呼び出します。
とあります。DRbObjectではこのメソッドをオーバーライドして、その中でリモートのメソッド呼び出しをやっているんですね。この中身も見たいところだが、今日はもう時間切れ。とりあえず「Hello, World.」が出るのは出た!
後、クライアントからputsで文字列を渡すとサーバー側の標準出力に出るってのは確認できましたが、サーバー側のリモートオブジェクトからクライアント側の@stdoutには出せないのかとか、色々気になりはじめたことがあります。が、これらも今追求せずとも読み進めるにつれて明らかになっていくでしょう。