ビューの仕組み
TreeFrogでは、今のところ2つのテンプレートシステム(ライブラリ)、ERBと Otamaを搭載してます。ジェネレータで自動生成されるビューはOtamaシステムのファイルです。
Otamaはテンプレートとプレゼンテーションロジックを完全に分離したテンプレートシステムです。HTMLテンプレートには完全なHTMLを記述し、動的に書き換えたい部分の要素(開始タグ)に「マーク」をつけます。プレゼンテーションロジックファイルには、その「マーク」に関連づけてロジック(C++コード)を記述します。ファイルを分けたことにより、デザイナーとプログラマの分業が可能です。
「index.html」の中身を見てみましょう。
indexアクションからrender()メソッドが呼び出されると、このindex.htmlの内容がレスポンスとして返されます。ファイルを見ると分かりますが、HTML(バージョン5)に準拠しているので、今時のブラウザで開けばデザインは崩れません。
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
<title data-tf="#head_title"></title>
</head>
<body>
<h1>Listing Blog</h1>
<table border="1" cellpadding="5" style="border: 1px #d0d0d0 solid; border-collapse: collapse;">
<tr>
<th>ID</th>
<th>Name</th>
<th>Message</th>
<th></th>
</tr>
<tr data-tf="#for"> ← #for という「マーク」。以下同様。
<td data-tf="#id"></td>
<td data-tf="#name"></td>
<td data-tf="#message"></td>
<td>
<a data-tf="#linkToShow">Show</a>
<a data-tf="#linkToEdit">Edit</a>
<a data-tf="#linkToRemove">Remove</a>
</td>
</tr>
</table>
<a data-tf="#linkToEntry">New entry</a>
</body>
</html>
「マーク」をつけるためにdata-tfというカスタム属性を使っています。HTML5でいうところのCustom Data Attributeのことです。その値の"#"で始まっている文字列が「マーク」です。
次に、プレゼンテーションロジックに該当する「index.otm」を見てみましょう。
上記のテンプレートで宣言されたマークに、ロジックが関連づけられています(#includeは予約語なので特別)。マークから空行までが1セットです。ロジックの部分はほぼC++コードです。関連づけには演算子(~=とか:==など)も使われています。この演算子によって、振る舞いが変わります。
#include "blog.h" /* これは C++ コードそのまま。blog.h をインクルード */
#head_title ~= controller()-> controllerName() + ": " + controller()-> actionName()
#for :
tfetch(QList<Blog> , blogList); /* コントローラから渡されたデータを使う宣言 */
for (QListIterator<Blog> it(blogList); it.hasNext(); ) {
const Blog &i = it.next(); /* i は Blog オブジェクトの参照 */
%% /* ループ(for文)するときのお決まりで、その要素と子要素を繰り返す */
}
#id ~= i.id() /* #id というマークのある要素のコンテントに i.id() の結果を入れる */
#name ~= i.name()
#message ~= i.message()
#linkToShow :== linkTo("Show", urla("show", i.id())) /* その要素と子要素を linkTo() の結果で置き換える */
#linkToEdit :== linkTo("Edit", urla("edit", i.id()))
#linkToRemove :== linkTo("Remove", urla("remove", i.id()), Tf::Post, "confirm('Are you sure?')")
#linkToEntry :== linkTo("New entry", urla("entry"))
簡単にOtama演算子を説明します。
~(チルダ)は、右辺の結果を、マークされた要素のコンテントに設定します。=は、HTMLエスケープして出力します。従って、~=は右辺の結果をHTMLエスケープし、要素のコンテントに設定します。
HTMLエスケープしたくなかったら、~==を使います。
:(コロン)は、マークされた要素および子要素をその右辺の結果で置き換えます。従って、:==はHTMLエスケープせずに要素を置き換えます。
コントローラからビューへのデータの引き渡し
コントローラでtexportされたデータ(オブジェクト)をビューで使う場合は、tfetch()メソッド(実はマクロ)で宣言する必要があります。引数には変数の型と変数名を指定します。すると、指定された変数はtexportされる直前の状態と同じになるので、通常の変数とまったく同じように使えます。
使い方の例
int hoge; hoge = ... texport(hoge);
tfetch(int, hoge);
Otamaシステムでは、これらテンプレートファイルとプレゼンテーションファイルを元にC++コードが生成されます。内部的にはtmakeコマンドが処理しています。その後、コードはコンパイルされ、ビューとして1つの共有ライブラリになります。なので、動作は非常に高速です。
要素(element)は、開始タグ(Start-tag)、コンテント(Content)、終了タグ(End-tag)の3つで構成されます。例として"<p>Hello</p>"という要素があったとすると、<p>が開始タグ、Helloがコンテント、</p>が終了タグになります。一般にコンテントのことを「内容」と呼ぶことの方が多いようですが、個人的に少々紛らわしいと思うので、ここではコンテントと書いています。
