Perl Hackers Hub

第5回Xslate 次世代テンプレートエンジン(3)

TTerse Template Toolkit互換の構文

KolonはXslateの機能を引き出すのに最適な構文です。しかしXslateでは、TT2からの移行を容易にするために、TT2のサブセットであるTTerseという構文も用意しています。

TTerseの使い方

TTerseを使うには、Text::Xslate->new()メソッドのsyntaxオプションに「TTerse」を渡します。別のディストリビューションとして配布しているText::Xslate::Bridge::TT2Likeを使うと、TT2組み込みのメソッドやフィルタを使えるようにもなりますリスト17⁠。

リスト17 TTerseの使い方(tterse.pl)
use Text::Xslate;
my $tx = Text::Xslate->new(
    syntax => 'TTerse',
    module => ['Text::Xslate::Bridge::TT2Like'],
);
my $template = 'Hello, [% $lang | upper %] world!';
my $result   = $tx->render_string(
    $template,
    { lang => 'Template' },
);
print $result, "\n";
# => 'Hello, TEMPLATE world!'

TT2とTTerseの違い

TTerseは既存のTT2テンプレートを置き換えることを目的としており、高い互換性を持っています。しかし、TT2の仕様は非常に大きく複雑なため、すべての機能を実装しているわけではありません。テンプレートの外部環境へアクセスするUSEディレクティブや、任意のPerlコードを実行するための構文など、機能の制約というコンセプトから逸脱しているために意図的に取り除いている機能もあります。リスト7のようにテンプレート式を自動エスケープする点もTT2とは異なります。ただしこれに関しては、二重エスケープを防止するメカニズムによって、htmlフィルタの適用結果はTT2とほぼ等しくなります。

また、TT2は記号の意味がコンテキストによって異なることがありますが、TTerseではそのようなケースをなくし、可能な限り意味が一意になるようにしています。そのような非互換な例としては、リスト18のようなものがあります。

リスト18 TTのあいまいな構文(ambiguous.tt)
[%# TT2:    FORに続く"="は"IN"と同じ意味になる
  # TTerse: "="は常に代入演算子なので構文エラー %]
[% FOR item = data %]...[% END %]

[%# TT2:    INCLUDEの引数のクォートは以下のように省略できる
  # TTerse: INCLUDEの引数のクォートは必須である。なぜなら
  #         foo.ttはfoo変数のttフィールドを参照している
  #         とみなすからである。よって以下は実行時エラー
[% INCLUDE foo.tt %]

TTerseを使うことの利点は、TT2に親しんだエンジニア/デザイナであれば、わずかな違いを覚えるだけで済むことです。その一方で、Xslateの大きな特徴の一つであるカスケードは利用できません。

Text::Clevery Smarty互換の構文

Text::CleveryはPHPのSmartyというテンプレートエンジンと互換性のある構文です。大規模な拡張なので別のディストリビューションにしていますが、CPANにアップロードしてあるので、Text::Xslate同様にCPANクライアントでインストールできます。Cleveryは既存のSmartyアプリケーションをPerlに移植することを想定しています。ただしSmartyの微妙な挙動を再現するために、実行速度はKolonやTTerseより若干遅くなる傾向があります。

Cleveryを使用するときは、リスト19のようにrender()にPSGI リクエストを渡すことで、$smarty.getや$smarty.postが使用できるようになります。

リスト19 Cleveryの使い方(clevery.pl)
use Text::Clevery;
my $tc  = Text::Clevery->new();
my %env = (QUERY_STRING => 'lang=Clevery');
print $tc->render_string(<<'TPL', {}, env => \%env);
Hello, {$smarty.get.lang} world!
TPL
# => Hello, Clevery world!

CleveryもやはりSmartyと異なる点がいくつかあるものの、Smartyに親しんだエンジニア/デザイナであればすぐに使いこなせるでしょう。しかしSmartyに慣れているのでなければ、あえてCleveryを選択する理由はありません。

Xslateの拡張

組み込み関数やメソッドについては、Xslateのコアは必要最小限に絞っています。しかし拡張性は確保しているため、関数やメソッドを追加することは簡単です。Xslateの拡張はperldoc Text::Xslate::Manual::Cookbookでもいくつか紹介しているため、そちらも参考にしてください。

モジュールを使う

カレントディレクトリを得るCwd.pmやJSONデータの操作に使うJSON.pmのような関数インタフェースを備えたモジュールは、リスト20のようにmoduleオプションで利用できます。

リスト20 moduleオプションの使い方(module.pl)
use Text::Xslate;
my $tx = Text::Xslate->new(
    module => [
        'Cwd', # デフォルトのimportを使う
        'JSON' => ['decode_json'], # 関数を指定
    ],
);
# 以下はカレントディレクトリを表示する
print $tx->render_string('<: cwd() :>'), "\n";

関数を追加する

クラスメソッドを使うインタフェースを備えたモジュールを使うには、ラッパー関数を作ってfunctionオプションに渡します。また、HTMLソースコードを組み立てて返すモジュールについては、戻り値を自動エスケープ回避するための印を付ける必要があるため、ラッパー関数が必要です。ラッパー関数はhtml_builder()というユーティリティ関数で簡単に作成できます。

リスト21はfunctionオプションを使用する一例です。

リスト21 functionオプションの使い方(function.pl)
use Text::Xslate;
use Text::Xslate::Util qw(html_escape html_builder);
use URI ();    # クラスライブラリ
use CGI qw(p); # HTMLを生成するモジュール
my %funcs = (
    URI => sub { URI->new(@_) },
    p   => html_builder {
        my($text) = @_;
        # 引数はエスケープされないので手動でする
        return p( html_escape($text) );
    },
);
my $tx = Text::Xslate->new(
    function => \%funcs,
);
print $tx->render_string(<<'T', { foo => '<foo>'});
<: URI("http://example.com/foo/").host :>
<: p($foo) :>
T
# => example.com
#       <p>&lt;foo&gt;</p>

メソッドを追加する

オブジェクトについては、あらゆるメソッドを呼び出すことができるため、Xslate側で拡張する必要はありません。配列やハッシュなどの基本型については、ブリッジモジュールを作成することで拡張できます。詳細はperldoc Text::Xslate::Bridgeを参照してください。なお、作成したブリッジモジュールをText::Xslate::名前空間に置く必要はありません。

落穂拾い

最後に、紙幅の都合で詳しく掘り下げられなかった点について少しだけ触れておきます。

UTF-8の扱い

Xslateの文字列の扱いは、モダンなテキスト処理モジュールに準じています。すなわち、操作の対象はPerlの文字列であり、Perl APIを使って連結や比較をしています。したがって、Perlの文字列を渡せば、Perlレベルと同じように動くことが期待できます。テンプレート変数についてはデコードして渡してください。またrender()メソッドの戻り値を出力する際にはエンコードする必要があります。

テンプレートファイルのエンコーディングはデフォルトではUTF-8とみなしますが、これは変更可能です。

xlsateコマンド

Xslateをインストールすると、xslateというコマンドもインストールされます。これは、あるディレクトリ以下のテンプレートファイルを一括で処理するものです。また、perlコマンドのように、-eスイッチでワンライナーを実行することもできます。

詳細はperldoc xslateを参照してください。

マクロ

Xslateはマクロもサポートしています。マクロはKolonとTTerseで利用でき、テンプレートを構造化したり、関数やメソッドのコールバックとして渡したりすることができます。

詳細はperldoc Text::Xslate::Syntax::Kolonおよびperldoc Text::Xslate::Syntax::TTerseを参照してください。

Data::Section::Simpleとの連携

Text::Xslate->new()メソッドのpathオプションにハッシュリファレンスを渡せることはすでに述べました。この機能と__DATA__以下のデータを構造化するData::Section::Simpleを組み合わせると、スクリプトとテンプレートを1つのファイルにまとめることができます。

具体例はperldoc Text::Xslate::Manual::Cookbookにあります。

デバッグ

テンプレートのデバッグにはdumpフィルタが使えます。これは、Data::Dumperを使って変数の内容を整形するフィルタです。このほか、デバッグについての詳細はperldoc Text::Xslate::Manual::Debuggingにまとめてあります。

おわりに

本記事では、テンプレートエンジンXslateを紹介しました。従来のテンプレートエンジンと比較して高速かつ安全であり、PSGIアプリケーションとの相性も抜群です。また、TT2やSmartyからの移行を簡単に行うためのサポートもあります。これを機にお試しくださると幸いです。

次回の執筆者は奥一穂さんで、テーマはシステムプログラミングです。筆者もシステム寄りの技術で奥さんにご意見をいただくことがあるため、個人的にも非常に楽しみです。

おすすめ記事

記事・ニュース一覧