Rubyのexecjsがすごい件
できることと起動方法とエンジンの違い
- RubyスクリプトからJavaScriptコードを実行できる
- V8, node, spidermonkey, rhino などの中からそのとき利用できるエンジンを autodetect して実行してくれる
- 環境変数からエンジンを指定できる
- ExecJS::Runtimes の中で定義されている RubyRacer や Node の名前で export EXECJS_RUNTIME=Node などと指定する
- 例えば Rhino は therubyrhino gem に依存する。こうした依存 gem は自動では入らないので注意が必要
- 何の gem も準備していなければ execjs 1.2.4 の段階では node.js, JavaScriptCore, SpiderMonkey, JScript の順で利用可能かどうか探してくれる。
- 当然上に挙げた ExternalRuntime は外部プロセスの起動が入るので重い。Node よりも TheRubyRacer で V8 を直接叩いた方が速い。
※ 手元でいくつかのファイルを jshint に掛けるツールを動かしたら TheRubyRacer で 0.14s 掛かる処理が Node だと 2.28s 掛かった。だいたいこの数値で安定しているので、16倍くらい違う。数が多くなればなるほど差は開く。
Windows の人も node.exe に PATH が通ってれば node.js で動かせる。残念ながら今のところ Windows では npm は動かないので node.js の旨味がだいぶ減ってしまっているんだけど、execjs を使えば node.exe を使うスクリプトを自由に実行することができる。
というわけで手始めに jshint4rというものを作りました。使ってみてね。
簡単な使い方
- exec
- eval
- compile & call
の3つの使い方がある。
副作用だけで希望の処理が実現できるならそのまま exec にコードを与えれば ok. 戻り値が欲しい場合は eval で呼ぶ。
ExecJS.exec('1 + 1') # => nil
ExecJS.eval('1 + 1') # => 2
中で
(function( ... ){})()
の中に source を入れて実行するだけなのが exec. この前に return を置いて値を返すのが eval.
call は eval の応用みたいな感じで、compile 済みの JS ファイルの中のある function を call する格好になる。例えば
context = ExecJS.compile('function add() { return 1 + 1 }')
context.call('add') # => 2
呼び出し方は
context.call( FUNCTION, ARG1, ARG2, ... )
みたいになる。たぶんちょっとやってみればすぐに分かると思う。
気をつけなきゃいけないのは compile に文字列を与えるところかな。だから例えば jshint に JavaScript のコードを与えたい場合は
context = ExecJS.compile( File.read( /PATH/TO/jshint.js ) )
context.call( 'JSHINT', File.read( /PATH/TO/SRC ), opts )
みたいな形になる。(ただし、JSHINT 関数は true か false しか返さないため、このままでは使いものにならない。)
JavaScript 側に特別手を入れずに Ruby でその実行結果を活かそうと思ったら eval か call を使う形になるのではなかろうか。
まとめ
compile とかうまく隠蔽すればかなり自然に Ruby コードの中から JavaScript を実行してその結果を利用することができると思う。TheRubyRacer を使えばオーバーヘッドもまったく気にならない。
いやこれはすごいな。JavaScript は今後もどんどん応用範囲が広がって行くと思うんだけど、その成果を丸ごと(対応していないオブジェクトはダメだけど)いただくことができる。なんて贅沢。
おまけ
Rails 3.1 から execjs を利用しているため、何らかの JavaScript Runtime がインストールされていないと Rails 環境を起こすことができない。
Windows の場合は WSH が、Mac の場合は JavaScriptCore が最初から入っているので問題ない。Linux の場合はなんらかの JavaScript 実行環境が必要になる。node.js をやっている人はこれを利用するので問題ないけど、普段そこまで JavaScript に入れ込んでない人は TheRubyRacer でも TheRubyRhino でも入れるとよい。いちばん簡単に入るのはたぶん SpiderMonkey じゃないかな。結構いろんな環境でサクっとインストールできるはず。
[2012-06-11 追記] 2012-05-20 の commit で SpiderMonkeyRuntime が deprecated となり、環境変数 EXECJS_RUNTIME で明示しない限り起動することができなくなりました。バージョンで言うと v1.4.0 以降。Linux 環境では node.js とか Rhino とか入れて使うようにしていこうぜ、という方針のようです。
cf. Deprecate SpiderMonkey runtime · 71fe9e8 · sstephenson/execjs
同時に Johnson も Mustang も deprecated になってますね。