Nora チュートリアル 【第 2 回】

Nora チュートリアル 第 2 回

MoonWolf (moonwolf@moonwolf.com)

MoonWolf Development

2004 年 10 月 14 日

nora_neko.png前回の記事では Nora の最低限の入出力の方法だけ書きました。今回は Hello World! から一歩進んでテンプレートについて紹介します。

テンプレート

Nora による最も原始的な HTML 生成方法は、前回のリスト 2 のように Ruby プログラムに直接 HTML を埋め込んでしまう方法です。 HTML が小さく変更がほとんど無いならば、それでも特に問題にはなりません。 しかし HTML が多く大きくなったり、変更が頻繁にあったりする場合、管理しきれなくなっていきます。 そこでテンプレートにより原始的な HTML 生成方法の問題を解決します。

原始的な HTML 生成方法の問題

  • Ruby と HTML がごちゃまぜになって何処に何があるのかわからなくなる。
  • HTML を埋め込んだ部分を実行しないと何が出力されるかわからない。
  • Macromedia DreameaverIBM ホームページ・ビルダー等の HTML エディタで編集できない。

テンプレートによる問題の解決

  • HTML を 1 ページ 1 ファイルとしてプログラムと別のファイルとして管理します。バージョン管理 (CVS/Subversion/etc) も個別にできるようになります。
  • テンプレートだけを実行することができます。必要なのは Hash と Array による簡単なデータだけです。
  • HTML とテンプレート固有の記号があるだけなので HTML エディタで編集することができます。

テンプレートエンジン

Ruby には色々なテンプレートエンジンがあります。 RubyGarden の HtmlTemplates ページRAA にもたくさん登録されています。 Nora にも Web::Template というテンプレートエンジンが含まれています。

Web::Template の使い方

まずはサンプルを見てください。

テンプレートのサンプル 1
template1.rb
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)

template1.html
<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>

template1.rb 実行結果
<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 行目ではループ用のデータを設定しています。この場合はキー:ハッシュの配列を設定します。

値の置き換え
  {{var name=キー}}

パラメタから文字列を取り出してテンプレートに出力します。 デフォルトでは文字列は HTML エスケープして出力されます。 エスケープせずに出力する場合には、

 {{var name=キー escape=none}}

と書きます。

繰り返し
  {{loop name=キー}}
  {{/loop}}

パラメタからハッシュの配列を取り出して、それぞれのハッシュに対して loop 〜 /loop の処理を繰り返します。

条件判断
  {{if name=キー}}
  {{else}}
  {{/if}}

パラメタから取り出した値によって条件分岐します。 値が真なら if 〜 else までが実行されます。 値が偽なら else 〜 /if までが実行されます。

実用的なサンプル

テンプレートを使った簡単で実用的なサンプルを紹介します。 htreeと REXML を使って XPath のテストをするプログラムです。 テキストフィールドに XPath 式を、テキストエリアに HTML を入力して Go ボタンをクリックします。 すると、マッチした要素が下のテーブルに表示されます。

xpath.cgi
#!/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
}

xpath.html
<!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.jpg MoonWolf は半導体メーカに勤めるプログラマです。2000 年に Ruby に触れ、それ以降 RAA (Ruby Application Archive) にてライブラリ・アプリケーションを発表し続けています。登録プロジェクト数 30 と世界 2 位であり 1 位を目指して日夜拡張ライブラリの書けそうな C ライブラリを探しています。Ruby関連の記事の執筆もしますので出版関係者のかた連絡お願いします。著者の連絡先は moonwolf@moonwolf.com です。