Mojo::Templateで自分好みのテンプレートクラスを作る
こんにちは。須釜です。
今日は、Mojoliciousに同梱される便利なモジュール群の中からMojo::Templateを紹介したいと思います。
Mojo::Templateはテキストの中にPerlコードをそのまま書いてしまおうという趣旨のテンプレートエンジンです。実際にはMojoliciousのフレームワークの標準のレンダラーのバックエンドとして使われるのみで、単体で使おうとする人はあまりいないかも知れませんが、少し工夫すると結構便利に使えそうです。
下記のようなデータがあって、テンプレートで出力したいとします。
my $people = [ {name => 'Sato', age => '30'}, {name => 'Saito', age => '30'}, {name => 'Suzuki', age => '30'}, ];
printfを使って書くと、こうでしょうか。
my $template = <<EOF; name : %s age : %s EOF for my $person (@$people) { printf($template, $person->{name}, $person->{age}); }
結果はこうなります。
name : Sato age : 30 name : Saito age : 30 name : Suzuki age : 30
これをMojo::Templateで書き直してみます。
my $template = <<'EOF'; name : <%= $_[0]->{name} %> age : <%= $_[0]->{age} %> EOF my $mt = Mojo::Template->new; for my $person (@$people) { print $mt->render($template, $person); }
こうすることで、名前付き引数風になってテンプレートの可読性があがりますし、 Perlコードで何でもできるようになります。 具体的な例は公式ドキュメントを参照してください。
コンパイルしたテンプレートを使い回すと少し速くなるかも知れません。
my $template = <<'EOF'; name : <%= $_[0]->{name} %> age : <%= $_[0]->{age} %> EOF my $mt = Mojo::Template->new; $mt->parse($template)->build->compile; for my $person (@$people) { print $mt->interpret($person); }
ちなみに、$mt->codeには下記のようなテンプレートのコンパイル結果が格納されています。
package Mojo::Template::SandBox; no warnings 'redefine'; sub _escape { no warnings 'uninitialized'; ref $_[0] eq 'Mojo::ByteStream'? $_[0] : Mojo::Util::xml_escape("$_[0]") } use Mojo::Base -strict; sub { my $_M = ''; ; do { $_M .= "name : "; $_M .= scalar $_[0]->{name} ; $_M .= "\n"; $_M .= "age : "; $_M .= scalar $_[0]->{age} ; $_M .= "\n"; ; $_M } };
Mojo::Templateにはprependメソッドというものが用意されています。これは、文字列で渡したPerlコードをテンプレートの前段の処理として追加できるというものです。prependを使えば、テンプレート内で使用できる自作の関数を定義することもできます。
my $template = q{<%= my_func(shift) %>}; my $mt = Mojo::Template->new; $mt->prepend('sub my_func { $_[0] ** 2 }'); $mt->parse($template)->build->compile; print $mt->interpret(1); # 1 print $mt->interpret(2); # 4 print $mt->interpret(3); # 9
ただ、さすがにPerlコードを文字列で組み立てるのは汚らしいので、自前のクラスでMojo::Templateを隠蔽してしまいましょう。
package Text::MojoEP; use Mojo::Base -base; __PACKAGE__->attr(mt => sub { Mojo::Template->new }); __PACKAGE__->attr(funcs => sub {{}}); sub add_function { my ($self, $name, $cb) = @_; $self->funcs->{$name} = $cb; return $self; } sub render { my ($self, $path, @args) = @_; my $mt = $self->mt; if (! $mt->compiled) { my $prepend = q/no strict 'refs'; no warnings 'redefine';/; $prepend .= 'my $_H = shift; my $_F = $_H->funcs;'; for my $name (keys %{$self->funcs}) { $prepend .= "sub $name; *$name = sub {\$_F->{$name}->(\@_)};"; } $mt->prepend($prepend); return $mt->render($path, $self, @args); } return $mt->interpret($self, @args); }
これで、関数の追加が楽なテンプレートクラスの出来上がりです。下記のように使うことができます。
my $template = q{<%= my_func(shift) %>}; my $mt = Text::MojoEP->new; $mt->add_function('my_func', sub { $_[0] ** 2 }); print $mt->render($template, 1); # 1 print $mt->render($template, 2); # 4 print $mt->render($template, 3); # 9
テンプレート関数に関してはMojolicious::Plugin::EPの内部でも、ほぼ同様の実装がなされています。ただ、 EPではその他にキャッシュの仕組みや、フレームワーク固有の処理なども追加されているので、単体では使えそうもない感じに仕上がっています。そういう訳で、上記のText::MojoEPクラスのようなものを自作しておくと、自分好みのテンプレートエンジンとして使えて便利かもしれません。
Text::MojoEPはgithubに置いておきましたので、興味がありましたら使ってみてください。