Mustache & CouchDB入門。¶
CouchDBのデザインドキュメントにMustacheのテンプレートエンジンを適用する方法についてのお話。これは英語のブログではそこそこ情報があるのだが、日本語ではid:yssk22さんのブログに 部分的に情報があるだけ なのでまとめてみた。
前フリ¶
CouchDBのデザインドキュメントでHTMLの画面を作る場合、もっともプリミティブな方法としては、JavaScriptで
function(head, req) {
provides("html",function() {
send("<!doctype html>");
(snip)
send("</body>");
return "</html>";
});
}
とか、
function(doc, req) {
provides("html", function() {
return "<!doctype html>" +
(snip)
"</body>" +
"</html>";
});
}
とするが、これは非常にコードが見づらい。CouchDBへのアプリのデプロイにCouchAppを使っていれば、これに mustache の JavaScript用のテンプレート がバンドルされているので、これを使おう。
前提環境¶
今回の環境は以下のとおり。
mutacheは前述のとおり、CouchAppに含まれているものを使う。
$ couchapp generate hoge
とすると、hoge/vendor/couchapp/lib/mustache.jsとして作られる。テンプレートは、hogeディレクトリの直下にtemplatesディレクトリを作り、そこにヒゲテンプレートを作るので、まず、ディレクトリを作っておこう。
$ mkdir hoge/templates
シンプルなテンプレートでHTMLを生成する。¶
まずはmustacheだけに慣れるために、ドキュメントを必要としないHTMLを生成してみよう。CouchDBのDB名をsampledbとして、”http://127.0.0.1:5984/sampledb/_design/hoge/_show/moge”でアクセスするとテンプレートで生成されたHTMLが返されるようにしてみる。用意するファイルは次の2つ。
hoge/shows/moge.js
hoge/templates/moge.html
内容はそれぞれ以下の通り。
moge.js¶
function(head, req) {
start({
"headers" : {
"Content-type": "text/html"
}
});
var mustache = require("vendor/couchapp/lib/mustache");
var data = {
"title": "タイトル",
"hello": "こんにちは",
"world": "世界"
}
return mustache.to_html(this.templates.moge, data);
}
moge.html¶
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<title>{{title}}</title>
</head>
<body>
<h1>{{title}}</h1>
<p>{{hello}}、{{world}}</p>
</body>
</html>
テンプレートのhoge/templates/mgoe.htmlが、moge.jsのto_html()の第一引数でthis.template.mogeにマッピングされ、第二引数にテンプレートに渡す変数をJSONで渡しているのが肝。デモは こちら 。
ループ処理を加えたテンプレートでHTMLを生成する。¶
先ほどのto_html()に渡すJSONを次のfuga.jsのように変更し、{{#list}} {{/list}}の間でループ処理させることができる。
hoge/shows/fuga.js
hoge/templates/fuga.html
内容はそれぞれ以下の通り。
fuga.js¶
function(head, req) {
start({
"headers" : {
"Content-type": "text/html"
}
});
var mustache = require("vendor/couchapp/lib/mustache");
var data = {
"title": "タイトル2",
"datalist": [
{
"hello": "やあ",
"world": "ヒゲさん。"
},
{
"hello": "こんにちは",
"world": "ボーズ。"
}
]
};
return mustache.to_html(this.templates.fuga, data);
}
デモは こっち 。
fuga.html¶
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<title>{{title}}</title>
</head>
<body>
<h1>{{title}}</h1>
{{#datalist}}
<p>{{hello}}、{{world}}</p>
{{/datalist}}
</body>
</html>
ドキュメントをテンプレートで表示する。¶
今まではテンプレートで埋め込むデータをJavaScriptに直接埋め込んでいたが、これをドキュメントから取得するようにしてみよう。次のファイルだけを用意し、テンプレートは最初のmoge.htmlを利用する。
hoge/shows/foo.js¶
function(doc, req) {
start({
"headers" : {
"Content-type": "text/html"
}
});
var mustache = require("vendor/couchapp/lib/mustache");
var data = {
"title": doc.title,
"hello": doc.hello,
"world": doc.world
};
return mustache.to_html(this.templates.moge, data);
}
ドキュメント自体は 次のようなのもの用意 する。
{"_id":"4efbb2aec6ca7318c341885aa700368b","_rev":"1-13a1fc74866dfc068bb06d7095169034","title":"\u30bf\u30a4\u30c8\u30eb\uff13","hello":"\u3084\u3042","world":"\u3053\u307e\u3061\u3083\u3093"}
先ほどと違い、1行目のfunction()の第一引数がheadではなくdocになっているが、docにすることで、URLとしては デモ のURIのようにdoc._idを渡すことができる。
ドキュメントの各フィールドの値は、上記を見て分かる通り、”doc.fieldname”で取得できる。
ドキュメントのリストをテンプレートで表示する。¶
リストで表示するには、viewとlistを作る必要がある。テンプレートはfuga.htmlを使ってみる。
hoge/views/greetings/map.js
hoge/lists/bar.js
map.js¶
function(doc) {
emit(doc._id, doc);
}
これは単純にdoc._idをキーに、ドキュメントを返す。 こんなドキュメントを用意 した。
{"total_rows":2,"offset":0,"rows":[
{"id":"4efbb2aec6ca7318c341885aa700368b","key":"4efbb2aec6ca7318c341885aa700368b","value":{"_id":"4efbb2aec6ca7318c341885aa700368b","_rev":"1-13a1fc74866dfc068bb06d7095169034","title":"\u30bf\u30a4\u30c8\u30eb\uff13","hello":"\u3084\u3042","world":"\u3053\u307e\u3061\u3083\u3093"}},
{"id":"4efbb2aec6ca7318c341885aa7005ba7","key":"4efbb2aec6ca7318c341885aa7005ba7","value":{"_id":"4efbb2aec6ca7318c341885aa7005ba7","_rev":"1-712121ac547e77bb58ab256928e07f53","hello":"\u304a\u3063\u3059","world":"\u304a\u3089xxx"}}
]}
bar.js¶
function(head, req) {
start({
"headers" : {
"Content-type": "text/html"
}
});
var mustache = require("vendor/couchapp/lib/mustache");
var datalist = [];
while(row = getRow()) {
datalist.push({
"hello": row.value.hello,
"world": row.value.world
});
}
var data = {
"title": "タイトル4",
"datalist": datalist
};
return mustache.to_html(this.templates.fuga, data);
}
getRow()でviewで返される”rows”を取得できるので、これをwhileで処理し、push()で各ドキュメントの値をJSONに格納するのがミソ。デモは こんな感じ に。
{{#hoge}}{{/hoge}}による処理¶
これはループを回すだけではなく、if文の代わりにも使える。hogeの値がtrueの時のみ、この中が処理されるので表示させたくないときはfalseに設定した変数を使えば、例えば同じ日付のうち最初の1回のみ表示させ、2回目以降は表示しない、ということもできる。こんな感じ。
まとめ¶
ということで、とりあえずヒゲテンプレートのmustacheを使うとかなり便利。このサンプルコードは GitHub で公開。