D言語 Advent Calendar 2012の7日目の記事です.自分で実装しておきながら日本語で紹介した記憶もないので,これを気に現状のテンプレートエンジン周りについて書いておきます.
D言語は言語機能としてevalなどを現状提供していないのもあり,テンプレートエンジン界隈は盛んではありません.なので数が少ないです.
今の所存在しているアプローチは以下の4つです
- logic-lessで値を埋め込むやつ (mustache-d)
- D言語のコードにしてしまう (diet, 新jade)
- dmdscriptなど外部の処理系を呼び出す (旧jade)
- 手で頑張る (web.dなど.そもそもテンプレートエンジンではないが…)
上から見ていきます.
mustache-d
俺が書いてるやつです.mustacheはD言語特有のものではなくて,有名なテンプレートフォーマットです.サイトにもたくさんの言語での実装が載っています.
使い方
READMEにも載ってますが
- Variables: 値の埋め込み
- Sections: リストの列挙や分岐
- Partials: 別ファイルの読み込み
- Comments: コメント
が使えます.細かく見ると色々書くことが増えるので,必要そうなのを見ていきます.以下のは例として同梱しているbasic.mustacheです:
Hello {{name}}
You have just won ${{value}}!
{{#in_ca}}
Well, ${{taxed_value}}, after taxes.
{{/in_ca}}
{{name}}
が値の埋め込みで,nameというキーに対応する値が埋め込まれます.{{#in_ca}}
がセクションの中の一つ条件分岐で,in_ca に値があれば{{/in_ca}}
までが処理されます.
これに対応するD言語のコードは以下になります.Contextという連想配列っぽいものに値をセットしてレンダリングします:
// string/dstringに対応しているので,自分の使いたい型を指定する.wstringはちょっとPhobosがバギーなので今は未サポート
alias MustacheEngine!(string) Mustache;
Mustache mustache;
auto context = new Mustache.Context; // これに値を入れる
context["name"] = "Chris";
context["value"] = 10000;
context["taxed_value"] = 10000 - (10000 * 0.4);
context.useSection("in_ca"); // useSectionを使うことで条件分岐をする
mustache.render("basic", context)); // renderString("Hello {{name}}", context)とかも出来ます
セクションというのがmustacheでの肝になっていて,キーに対応する値のタイプによって,リストの列挙,条件分岐,コールバック呼び出し,をそれぞれ行います.
例えば,他に同梱しているprojects.mustacheの例では,リストの列挙は以下のようになってます.
{{#projects}}
<a href="{{url}}" class="block">
<h2> {{name}} </h2>
<p> {{description}} </p>
</a>
{{/projects}}
Mustache mustache;
auto context = new Mustache.Context;
foreach (ref project; projects) {
auto sub = context.addSubContext("projects"); // リストを列挙するには新たにコンテキストを追加する
sub["name"] = project.name;
sub["url"] = project.url;
sub["description"] = project.description;
}
mustache-dでは基本機能は一通り実装してあるので,もう少し知りたい方はDDocやunittestを参照してみてください(Webに上げないと…).フォーマットに関してはmustache本家にドキュメントがあります.
1ファイルでサクッと使えるからか,メールのテンプレート作成など,ちょくちょくユーザがいるようです.
diet
vibe.dが提供しているjadeシンタックスを持つテンプレートエンジンです.他のvibe.dのモジュールに依存しているので,mustacheのように単独で使える状態にはなってません.
dietの特徴は,アプリケーションのコンパイル時に,テンプレートファイルも一緒にコンパイルして,D言語のコードに変換してしまう所です.そのため,実際にレンダリングする時にパースなどの処理が発生せず,またD言語のコードになっているので,mustacheのContextのような中間オブジェクトが必要になりません.
例
良い例が見つからなかったので,vibe.dのサンプルの中のエラー出力のを引用します.
以下はerror.dtの内容になります:
extends layout
block title
- auto title = "HTTP error " ~ to!string(error.code);
block body
p An error has occured. We are really sorry about that!
p Error description: #{error.message}
これに対するD言語のコードは以下のようになります.render自体はHTTPサーバ側にあるコードで,実質はdietのコードではないんですが,中ではdietのparseDietFile呼んでいるだけです.
void showError(HttpServerRequest req, HttpServerResponse res, HttpServerErrorInfo error)
{
res.render!("error.dt", req, error);
}
この例を見ると分かるのは,auto title
やto!string(error.code)
のようにD言語のコードがjadeのコードブロックに直接掛けるのと,renderのテンプレート引数に使われているerrorという変数を,直接テンプレートファイルの中で使えるという所です(templateのaliasパラメータは強力ですね).
基本dietはこんな感じです.mustacheと違ってそもそもD言語のコードが掛けるので,jadeの構文を覚えてしまえば,後はかなり自由に書けるようになってます.実際,static if で分岐している例や,foreachを使っている例も普通にあります.
正直dietだけを別途公開して欲しい所です…
その他
使ったことないやつです.
jade
これは名前の通り,dietと同じjadeのD言語実装ですが,これもoakという謎ライブラリに依存してます.
最初google codeで公開されてたやつはdmdscriptかV8が必要で,今のgithubのもpcreが必要だったりと面倒なので試してないです.後なんかdmdへのパッチみたいなのも同梱しているので,ちょっと大がかりかなと.
dietがあるので,今では正直立ち位置がよく分からんという感じです.
serenity
これ自身はWebフレームワークなんですが,ビューの所にHTMLを出力するクラスみたいなのがあります.DOMベースで,マニュアルでHTMLを生成する感じなので,テンプレートエンジンとは少し違いますね…
misc-stuff-including-D-programming-language-web-stuff
Adamがたくさん公開しているWeb向けモジュール群です.serenityと似たような感じでDOMベースで書いたり,html.dでJSとかCSS辺りも操作出来ますが,これもテンプレートエンジンではないですね…
Adamのサイトはここにあるweb.dとかを使って構築されているらしいですが,使いやすさはよく分からんです.
まとめ
現状俺の知る限り,単独のテンプレートエンジンはmustache-dのみで,他のは何かしらのフレームワークに依存しています.
個人的には本当に必要になった場合には仮想マシンベースのテンプレートエンジンを書きたいと思ってるんですが,まぁ現状mustache-dで困ってないので当分先になりそうです.
次,8日目はまた@ueshitaさんです!