モダンPerlの世界へようこそ

第40回 Text::MicroTemplate:得意分野なんだからPerlを使えばいいじゃない,という方に

この記事を読むのに必要な時間:およそ 5 分

Text::MicroTemplate

三番目のグループに属するのは,MojoliciousのテンプレートエンジンであるMojo::Templateと,そこから派生したText::MicroTemplate(いずれも2008年)の一族です。

Mojoliciousについては第22回でも取り上げましたし,Text::MicroTemplateの方はtypesterこと村瀬大輔氏のArkや,宮川達彦氏のTatsumakiなどでも標準のテンプレートになっているため,利用したことのある方も少なからずおられることでしょう。

両者の大きな違いは,単体で使えるかどうかと,埋め込む文字列が標準でHTMLエスケープされるかどうかにあります(Text::MicroTemplateはウェブでの利用が前提となっているため,基本的にはすべてがHTMLエスケープされるようになっていますが,Mojo::TemplateはもともとMojoのひな形を作成するなど,ウェブ以外の用途も視野に入れてつくられているため,デフォルトでは不要なエスケープはしないようになっています)⁠ただし,最近ではMojo::Templateにもデフォルトでエスケープを有効にできるようなオプションが用意されていますし,逆に,最近のText::MicroTemplateには埋め込み時にHTMLエスケープしないという表記がなくなったため,生のHTMLを埋め込みたいときは一工夫する必要があります。また,Text::MicroTemplateのフォークはかなり早い時期に行われたため,Mojo::Templateにあとから追加されたいくつかのメソッドには(いまのところ)対応していません。

それぞれ簡単なサンプルを載せておきます。

Mojo::Templateの場合

use strict;
use warnings;
use Mojo::Template;

my $mt = Mojo::Template->new(auto_escape => 1);
print $mt->render(<<'END', 'me <ishigaki@cpan.org>', [1, 2, 3]);
% my ($author, $items) = @_;
This report is created by <%== $author %>.
% for (@$items) {
item <%= $_ %>
% }
END

Text::MicroTemplateの場合

use strict;
use warnings;
use Text::MicroTemplate 'encoded_string';

my $renderer = Text::MicroTemplate->new(template => <<'END')->build;
? my ($author, $items) = @_;
This report is created by <?= encoded_string($author) ?>.
? for (@$items) {
item <?= $_ ?>
? }
END

print $renderer->('me <ishigaki@cpan.org>', [1, 2, 3]);

なお,ここではあえて一手間かける例を紹介していますが,オプションを指定する必要がなかったり,一回こっきりの処理の場合は,build_mt,render_mtという関数を利用するともう少し簡潔に書くこともできます。

Text::MicroTemplateはテンプレートを文字列として渡すようになっていますが,ファイルからテンプレートを読み込みたい場合は,同梱されているText::MicroTemplate::Fileを使うと自前でファイルの読み込みを書く手間を省けます。

use strict;
use warnings;
use Text::MicroTemplate::File;

our $mt = Text::MicroTemplate::File->new;
print $mt->render_file('template.mt', 'me <ishigaki@cpan.org>', [1, 2, 3]);

ここであえて$mtをourでグローバル化しているのは,テンプレートの中から別のテンプレートを呼び出すときにinclude_pathなどの情報が保存されているText::MicroTemplate::Fileのオブジェクトを再利用できるようにするため。いささか場当たり的なやり方ですが,たとえばカレントディレクトリにtemplate.mtとして次のようなテンプレートを用意し,

? my ($author, $items) = @_
This report is created by <?= encoded_string($author) ?>.
? for (@$items) {
item <?= $_ ?>
? }
?= $main::mt->render_file('footer.mt', $author);

footer.mtとしてこのようなテンプレートを用意すると,期待通りにfooter.mtの内容が埋め込まれた結果が表示されます(footer.mtの内容が二重にエスケープされるようなことはありません)⁠

? my $author = shift;
<footer><?= $author ?></footer>

ここでは前からの例にならってあえて配列で引数を渡していますが,統一的に処理したければ,コンテキストオブジェクトなりハッシュリファレンスなりを使い回すようにしておけば,テンプレート間で引数の違いに悩まされることもなくなります。

use strict;
use warnings;
use Text::MicroTemplate::File;

my $context = {
  author => 'me <ishigaki@cpan.org>',
  items  => [1, 2, 3],
};

our $mt = Text::MicroTemplate::File->new;
print $mt->render_file('template.mt', $context);

# あとはすべてのテンプレートの先頭に「?= my $context = shift;」を書くだけ

また,コンテキストからText::MicroTemplate::Fileのインスタンス(や,それを含むビュークラスのインスタンス)にアクセスできるようにしておけば,$mtをグローバルにする必要もなくなります。

use strict;
use warnings;
use Text::MicroTemplate::File;

my $mt = Text::MicroTemplate::File->new;

my $context = {
  author => 'me <ishigaki@cpan.org>',
  items  => [1, 2, 3],
  mt     => $mt,
};

print $mt->render_file('template.mt', $context);

# template.mtでは ?= $context->{mt}->render_file(...)のようにします

なお,サンプルは掲載しませんが,村瀬氏作のText::MicroTemplate::Extendedを使うと,テンプレートの一部分を継承して再利用することもできます。また,Mojo::Templateの方はテンプレート間の継承だけでなく,テンプレート内での再利用もできるようになっています(詳しくはMojo::TemplateやMojolicious::Guides::Renderingをご覧ください)⁠

これらのテンプレートは,Perlを解するプログラマにとっては非常に便利なものです。一方で,自由度が高すぎるためにさまざまな問題を起こす可能性もはらんでいます。次回はその問題と,別のアプローチからの対策についてまとめていきます。

著者プロフィール

石垣憲一(いしがきけんいち)

あるときは翻訳家。あるときはPerlプログラマ。先日『カクテルホントのうんちく話』(柴田書店)を上梓。最新刊は『ガリア戦記』(平凡社ライブラリー)。

URLhttp://d.hatena.ne.jp/charsbar/