HotRuby (Ruby VM on JavaScript) をいろいろいじってRPCとか実装してみた
HotRubyというJavaScript上で実装されたRuby VMを発見して、
ということでDRb風のRPCを実装してみました。その過程でHotRubyに足りない機能を追加したりバグを直したり、かなりごちゃごちゃいじりました(後述)。
ソースはgithubからどうぞ。
注: 実験レベルのものです。現状実用にはなりません。
デモとしてシンプルなチャットを作りました: HotRuby+RPC Chat (Firefox 3以外では動かないかも)
クライアント側のソースはこんな感じです。
@log_div = $native.document.getElementById("log") @author_field = $native.document.getElementById("author") @message_field = $native.document.getElementById("message") @form = $native.document.getElementById("form") @server = RPCClient.new("rpc/chat_server").root def update_log() # RPC呼び出し messages = @server.recent_messages(10) @log_div.innerHTML = messages. map(){ |m| CGI.escapeHTML(m["author"] + ": " + m["body"]) }. join("<br>") end on_submit = proc() do |e| $native.Event.stop(e) # RPC呼び出し @server.post(@author_field.value, @message_field.value) update_log() @message_field.value = "" @message_field.focus() end $native.Event.observe(@form, "submit", on_submit, true) # 10秒ごとにログを更新 while true update_log() sleep(10) end
メッセージの取得(recent_messages)と投稿(post)をRPCでやっています。内部ではXMLHTTPRequestが呼ばれています。RPC以外はかなりJavaScriptくさいRubyコードになっています。Ruby friendlyなUIライブラリがほしいところですね。
対応するサーバ側はこんな感じです。
class ChatServer < RPCServer extend(Publishable) def initialize(*args) super @messages = [] post("System", "Chat server has started.") end published def recent_messages(n) n = [n, @messages.size].min return @messages[-n..-1] end def post(author, body) @messages.push({"author" => author, "body" => body}) end end
独自拡張のpublishedというアクセス権限を持ったメソッドだけが、クライアントから呼び出せるようにしました。Web上に公開されるので、DRbみたいになんでも呼べてしまうのは危ないですからね。
これを作る過程でHotRubyに加えた変更は、こんな感じです(抜けがあるかも)。
- (JS側の)非同期関数を(Ruby側では)同期的に呼び出せるように。
- RPCとかsleepとかを同期関数のように呼べているのはこのおかげ。
- これのせいでJavaScript側のコードが全体的に複雑に…。
- スレッドもどきの実装。
- 上記非同期関数が呼ばれるところでしかコンテキストスイッチしません。
- 例外の実装。(てきとー)
- VMオブジェクトをシングルトンに。
- Multi VMができなくなってしまいましたが…。この方が楽だったもんで。
- 組み込みクラスの足りないメソッドを一部実装。
- 一部メソッドはRubyレベルで実装。
- instructionのバグ修正。(topn, setn, expandarray)
- 未実装だったinstructionを一部実装。(invokeblock)
- method_missingを実装。
- メソッド引数の*args, &blockに対応。
- JavaScript上でRubyのクラス/メソッドを定義するときのインタフェースの変更。
- 特異クラスの導入。
- その他リファクタリング(のつもり)。
色々どうでもいいところまでいじりすぎたので、本家へのパッチという形にするのが難しくなってしまいました。とりあえず変更後のHotRuby.jsはこちら。
変更後のHotRubyを試すためのコンソール(diy.htmlベース)を用意しました。
いまだに未実装なもの:
モジュール。→実装されました。ブロック中のbreak/return。→実装されました。- 標準クラスとそのメソッドはまだまだ未実装。→主要クラスはそれなりに実装されました。
- eval。無理。
- require。
- 他にもある気がする。
あとFlash対応のコードは放置しているので、たぶん動かなくなっていると思います。