できることと起動方法とエンジンの違い
- 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 になってますね。
試し始めた Cperl-mode だけど、Debian etch(4.0) の Emacs(emacs-snapshot) との組み合わせが悪いのか、なんかプロセッサパワーを食いつぶしちゃう。これはさすがにおかしいなぁということで使用を諦めることにしようかと思ったけど、Fink で入れた Emacs 22.1 (OSX PPC) 上で使う分にはたぶん大丈夫な感じなので、testing の 22.1 を入れたらいいのかもしんないな。もしかすっと。
と思って改めて見てみると backports に Emacs 22.1 が来てた。おぉ。ということで emacs-snapshot を apt-get remove して emacs22-nox をインストール。
……。
んー。まだ跳ね上がるときがあるなぁ。ずっと上がりっぱなしではなくなったけれども。メモリじゃなくてプロセッサパワーっつーところがいったい何しとんじゃいという感じ。こんなにクソ重ければそれなりに話題になっていそうなもんなのに、そういう話は見ないもんなぁ。
別段新しい考え方でもなんでもない気がするんだけど、思いついたのでメモ。
Rails を引き合いに出すまでもなく MVC という単語だけはずいぶん浸透している。でまぁ、この V に当たる部分をロジックから分離する方法の一つとして1テンプレートの活用というのがあるわけなんだけど、
このテンプレートって配置に困りませんか?
プログラマが、あるいはアプリのワクが分かっているデザイナがテンプレートを書いているなら困らないかもしれないけれど、そういうことが分からない人がデザインを担当している場合、
テンプレートとそれ以外のパーツが泣き別れになると作業しにくい
と思うわけ。テンプレートはテンプレート用のフォルダに、画像や CSS などのパーツは Web ブラウザから直接見える場所に置いてください、というのはやはり不親切ではないかと。だからと言ってテンプレートをブラウザから見える場所に、具体的にはアプリケーションと同じ階層に置くと今度はお互いに管理しにくいし危ない。
そこでブラウザから見えない場所に viewset の置き場所を作って、そこにまとめて置いてもらうっていうのはどうだろう。具体的には以下のような構成になるんじゃないかと思う。
|-- app ← ブラウザがアクセスするところ
`-- viewset ← テンプレートおよびデザインパーツ置き場
|-- VIEWNAME
| |-- css
| | |-- base.css
| | `-- flavour.css
| |-- img
| | |-- dummy1.img
| | `-- dummy2.img
| `-- template.htm とか template.erb とか
`-- VIEWNAME2
で、アプリの側からは VIEWNAME を指定するだけで、いい具合に CSS や画像も呼び出せるようになるという寸法。デザインリニューアルの際にはこの VIEWNAME を切り替えるだけでガラっと印象を変えることができる。また画像や CSS を直接上書きしなくて済むのでリニューアルというほど大掛かりではないが、定期的にデザインが変わるような場合も更新作業が楽になるのではないかと思う。
ファイルの管理上も都合がよい。デザインする人はデザイン過程と同じ要領でテンプレートも画像も CSS もひと固まりで管理しておけるし、アプリケーションとは別な領域に置いておけるので開発する人もデザインする人もお互いに安心。おまけに VIEWNAME が時系列で並ぶようにすれば簡単な履歴管理にもなる。
どうだろ。
実現方法をなんにも書いてないけど、それはまだ何も考えてないからです。
メリットはそれだけじゃないけど ↩
今までもテストコードは書いていたんだけど、使い捨てだったのでこれはいかんなぁと思っていたのと、確認は人間の目で行っていて、成否をいちいち人間が判断するという効率の悪さが気になっていたので、テストツールを使うことにした。
とりあえず Ruby の Test::Unit から始めた。うーん最初はやはりかなり邪魔くさい。あと、assert にはテストしたいメソッドの結果が直接返ってこないとダメなのかと思いこんでいたけど、たぶんそうじゃないんだな。確認用のメソッドを作ってそっちを呼んでも、とりあえず問題はないと判断。1
ユニットテストの自動化のメリットとして
テストしやすい書き方をするから書き方のバラつきが少なくなる
てな記述を見かけるんだけど、これはほんとかもしれないと思った。こういう自動化ツールは中途半端に使っても嬉しくなく、徹底的に依存しちゃう方が効果は大きい。そうすると、確かにテストしやすいコードに自然と直ってくる。ような気がする。まだコードそのものが変わっちゃうほど使い込んでいないけど、このなんかこう、居心地の悪さは、慣れないことをやっているとき特有のもので、慣れると気持ちよくなりそうなそんな期待を感じることのできる気持ち悪さだ。cvs なんかを使い始めるときもこんな感じかな。最初のうちは面倒なだけで必要性を感じられないんだけど、慣れると使ってないと気持ち悪くなるところが。
ただ、テストコードを書きにくいものはどうしても残りそうだし、とりあえず Web 上を見て回ってもそれに対する有効な回答は見つけられなかった。なんか、そのうちうまく書けるようになる、みたいなちょっと騙されたような感じ。(まぁよほど経験を積んだうえでないと具体的にテストしにくいものはこうする、なんてことは書けないもんな。)
テストコードはどこに置いておくのがいいんだろう? 別ディレクトリに分けてリポジトリにつっこんでおくのがいいのかな? コンパイラ言語なら中に書いちゃって ifdef で切り分け、なんてのも ok なんだろうけど、LL の場合はそうはいかないもんな。
これしかし、自分で書いたものを自分でチェックしてるだけなんだけど、プログラムが自動で成否を判断してオッケーって言ってくれてるような感じが嬉しくて面白い。錯覚なんだけども、面白いと感じることができる要素があれば続けられそう。
この辺、assert の書き方のパターンがどこかにいっぱいあると嬉しいかな。 ↩
なんとか Netscape 4 で問題が目立たないくらいには CSS の修正ができたと思う。あー面倒くさかった。
どうも12, 14ポイントで通常のアプリ(iText でテスト)で正しく表示できなくなる。具体的にはカーニングが狂って文字が重なって表示されてしまう。NeoOffice/J では問題なく使えたけど、ちょーっと難しいフォントだな。何が問題なんだろ?
ちなみにライセンスによると、
- このフォントだけでの再配布は不可。Grass とともに配布すること。
- このフォントを他のアプリと組み合わせて利用するのは問題ない。
そうです。
参考
テスト状況
アプリ | platform | 結果 |
NeoOffice/J 0.8.4 | Panther | OK |
iText 3.1.3 | Panther | NG(12,14ptでカーニングが狂う) |
mi 2.1.5 | Panther | NG(12,14ptでカーニングが狂う) |
ターミナル | Panther | OK |
Firefox 0.9.1 | Mac | OK |
mEdit 0.98 | Mac | OK |
Safari 1.2.3 | OK | |
iText 3.1.5 | Win2k | OK(11, 13 ptがアンチエイリアスなしでもきれい) |
Word 2002 | Win2k | OK |
Illustrator 10 | Win2k | OK |
メモ帳 | Win2k | OK |
サクラエディタ | Win2k | NG(等幅のはずのIPAフォントを認識せず) |
cko | cygwin | OK |
xyzzy | Win | NG(認識せず) |
12ポイント問題って特殊な事例なのかな? あ。ターミナルはフォントのパネルで文字幅が設定できるな。OS X 標準パネルを使うアプリはひょっとして大丈夫か?
cko で試したところ、Windows では 12, 14,16 でドットフォントでも「見れる」状態になる。アンチエイリアスで使うのが基本だとは思うが、そうならない環境でもこの辺のサイズを選べばイケる。iText だと(日本語)と書いてあるフォントにしないと表示できない。11, 13ptがきれい。この辺、なぜ食い違うのかはよく分からない。
本家のアドレスへやたらと来る worm, spam に業を煮やしてアドレスの書き換えを敢行したが、少しずつ効果が出てきた。まだまだ本家のアドレスにばかすか来るけど、どんどん減ってほしいです。えぇ、ほんとに。