MoonWolf (moonwolf@moonwolf.com)
2004 年 10 月 14 日
前回の記事では Nora の最低限の入出力の方法だけ書きました。今回は Hello World! から一歩進んでテンプレートについて紹介します。
Nora による最も原始的な HTML 生成方法は、前回のリスト 2 のように Ruby プログラムに直接 HTML を埋め込んでしまう方法です。 HTML が小さく変更がほとんど無いならば、それでも特に問題にはなりません。 しかし HTML が多く大きくなったり、変更が頻繁にあったりする場合、管理しきれなくなっていきます。 そこでテンプレートにより原始的な HTML 生成方法の問題を解決します。
Ruby には色々なテンプレートエンジンがあります。 RubyGarden の HtmlTemplates ページやRAA にもたくさん登録されています。 Nora にも Web::Template というテンプレートエンジンが含まれています。
まずはサンプルを見てください。
require 'web/template'
tmpl = Web::Template.new('filename'=>'template1.html')
tmpl.param = {
'title' => 'Script Languages',
'table' => [
{ 'name' => "Ruby", 'author' => 'matz' },
{ 'name' => "perl", 'author' => 'Larry Wall' },
{ 'name' => "python", 'author' => 'Guido van Rossum' },
],
}
tmpl.output(STDOUT)
<h1>\{\{var name=title\}\}</h1>
<table border="1">
<tr>
<th>name</th>
<th>author</th>
</tr>
\{\{loop name=table\}\}
<tr>
<td>\{\{var name=name\}\}</td>
<td>\{\{var name=author\}\}</td>
</tr>
\{\{/loop\}\}
</table>
<h1>Script Languages</h1>
<table border="1">
<tr>
<th>name</th>
<th>author</th>
</tr>
<tr>
<td>Ruby</td>
<td>matz</td>
</tr>
<tr>
<td>perl</td>
<td>Larry Wall</td>
</tr>
<tr>
<td>python</td>
<td>Guido van Rossum</td>
</tr>
</table>
template1.rb の 3 行目でテンプレートを作成しています。 4 〜 11 行目でテンプレートの param 変数にデータを設定しています。 param は通常のハッシュオブジェクトです。基本的にキー:文字列という風にテンプレートに埋める値を書いていきます。 6 〜 10 行目ではループ用のデータを設定しています。この場合はキー:ハッシュの配列を設定します。
パラメタから文字列を取り出してテンプレートに出力します。 デフォルトでは文字列は HTML エスケープして出力されます。 エスケープせずに出力する場合には、
と書きます。
パラメタからハッシュの配列を取り出して、それぞれのハッシュに対して loop 〜 /loop の処理を繰り返します。
パラメタから取り出した値によって条件分岐します。 値が真なら if 〜 else までが実行されます。 値が偽なら else 〜 /if までが実行されます。
テンプレートを使った簡単で実用的なサンプルを紹介します。 htreeと REXML を使って XPath のテストをするプログラムです。 テキストフィールドに XPath 式を、テキストエリアに HTML を入力して Go ボタンをクリックします。 すると、マッチした要素が下のテーブルに表示されます。
#!/usr/local/bin/ruby -Ku
require 'web'
require 'web/template'
require 'htree'
require 'rexml/document'
api = Web::Interface::AUTO.new
api.each {|req|
param = {
'title' => 'XPath Test',
'cgi_name' => req.script_name,
}
param['xpath'] = req['xpath'] || ''
param['textarea'] = req['textarea'] || ''
list = []
if !param['xpath'].empty? && !param['textarea'].empty?
doc = HTree(param['textarea']).to_rexml
match=REXML::XPath.match(doc, param['xpath'])
match.each_with_index {|elem,index|
list << {
'index' => (index+1).to_s,
'elem' => elem.to_s
}
}
end
param['list'] = list
rsp = Web::Response.new
rsp.content_type = 'text/html; charset=UTF-8'
tmpl = Web::Template.new('filename'=>'xpath.html')
tmpl.param = param
tmpl.output(rsp.body)
api.response req,rsp
}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>\{\{var name=title\}\}</title>
</head>
<body>
<h1>\{\{var name=title\}\}</h1>
<form method="post" action="\{\{var name=cgi_name\}\}">
<input name="xpath" type="text" value="\{\{var name=xpath\}\}" size="100" maxlength="200" />
<input type="submit" name="Submit" value="Go" />
<br />
<textarea name="textarea" cols="100" rows="20">\{\{var name=textarea\}\}</textarea>
<table border="1">
<tr>
<th scope="col">Index</th>
<th scope="col">Match</th>
</tr>
\{\{loop name=list\}\}
<tr>
<td scope="col">\{\{var name=index\}\}</td>
<td scope="col">\{\{var name=elem\}\}</td>
</tr>
\{\{/loop\}\}
</table>
</form>
</body>
</html>
次回はアプリケーションフレームワークについて紹介いたします。
MoonWolf は半導体メーカに勤めるプログラマです。2000 年に Ruby に触れ、それ以降 RAA (Ruby Application Archive) にてライブラリ・アプリケーションを発表し続けています。登録プロジェクト数 30 と世界 2 位であり 1 位を目指して日夜拡張ライブラリの書けそうな C ライブラリを探しています。Ruby関連の記事の執筆もしますので出版関係者のかた連絡お願いします。著者の連絡先は moonwolf@moonwolf.com です。