monthly gimite

試験運用中。

HotRuby (Ruby VM on JavaScript) をいろいろいじってRPCとか実装してみた

HotRubyというJavaScript上で実装されたRuby VMを発見して、

  • これを使えばWebアプリのサーバ側もクライアント側もRubyで書く、とかできるのか
  • つまりGoogle Web ToolkitのRuby
  • そのためにはまずRPCかな

ということで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ができなくなってしまいましたが…。この方が楽だったもんで。
  • 組み込みクラスの足りないメソッドを一部実装。
  • instructionのバグ修正。(topn, setn, expandarray)
  • 未実装だったinstructionを一部実装。(invokeblock)
  • method_missingを実装。
  • メソッド引数の*args, &blockに対応。
  • JavaScript上でRubyのクラス/メソッドを定義するときのインタフェースの変更。
  • 特異クラスの導入。
  • その他リファクタリング(のつもり)。

色々どうでもいいところまでいじりすぎたので、本家へのパッチという形にするのが難しくなってしまいました。とりあえず変更後のHotRuby.jsはこちら

変更後のHotRubyを試すためのコンソール(diy.htmlベース)を用意しました。

いまだに未実装なもの:

  • モジュール。→実装されました。
  • ブロック中のbreak/return。→実装されました。
  • 標準クラスとそのメソッドはまだまだ未実装。→主要クラスはそれなりに実装されました。
  • eval。無理。
  • require。
  • 他にもある気がする。

あとFlash対応のコードは放置しているので、たぶん動かなくなっていると思います。