JSRuby は Javascript で実装された Ruby インタプリタです。
動作イメージ&サンプルは記事
「Javascript で実装した Ruby インタプリタ JSRuby 0.1 リリースしました。」を参照していただくとして、ここでは JSRuby の Javascript 連携まわりを解説します。
JSRuby は Javascript と自然な連携ができるよう、以下の機能を持っています。
- JSRuby-Javascript 間での任意の Javascript オブジェクトの受け渡し
- Ruby で定義されたメソッドの Javascript 側からの呼び出し
- JSRuby 内での Javascript メソッド実行&関数オブジェクトの呼び出し、インスタンス化
このあたりの機能を整理して使いやすくしたものを JSRuby 0.1.1 としてリリースしました。最新版の取得などは CodeRepos 上の Project Page をごらんください。
- JSRuby Project Page (CodeRepos)
- http://coderepos.org/share/wiki/JSRuby
前回の記事では、Ruby 上のメソッドを Javascript から呼び出す場合は alert( ruby.call("fib", [8]) ) のように書くと説明しましたが、0.1.1 からは定義されたメソッド名でそのまま自然に呼び出すことが出来るようになりました。
<script type="text/ruby">
def fib(n)
if n<2 then n else fib(n-1)+fib(n-2) end
end
</script>
<script type="text/javascript">
// JSRuby インタプリタの取得
var ruby = new RubyEngine.Interpreter();
// type="text/ruby" を実行 ( fib メソッドの定義。 ruby.fib() が使えるように )
ruby.exec(RubyEngine.Util.getRubyScript());
alert( ruby.fib(10) ); // => 55
</script>
このように JSRuby 側で def fib すれば、その関数が ruby.fib() で Javascript 側から呼び出せます。
もちろん ruby.fib は普通の関数オブジェクトですので、setTimeout に与えたり、apply したりとかもできます。
ただし JSRuby インタプリタのオブジェクトがもともと持つプロパティ名と重複している場合( exec, run など)は、従来通り ruby.call(メソッド名, [引数列]) で呼び出してください。
上の例では ruby.fib() の引数として通常の整数を与えましたが、ここに任意の Javascript オブジェクトを与えることが出来ます。
通常の「 Javascript なオブジェクト」(document とか)を与えてまるで Ruby のオブジェクトのようにメソッド呼び出しなどが出来るのはもちろんですが、コンストラクタを渡して JSRuby 側で new してそのインスタンスを返すなんてこともできます。
<script type="text/ruby">
def newinstance(f)
f.new 2
end
</script>
<script type="text/javascript">
var ruby = new RubyEngine.Interpreter();
ruby.exec(RubyEngine.Util.getRubyScript());
function Foo(n) = { this.bar = n * 2; }
Foo.prototype.getbar = function() { return this.bar; }
alert( ruby.newinstance(Foo).getbar() ); // => 4
</script>
またRuby にはいわゆる「関数オブジェクト」にあたるものがないのですが( Proc をそう呼ぶのはなー)、JSRuby では Javascript の関数オブジェクトを受け取って、ちゃんと関数オブジェクトらしく呼び出すことが出来ます。
<script type="text/ruby">
def callfunc(fnc)
fnc 2
end
</script>
<script type="text/javascript">
var ruby = new RubyEngine.Interpreter();
ruby.exec(RubyEngine.Util.getRubyScript());
alert( ruby.callfunc( function(x){return x*3;} ) ); // => 6
</script>
気をつけないといけないのは、ruby の文法では関数名を書いただけでその関数の呼び出しが実行されることです。つまり渡された関数オブジェクトをさらに別の Ruby メソッドに渡してその先で実行ということは出来ません。
def callfunc(fnc)
callfunc2 fnc # × 関数そのものではなく、関数の実行結果を渡そうとする
end
def callfunc2(fnc)
fnc 2
end
jQuery などのライブラリを JSRuby で使うにはこれは不便です(やっと本題)。
そこで、JSRuby のグローバル変数にそれらのライブラリ用のオブジェクトを設定できるようにしています。
<script type="text/ruby">
def getfooter
$jquery('div.footer')[0].innerHTML
end
</script>
<script type="text/javascript" src="jquery-*.*.*.js"></script>
<script type="text/javascript">
var ruby = new RubyEngine.Interpreter();
ruby.exec( RubyEngine.Util.getRubyScript() );
// jQuery の $ をグローバル変数 $jquery で利用できるように
ruby.put( "$jquery", $ )
alert( ruby.getfooter() ); // => (content of footer)
</script>
<div class="footer"> (content of footer) </div>
JSRuby の $window や $document は同じように window, document オブジェクトがあらかじめそれらのグローバル変数に代入されているだけですね。
まだ他の様々な Javascript のライブラリ( prototype.js とか script.aculo.us とかとかとかとか)で検証が出来ているわけではないので、実際の運用ではまだまだ不具合の生じる可能性はあるかもしれませんが、基本的には上述のように自然な利用ができるように想定しています。