<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
   <channel>
      <title>dRubyの正面</title>
      <link>http://www.grandnature.net/druby/</link>
      <description>このサイトは、「dRubyによる分散・Webプログラミング」という書籍の一読者が、理解を深めるために記述したメモです。</description>
      <language>ja</language>
      <copyright>Copyright 2011</copyright>
      <lastBuildDate>Wed, 14 Nov 2007 10:45:38 +0900</lastBuildDate>
      <generator>http://www.sixapart.com/movabletype/</generator>
      <docs>http://blogs.law.harvard.edu/tech/rss</docs> 

            <item>
         <title>2章.Hello, dRuby （その3）</title>
         <description><![CDATA[「2章.Hello, dRuby の3回目（その3）」ですが、前回のエントリでは見れなかったDRbObjectのmethod_missingの中が気になるので、今回は本を読み進めず、こちらの中の概要だけでも見てみることにしました。以下がDRbObjectのmethod_missingの全てです。

<blockquote>
<pre>
def method_missing(msg_id, *a, &b)
  if DRb.here?(@uri)
obj = DRb.to_obj(@ref)
DRb.current_server.check_insecure_method(obj, msg_id)
return obj.__send__(msg_id, *a, &b) 
  end
  succ, result = self.class.with_friend(@uri) do
    DRbConn.open(@uri) do |conn|
      conn.send_message(self, msg_id, a, b)
    end
  end
  if succ
    return result
  elsif DRbUnknown === result
    raise result
  else
    bt = self.class.prepare_backtrace(@uri, result)
result.set_backtrace(bt + caller)
    raise result
  end
end
</pre>
</blockquote>

すごくちょっとしかありません。これくらいならみれるかなあ。じゃあまず最初の分岐。

<blockquote>
if DRb.here?(@uri)
</blockquote>

これは、スクリプトを実行している自分がオブジェクトを公開しているサーバー自身かどうかを訊ねています。自分がオブジェクトを公開しているサーバーだったら、リモート呼び出しの仕組みを使わずにそのまま呼んじゃえという事だと思われます。対して、リモート呼び出しのときに実行されるのが以下。

<blockquote>
<pre>
succ, result = self.class.with_friend(@uri) do
  DRbConn.open(@uri) do |conn|
    conn.send_message(self, msg_id, a, b)
  end
end
</pre>
</blockquote>

かなり短いですが、結構ムズい。渡されるブロックが入れ子になって2つあります。多分yieldの2段呼びだと想像しつつ、この5行をもうしばらく眺める。ちなみにyieldとはメソッドにブロックを渡すとそれを任意のタイミングでメソッドの中から呼べるというものみたいです。こんなやつです。

<blockquote>
<pre>
def add 
  p '足します'
  yield(1,2) 
  p '足しました'
end
add do |a,b|
  p a + b
end
</pre>
</blockquote>

上のスクリプトを実行すると、下のように出力されます。

<blockquote>
<pre>
足します
3
足しました
</pre>
</blockquote>

　大抵の場合、yieldは上の例のようなヘボい使い方はせず、イテレータで回す時に使われるようです。yieldの説明はこれくらいにして、さっきの5行ですが、よく見ると単純で、多分サーバーへの接続を確保し、そこへメソッド呼び出しのメッセージを送るという処理なのだなと言う想像くらいは容易につきます。これをわざわざyieldでブロックを渡してやろうとしているということは、複数の接続先が配列などで管理されていて、そこから今回の送り先を選び出して送るか、または、送り先全てに対して順次送ったりするんだなと推測出来そう。っと、そういう前提を抱きつつ中を見てみることにします。

<blockquote>
<pre>
def self.with_friend(uri)
  friend = DRb.fetch_server(uri)
  return yield() unless friend
・・・
</pre>
</blockquote>

　。。。。はい、全然よくわかりません。。friendって何かしら。お友達関係にあるサーバーと連携できるのかな。とりあえずHello, World.レベルの単純なクライアントからの場合はfriendはnilっぽいですね。とりあえず今回はfriendはnilと読みかえることにします。だからこのreturn yieldも実行されて、中のDRbConn.open(@uri) do ・・・ブロックが動きます。

　DRbConnとは、DRbObjectの中で使われる単純なクラスで、具体的にはサーバーとの接続をプールしているみたいです。毎回繋げに行くのはコストが掛かるから、一度つなげた接続は使いまわして、メソッド呼び出しに掛かるコストを減らしてくれるんですね。で、えっと、DRbConn.openなので、これはクラスメソッドか。Rubyでは“self.メソッド名”と定義すると、クラスメソッドになるようです。なので、self.openメソッドを見てみます。

<blockquote>
<pre>
def self.open(remote_uri)  # :nodoc:
  begin
conn = nil
・・・
conn = self.new(remote_uri) unless conn
succ, result = yield(conn)
return succ, result
・・・
</pre>
</blockquote>

　実際のコードはpoolにいれたりpoolに照会したり、poolに対するアクセスをアトミックにしたりともっと長いけど、肝心なところだけ抜粋したつもりです。poolに何も入っていない最初のアクセスの場合は、connもnilのままなので、ここでDRbConnのインスタンスを作って、それ自身を渡してyieldですね。で、そうすると2段ブロックの中の方が動いてその結果を返すと。ふむふむ。ということで次はDRbConnのsend_messageです。

<blockquote>
<pre>
def send_message(ref, msg_id, arg, block)  # :nodoc:
  @protocol.send_request(ref, msg_id, arg, block)
  @protocol.recv_reply
end
</pre>
</blockquote>

ここでのrefはDRbObjectですね。msg_id, arg, blockは、method_missingに渡された引数そのままです。@protocolはDRbTCPSocketだということは以前のエントリで既に見たので、DRbTCPSocketのsend_requestを見てみます。

<blockquote>
<pre>
def send_request(ref, msg_id, arg, b)
  @msg.send_request(stream, ref, msg_id, arg, b)
end
</pre>
</blockquote>

ちなみに@msgは何物かというと

<blockquote>
@msg = DRbMessage.new(config)
</blockquote>

　と初期化されていました。他プロセスのオブジェクトに対して実際にメッセージを送るのはDRbMessageがやっているようです。で応答をrecv_replyで受け取ってsuccとresultに詰めるという感じです。succが成功or失敗の真偽値、resultがメソッドの戻り値です。ちなみにDRbMessageの中ではMarshalを使ってdumpしているようです。詳しくはまた今度見ることにします。

　終わった。送信部分の流れは大体わかりました。もしかして後はDRbServerのmain_loopの中さえ見りゃ、dRubyの基本的な内部構造にはさらっと目を通したことになる。か？

　ところで、yield2段を目で追うのがつらかったので、今回からデバッガを使い始めました。しかし、そこで少しハマりました。デバッガとかで見ていると、デバッガが変数のインスペクション情報を取得するために勝手にto_sとかを呼び、私自身が試そうとしているスクリプトよりも先にリモート呼び出しをしているので、既にDRbConnにプールされていました。くそー、まだ一度もリモートで呼び出ししていないからインスタンス出来てないはずなのにどこで作ってるんだよ、わからん、って思って悩んだというわけです。それは置いといて、デバッガもリモートオブジェクトだと言う意識がないということだなあ。デバッガも騙されるdRubyって。。ちょっとびっくりです。

　次から本に戻ります。どこを読んでたかなあ。あ、まだHello, World.か。
]]></description>
         <link>http://www.grandnature.net/druby/2007/09/2hello_druby_3.html</link>
         <guid>http://www.grandnature.net/druby/2007/09/2hello_druby_3.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">2章</category>
        
        
         <pubDate>Fri, 07 Sep 2007 12:55:59 +0900</pubDate>
      </item>
            <item>
         <title>2章.Hello, dRuby （その4）</title>
         <description><![CDATA[本の続きに戻ります。サーバを停止させた状態でリモート呼び出しをしたらどうなるか実験してみます。puts00のサーバーを停止してthere.putsを実行っと。 

<blockquote>
DRb::DRbConnErrorという例外が発生しました。 DRbConnErrorはdRuby内部で通信エラーが発生したことを示します。サーバが停止してしまったので、メソッド呼出しに失敗してしまったのです。

2.1.2. irbからの利用
</blockquote>

確かに本の通りにDRbConnErrorがでました。

<blockquote>
<pre>
def initialize(remote_uri)   # :nodoc:
  @uri = remote_uri
  @protocol = DRbProtocol.open(remote_uri, DRb.config)
end
</pre>
</blockquote>

場所はここ、DRbConnのインスタンス生成時のDRbProtocol.open。このメソッドは

<blockquote>
<pre>
def open(uri, config, first=true) 
  @protocol.each do |prot|
    begin
      return prot.open(uri, config)
    rescue DRbBadScheme
    rescue DRbConnError
      raise($!)
    rescue
      raise(DRbConnError, "#{uri} - #{$!.inspect}")
    end
  end
  ・・・
</pre>
</blockquote>

　です。このprot.openでは、実際にはDRbTCPSocketのクラスメソッドopenが呼ばれるので、この中のTCPSocket.openで例外が発生している感じですね。それをrescueでつかまえてraiseしてるのか。ふむふむ。ではopenは成功したけどその後に通信エラーになった場合はどうなるのかなあ。DbConnのopenの中身の一部です。

<blockquote>
<pre>
  ・・・
@pool.each do |c|
   if conn.nil? and c.uri == remote_uri
     conn = c if c.alive?
   else
     new_pool.push c
   end
end
@pool = new_pool
  ・・・
</pre>
</blockquote>

　プールから取る時の、「conn = c if c.alive?」で、この時点でサーバー側が死んでいたらそれを使わないようになっています。で、もしここでも接続は生きてたけど、送信までに死んだ場合は、recv_replyのどこかで例外が出るようです。
　ここでは、@poolの要素群をnew_poolという入れ物に一度移し替えて、それを@poolに戻しています。ただ今回使われることになった接続オブジェクトは、この時点ではnew_poolに入っていません。プールに接続オブジェクトを入れているのは、その下に記載されているのensureです。（ensureってのは、javaでいうfinallyみたいなやつです。）

<blockquote>
<pre>
@pool.unshift(conn)
@pool.pop.close while @pool.size > POOL_SIZE
</pre>
</blockquote>

　新しいconnをプール用配列の先頭に入れ、プールの要素数の上限を越えた要素は取り除いて閉じる。ってことか。whileも右側に書けるのですね。かっこいいコードだなあ。

　
　ところで、サーバーは無事生きているけど、リモートオブジェクトに存在しない操作を行なった場合はどうなるのでしょうか。ここではhogeメソッド（Putsに存在しない操作）を呼んでみました。

<blockquote>
there.hoge
</blockquote>

すると以下のようなresultになりました。ちなみに、成功/失敗が設定されるsuccの値はfalseになっています。

<blockquote>
result undefined method `hoge' for #&lt;Puts:0x29ba790 @stream=#&lt;IO:0x294e758&gt;&gt;
</blockquote>
]]></description>
         <link>http://www.grandnature.net/druby/2007/09/2hello_druby_4.html</link>
         <guid>http://www.grandnature.net/druby/2007/09/2hello_druby_4.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">2章</category>
        
        
         <pubDate>Mon, 10 Sep 2007 14:44:39 +0900</pubDate>
      </item>
            <item>
         <title>[番外]今までの整理</title>
         <description><![CDATA[ひとまず今までのところで学んだことを整理することにしました。

<strong>dRubyとは</strong>

<ol>
<li>Rubyのオブジェクトをプロセス間でやり取りするための仕掛け</li>
<li>dRubyは100%Rubyなので、変数が型に縛られないから楽に使える。IDLとかもいらない</li>
<li>dRubyはプロセス間通信の仕組み自体もRubyで書かれているので、OSに依存しない</li>
</ol>

<strong>dRubyの仕組み</strong>

<ol>
<li>URIに対してひとつのオブジェクトを紐付けられる</li>
<li>複数のプロトコルを扱える。デフォルトはDRbTCPSocket。自分で追加も可能</li>
<p/>URI文字列のパースや、メッセージをやり取りするときにProtocol関係のオブジェクトへ委譲されるため
<li>サーバー側でどんなオブジェクトを公開してようとも、クライアントが扱えるリモートオブジェクトは常にDRbObject</li>
<li>DRbObjectを使った呼び出しであっても、自分自身がサーバー側であれば、ローカルのオブジェクトのメソッドを直接呼ぶ</li>
<li>DRbObjectのmethod_missingからリモート呼び出しをしている</li>
<li>メッセージの送受信を実際に行っているのはDRbMessage</li>
<li>サーバーへの接続はDRbConnによってプールされ、接続の確立にかかるコストを抑えている</li>
</ol>

<strong>まだわからないところ</strong>

<ol>
<li>puts00でサーバー側のPutsからクライアント側の@stdoutに出すにはどうしたらいいの？</li>
<li>サーバー側は、マルチスレッドで並列にメッセージを処理している？</li>
</p>まだmain_loop内を見ていないのでわからない
<li>fetch_serverのfriendって何？どういうときに使われるの？</li>
<li>通信時のMarshalを使った仕組みの詳細</li>
<li>プールのところにある#FIXMEがなぜFIXMEなのか</li>
<li>URIのoption引数の使い方、というか使われ方</li>
</ol>
]]></description>
         <link>http://www.grandnature.net/druby/2007/09/post_2.html</link>
         <guid>http://www.grandnature.net/druby/2007/09/post_2.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">2章</category>
                  <category domain="http://www.sixapart.com/ns/types#category">その他</category>
        
        
         <pubDate>Tue, 11 Sep 2007 09:42:35 +0900</pubDate>
      </item>
            <item>
         <title>[番外]単純な送信時のメッセージパッシングの雰囲気</title>
         <description><![CDATA[今まで学んだ部分（クライアント側）をコミュニケーション図で整理してみました。が、正確ではありません。yieldや、moduleの扱いをどう描いたものか難しいですねえ。。クラスメソッドとインスタンスメソッドも区別していません。細かいところも結構端折ってます。なので、あくまでも雰囲気ということで。

（画像をクリックすると別窓に表示します）
<a href="http://www.grandnature.net/druby/img/com_d1.jpg" target="_blank"><img src="http://www.grandnature.net/druby/img/com_d1.jpg" width="100%"></a>]]></description>
         <link>http://www.grandnature.net/druby/2007/09/post_3.html</link>
         <guid>http://www.grandnature.net/druby/2007/09/post_3.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">2章</category>
                  <category domain="http://www.sixapart.com/ns/types#category">その他</category>
        
        
         <pubDate>Wed, 12 Sep 2007 10:44:55 +0900</pubDate>
      </item>
            <item>
         <title>2章.Hello, dRuby （その5）</title>
         <description><![CDATA[本の続きに戻ります。「2.1.4 dRubyのURI」からです。小見出しの通り、DRbServerとURIとの関係についてのお話です。

<blockquote>
TCPを使って接続する場合のdRubyのURIの書式は次の通りです。

<strong>druby://ホスト名:ポート番号</strong>

URIはホスト名とポート番号から構成されます。 URIによってdRubyのサーバを特定することができます。

URIを使用するのは、サービスの起動時とDRbObjectの生成の場面です。 DRb.start_serviceはURIとそれに関連付けるオブジェクトを指定して dRubyのサービス（DRbServer）を開始します。 DRbServerにはサーバソケットの管理やソケットを待ち受けるスレッドなどサーバプログラミングに必要なもの一式が入っています。具体的には、URIのホスト名とポート番号を用いて生成したTCPServerや、 acceptで接続を待ち受けるスレッドなどのオブジェクトです。 URIはDRbServerを特定する情報で、DRbServerは一つの固有なURIを持ちます。

<strong>DRb.start_service(uri, front)</strong>

2.1.4 dRubyのURI

</blockquote>

ふむふむです。今までのエントリで、さらっと中身を覗いたこともあり、大体感じはわかった。ちなみに、本文中のacceptとは、クライアントからの接続があるまで待ち続けるシステムコール（もしくはシステムコールを呼び出すことのできる何か）のことだと思います。ソケット通信とかのサーバーのプログラムを自分で書くときは、大抵の場合、acceptで待ち続け、クライアントからの要求に応じてスレッドを生成したり、forkして子プロセスを作ったりすることが多いと思います（forkとは分身の術のようなものです。）。こうすることでクライアントからの複数の要求を並列に処理することが出来ます。dRubyのDRbServerのmain_loopでも、スレッドを生成して並列に処理しているのだと思います（と想像）。で次と。

<blockquote>

URIと関連付けられたオブジェクトは、そのサービスの入り口（受け付け担当）となるため、フロントオブジェクトと呼びます。 DRbObject.new_with_uri()で作成したオブジェクトへのメソッド呼び出しは、全てこのフロントオブジェクトに届くことになります。実際のアプリケーションでは、アプリケーション内部のオブジェクトをそのまま URIに関連付けずに、アクセス制御をしたり、複数の処理をまとめて行うユーティリティ的なメソッドを用意したりした、特別なクラスをフロントに置くことが多いです。

2.1.4 dRubyのURI

</blockquote>

DRbServerで公開するリモートオブジェクトは、フロントオブジェクトと言うのですね。で、門番とか総合受付みたいなことをさせることが多いと。図がわかりやすいです。オブジェクトを公開しているDRbServerはURIによって特定されると言うことなので、URIの理解が大事なようです。本ではしばらくURIの話が続きます。URIは下記のように一部省略できるようです。

<blockquote>

'druby://hostname:12345' - 全て指定した完全な形 
'druby://:12345' - ホスト名を省略した形式 
'druby://hostname:0' - ホスト名を指定するがポート番号を省略した形式 
'druby://:0' - ホスト名もポート番号も省略した形式 
nil - ホスト名もポート番号も省略 

2.1.4 dRubyのURI

</blockquote>

続いて、本に従いirbで実行っと。。ちゃんとできました！！別のコンピュータとも問題なくやり取り出来ますね、dRubyのオブジェクトのやり取りは、標準でTCPSocketを使っているため、同じコンピュータのプロセス間のみではなく、別コンピュータのプロセスともやり取りできます。可能性無限大。

。。。でも、あれれ？先日のエントリで確認した限りでは、「druby://」のプロトコルでは、ポート番号の後に引数を追加して何か出来るような気がしました。

<blockquote>
druby://ホスト名:ポート番号?オプション
</blockquote>

URIのパース部分の正規表現はこう


<blockquote>

/^druby:\/\/(.*?):(\d+)(\?(.*))?$/

</blockquote>

オプションは確かDRbURIOptionになっていたやつでした。これは何に使うんだろうなあ、という疑問がわきます。メッセージ送信部分のソース（DRbMessageのsend_request）を一応みてみると

<blockquote>
<pre>
def send_request(stream, ref, msg_id, arg, b) # :nodoc:
  ary = []
  ary.push(dump(ref.__drbref))
  ary.push(dump(msg_id.id2name))
  ary.push(dump(arg.length))
  arg.each do |e|
    ary.push(dump(e))
  end
  ary.push(dump(b))
  stream.write(ary.join(''))
rescue
  raise(DRbConnError, $!.message, $!.backtrace)
end
</pre>
</blockquote>

とあります。ここのref.__drbrefは、オプション引数DRbURIOptionそのものなので、dumpされた情報には確かに含まれているようです。おそらくサーバー側でのマーシャルデータ復元時に、DRbObjectのrecv_requestとかそのあたりでよきに計らってくれるようですが、ちゃんと追いかけることができていません。このあたりの世界は、もうちょっと本を読み進めてから再挑戦することにします。

でも、内部の仕組みはともかく、オプションの利用方法くらいは知りたいなあ。と思って調べたら、咳さんのはてなに載ってました。

<blockquote>

URIから参照を作ろうとすると、サービスの入り口のオブジェクトしか参照できないことになるんだけど、数年前に追加されたオプションを使うともうちょっと先のオブジェクトを参照できる。

<strong> druby://localhost:12345?option </strong>

「?」の後の文字列はURIのオプション引数(?)となり、frontオブジェクトにこの文字列を問い合わせた結果のオブジェクトを参照する。問い合わせはメソッドに?以後の文字列を与えて行う。

<a href="http://d.hatena.ne.jp/m_seki/20061028#1162045404" target="_blank">I like Ruby too. - dRubyのURIのオプション </a>
</blockquote>

ふむふむ、そういうことか。（てか、最近のエントリでこのサイトも紹介してくれてる！）

雰囲気を味わうために、puts00.rbをちょっと改造してみます。

<blockquote>
require 'drb/drb' 

class Puts
　def initialize(stream=$stdout)
　　@stream = stream
　end
　def puts(str)
　　@stream.puts("#{str}  [Puts]")
　end
end

class Puts2
　def initialize(stream=$stdout)
　　@stream = stream
　end
　def puts(str)
　　@stream.puts("#{str} [Puts2]")
　end
end

class Front
　def initialize
　　@hash = {"puts" =&gt; Puts.new, "puts2" =&gt; Puts2.new }
　end
　def [](arg)
　　@hash[arg]
　end
end

uri = ARGV.shift
DRb.start_service(uri, Front.new)
puts DRb.uri
sleep
</blockquote>

Puts2という、Putsのようなものをもうひとつ定義し、それぞれのインスタンスをハッシュに入れて切り替えられるようにしただけです。
ちなみに{}で定義できるものをハッシュといい、キーと値のペアで情報を出し入れできる便利ないれものです。なお#{hoge}は式展開といい、文字列中にオブジェクトの内容を入れることができる書き方です。Ruby好きな人は式展開が好きらしいです。

はい、で、ハッシュのキーをオプションの文字列にしました。で、クライアント側のコードは

<blockquote>
require 'drb/drb' 

there = DRbObject.new_with_uri('druby://localhost:12345?puts')
there.puts('Hello, World.')

there2 = DRbObject.new_with_uri('druby://localhost:12345?puts2')
there2.puts('Hello, World.')
</blockquote>

とかで。するとこんな風に出ます。一応出来たのは出来た。

<blockquote>

Hello, World. [Puts]
Hello, World. [Puts2]
</blockquote>


今回はこれにて終了。次はリマインダーのサンプルを見ていきます。]]></description>
         <link>http://www.grandnature.net/druby/2007/09/2hello_druby_5.html</link>
         <guid>http://www.grandnature.net/druby/2007/09/2hello_druby_5.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">2章</category>
        
        
         <pubDate>Fri, 14 Sep 2007 10:14:24 +0900</pubDate>
      </item>
            <item>
         <title>2章.Hello, dRuby （その6）</title>
         <description><![CDATA[今日は、dRuby幸福の王子本のp.16 「2.2 Reminder」からです。ここでは、Reminderのアプリケーションを作って動かしてみます。いよいよdRubyを使ってアプリケーションを作成するときが来ました。要件は本に記載されている通りです。

<blockquote>

だれでも書き込め、削除できるメモ帳。 
ユーザインターフェイスはirb。

2.2 Reminder
</blockquote>

本の図を見ると大体の構造がわかりますね。公開されるReminderのオブジェクト（フロントオブジェクト）は以下です（転載）

<blockquote>

# reminder0.rb
class Reminder
　def initialize
　　@item = {}
　　@serial = 0
　end

　def [](key)
　　@item[key]
　end

　def add(str)
　　@serial += 1
　　@item[@serial] = str
　　@serial
　end

　def delete(key)
　　@item.delete(key)
　end

　def to_a
　　@item.keys.sort.collect do |k|
　　　[k, @item[k]]
　　end
　end
end
</blockquote>

メモの文字列はハッシュに入れられると、で、ハッシュには1からのシリアル番号が振られ、それがキーになり添字でアクセスできるようになっていると。はい、理解しました。で、本に従い、irbからサーバーとクライアントを立ち上げて、リマインダーに登録してみます。

<blockquote>

r.add('13:00 ミーティング')
r.to_a
=> [[1, "13:00 ミーティング"]]

</blockquote>

無事登録できましたね。それから本の通り、ターミナルの数を増やしたり、クライアントを落としてから再度立ち上げたりしてみましたが、今まで登録した内容はちゃんと残っています。めでたし。

ところで、ここで

<ol>
<li>サーバーを落とす</li>
<li>もう一度サーバーを立ち上げる</li>
<li>クライアントから操作する</li>
</ol>

とすると、今までリマインダーに登録した情報は消えています。つまりリマインダーの情報はサーバー側が持っているのですね。ここをもうちょっと調べるために、通信の内容を間単に見てみます。

<dl>
<dt>サーバー</dt>
<dd>192.168.0.4</dd>
<dt>ポート</dt>
<dd>9898</dd>
<dt>クライアント</dt>
<dd>192.168.0.2</dd>
</dl> 

としています。つまり、サーバーのURIは
<strong>druby://192.168.0.4:9898/</strong>
です。

クライアントから、

<blockquote>

r = DRbObject.new_with_uri('druby://192.168.0.4:9898')
r.add 'foo'
r.add 'fooo'
r.add 'foooo'
</blockquote>

としてみました。サーバーもクライアントもirbで起動しています。以下はそのパケットのダンプです。

（画像をクリックすると別窓に表示します）
<a href="http://www.grandnature.net/druby/img/cap1.jpg" target="_blank"><img src="http://www.grandnature.net/druby/img/cap1.jpg"  width="100%"></a>

送信のたびにデータ部（データ部とはTCPパケット内の、アプリケーションが自由に使えるデータが含まれる箇所の名前です。）のサイズが44,45,46と1byteずつ大きくなっているのは、addの引数の文字数の差だと思われます。サーバーから送られてくる15byteはメソッドの戻りだと思われます。
データの中身を見なくとも、送信されるデータが44byteと小さいですし、戻されるデータはリマインダーのデータ量に依存せず常に小さいので、本当にメソッド呼び出しだけしかしていない（クライアント側にリマインダーのフロントオブジェクトそのものが渡ってきているわけではない）ことがわかります。

　では次に、フロントオブジェクトそのものではなく、フロントオブジェクトが参照している他のインスタンス（@itemとか）について見てみます。サーバー側のirbでリマインダーに情報を追加してみます。

<blockquote>
<pre>
irb(main):008:0> r.add 'hogehoge'
=> 1
</pre>
</blockquote>

それからサーバー側でオブジェクトIDを見てみます。ちなみに、__id__で返される数値のことをここではオブジェクトIDと呼んでいます。これは、各オブジェクトの間で重複しないように付けられた識別番号（住民票コードのようなもの）です。もちろんプロセスが違っていれば同じ値が振られることもありますが、同一プロセスの中ではユニークになります。


<blockquote>

irb(main):011:0&gt; str = r[1]
=&gt; "hogehoge"
irb(main):012:0&gt; str.__id__
=&gt; 21567584
irb(main):013:0&gt; r[1].__id__
=&gt; 21567584
</blockquote>

ふむふむ、続いてクライアント側のirbで同様のことをしてみます。

<blockquote>

irb(main):009:0&gt; str = r[1]
=&gt; "hogehoge"
irb(main):010:0&gt; str.__id__
=&gt; 21563792
irb(main):011:0&gt; r[1].__id__
=&gt; 21778008
</blockquote>

　おや？クライアント側は、代入した時点で別のオブジェクトIDになっています。つまりstrとr[1]は別物です。これは多分、クライアント側の

<blockquote>
<pre>
irb(main):009:0> str = r[1]
</pre>
</blockquote>

　この行で、r[1]をサーバー側に問い合わせた時点で、オブジェクトの参照ではなく複製されたものが取得されるからでしょう。つまりr[1]は値渡しです。通常のRubyプログラミングと違いますね。このあたりは多分色々と難しいことが多そうなので、本を読み進めながら徐々に理解していきたいと思います。

　で、それから後ですが、ReminderCUIを作って遊び、「2.3 まとめ」をふむふむと読みました。はい、これで「2章：Hello, dRuby」は終わりにしたいと思います。結構長かったけど、dRubyの雰囲気は一応理解できた！つもりです。

　本だと、この次は「3章：eRuby」に続きますが、このサイトでは「3章：eRuby」を飛ばして「4章：参照渡しと値渡し」に入ります。「3章：eRuby」を飛ばす理由は、Webアプリケーションの話が入ってくると、多分dRubyそのもの以外に色々と面倒なことが出てきて混乱しそうだからです。このサイトでは、まずdRubyのことを学びたいので、Webアプリケーションで活用することは後回しにしたいと思います。

　「4章：参照渡しと値渡し」では、今日のこのエントリで見た付近の仕組みなども解説されているようなので、今から楽しみにしておきます。

　dRubyは結構脳ミソが刺激されていいです。]]></description>
         <link>http://www.grandnature.net/druby/2007/09/2hello_druby_6.html</link>
         <guid>http://www.grandnature.net/druby/2007/09/2hello_druby_6.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">2章</category>
        
        
         <pubDate>Thu, 20 Sep 2007 21:53:22 +0900</pubDate>
      </item>
            <item>
         <title>4章.参照渡しと値渡し （その1）</title>
         <description><![CDATA[今日から4章（p.55～）に入ります。「参照渡しと値渡し（pass by reference, pass by value）」とは、今までにやった内容をさらに深く踏み込むようなタイトルがついている章ですね。この章で主に説明されているのはオブジェクトの渡し方についてです。すごくワクワクします。うん。それでははじまりはじまり。


<strong>Rubyの場合</strong>

<ol>
<li><strong>参照渡し</strong></li>

Rubyの場合は、基本は参照渡しです。本に記載されているスクリプトを抜粋します。

<blockquote>
<pre>
% irb --prompt simple
>> def foo(str); str.chop!; end
>> my_str = "Hello, World."
>> foo(my_str)
>> my_str
=> "Hello, World"
>> foo(my_str)
>> my_str
=> "Hello, Worl"
</pre>
</blockquote>

foo(my_str)とすると、my_strの内容が複製されて渡されるわけじゃなく、my_strの参照が渡されます。chop!ってのはStringクラスのメソッドで、文字列の最後の1文字を取り除きます。chop!は、渡されたオブジェクトの内容を書き換えますので、foo(my_str)でmy_strの方も変わっちゃうということです。（!がメソッド名の末尾についているやつは、大抵自分自身の内容を直接変えるやつのようです。）

ちなみに、代入も参照の代入なので

<blockquote>
<pre>
my_str = "Hello, World."
my_str2 = my_str
my_str2.chop!
</pre>
</blockquote>

とすると、my_strも変わりますね。

<li><strong>値渡しその1：浅いコピー（shallow copy）</strong></li>

値渡しをしたいときは、dupで複製を作って渡すようです。以下も本からの抜粋です。

<blockquote>
<pre>
>> my_str = "Hello, World."
>> foo(my_str.dup)
=> "Hello, World"
>> my_str
=> "Hello, World."
>> foo(my_str.dup)
=> "Hello, World"
>> my_str
=> "Hello, World."
</pre>
</blockquote>

はい、出来ました。なお、dupはオブジェクトを複製するメソッドですが、浅くコピる（インスタンス変数などまでは複製しない）ようです。2章で本に登場したReminderクラスを使って試してみます。Reminderクラスとは、本のp.16に載っているreminder0.rbのことです。今回はRubyの実験なのでdRubyは使いませんよ。

<blockquote>

irb(main):002:0&gt; r = Reminder.new
=&gt; #&lt;Reminder:0x2927ce4 @serial=0, @item={}&gt;
irb(main):003:0&gt; r.add('hogehoge')
=&gt; 1
irb(main):004:0&gt; r.to_a
=&gt; 1, "hogehoge"
irb(main):005:0&gt; r2 = r.dup
=&gt; #&lt;Reminder:0x29a1abc @serial=1, @item={1=&gt;"hogehoge"}&gt;
irb(main):006:0&gt; r.__id__
=&gt; 21577330
irb(main):007:0&gt; r2.__id__
=&gt; 21826910
irb(main):008:0&gt; r[1].__id__
=&gt; 21564760
irb(main):009:0&gt; r2[1].__id__
=&gt; 21564760
irb(main):010:0&gt; r2.delete 1
=&gt; "hogehoge"
irb(main):011:0&gt; r.to_a
=&gt; []
irb(main):012:0&gt; r2.to_a
=&gt; []
</blockquote>

r（Reminderのインスタンス）と、rをdupして作成したr2はそれぞれ別のIDになっていますが、インスタンス変数である@itemの各要素までは複製されていないため同じIDを返します。なので、r2で要素を消すとrにも影響を与えます。

<li><strong>値渡しその2：深いコピー（deep copy）</strong></li>

Rubyで深いコピーを実現したいときには、Marshalを使うようです。Marshalはオブジェクトを文字列に書き出したり、それらからオブジェクトに復元したりするもの（モジュール）です（<a href="http://www.ruby-lang.org/ja/man/?cmd=view;name=Marshal" target="_blank">http://www.ruby-lang.org/ja/man/?cmd=view;name=Marshal</a>）

先ほどirbで書いたスクリプトでdupしている部分をMarshalに変えてみます。

<blockquote>

irb(main):003:0&gt; r = Reminder.new
=&gt; #&lt;Reminder:0x2921498 @serial=0, @item={}&gt;
irb(main):004:0&gt; r.add('hogehoge')
=&gt; 1
irb(main):005:0&gt; r.to_a
=&gt; 1, "hogehoge"
irb(main):006:0&gt; <font color="red"><strong>r2 = Marshal.load(Marshal.dump(r))</strong></font>
=&gt; #&lt;Reminder:0x2999e0c @serial=1, @item={1=&gt;"hogehoge"}&gt;
irb(main):007:0&gt; r.__id__
=&gt; 21563980
irb(main):008:0&gt; r2.__id__
=&gt; 21810950
irb(main):009:0&gt; r[1].__id__
=&gt; 21832900
irb(main):010:0&gt; r2[1].__id__
=&gt; 21810890
irb(main):011:0&gt; r2.delete 1
=&gt; "hogehoge"
irb(main):012:0&gt; r.to_a
=&gt; 1, "hogehoge"
irb(main):013:0&gt; r2.to_a
=&gt; []
</blockquote>

変えた部分は赤字の部分のみです。Marshalを使うとごっそり複製されるので、r2で要素を消してもrに影響がありません。

</ol>

<strong>dRubyの場合</strong>

ではdRubyの場合はどうなのかと言うと

<blockquote>

dRubyではRubyのクラスライブラリMarshalを用いて他のプロセスにオブジェクトを渡します。 

4.1.2 dRubyでは？
</blockquote>

dRubyの場合は、先ほど試してみたMarshalの仕組みを使って、オブジェクトを文字列化して転送しているようです。なので、値渡しということですね。今日はここまで、続きはまた次回！
]]></description>
         <link>http://www.grandnature.net/druby/2007/09/4_1.html</link>
         <guid>http://www.grandnature.net/druby/2007/09/4_1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">4章</category>
        
        
         <pubDate>Tue, 25 Sep 2007 16:14:10 +0900</pubDate>
      </item>
            <item>
         <title>4章.参照渡しと値渡し （その2）</title>
         <description><![CDATA[はい、では4章の続きです。今度はdRubyでのオブジェクトの渡し方です。基本は本の通りに進めますが、「2章.Hello, dRuby.」で使ったReminderクラス（p.16のreminder0.rb）を使って試そうと思います。多分このクラスが一番馴染み深いと思うからね。じゃあはじめます。

<strong>dRubyの場合</strong>

<blockquote>

dRubyではRubyのクラスライブラリMarshalを用いて他のプロセスにオブジェクトを渡します。 Marshalは任意のオブジェクトをバイト列に直列化するdumpメソッドと、直列化されたバイト列からオブジェクトを再現するloadメソッドからなります。 MarshalはRubyでdeep copy（深いコピー）を行うのにも利用できます。

4.1.2 dRubyでは？
</blockquote>

とあるので、前回の最後に書いたとおり、dRubyの場合は値渡しということになりそうです。本ではここでMarshalの実験をしています。が、このサイトでは、まずReminderを使った別の実験をしてみます。というわけで、何回か前のエントリでやったように電文を見ます。今回はデータ部の中身までじろじろ見ていきます。TCPパケットは能弁に語る、気がする。

サーバー側は、2章で登場したサーバーをそのまま使います。クライアント側は

<blockquote>

require 'drb/drb' 
r = DRbObject.new_with_uri('druby://hoge')
r.add('foo')
str = r[1]
</blockquote>

とやり、パケットをダンプしました。
まずは

<blockquote>
r.add('foo')
</blockquote>
を見てみます。この送信電文は以下の通りです。色が変わっている部分がTCPのデータ部です。

<a href="http://www.grandnature.net/druby/img/cap2.jpg" target="_blank"><img src="http://www.grandnature.net/druby/img/cap2.jpg" width="100%"></a>

うーん、さっぱりわかんね。fooとかaddとか断片的にわかりますが、転送されるデータの内容（データ部の内訳）は謎です。仕方ないので送信部分のソースも見ていくことにします。

まず、DRbMessageの送信部分です。

<blockquote>

def send_request(stream, ref, msg_id, arg, b) # :nodoc:
　ary = []
　ary.push(dump(ref.__drbref))
　ary.push(dump(msg_id.id2name))
　ary.push(dump(arg.length))
　arg.each do |e|
　　ary.push(dump(e))
　end
　ary.push(dump(b))
　stream.write(ary.join(''))
・・・
</blockquote>

です。最後につけられるbってのがわからなかったので調べました。これはmethod_missingの引数の一部みたいですが、どうやらブロック（do … end）が渡されたときにはこっちに入るようです。ブロックを渡すようなことを当面はしそうに無いので、今回はあまり深く考えずに置いておきます。
さらに、このメソッドの中で各所で呼ばれているdumpメソッドの内容も見てみます。

<blockquote>

def dump(obj, error=false)  # :nodoc:
　obj = make_proxy(obj, error) if obj.kind_of? DRbUndumped
　begin
　　str = Marshal::dump(obj)
　rescue
　　str = Marshal::dump(make_proxy(obj, error))
　end
　[str.size].pack('N') + str
end
</blockquote>

ここで一番重要な処理は

<blockquote>
[str.size].pack('N') + str
</blockquote>

ではないかと思われます。strとは、オブジェクトを文字列に符号化したもの（マーシャルデータ）です。ここではそのstrの前に、strのサイズ（バイト数）を固定長のバイナリデータとしてくっつけています。リファレンスを見てみると、pack('N')とは、big endian unsigned 32bitの形式で、バイナリにパックするメソッドです。で、こいつはプラットフォームに非依存なので、エンディアンの変換は気にしなくてもいいということですね。マーシャルデータの前にサイズをつけているのは、どこまでを読んで復元してよいのかわからないからです。可変長のデータを送受信するときの常套手段かな（それか最後に、“ここまで”という記号をつけて送るかどちらか。）

これでデータ部をだいたい理解できるようになったはず。ではデータ部の先頭から順番に見ていきます。ここはオプション引数が入っているはずの部分です。

<blockquote>
00 00 00 03 04 08 30
</blockquote>

先頭から4バイトが、データ長をpack('N')したものです。03なので、ここから3バイトがオブジェクトの内容だと言う事だと思います。内容は04 08 30ですが、これはnilってことみたいです。実際に以下のようなコードを実行してみると同じ結果になります。

<blockquote>
<pre>
obj = nil
Marshal::dump(obj).each_byte do |b|
  puts sprintf("%x",b)
end
</pre>
</blockquote>

今回はオプション引数が無いのでnilのようです。もし「<a href="http://www.grandnature.net/druby/2007/09/2hello_druby_5.html" target="_blank">2章.Hello, dRuby （その5）</a>」で試したようにオプション引数を使っていればその文字列が渡されてくるのだと思います。では次。

<blockquote>
00 00 00 07 04 08 22 08 61 64 64
</blockquote>

先頭4バイトは7なので、それに続く7バイトを見ます。04 08 22 08 61 64 64は'add'をマーシャルデータ化したものです。最後の61 64 64は、ASCIIで'add'ですね。04 08 22 08 はよくわかんないけど、文字列だよ、みたいなことを教えてる情報なんでしょうきっと。

続きも見ていきます。

<blockquote>
00 00 00 04 04 08 69 06
</blockquote>

これは、数値の1のマーシャルデータです。引数の個数を示します。

<blockquote>
00 00 00 07 04 08 22 08 66 6f 6f
</blockquote>

次に、上記の個数分だけ引数の内容が続きます。今回は、'foo'という文字列のオブジェクトのマーシャルデータです。（66 6f 6fが'foo'です。）複製されたオブジェクトが渡されています。なので確かに値渡しですね。

　ということで、以下がdRubyの（通常の）TCP送信時の電文形式のようです。各々先頭に、サイズを示す4バイトの固定長バイナリが付加されます。

<blockquote>

1. オプション引数
2. メソッド（メッセージ）名
3. 引数の数
4. 引数の内容（3の数だけ存在）
5. ブロックの情報
</blockquote>

以上です。次回は、サーバーからの応答（メソッドの戻り値）を見ていきたいと思います。
]]></description>
         <link>http://www.grandnature.net/druby/2007/10/4_2.html</link>
         <guid>http://www.grandnature.net/druby/2007/10/4_2.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">4章</category>
        
        
         <pubDate>Wed, 03 Oct 2007 11:06:16 +0900</pubDate>
      </item>
            <item>
         <title>4章.参照渡しと値渡し （その3）</title>
         <description><![CDATA[前回のエントリでは、メッセージ呼び出し時の引数について値渡しの確認をしました。今回は応答を見ます。メソッドの戻り値というやつです。前回と同様に2章で利用したリマインダーのdRubyサーバーを立ち上げておき、以下のスクリプトを実行してその中身を見ます。

<blockquote>

require 'drb/drb' 
r = DRbObject.new_with_uri('druby://hoge')
r.add('foo')
str = r[1]
</blockquote>

このスクリプトのうち、前回は

<blockquote>
r.add('foo')
</blockquote>

を見ました。今回はその次のステップである

<blockquote>
str = r[1]
</blockquote>

を見ていくことにします。まず送信部分のTCPデータ部です。前回と同じように、色が変わっている部分がデータ部です。

<a href="http://www.grandnature.net/druby/img/cap3.jpg" target="_blank"><img src="http://www.grandnature.net/druby/img/cap3.jpg" width="100%"></a>

見てわかるとおり、r[1]もメソッドです。（[]というメソッドに、引数：1が渡される）ので、前回のadd('foo')の時と殆ど変わりはありません。解説は省略します。
次はサーバーから返されるデータ（クライアントが受信するデータ）のデータ部の内容を見ていきます。これも色が変わっている部分がデータ部です。

<a href="http://www.grandnature.net/druby/img/cap4.jpg" target="_blank"><img src="http://www.grandnature.net/druby/img/cap4.jpg" width="100%"></a>

データ部は18バイトしかありません。短いですね。先頭から見ていきます。

<blockquote>
00 00 00 03 04 08 54
</blockquote>

これはboolのtrueを表します。リモートのメッセージパッシングが成功したらtrue。失敗したらfalseが入ってくるようです。

で、これに続くのが、 

<blockquote>
00 00 00 07 04 08 22 08 66 6f 6f
</blockquote>

これは、'foo'という文字列のオブジェクトのマーシャルデータです。（66 6f 6fが'foo'です。）複製されたオブジェクトが渡されています。

なお、DRbMessageの以下のメソッドが、該当の処理を行なっている部分だと思います。

<blockquote>

def send_reply(stream, succ, result)  # :nodoc:
　stream.write(dump(succ) + dump(result, !succ))
rescue
　raise(DRbConnError, $!.message, $!.backtrace)
end
</blockquote>

このことからdRubyのメソッドの戻り値の電文形式は以下の通りとわかりました。各々先頭に4バイトのサイズを示す固定長バイナリデータが付加される点は、送信時と同様です。

<blockquote>

1. 成否のbool値
2. メソッドの戻り値のオブジェクト（複製）
</blockquote>

この結果から、通常のdRubyのメッセージは、送受信共に確かに値渡しだということがわかりました。今回はここまでにします。次回は参照渡しをしてみます。
]]></description>
         <link>http://www.grandnature.net/druby/2007/10/4_3.html</link>
         <guid>http://www.grandnature.net/druby/2007/10/4_3.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">4章</category>
        
        
         <pubDate>Thu, 04 Oct 2007 09:50:55 +0900</pubDate>
      </item>
            <item>
         <title>4章.参照渡しと値渡し （その4）</title>
         <description><![CDATA[4章.参照渡しと値渡し （その4）

今回は参照渡しをしてみます。ちょっと本に戻ります。p.57の「4.1.2 dRubyでは？」からです。が、Marshalのあたりは、同じようなことを既に確認したような気がしますのでここでは省略します。本に沿って試してみて下さいませ。ここではその続き「p.58　参照の値渡し」から読みます。

<blockquote>

Marshal.loadによって直列化したオブジェクトをMarshal.dumpで復元するということは、つまりオブジェクトの複製を作ることにほかなりません。ではdRubyはいつも値渡しなのでしょうか？
実は当たっているとも言えるし違うとも言えます。 dRubyでの参照渡しは、元のオブジェクトの代わりに参照情報を含むオブジェクトを直列化することで実現されます。つまり、参照渡しは参照オブジェクトの値渡しなのです。逆に、もう一方の値渡しは単純にオブジェクトを直列化するだけです。

4.1.2 dRubyでは？
</blockquote>

ふむふむ。

<blockquote>

Hello, World.の実験で使ったDRbObjectを覚えていますか？ DRbObjectは元のオブジェクトを参照する情報をもったオブジェクトです。 DRbObjectには目的にごとに二つの生成方法が用意されています。一つはDRbObject.new_with_uriを使ってURIからリモートオブジェクトへの参照を作ることです。もう一つはDRbObject.new(obj)のようにオブジェクトを与え自プロセスのオブジェクトへの参照を作ることです。

4.1.2 dRubyでは？
</blockquote>

ふむふむふむ。

<blockquote>

DRbObject.new(obj)によって、任意のオブジェクトへの参照オブジェクトを作ります。参照を作るということは、他のプロセスがこの参照をつかってメソッド呼び出しする可能性があるということです。ですから、オブジェクトへの参照を作る前に、DRb.start_serviceによってdRubyのサーバを自プロセスで起動しておかなくてはなりません。

4.1.2 dRubyでは？
</blockquote>

ふむふむふむふむふむ！！（こればっか）

元の文章でも充分わかりやすいですが、整理します。ここ、すごい重要な気がします。
<strong>
<ol>
<li>dRubyは、通常は値渡しだが、参照渡しにすることも出来る</li>
<li>dRubyでの参照渡しは、厳密に言うと参照オブジェクトの値渡しだ</li>
<li>参照オブジェクトとは、すなわちDRbObjectのこと</li>
<li>DRbObjectにはふたつの使い道がある</li>
<p/>4-1. DRbObject.new_with_uriを使ってURIからリモートオブジェクトへの参照を作る
4-2. DRbObject.new(obj)のようにオブジェクトを与え自プロセスのオブジェクトへの参照を作る
<li>今までは上 (4-1) の使い道でしか使ってなかった。</li>
<li>DRbObject.new(obj)で参照を作るためには、クライアントであってもDRb.start_serviceしないと駄目</li>
<p/>なぜなら、そのオブジェクトを他からリモート呼び出しすることがあるから（オブジェクトの実体はクライアントにあるから）
</ol>
</strong>

すげ！ちょっと感動します。

確かめてみます。p.60に掲載されているように、ハッシュを公開するサーバーを用意します。

<blockquote>
<pre>
% irb --prompt simple -r drb/drb
>> front = {}
>> DRb.start_service(nil, front)
=> #<DRb::DRbServer:0x ..... >
>> DRb.uri
=> "druby://yourhost:1426"
</pre>
</blockquote>

はい立ち上げてみました。

前回の値渡しの復習をかねて、文字列をハッシュ（フロントオブジェクト）に登録してみます。

<blockquote>

there = DRbObject.new_with_uri('druby://yourhost:1426')
str = "Hello, World."
there[1] = str
</blockquote>

これは勿論うまくいく。では次に参照を渡してみます。参照はDRbObjectのことです。

<blockquote>

there = DRbObject.new_with_uri('druby://yourhost:1426')
str = "Hello, World."
there[1] = str
<font color="#ff0000"><strong>there[2] = DRbObject.new(str)</strong></font>
</blockquote>

しかしこれではうまくいきません。もしこのまま実行すると

<blockquote>
in `current_server': DRb::DRbServerNotFound (DRb::DRbServerNotFound)
</blockquote>

というエラーが出てしまいます。
なぜなら、DRb.start_serviceをしていないからです。クライアントからオブジェクトの参照を放り込んでいるということは実体がクライアントにあるということです。なのでメッセージに応答するのはクライアントです。クライアントが他局からのメッセージ呼び出しを受け付けることができないと駄目なので、dRubyのサービスが必要です。これがDRb.start_serviceをしなければならない理由です。

<blockquote>

<font color="#ff0000"><strong>DRb.start_service</strong></font>
there = DRbObject.new_with_uri('druby://yourhost:1426')
str = "Hello, World."
there[1] = str
<font color="#ff0000"><strong>there[2] = DRbObject.new(str)</strong></font>
</blockquote>

これでうまくいきます。
今日はここまで。次回は今回やった参照渡しの部分をもうちょっと詳しく見るつもりです。
]]></description>
         <link>http://www.grandnature.net/druby/2007/10/4_4.html</link>
         <guid>http://www.grandnature.net/druby/2007/10/4_4.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">4章</category>
        
        
         <pubDate>Thu, 11 Oct 2007 10:08:56 +0900</pubDate>
      </item>
            <item>
         <title>[番外]咳さんに褒められていた件</title>
         <description><![CDATA[おおおおおおおおお、わーい！ありがとうございます。
<blockquote>
記事に出てくる質問に、一つ一つ応えていってよいものなのかしら。
<a href="http://d.hatena.ne.jp/m_seki/20071017" target="_blank">I like Ruby too. - dRubyの正面がすばらしい件について</a>
</blockquote>
ぜひぜひ、気が向いたのだけでもいいですので～

そう言えば誰からもコメントとかないなあ。僕からのインタラクションがないからだと思いますが。
]]></description>
         <link>http://www.grandnature.net/druby/2007/10/post_4.html</link>
         <guid>http://www.grandnature.net/druby/2007/10/post_4.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">その他</category>
        
        
         <pubDate>Fri, 19 Oct 2007 18:40:34 +0900</pubDate>
      </item>
            <item>
         <title>4章.参照渡しと値渡し （その5）</title>
         <description><![CDATA[dRubyでの参照渡しの続きです。dRubyでの参照渡しは、厳密に言うと参照オブジェクトの値渡しであることは前回学びました。参照オブジェクトとはDRbObjectのことなので、まずDRbObjectの内容を見ていくようにしましょう。
DRbObjectには二つのインスタンス変数があります。
<dl>
<dt>@ref</dt>
<dd>オブジェクトの実体のID(hoge.__id__したものです。)</dd>
<dt>@url</dt>
<dd>dRubyサーバーを特定するURI</dd>
</dl>
DRbObjectをnewするときに何かオブジェクトを渡すと、それが@refに入ります。例えばirbでこんなことをやってみます。

<blockquote>

[サーバー側]

irb(main):001:0&gt; require 'drb/drb'
=&gt; true
irb(main):002:0&gt; uri = 'druby://サーバーのホスト名:9898'
=&gt; "druby://サーバーのホスト名:9898"
irb(main):003:0&gt; h = {}
=&gt; {}
irb(main):004:0&gt; DRb.start_service(uri, h)
=&gt; #&lt;DRb::DRbServer:0x295ecd0 ・・・
irb(main):005:0&gt; str = 'foo'
=&gt; "foo"
irb(main):006:0&gt; str.__id__
=&gt; 21680810
irb(main):007:0&gt; <font color="#ff0000"><strong>ref = DRbObject.new(str)</strong></font>
=&gt; #&lt;DRb::DRbObject:0x29530b0 @ref=21680810, @uri="druby://サーバーのホスト名:9898"&gt;
irb(main):008:0&gt; h[1] = ref
=&gt; #&lt;DRb::DRbObject:0x29530b0 @ref=21680810, @uri="druby://サーバーのホスト名:9898"&gt;
</blockquote>

　最後から2つ目の行（:007）を見ます。ここで作成したDRbObjectのインスタンスの内容を確認します。@refは、引数に渡されたstrというオブジェクトのIDが設定されています。@uriは、start_service時に指定したuriが入っています。DRbObjectのインスタンスを作ると、dRubyサービスのURIとオブジェクトが自動的に紐付きます。

　なお、前回のエントリで確認したように、DRbObjectのインスタンスを作る前にstart_serviceしてないとdRubyサービスが無いのでエラーになります。ただ、すっかりおなじみのnew_with_uriだけは特殊で、指定したURIのフロントオブジェクトに対する参照をもらうだけなのでstart_serviceしてなくても大丈夫です。irbをもうひとつ立ち上げて、クライアント側から（new_with_uriを使って）サーバーのフロントオブジェクトを取得するスクリプトを見ます。このエントリではこの2つのirbをそのまま使い続け、どんどん追記していきます。どちらのことか判断しやすいように、各々[サーバー側]と[クライアント側]という風に明記することにします。

<blockquote>

[クライアント側]

irb(main):001:0&gt; require 'drb/drb'
=&gt; true
irb(main):002:0&gt; there = DRbObject.new_with_uri('druby://サーバーのホスト名:9898')
=&gt; #&lt;DRb::DRbObject:0x29a5f68 @ref=nil, @uri="druby://サーバーのホスト名:9898"&gt;
</blockquote>

この場合、@refはnilです。本にも

<blockquote>

@refがnilの場合は特別にURIに関連づけられたフロントオブジェクトを参照します。

4.1.2 dRubyでは？
</blockquote>

と記載がありますね。ちなみに前にも見たかもしれないですが、DRbObjectのnew_with_uri(uri)は、DRbObject.new(nil, uri)と同じです。

<blockquote>

# Create a new DRbObject from a URI alone.
def self.new_with_uri(uri)
　self.new(nil, uri)
end
</blockquote>

では、クライアントのirbから操作を続けてみます。

<blockquote>

[クライアント側]

irb(main):003:0&gt; s = there[1]
=&gt; #&lt;DRb::DRbObject:0x29a59a0 @ref=21680810, @uri="druby://サーバーのホスト名:9898"&gt;
</blockquote>

これは、先ほどサーバーで作成したDRbObjectです。@refは21680810ですね。サーバー側のオブジェクトIDが設定されています。参照オブジェクトに対する操作をクライアント側で引き続き行ってみます。

<blockquote>

[クライアント側]

irb(main):004:0&gt; s.upcase!
</blockquote>

これをしてから、サーバー側のirbを操作して中身を確認してみましょう。

<blockquote>

[サーバー側]

irb(main):009:0&gt; str
=&gt; "FOO"
irb(main):010:0&gt; ref.to_s
=&gt; "FOO"
irb(main):011:0&gt; h[1].to_s
=&gt; "FOO"
</blockquote>

upcase!は、文字をすべて大文字に書き換えるメソッドです。はい、ちゃんとサーバー側のオブジェクトの実体（IDが21680810のオブジェクト）に変更が加えられていました。めでたし。

ところで、ここでちょっと疑問です。どうやらオブジェクトのIDを知っていることで、そのIDに紐付いたオブジェクトの実体を触れるようですが、いったいどのような仕組みになっているのでしょうか。仕組みを知るためにDRbServerのソースを見てみます。

<blockquote>

# Convert a dRuby reference to the local object it refers to.
def to_obj(ref)
　return front if ref.nil?
　return front[ref.to_s] if DRbURIOption === ref
　<font color="#ff0000"><strong>@idconv.to_obj(ref)</strong></font>
end
</blockquote>

ここで、今回関係ある部分は「@idconv.to_obj(ref)」です。@idconvは、デフォルトではDRbIdConvのインスタンスが設定されているようなので、このクラスを見ることにします。

<blockquote>

class DRbIdConv

# Convert an object reference id to an object.
#
# This implementation looks up the reference id in the local object
# space and returns the object it refers to.
def to_obj(ref)
　ObjectSpace._id2ref(ref)
end
・・・
</blockquote>

ここではObjectSpaceというものを使って変換しているようです。Rubyのリファレンスを見てみます。

<blockquote>

ObjectSpace
全てのオブジェクトを操作するためのモジュール。

モジュール関数:
ObjectSpace._id2ref(id) 
オブジェクト ID(Object#__id__)からオブジェクトを得ます。対応するオブジェクトが存在しなければ例外 RangeError が発生します。

Rubyリファレンスマニュアル　-　<a href="http://www.ruby-lang.org/ja/man/index.cgi?cmd=view;name=ObjectSpace" target="_blank">http://www.ruby-lang.org/ja/man/index.cgi?cmd=view;name=ObjectSpace</a>
</blockquote>

ObjectSpaceモジュールとは、どうやらRubyスクリプト内で実体化されたすべてのインスタンスの集合に対して色々できるやつのようです。なるほど、こういうやつが居るんですね。わかったので先へ進みます。

では、次はクライアントのirbから参照を渡します。ちゃんとstart_serviceしておきます。引数を指定しない場合、空いている適当なポート番号が割り当てられます。ここでは30273が割り当てられました。

<blockquote>

[クライアント側]

irb(main):005:0&gt; DRb.start_service
=&gt; #&lt;DRb::DRbServer:0x296448c ・・・・
irb(main):006:0&gt; str = 'bar'
=&gt; "bar"
irb(main):007:0&gt; there[2] = DRbObject.new(str)
=&gt; #&lt;DRb::DRbObject:0x2937ce8 @ref=21696060, @uri="druby://クライアントのホスト名:30273"&gt;
</blockquote>


そしてサーバー側で内容を書き換えます。

<blockquote>

[サーバー側]

irb(main):012:0&gt; h[2]
=&gt; #&lt;DRb::DRbObject:0x297898c @ref=21696060, @uri="druby://クライアントのホスト名:30273"&gt;
irb(main):013:0&gt; h[2].upcase!
=&gt; "BAR"
</blockquote>

クライアント側の実体が書き換えられていることを確認します。

<blockquote>

[クライアント側]

irb(main):008:0&gt; str

=&gt; "BAR"
</blockquote>


はい、ちゃんと書き換えられていますね。最後に、ここでクライアントのirbを終わらせてから、クライアントが実体を持っている参照オブジェクトに対してサーバーから操作してみましょう。エラーになるはずです。

<blockquote>

[サーバー側]

irb(main):014:0&gt; h[2].to_s
DRb::DRbConnError: druby://クライアントのホスト名:30273 - #&lt;Errno::EBADF: Bad file descriptor - connect(2)&gt;
</blockquote>

　DRbObjectについての理解がだいぶ深まったような気がします。なお、このエントリでは、本の中で紹介しているものとほぼ一緒の内容を試していますが、細かな手順が結構違っています。なのでこのエントリを読まれた方も是非一度、本の「4.1.2 dRubyでは？」に載っている手順に沿っておさらいしてみて下さい。次回はその続き、「4.2 自動的な参照渡し」に入るつもりです。
]]></description>
         <link>http://www.grandnature.net/druby/2007/10/4_5.html</link>
         <guid>http://www.grandnature.net/druby/2007/10/4_5.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">4章</category>
        
        
         <pubDate>Wed, 24 Oct 2007 10:02:24 +0900</pubDate>
      </item>
            <item>
         <title>4章.参照渡しと値渡し （その6）</title>
         <description><![CDATA[はい、今回は「4.2 自動的な参照渡し」から読み続けます。

<strong>4.2 自動的な参照渡し</strong>

<blockquote>

参照渡しにするには、いつも明示的にDRbObject.new()しなくてはならないのでしょうか？なんだか面倒ですね。 dRubyでは明示的に参照渡しをしなくても、ライブラリが自動的に判断して参照渡しと値渡しを選択する仕組みが用意されています。

dRubyでは次の規則で参照渡しと値渡しを選択します。

<ul>
<li>Marshal.dumpできるものは値渡し </li>
<li>Marshal.dumpできないものは参照渡し </li>
</ul>

4.2 自動的な参照渡し
</blockquote>

ほうほう、今までは、参照渡しにするために明示的にDRbObject.newをしていました。ですが、Marshal.dumpできないものは参照渡しとなるわけですね、なるほど。本の「4.2.1 can't dump」あたりを読みながら実験してみます。本ではMarshal.dumpできない$stdoutを使って試していますね、では、本の通りにやってみます・・・はい、できました。（はやっ）

<strong>4.2.1 can't dump</strong>

<blockquote>

front[:stdout]はIOではなく、DRbObjectとなりました。 DRbObjectの@uriはターミナル2のDRb.uriです。 front[:stdout]のDRbObjectはターミナル2のオブジェクトを参照するオブジェクトであることがわかります。

何がおきたのでしょうか？ターミナル2から$stdoutをターミナル1に渡す時、 dRubyのライブラリはMarshal.dumpが失敗したことを捉えて値渡しの代わりに参照渡しを行なったのです。

4.2.1 can't dump
</blockquote>

にゃるほど、多分これに該当する箇所としてはDRbMessageの

<blockquote>

private
def make_proxy(obj, error=false)
　if error
　　DRbRemoteError.new(obj)
　else
　　DRbObject.new(obj)
　end
end
</blockquote>

だと思います。みなさんも本の内容に沿って一緒にやってみて下さい。短いですがこのくらいで「4.2.1 can't dump」はおしまいにします。

<strong>4.2.2 DRbUndumped</strong>

では次。「4.2.2 DRbUndumped」に入ります。DRbUndumpedとは、Marshal.dumpは成功するからそのままやっちゃうと値渡しになるんだけど、参照渡しにしたい。ってときに使うもののようです。

<blockquote>

いつも参照渡しとなるように、Marshal.dumpを失敗させるための Mixinモジュール DRbUndumped を用意しています。DRb::DRbUndumped（別名DRbUndumpedも使えます）は、クラスにincludeしたりオブジェクトにextendして使用するモジュールです。

4.2.2 DRbUndumped
</blockquote>

こちらも本に載っている手順の通りにやってみます。簡単ですね。

<blockquote>
foo.extend(DRbUndumped)
</blockquote>

とすると、そのオブジェクトが参照渡しになります。また、

<blockquote>

class Foo
　include DRbUndumped
</blockquote>

とすると、そのクラスから生成されるオブジェクトは常に参照渡しになるようです。はい、よくわかりました。

でも、ちょっと気になりませんか。このDRbUndumpedモジュールは常にMarshal.dumpを失敗させるとのことですが、一体どんな方法で仕組みを使って失敗させているのでしょう。気になるのでDRbUndumpedのソースを見てみます。

<blockquote>
<pre>
  module DRbUndumped 
    def _dump(dummy)  # :nodoc:
      raise TypeError, 'can\'t dump'
    end
  end
</pre>
</blockquote>

これだけですか、なんでこれでMarshal.dumpできないようになるのか全然わかりません。だいたい_dumpって何？むぅぅ、、ってことでRubyのリファレンスマニュアルを見てみます。

<blockquote>

Marshal.dump において出力するオブジェクトがメソッド `_dump' を定義している場合には、そのメソッドの結果が書き出されます。メソッド `_dump' は引数として再帰を制限するレベル limit を受け取り、オブジェクトを文字列化したものを返します。

<a href="http://www.ruby-lang.org/ja/man/?cmd=view;name=Marshal" target="_blank">ユーザ定義のMarshal</a>
</blockquote>

とありました。なるほど、Marshal.dumpの中で_dumpが必ず呼ばれるんですね。そりゃそうだろうなあ。Marshalの中を見て一応確かめてみることにします。。と、ソースを見ようと思ったらこいつはCで書かれているのか。marshal.cというソースファイルのようですね。Ruby本体のソースは全然構造や関数の用途を把握していないので全くわかんないけど。。。えーっと、_dumpはどこかなと

<blockquote>
<pre>
void
Init_marshal()
{
・・・
    s_dump = rb_intern("_dump");
・・・
</pre>
</blockquote>

いました。これですね。rb_internというのがよくわかんないけど、付近のコードを見ても定義を行なっているだけの雰囲気があるので、"_dump"という文字列に対応するような識別子がs_dumpに設定されて特定できるようになっているのでしょう多分。

じゃあ次にMarshal.dumpで呼ばれる部分にあたりをつけてみます。。と、あったあった、おそらくこの部分だと思います。

<blockquote>
<pre>
static VALUE
dump(arg)
    struct dump_call_arg *arg;
{
    w_object(arg->obj, arg->arg, arg->limit);
    if (arg->arg->dest) {
    rb_io_write(arg->arg->dest, arg->arg->str);
    rb_str_resize(arg->arg->str, 0);
    }
    return 0;
}
</pre>
</blockquote>

ここで呼ばれているw_objectの中も見てみます。この関数は、220行を超える結構長めの関数です。型などに応じて処理を分けるcaseがあるからデカいようですね。で、この中で

<blockquote>
<pre>
if (rb_respond_to(obj, s_dump)) {
    VALUE v;
    v = rb_funcall(obj, s_dump, 1, INT2NUM(limit));
・・・
</pre>
</blockquote>

としています。rb_respond_toは、おそらくRubyのrespond_to?と同じようなものでしょう。Rubyのrespond_to?は、引数の名前のメソッドを持っているかどうかを問うものです。で、rb_funcallは、名前からして処理を呼び出す（実行する）ものだと思われますから、ここでの処理は、

<blockquote>
_dumpと言う名前のメソッドがobjにあれば、それを実行しなさい
</blockquote>

と言うことだと思います。全て勝手な推測ばかりですが。一応_dumpを呼んでそうな気はしました。しかしRubyのソースを読めるようになるまでには多分かなりの時間がかかるだろうなあ。と思ってたら、これを見ると色々詳しくなるらしいと聞きました。が、まだ私は読めてません。
<ul><li><a href="http://i.loveruby.net/ja/rhg/book/" target="_blank">Rubyソースコード完全解説</a>
</li></ul>
なんか今回はかなり脱線してしまいました。
ので、ちょっと纏めます。

<strong>
今回のエントリのまとめ
<ul>
<li>Marshal.dumpできるものは値渡し、できないものは参照渡し</li>
<li>明示的にDRbObject.newすると参照渡し</li>
<li>DRbUndumpedをくっつけたオブジェクトは参照渡し</li>
</ul>
</strong>

今日のところはこれで終了です。次回は「4.3 未知のオブジェクトとDRbUnknown」に入りたいところですが、ページ数も短いのでさらっと読んだ後に「4章.参照渡しと値渡し」のまとめっぽい簡単なスクリプトを書きたいと思っています。次回で4章が終わる予定です。
]]></description>
         <link>http://www.grandnature.net/druby/2007/10/4_6.html</link>
         <guid>http://www.grandnature.net/druby/2007/10/4_6.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">4章</category>
        
        
         <pubDate>Mon, 29 Oct 2007 19:42:30 +0900</pubDate>
      </item>
            <item>
         <title>4章.参照渡しと値渡し （その7）</title>
         <description><![CDATA[　今回のエントリは、「4.3 未知のオブジェクトとDRbUnknown」です。前回のエントリでは4章全体のまとめも合わせて一つのエントリにすると言いましたが、（あわせると長くなりそうなので）やっぱり分けることにしました。

　はい、ではDRbUnknownって何なんでしょう、何のためにあるのでしょう。本を読みます。

<blockquote>

DRbUnknownとは知らないクラスをMarshal.loadしてしまったときの例外を捉え、ロードできなかったオブジェクトの代わりにロードされるオブジェクトです。 DRbUnknownはロードに失敗した原因を、二つ保持しています。一つはロードに失敗したバッファ、もう一つは定義が不明なクラス名／モジュール名です。

それぞれ次のメソッドで問い合わせることができます。

<dl>
<dt>DRbUnknown#buf</dt>
<dd>Marshal.loadに失敗した直列化されたオブジェクトのバッファ。</dd>
<dt>DRbUnknown#name</dt>
<dd>例外のメッセージから調べた、未知のクラス／モジュール名。</dd>
<dt>DRbUnknown#reload</dt>
<dd>もう一度Marshal.loadしてみる。</dd>
</dl>

4.3 未知のオブジェクトとDRbUnknown
</blockquote>

メモメモ。続きも読んでみます。

<blockquote>

dRubyのライブラリは知らないクラスを受けとってしまっても、そのバッファを包んだ DRbUnknownオブジェクトを自動的に生成します。 DRbUnknownに対して元のオブジェクトのつもりでメソッドを呼ぶことはできませんが、DRbUnknownを回送することはできます。

4.3 未知のオブジェクトとDRbUnknown
</blockquote>

　ほうほうです。Marshalを使うと、マーシャルデータからオブジェクトを復元するとき（Marshal.loadするとき）に、そのオブジェクトの型を知っていないと復元できないみたいです。dRubyではオブジェクトを渡すときにMarshalの仕組みを利用していますので、この挙動から影響を受けるんですね。

　DRbUnknownの中身をちょっとだけ見てみます。小さなクラスなので見やすいです。まずは_dumpメソッドから。

<blockquote>
<pre>
def _dump(lv) # :nodoc:
  @buf
end
</pre>
</blockquote>

_dumpはMarshalでのdump時に呼ばれるメソッドであることは前回のエントリで勉強しました。なので、DRbUnknownが別プロセスに渡されるときには、DRbUnknownが包んでいる元々のマーシャルデータが渡されます。で、受け取ったプロセスで復元できたらそれでよし、復元できなければそこでもDRbUnknownになります。ってことですね。

　ちなみに、@nameを設定している部分はここです。

<blockquote>
<pre>
def initialize(err, buf)
  case err.to_s
  when /uninitialized constant (\S+)/
@name = $1
  when /undefined class\/module (\S+)/
@name = $1
  else
@name = nil
  end
  @buf = buf
end
</pre>
</blockquote>

　例外の文字列から名前を取っているみたいです。$1ってのは、正規表現にマッチした最初の文字列が入ってる組み込み変数です。@nameがnilになることもあるのか。elseに入るのはどういうときなのかなあ、名前すらわからないエラーのときですよねきっと。多分marchal.cの
<blockquote>
<pre>
static VALUE
r_object0(arg, proc, ivp, extmod)
struct load_arg *arg;
VALUE proc;
int *ivp;
VALUE extmod;
{
・・・
</pre>
</blockquote>

のメソッドあたりで投げられている例外の大半が該当するのだと思います。実際にDRbUnknownが捕捉している例外の文字列はvariable.cから出るやつのようです。中身はややこしそうなので追わないことにしました。このくらいにして本を読み進めます。

<blockquote>

DRbUnknownの機構によって、クラス定義を知らないオブジェクトを（メソッド呼び出しはできないが）保持しておくことが可能になります。

プロセス間でオブジェクトを交換するためのQueueを考えてみましょう。中継するQueueのサービスがpushされる可能性のある全てのクラス定義を事前に知らなくてはならないのは、難しいことがあります。 DRbUnknownはこういった局面で特に有用な機能です。

4.3 未知のオブジェクトとDRbUnknown
</blockquote>

　本に書かれていることはよくわかります。プロセス間通信を行う上では、サービス側が全てのオブジェクトの型を知っておくことは非現実的であったり面倒であったりすることが多いです。なので、dRubyでは、マーシャルデータからオブジェクトを復元することが出来なくても、それを保持できる仕組みを提供しています。それがDRbUnknownです。

　ちょっとかなり端折り気味ですが、以下にまとめを書いてDRbUnknownは終わりにします。一度は本の内容にそって実験してみて下さいね。

<strong>DRbUnknownのまとめ
<ul>
<li>DRbUnknownオブジェクトはMarshal.loadが失敗したときに生成される</li>
<li>DRbUnknownオブジェクトは、オブジェクトに復元できなかったマーシャルデータの情報を保持する</li>
<li>DRbUnknownオブジェクトの状態では、(中身のオブジェクトの)メソッドを呼べない</li>
<li>DRbUnknownオブジェクトをdumpすると、包み込んでいるマーシャルデータがそのまま返される</li>
　つまりdRubyでのプロセス間通信時には、気にせずそのままホイホイ渡せばいい
<li>Marshal.loadが成功したら、DRbUnknownオブジェクトの中身のオブジェクトが取れる</li>
</ul>
</strong>

次回こそ、4章を終わりにします。
]]></description>
         <link>http://www.grandnature.net/druby/2007/11/4_7.html</link>
         <guid>http://www.grandnature.net/druby/2007/11/4_7.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">4章</category>
        
        
         <pubDate>Wed, 07 Nov 2007 09:47:48 +0900</pubDate>
      </item>
            <item>
         <title>4章.参照渡しと値渡し （その8）</title>
         <description><![CDATA[今回は4章のおさらいとして、observer(observer.rb)で遊ぼうと思います。
observerってのは、Rubyに標準で同梱されているライブラリです。これはRubyでオブジェクト指向プログラミングのデザインパターンのひとつであるObserverパターンの概念を、簡単に実装できるようにしてくれるもののようです。このソースのコメントに書かれているexampleのスクリプトをいじってみることにします。exampleは以下の通りです。

<blockquote>

require "observer"

class Ticker　　　　　### Periodically fetch a stock price.
　include Observable

　def initialize(symbol)
　　@symbol = symbol
　end

　def run
　　lastPrice = nil
　　loop do
　　　price = Price.fetch(@symbol)
　　　print "Current price: #{price}\n"
　　　if price != lastPrice
　　　　changed　　　　　　　　 # notify observers
　　　　lastPrice = price
　　　　notify_observers(Time.now, price)
　　　end
　　　sleep 1
　　end
　end
end

class Price　　　　　 ### A mock class to fetch a stock price (60 - 140).
　def Price.fetch(symbol)
　　60 + rand(80)
　end
end

class Warner　　　　　### An abstract observer of Ticker objects.
　def initialize(ticker, limit)
　　@limit = limit
　　ticker.add_observer(self)
　end
end

class WarnLow &lt; Warner
　def update(time, price)　　　 # callback for observer
　　if price &lt; @limit
　　　print "--- #{time.to_s}: Price below #@limit: #{price}\n"
　　end
　end
end

class WarnHigh &lt; Warner
　def update(time, price)　　　 # callback for observer
　　if price &gt; @limit
　　　print "+++ #{time.to_s}: Price above #@limit: #{price}\n"
　　end
　end
end

ticker = Ticker.new("MSFT")
WarnLow.new(ticker, 80)
WarnHigh.new(ticker, 120)
ticker.run
</blockquote>

簡単に解説します。
Tickerがイベント発生機です。ランダムに金額をfetchし、それをObserverに通知します。通知するためのメソッドがObservableモジュールに定義されていますので、それをincludeしておきます。
Tickerが発生したイベントは、各Observerに通知されます。通知されたイベントに反応するかどうかの判断は各Observer任せです。
WarnHighやWarnLowなどのクラスは、インスタンス生成時（initialize時）に、自分自身をObserverとしてTickerに登録します。

実行してみましょう。

<blockquote>

Current price: 83
Current price: 75
--- Sun Jun 09 00:10:25 CDT 2002: Price below 80: 75
Current price: 90
Current price: 134
+++ Sun Jun 09 00:10:25 CDT 2002: Price above 120: 134
Current price: 134
Current price: 112
Current price: 79
--- Sun Jun 09 00:10:25 CDT 2002: Price below 80: 79
・・・
</blockquote>

こんな感じに延々と出力されるはずです。

はい、ではこれにdRubyを使ってみます。ObservableとObserverのプロセスを別にして、プロセス間通信してみましょう。まずは何も考えず安直に作ってみます。わかりにくいので、各プロセスに、それぞれ便宜的に「Observable側」と「Observer側」という名前をつけることにしますね。

<blockquote>

<a href="http://www.grandnature.net/druby/src/1-1.rb" target="_blank">[Observable側]</a>

require "observer"
require 'drb/drb'

class Ticker　　　　　### Periodically fetch a stock price.
　include Observable

　def initialize(symbol)
　　@symbol = symbol
　end

　def run
　　lastPrice = nil
　　loop do
　　　price = Price.fetch(@symbol)
　　　print "Current price: #{price}\n"
　　　if price != lastPrice
　　　　changed　　　　　　　　 # notify observers
　　　　lastPrice = price
　　　　notify_observers(Time.now, price)
　　　end
　　　sleep 1
　　end
　end
end

class Price　　　　　 ### A mock class to fetch a stock price (60 - 140).
　def Price.fetch(symbol)
　　60 + rand(80)
　end
end

class Warner　　　　　### An abstract observer of Ticker objects.
　def initialize(ticker, limit)
　　@limit = limit
　　ticker.add_observer(self)
　end
end

class WarnLow &lt; Warner
　def update(time, price)　　　 # callback for observer
　　if price &lt; @limit
　　　print "--- #{time.to_s}: Price below #@limit: #{price}\n"
　　end
　end
end

class WarnHigh &lt; Warner
　def update(time, price)　　　 # callback for observer
　　if price &gt; @limit
　　　print "+++ #{time.to_s}: Price above #@limit: #{price}\n"
　　end
　end
end

ticker = Ticker.new("MSFT")
DRb.start_service('druby://[ホスト]:9191', ticker)
ticker.run

</blockquote>

<blockquote>

<a href="http://www.grandnature.net/druby/src/1-2.rb" target="_blank">[Observer側]</a>

require 'drb/drb' 

class Warner　　　　　### An abstract observer of Ticker objects.
def initialize(ticker, limit)
　　@limit = limit
　　ticker.add_observer(self)
　end
end

class WarnLow &lt; Warner
　def update(time, price)　　　 # callback for observer
　　if price &lt; @limit
　　　print "--- #{time.to_s}: Price below #@limit: #{price}\n"
　　end
　end
end

class WarnHigh &lt; Warner
　def update(time, price)　　　 # callback for observer
　　if price &gt; @limit
　　　print "+++ #{time.to_s}: Price above #@limit: #{price}\n"
　　end
　end
end

ticker = DRbObject.new_with_uri('druby://[ホスト]:9191')
low = WarnLow.new(ticker, 80)
WarnHigh.new(ticker, 120)

</blockquote>

スクリプトのダウンロード
<a href="http://www.grandnature.net/druby/src/1-1.rb" target="_blank">[Observable側] 1-1.rb</a>
<a href="http://www.grandnature.net/druby/src/1-2.rb" target="_blank">[Observer側] 1-2.rb</a>


　とりあえずできたかな。ではこれで動かしてみます。まずObservable側を動かしてから、次はObserver側を動かします。無事動きました！Observable側のコンソールにObserver側から登録したオブジェクトの出力メッセージが出ています。うまくいきました。

　・・・でも、よく考えると微妙です。まず、クラスの定義がどちらのプロセスにも重複して書いてあります。これを別のファイルにしてrequireしたとしても、Observable側が、扱うクラスをすべて知っておかなければならないって言うのは（Javaなどの型縛り言語なら当然だとしても）ちょっとRubyやdRubyの考え方からは外れるような気がします。あと、当然ですがObserver側はすぐ終了しちゃいますね。。で、実行結果のメッセージはすべてObservable側に出ている。で、今までに勉強したとおりdRubyは基本的に値渡しだから、WarnLowやWarnHighは、Observer側のインスタンスじゃなくて、それを複製したオブジェクトをObservable側が持ってしまってるって事です。これはまずいですね。Observer側のプロセスが持っているオブジェクトの内容を参照してもらわないと色々な場面で不便そうです。
そこで、

<strong><ul>
<li>Observable側が、扱うクラスを知ってないと駄目なのはだるそう</li>
<li>Observable側にObserverのインスタンスが値渡しされているのは不便そう</li>
</ul></strong>

の、この2点を解決してみたいと思います。じゃあ、まず、単純にObservable側から、Warnerクラスとその派生クラスたちの定義を消して動かしてみます。Observer側は変えませんよ。

<blockquote>

<a href="http://www.grandnature.net/druby/src/2-1.rb" target="_blank">[Observable側]</a>

require "observer"
require 'drb/drb'

class Ticker　　　　　### Periodically fetch a stock price.
　include Observable

　def initialize(symbol)
　　@symbol = symbol
　end

　def run
　　lastPrice = nil
　　loop do
　　　price = Price.fetch(@symbol)
　　　print "Current price: #{price}\n"
　　　if price != lastPrice
　　　　changed　　　　　　　　 # notify observers
　　　　lastPrice = price
　　　　notify_observers(Time.now, price)
　　　end
　　　sleep 1
　　end
　end
end

class Price　　　　　 ### A mock class to fetch a stock price (60 - 140).
　def Price.fetch(symbol)
　　60 + rand(80)
　end
end

ticker = Ticker.new("MSFT")
DRb.start_service('druby://[ホスト]:9191', ticker)
ticker.run

</blockquote>

スクリプトのダウンロード
<a href="http://www.grandnature.net/druby/src/2-1.rb" target="_blank">[Observable側] 2-1.rb</a>


では、動かします。・・・っと、あれれ？

<blockquote>
(druby://[ホスト]:9191) ・・・observer.rb:126 :in `add_observer': observer needs to respond to `update' (NoMethodError)
</blockquote>

エラーになっちゃいました。observer.rbのエラーになっている箇所を見てみます。

<blockquote>

module Observable

　#
　# Add +observer+ as an observer on this object. +observer+ will now receive
　# notifications.
　#
　def add_observer(observer)
　　@observer_peers = [] unless defined? @observer_peers
　　unless observer.respond_to? :update
　　　raise NoMethodError, "observer needs to respond to `update'" 
　　end
　　@observer_peers.push observer
　end

</blockquote>

うーん、respond_to?で聞いた結果、updateってメソッドがないって言われています。なぜでしょう？？仕方ないので、ここで、observerの中身を見てみます。ブレークポイントを置いたりしてみましょう。

<blockquote>
&lt;DRb::DRbUnknown:0x29a3e5c @buf="\004\bo:\fWarnLow\006:\v@limitiU", @name="WarnLow"&gt;
</blockquote>

あらら、DRbUnknownってオブジェクトになっていますね。これは前回のエントリで学んだやつです。そうなのか、なるほど、そうですね、考えてみれば当然ですね。WarnLowなどのクラスをObservable側のプロセスは知りません。自分の知らないものが値渡しとして渡されてくると、Marshal.load時に失敗するので駄目ってことですね。なるほど、結局参照渡しにしないと駄目ってことか。では、Observer側を参照渡しに変えてみます。

<blockquote>

<a href="http://www.grandnature.net/druby/src/2-2.rb" target="_blank">[Observer側]</a>

require 'drb/drb' 

<strong><font color="#ff0000">DRb.start_service</font></strong>
class Warner　　　　　### An abstract observer of Ticker objects.
<strong><font color="#ff0000">include DRbUndumped</font></strong>
def initialize(ticker, limit)
　　@limit = limit
　　ticker.add_observer(self)
　end
end

class WarnLow < Warner
　def update(time, price)　　　 # callback for observer
　　if price < @limit
　　　print "--- #{time.to_s}: Price below #@limit: #{price}\n"
　　end
　end
end

class WarnHigh < Warner
　def update(time, price)　　　 # callback for observer
　　if price > @limit
　　　print "+++ #{time.to_s}: Price above #@limit: #{price}\n"
　　end
　end
end

ticker = DRbObject.new_with_uri('druby://[ホスト]:9191')
low = WarnLow.new(ticker, 80)
WarnHigh.new(ticker, 120)
<strong><font color="#ff0000">DRb.thread.join</font></strong>

</blockquote>

スクリプトのダウンロード
<a href="http://www.grandnature.net/druby/src/2-2.rb" target="_blank">[Observer側] 2-2.rb</a>

3行変えました。
<ul>
<li>参照渡しなので、DRb.start_serviceする</li>
<li>参照渡しなので、DRbUndumpedをincludeする</li>
<li>実体がこっちのプロセスにあるため、死ぬとまずいのでDRb.thread.joinで待機</li>
</ul>

これで実行しましょう・・・うまくいきました！メッセージもちゃんとObserver側に出ていますね！めでたしめでたし。なんとなく参照渡しと値渡しの感覚がつかめたような気がします。皆さんもRubyのライブラリをdRubyにしてみたりして遊んでみて下さい。結構簡単に出来ちゃうことに驚くと思います。

これで4章は終了です。

]]></description>
         <link>http://www.grandnature.net/druby/2007/11/4_8.html</link>
         <guid>http://www.grandnature.net/druby/2007/11/4_8.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">4章</category>
        
        
         <pubDate>Wed, 14 Nov 2007 10:45:38 +0900</pubDate>
      </item>
      
   </channel>
</rss>

