みなさんはコードジェネレータを使った事があるでしょうか。私はあります。
SQLにHTML, JavaScript, はてはPHPまで、Rubyでコードジェネレータを使う機会は増すばかりです。
さて、Rubyでコードジェネレーションと言えばERbを使うことが多いですが、それで充分なのでしょうか。 他の選択肢はどうでしょうか。
今日は、wlangというコードジェネレータを紹介します。
template engine?
次のサンプルコードを見てください。 とてもシンプルなHTMLテンプレートエンジンのように見えます。
require 'wlang'
puts WLang::instantiate(<<EOS, {:name => "O'Reilly & Animals", :animals => %w(Koala Rhino Giraffe)}, "wlang/xhtml")
<h1>${name}</h1>
<div id="{name}">
<img alt='{name}' src="http://....."/>
<ul>
*{animals as animal}{${animal}}{, }
</ul>
</div>
EOS
このプログラムの出力結果はこうなります。
<h1>O'Reilly & Animals</h1>
<div id="O'Reilly & Animals">
<img alt='O\'Reilly & Animals' src="http://....."/>
Koala, Rhino, Giraffe
</div>
コンテキストに合わせて正しく、'
や&
がエスケープされているのがわかります。
また、記法にはあまり馴染みがありませんが、配列の展開もうまく動いています。
wlang >> template engine
次にwlangの仕組みを見ていきます。 wlangでは、wlangタグを使って、テンプレートを記述します。 wlangタグは次のような形式です。
記号{.....}{.....}{.....}
後ろの{}
はblockと呼ばれ、任意の個数持つ事ができます。
記号はたとえば $
, *
などです。
そう、実は先のテンプレートで出現した、${...}, "{...}, '{...}, *{...}{...}{...}
は全て、wlangタグだったのでした。
(ちなみに、”{name}” の末尾の”はwlangタグとは関係なく、ただの文字列です。
これは <div id="{name}'>
が文法エラーを出さずに動作する事で確認できます。このセンス嫌いじゃない)
なにやらあやしいかおりがしてきました。
wlang = programming language?
次に、WLang::instantiateの第三引数は”wlang/xhtml”となっていますが、WLangではこれをdialect(方言、なまりの意味)と呼んでおり、当然変えられます。
require 'wlang'
sql = "SELECT * FROM books WHERE name='{name}';"
values = {:name => "O'Reilly"}
puts WLang::instantiate(sql, values, "wlang/sql")
puts WLang::instantiate(sql, values, "wlang/sql/sybase")
# => SELECT * FROM books WHERE name = 'O\'Reilly';
# => SELECT * FROM books WHERE name = 'O''Reilly';
このように、dialect毎に'{...}
タグの動作が変わるわけです。
dialectは複数のRuleSetとEncoderSetで定義されています。
RuleSetはタグの機能をオーバライドしたり新たなタグを定義します。
EncoderSetはdialect固有のencoding、つまり文字列の変換方法を定義していて、
^{..encoding..}{...}
というタグで呼び出す事ができます。
以下は大文字に変換するencodingの例です。
# instantiateとか省略します ^{plain-text/upcase}{wlang} => WLANG
これが例えばwlang/xhtml
dialect であれば
wlang/xhtml/main-encoding
, wlang/xhtml/single-quoting
, wlang/xhtml/double-quoting
などが定義され、それぞれ違うエスケープがされるようになっています。
先のwlang/sql
とwlang/sql/sybase
の結果の違いも、ここで決まっています。
勘のいい方ならお気づきだと思いますが、${...}
, '{...}
, "{...}
はそれぞれdialectのmain-encoding, single-quoting, double-quotingを呼び出すためのショートカットになっています。
polyglot!
この様な拡張可能かつ再利用可能な変換規則を用意することで、Rubyの文字列を埋め込んだJavaScript、を埋め込んだxhtmlを埋め込んだRubyスクリプト、などといった複雑なコード生成を行う場合でも、 エスケープに悩まされることなく気楽にコード生成を行う事ができるwlangかっこいい!
でも、encodingの実装にバグがあるっぽくて、まだそれは無理っぽい。
参考:
紹介しようと思ったのですが、こちらにて詳細にまとめられていたので参照してください。
すごいポイントはいくつかありますが、特にテーブル編集中に自動でセルサイズが再計算されるのが感動です。ただ、pre中でも再レイアウトされるのは残念。
なお、現在のバージョンはいくつか相違点がありました。 * tableのsyntaxがちょっと違う * Vim7.3のconcealに対応している
関係ないけど、作者のMaxim KimさんはVim好きそうな名前ですね。