requires=
classes=DRb,DRb=DRbError,DRb=DRbServerNotFound,DRb=DRbRemoteError,DRb=DRbIdConv,DRbIdConv,DRb=DRbObject,DRbObject,DRb=DRbServer,DRb=DRbUnknown,DRb=DRbUnknownError,DRb=DRbUndumped,DRbUndumped,DRb=DRbProtocol,DRb=DRbConnError,DRb=DRbBadScheme,DRb=DRbBadURI
methods=
sublibraries=drb.acl,drb.extserv,drb.extservm,drb.gw,drb.observer,drb.ssl,drb.timeridconv,drb.unix
is_sublibrary=false
category=Network

分散オブジェクトプログラミングのためのライブラリです。

Ruby のプロセスから他のRubyプロセスにあるオブジェクトのメソッド
を呼びだすことができます。他のマシン上のプロセスにも
アクセスできます。

=== 概要
dRuby は Ruby 専用の分散オブジェクトシステムです。
Ruby のみで記述され、TCP socket のような Ruby 本体が提供する
通信手段があれば追加のインストール物なしに利用可能です。
独自のプロトコルで通信し、他の分散オブジェクトシステム
(CORBA, RMI, .NETなど)との相互運用性はありません。

dRuby は
  * 他のプロセスと Ruby オブジェクトのリファレンスをやりとりすること
  * そこからのメソッド呼び出し
  * メソッド呼出の引数/返り値を [[c:Marshal]] でバイト列に変換(マーシャリング)
    して通信先のプロセスと受け渡しすること
    
ができます。これらはすべて透過的に行われます。

リモートプロセスにあるオブジェクトはローカルには [[c:DRb::DRbObject]] の
インスタンスとして表現されます。このオブジェクトはリモートオブジェクトの
proxy のように振舞います。つまり、このオブジェクトのメソッドを呼び出すと
リモートオブジェクトに転送されます。
CORBA の IDL のようなリモートオブジェクトのインターフェースを
静的に宣言する必要はなく、すべては実行時に解決されます。

リモートプロセスからのメソッド呼出しはそれを受け取ったプロセスの
[[c:DRb::DRbServer]] オブジェクトが処理します。
受け取ったメッセージからメソッド呼出し情報を取り出し、ローカルにある
オブジェクトを特定し、
そのメソッドを呼び出し、返り値をリモートの呼び出し元に送ります。
どのようなオブジェクトのメソッドも呼びだすことができます。
何か特別なインターフェースを実装したり、特別な mixin を必要としたりは
しません。
オブジェクトの特定は DRb::DRbServer が自動でします。そのため
オブジェクトの登録のようなことは通常必要ありません。

[[c:DRb::DRbServer]] に URI(例: druby://example.com:8787)を関連付けること
で、他のプロセスからの通信(リモートメソッド呼び出し)ができるようになります
(逆に言うと、URIを指定しないことで、他のプロセスからのリモートメソッド
呼び出しを拒否することができます)。
また、DRb::DRbServer に「フロントオブジェクト」を登録しておくと、
サーバの URI からそのオブジェクトをリモートオブジェクト
として取り出すことができます。通常はこのオブジェクトから辿って
必要な(リモート)オブジェクトを取り出します。


リモートメソッド呼び出しはかなりの部分、同じプロセス内の
オブジェクトのメソッドを呼び出すのと同じ動作をします。
ブロック付きのメソッド呼び出しもできますし、
リモートプロセス上で生じた例外はローカルプロセス上に
転送されます。DRb 関連の例外は [[c:DRb::DRbError]] の
サブクラスです。

リモートメソッド呼び出しの引数や返り値には任意の Ruby オブジェクト
が使えます。デフォルトではオブジェクトをマーシャリングして
渡され、受け取った側が元のオブジェクトに戻します。つまり
オブジェクトはコピーされます。これは通常の同一プロセス上での
メソッド呼び出しと大きく異なる点です(通常のメソッド呼び出しでは
オブジェクトへのリファレンスが渡されます)。

ただし、マーシャリング不可能なオブジェクトは dRuby によって
ある種のリファレンスとして取り扱われます。これは [[c:DRb::DRbObject]]
のインスタンスとして表現されます。これはリモートオブジェクトの
proxy として動作し、proxy のメソッドを呼び出すと上に説明した通りの
方法でリモートオブジェクトのメソッドを呼び出します。

マーシャンリング可能なオブジェクトを DRbObject でリファレンスとして
渡したい、つまりコピーでなくリファレンスで渡したい場合は
そのオブジェクトに
[[c:DRb::DRbUndumped]] を [[m:Module#include]] します。

dRuby はブロック付きのメソッド呼び出しをサポートしていますが、
[[c:Proc]] はマーシャリング不可能なので、ブロックの中身は
(リモート側でなく)ローカルプロセス上で実行されます。
リモート側がブロックを呼び出そうとすると、ブロックの引数が
リモート側からローカル側に(上で説明したようにコピーもしくは dRuby のリファレンス
オブジェクトとして)渡され、ブロックが実行され、その返り値がリモート側に
送られます。

=== セキュリティ

dRuby でインターネット上に公開するサービスを作るべきではありません。
イントラネットのサービスとして動かす場合もセキュリティには気を使う
必要があるでしょう。

あるオブジェクトへの外部からのアクセスを許可すると、単にそのオブジェクトの
メソッドを外部から呼び出せるだけでなく、任意の Ruby のコードを実行できて
しまいます。例えば以下のようなことができます。

  # !! 危険 !!
  ro = DRbObject::new_with_uri("druby://your.server.com:8989")
  class << ro
    # リモートオブジェクトの instance_eval を呼ぶため
    # ローカルオブジェクトの instance_eval を取り除く
    undef :instance_eval  
  end
  ro.instance_eval("DANGEROUS RUBY CODE!")

このような instance_eval による危険性は [[m:$SAFE]] を 1 以上に
することで防げます。
[[m:DRb.#start_service]] の :safe_level オプションで
リモートからのメソッド呼び出しのコンテキストで指定されるセーフレベルを
指定できます。

また、[[c:DRb::DRbServer]] にはアクセスコントロールリスト(アクセスを許可/拒否
する IP のリスト)によりアクセス制御をすることができます。
この機能は [[c:ACL]] で実現されています。
このアクセス制御は単体で使うのではなく、
適切なファイアウォールと併用すべきです。

=== リファレンス
  * [[url:http://www2a.biglobe.ne.jp/~seki/ruby/druby.html]]
  * [[url:http://www.ruby-doc.org/stdlib/libdoc/drb/rdoc/index.html]]

=== Example
単純なクライアント-サーバシステムの例。

ターミナルを2つサーバ側/クライアント側として起動して、サーバ側を
先に動かしてください。

==== サーバ側コード
  require 'drb/drb'
  
  # 通信を待ち受ける URI
  URI="druby://localhost:8787"
  
  class TimeServer
  
    def get_current_time
      return Time.now
    end
  
  end
  
  # サーバ側でリクエストを受け付けるオブジェクト
  FRONT_OBJECT=TimeServer.new
  
  # サーバを起動する
  DRb.start_service(URI, FRONT_OBJECT, :safe_level => 1)
  # DRb のスレッドが終了するのを待つ
  DRb.thread.join

==== クライアント側コード
  require 'drb/drb'
  
  # 接続先の URI
  SERVER_URI="druby://localhost:8787"
  
  # DRbサーバを起動する
  # この例には必要ないが、front オブジェクト以外をの
  # リモートオブジェクトのメソッドを呼び出す時には必要
  DRb.start_service
  # リモートオブジェクトの取得
  timeserver = DRbObject.new_with_uri(SERVER_URI)
  # リモートメソッドの呼び出し
  puts timeserver.get_current_time
