Kwartzテンプレートシステム入門

― デザイナーとプログラマーが真に協業する方法 ―

目次

  1. はじめに _
  2. Kwartz概要 _
  3. 高度なトピック _
  4. ディレクティブ _
  5. プレゼンテーションパターン _

目次

  1. はじめに
  2. Kwartz概要 _
  3. 高度なトピック _
  4. ディレクティブ _
  5. プレゼンテーションパターン _

テンプレートシステムとは?

テンプレート:
<ul>
#foreach(item in list)
  <li>${item}</li>
#end
</ul>
出力結果:
<ul>
  <li>foo</li>
  <li>bar</li>
  <li>baz</li>
</ul>

テンプレートシステムの種類(1)

例:Velocityテンプレート
<table>
#foreach(item in list)
  <tr>
    <td>${item}</td>
  </tr>
#end
</table>
例:HTML::Templateテンプレート
<table>
<TMPL_LOOP NAME=list>
  <tr>
    <td><TMPL_VAR NAME=name></td>
  </tr>
</TMPL_LOOP>
</table>

テンプレートシステムの種類(2)

例:XMLCテンプレート
<table>
  <tr id="list">
    <td id="item">foo</td>
  </tr>
</table>
例:XMLCメインプログラム(概要)
1. テンプレートを読んでDOMツリーを作成
2. id属性をつけた要素を操作
  2-1. id="item"の要素にテキストを設定
  2-2. id="list"の要素をコピー
  2-3. コピーした要素からid属性を削除
  2-4. リスト中のデータごとに繰り返す
3. DOMツリーをHTMLファイルに変換して出力

ビジネス層とプレゼンテーション層(1)

ビジネス層とプレゼンテーション層(2)

問題点と解決策(1)

問題点と解決策(2)

問題点と解決策(3)

目次

  1. はじめに _
  2. Kwartz概要
  3. 高度なトピック _
  4. ディレクティブ _
  5. プレゼンテーションパターン _

Kwartzとは?

サンプル:テンプレート

プレゼンテーションデータ(ex1.html)
<table>
  <tr id="mark:list">
    <td id="mark:item">foo</td>
  </tr>
</table>
  • マーキングにはid属性を使用
    • 「id="xxx"」または「id="mark:xxx"」
    • マーキングされた要素がプレゼンテーションロジックでの操作対象
    • 「id="mark:xxx"」は自動的に消える(「id="xxx"」なら残る)
プレゼンテーションロジック(ex1.plogic)
#list {
  logic: {
    for user in @users
      _stag   # start-tag
      _cont   # content
      _etag   # end-tag
    end
  }
}
#item {
  value: user;
}
  • CSSと同じ形式
  • 式や文はターゲット言語(Ruby, PHP, ...)で記述

サンプル:コンパイル&出力用スクリプト

コンパイル
$ kwartz -l eruby -p ex1.plogic ex1.html > ex1.rhtml
出力用スクリプト(ex1.rhtml)
<table>
<%     for user in @users %>
  <tr>
    <td><%= user %></td>
  </tr>
<%     end %>
</table>

応用例(1)

要素全体を繰り返す
#list {
  logic: {
    for user in @users
      _stag
      _cont
      _etag
    end
  }
}
内容だけを繰り返す
#list {
  logic: {
    _stag
    for user in @users
      _cont
    end
    _etag
  }
}

応用例(2)

ある条件のときだけ要素を表示
#list {
  logic: {
    if condition
      _stag
      _cont
      _etag
    end
  }
}
ある条件のときはタグを外す
#list {
  logic: {
    if condition
      _cont
    else
      _stag
      _cont
      _etag
    end
  }
}

応用例(3)

内容のかわりに式の値を出力 *
#list {
  logic: {
    _stag
    print value
    _etag
  }
}
要素のかわりに式の値を出力 *
#list {
  logic: {
    print value
  }
}
値が設定されているときだけ出力 *
#list {
  logic: {
    _stag
    if value && !value.empty?
      print value
    else
      _cont
    end
    _etag
  }
}

応用例(4)

奇数行と偶数行で背景色を変える *
#list {
  attrs: 'bgcolor' color;
  logic: {
    i = 0
    for user in @users
      i += 1
      color = i%2==0 ? '#CCF':'#FCC'
      _stag
      _cont
      _etag
    end
  }
}
ダミーの要素を削除 *
#list {
  logic: { }
}

!!重要!!

プレゼンテーションロジックを変更しても、プレゼンテーションデータは変更する必要がない

プレゼンテーションデータからプレゼンテーションロジックが完全に分離・独立

特徴と特長

プレゼンテーション層の構成

  • 役割分担
    • HTML … ドキュメント構造
    • CSS … ドキュメントデザイン
    • JavaScript … クライアント側ロジック
    • Kwartz … サーバ側ロジック *
  • 1つのファイルが1つの役割だけを担当
position of Kwartz in presentation layer

目次

  1. はじめに _
  2. Kwartz概要 _
  3. 高度なトピック
  4. ディレクティブ _
  5. プレゼンテーションパターン _

プロパティ

プロパティの一覧
#id {
  value: username;           // 要素の内容('cont:'と同じ)
  attrs: 'class' klass,      // 属性値の変更
         'bgcolor' color;
  logic:  { ... }            // 要素を操作するロジック
  remove: 'id';              // 属性の削除
  append: cond ? ' checked="checked"':'';   // 属性の追加

  stag:  start_form_tag :action=>'update';  // 開始タグ
  cont:  render :partial=>'form';           // 要素の内容
  etag:  "</form>";                         // 終了タグ
  elem:  link_to 'List' :action=>'list';    // 要素全体
}

自動エスケープ

プレゼンテーションデータ
<p id="mark:p1">foo</p>
<p id="mark:p2">bar</p>
<p id="mark:p3">baz</p>
プレゼンテーションロジック
#p1 {
  value: text;
}
#p2 {
  Value: text;
}
#p3 {
  VALUE: text;
}
コンパイル結果(-eオプションなし)
<p><%= text %></p>
<p><%=h text %></p>
<p><%= text %></p>
コンパイル結果(-eオプションあり)
<p><%=h text %></p>
<p><%=h text %></p>
<p><%= text %></p>

マルチ言語サポート

プレゼンテーションロジック
#name {
  logic: {
    _stag;
    if (user != null) {
      print(user);
    } else {
      _cont;
    }
    _etag;
  }
}
プレゼンテーションデータ
Hello <span id="name">guest</span>!
Java実装でJSPへコンパイル
Hello <span id="name">
<c:choose><c:when test="${user ne null}">
<c:out value="${user}"/>
</c:when><c:otherwise>
guest</c:otherwise></c:choose>
</span>!

テキスト汎用

プレゼンテーションデータ
<span id="mark:m1">
  <foo> & <bar>
</span>
プレゼンテーションロジック
#m1 {
  logic: {
    for i in 0..3 do
      _cont
    end
  }
}
コンパイル
<%     for i in 1..3 do %>
  <foo> & <bar>
<%     end %>
実行結果
  <foo> & <bar>
  <foo> & <bar>
  <foo> & <bar>

Ruby on Railsサポート

プレゼンテーションロジック
#form {
 stag: start_form_tag :action=>'create';
}
#user_name {
 elem: text_field 'user', 'name';
}
#submit {
 elem: submit_tag 'Create';
}
プレゼンテーションデータ
<form id="mark:form">
  Name: <input type="text" id="user_name">
  <input type="submit" id="submit">
</form>
コンパイル結果
<%= start_form_tag :action=>'create' %>
  Name: <%= text_field 'user', 'name' %>
  <%= submit_tag 'Create' %>
</form>

目次

  1. はじめに _
  2. Kwartz概要 _
  3. 高度なトピック _
  4. ディレクティブ
  5. プレゼンテーションパターン _

ディレクティブについて

マーキング

プレゼンテーションデータ
<table id="table">
  <tr id="mark:list">
    <td kw:d="mark list">foo</td>
  </tr>
</table>
コンパイル結果
<table id="table">
  <tr>
    <td>foo</td>
  </tr>
</table>

値の出力(1)

プレゼンテーションデータ
Hello <span kw:d="value name">world</span>!

<a href="mailto:@!{user.email}@">@{user.name}@</a>
コンパイル結果
Hello <span><%= name %></span>!

<a href="mailto:<%= user.email %>"><%=h user.name %></a>

値の出力(2)

プレゼンテーションデータ
<form kw:d="stag start_form_tag :action=>'create'">
  <a href="#" kw:d="elem link_to 'List' :action=>'list'">List</a>
</form>
コンパイル結果
<%= start_form_tag :action=>'create' %>
  <%= link_to 'List' :action=>'list' %>
</form>

値の出力(3) デフォルト値

プレゼンテーションデータ
Hello <span kw:d="default user">guest</span>
コンパイル結果(実際には1行)
Hello <% if (user) && !(user).to_s.empty? then %>
<%= user %><% else %>guest<% end %>

繰り返し(1) foreach, list

プレゼンテーションデータ
<ul kw:d="for item in list">
  <li>@{item}@</li>
</ul>
コンパイル結果
<% for item in list do %>
<ul>
  <li><%=h item %></li>
</ul>
<% end %>
プレゼンテーションデータ
<ul kw:d="list item in list">
  <li>@{item}@</li>
</ul>
コンパイル結果
<ul>
<% for item in list do %>
  <li><%=h item %></li>
<% end %>
</ul>

繰り返し(2) Foreach, List

プレゼンテーションデータ
<ul kw:d="For item in list">
  <li>@{item}@</li>
</ul>
コンパイル結果
<% item_ctr = 0 %>
<% for item in list do %>
<%   item_ctr += 1 %>
<ul>
  <li><%=h item %></li>
</ul>
<% end %>
プレゼンテーションデータ
<ul kw:d="List item in list">
  <li>@{item}@</li>
</ul>
コンパイル結果
<ul>
<% item_ctr = 0 %>
<% for item in list do %>
<%   item_ctr += 1 %>
  <li><%=h item %></li>
<% end %>
</ul>

繰り返し(3) FOREACH, LIST

プレゼンテーションデータ
 <tr class="@!{item_tgl}@" kw:d="FOR item in list">
  <td>@{item}@</td>
 </tr>
コンパイル結果
<% item_ctr = 0 %>
<% for item in list do %>
<%   item_ctr += 1 %>
<%   item_tgl = item_ctr%2==0 ? 'even' : 'odd' %>
 <tr class="<%= item_tgl %>">
  <td><%=h item %></td>
 </tr>
<% end %>

繰り返し(4) while, loop

プレゼンテーションデータ
<ul kw:d="while r = h.fetch()">
  <li>@{r['name']}@</li>
</ul>
コンパイル結果
<% while r = h.fetch() do %>
<ul>
  <li><%=h r['name'] %></li>
</ul>
<% end %>
プレゼンテーションデータ
<ul kw:d="loop r = h.fetch()">
  <li>@{r['name']}@</li>
</ul>
コンパイル結果
<ul>
<% while r = h.fetch() do %>
  <li><%=h r['name'] %></li>
<% end %>
</ul>

条件分岐

プレゼンテーションデータ
<p kw:d="if val > 0">
  val is positive.
</p>
<p kw:d="elsif val < 0">
  val is negative.
</p>
<p kw:d="else ">
  val is zero.
</p>
コンパイル結果
<% if val > 0 %>
<p>
  val is positive.
</p>
<% elsif val < 0 %>
<p>
  val is negative.
</p>
<% else %>
<p>
  val is zero.
</p>
<% end %>

代入

プレゼンテーションデータ
<table kw:d="set i=0">
 <tbody kw:d="list item in items">
  <tr kw:d="set i+=1">
   <td>@!{i}@</td>
   <td>@{item}@</td>
  </tr>
 </tbody>
</table>
コンパイル結果
<% i=0 %>
<table>
 <tbody>
<% for item in items do %>
<% i+=1 %>
  <tr>
   <td><%= i %></td>
   <td><%=h item %></td>
  </tr>
<% end %>
 </tbody>
</table>

ダミー要素

プレゼンテーションデータ
<table>
 <tr bgcolor="#FCC">
  <td>foo</td>
 </tr>
 <tr bgcolor="#CCF" id="dummy:d1">
  <td>bar</td>
 </tr>
 <tr bgcolor="#FCC" kw:d="dummy ">
  <td>baz</td>
 </tr>
</table>
コンパイル結果
<table>
 <tr bgcolor="#FCC">
  <td>foo</td>
 </tr>
</table>

属性値

プレゼンテーションデータ
<p bgcolor="#FCC"
   kw:d="attr 'bgcolor' color; attr 'class' klass">
  Hello world
</p>
コンパイル結果
<p bgcolor="<%= color %>" class="<%= klass %>">
  Hello world
</p>

属性の追加

プレゼンテーションデータ
<input type="checkbox" kw:d="append cond ? ' checked' : ''">
コンパイル結果
<input type="checkbox"<%= cond ? ' checked' : '' %>>

要素や内容の置換

プレゼンテーションデータ
<div id="nav">
 <a href="/">Top</a> &lt;
 Documents
</div>
...
<div
 id="replace_content_with_content:nav">
 Top &lt; Documents
</div>
コンパイル結果
<div id="nav">
 <a href="/">Top</a> &lt;
 Documents
</div>
...
<div>
 <a href="/">Top</a> &lt;
 Documents
</div>

目次

  1. はじめに _
  2. Kwartz概要 _
  3. 高度なトピック _
  4. ディレクティブ _
  5. プレゼンテーションパターン

プレゼンテーションパターンとは?

Replace Elem/Cont with Elem/Cont Pattern

プレゼンテーションデータ(レイアウト)
<html lang="en">
 <body onload="init()">
  <div id="placeholder">...</div>
 </body>
</html>
プレゼンテーションデータ(コンテンツ)
<html><body>
  <div id="mark:pagecontent">
    Hello Kwartz!
  </div>
</body></html>
プレゼンテーションロジック
#placeholder {
  logic: {
    _element(pagecontent)
  }
}
コンパイル結果
<html lang="en">
 <body onload="init()">
  <div>
    Hello Kwartz!
  </div>
 </body>
</html>

Select Element/Content Pattern

プレゼンテーションデータ
<div id="mark:message">
 <p style="color:red"
    id="mark:error">ERROR!</p>
 <p style="color:blue"
    id="mark:warning">Warning:</p>
 <p id="mark:good">No error.</p>
</div>
コンパイル結果
<%     if status == 'error' %>
 <p style="color:red">ERROR!</p>
<%     else if status == 'warning' %>
 <p style="color:blue">Warning:</p>
<%     else %>
 <p>No error.</p>
<%     end %>
プレゼンテーションロジック
#message {
  logic: {
    if status == 'error'
      _element(error)
    else if status == 'warning'
      _element(warning)
    else
      _element(good)
    end
  }
}

Pick-up Element/Content Pattern

プレゼンテーションデータ
<div id="breadcrumbs">
  <a href="/" id="mark:crumb">Home</a>
  <span id="mark:separator">&gt;</span>
  <a href="/aaa/">AAA</a> &gt;
  <a href="/aaa/bbb/">BBB</a> &gt;
  <a href="/aaa/bbb/ccc">CCC</a> &gt;
  <strong id="mark:title">title</strong>
</div>
コンパイル結果
<%     for item in list %>
  <a href="<%=e.path%>"><%=e.name%></a>
&gt;<%     end %>
  <strong><%= doctitle %></strong>
プレゼンテーションロジック
#breadcrumbs {
  logic: {
    for e in items
      _element(crumb)
      _content(separator)
    end
    _element(title)
  }
}
#crumb {
  value:  e.name;
  attrs:  'href' e.path;
}
#title {
  value: doctitle;
}

Extract Element/Content Pattern

プレゼンテーションデータ
<html id="mark:document">
  <body>
    <div class="tabs" id="mark:tabs">
      <a href="/" id="mark:tab">Home</a>
      <a href="/download/">Download</a>
      <a href="/support/">Support</a>
    </div>
</body></html>
コンパイル結果
    <div class="tabs">
<%     for tab in tablist %>
      <a href="/">Home</a>
<%     end %>
    </div>
プレゼンテーションロジック
#document {
  logic: {
    _element(tabs)
  }
}
#tabs {
  logic: {
    _stag
    for tab in tablist
      _element(tab)
    end
    _etag
  }
}

Get Kwartz at
http://www.kuwata-lab.com/kwartz

Thank You