[perl] SledgeでText::Xslateを使う

2010年9 月1日  |  Written by matsumoto  |  under Perl Yahoo!ブックマークに登録    はてなブックマーク - [perl] SledgeでText::Xslateを使う

こんにちは、松本です。
3月の最終エントリ以後、暑すぎて ダラダラし過ぎました。Blogをちゃんと書くようにします。

今回はperlのフレームワーク "Sledge" でテンプレートエンジンの "Text::Xslate" を使います。

Template-Toolkitについて

Sledgeで利用するテンプレートエンジンは TT(Template-Toolkit) がデファクトスタンダードであります。
(HTML::Templateも使えますが・・・)

TTは、テンプレートエンジンとして非常に柔軟性が高く、大変に使いやすいのですが何せ、処理が重いです。

6A(シックス・アパート)のBlogサービスの、VoxもTemplate-Toolkitを利用しているらしいのですが

Template Toolkit: profile. Wow! Template Toolkit takes 60% of the request time.

との事で、リクエストの処理全体の6割をTTが占有してしまっているという驚愕の事実らしいです。

弊社でも ClearSilverText::MicroTemplateTenjiin などを Sledge に組み込めるか、試行錯誤してみたのですが
TTと全く違う文法で記述されたHTMLテンプレートが、プロジェクト毎に混在するとなると、運用面で人的負荷や稼動コストが増える可能性があったため、見送っていました。


Text::Xslate

Text::Xslate は、藤 吾郎さん が CPAN で公開されているモジュールで、精力的にUPDATEされているテンプレートエンジンです。

TTとほとんど同一の文法でHTMLテンプレートを記述することができ、更に高速動作するモジュールでして
先の 6割の処理の部分を抑える事ができるのでは?と期待大です。

TTの文法により近づけるために Text::Xslate::Bridge::TT2Like モジュールも併せて利用します。

インストール

cpanm で さっくりインストールです。

cpanm -S Text::Xslate Text::Xslate::Bridge::TT2Like

Sledgeのテンプレートクラス

ズバリ、以下のテンプレートクラスをPages.pmから呼び出しています。

Prog/Pages.pm (一部)

シンタックスハイライトを付けてある、全コードは こちら

sub create_template {
    my ($self, $file) = @_;
    my $tmpl = Proj::Template::Text::Xslate->new($file, $self);
    return $tmpl;
}

Proj/Template/Text/Xslate.pm

シンタックスハイライトを付けてある、全コードは こちら

package Proj::Template::Text::Xslate;

use utf8;
use strict;
use warnings;
#use base qw/Sledge::Template/;

use Text::Xslate;
use Scalar::Util;

use vars qw($Tx);
$Tx = '';

sub new {
    my($class, $file, $page) = @_;
    bless {
        _options => {
            filename => $file,
        },
        _params  => {
            config  => $page->create_config,
            r       => $page->r,
            session => $page->session,
        },
    }, $class;
}

sub add_associate       { Sledge::Exception::UnimplementedMethod->throw }
sub associate_namespace { Sledge::Exception::UnimplementedMethod->throw }

sub output {
    my $self = shift;
    #my %config = %{$self->{_options}};
    #my $input  = delete $config{filename};
    my $input  = delete $self->{'_options'}->{'filename'}
                           || Sledge::Exception::TemplateNotFound->throw(
                                   "No template file detected. Check your template path.",
                              );
    if(!$Tx) {
        my %config = %{$self->{_options}};

        $config{syntax}    = 'TTerse';
        $config{header}    = [ qw/commons.inc/ ];
        $config{module}    = [ qw/Text::Xslate::Bridge::TT2Like/ ],
        $config{path}      = [ $self->{'_params'}->{'config'}->tmpl_path, $self->{'_params'}->{'config'}->tmpl_path . '/includes' ];
        $config{cache}     = 1;
        $config{cache_dir} = $self->{'_params'}->{'config'}->cache_dir . '/text_xslate_cache';
        $config{type}      = 'text';
        $config{function}  = {
            nl2br      => \&Text::Xslate::Bridge::TT2Like::html_line_break,
            uri_escape => \&Text::Xslate::uri_escape,
        };
        $Tx = Text::Xslate->new(%config);
        #warn "[ CREATE TX ]";
    }
    unless (-e $input) {
        Sledge::Exception::TemplateNotFound->throw(
             "No template file detected. Check your template path.",
        );
    }
    $input =~ s@/path/to/proj/view/@@;
    my $output = $Tx->render($input, $self->{'_params'});
    $self->finalize;
    return $output;
}

sub param {
    my $self = shift;
    if (@_ == 0) {
        return keys %{$self->{'_params'}};
    }
    elsif (@_ == 1) {
        return $self->{'_params'}->{$_[0]};
    }
    else {
        while (my($key, $val) = splice @_, 0, 2) {
            $self->{'_params'}->{$key} = $val;
        }
    }
}

sub finalize {
    my $self = shift;
    for my $key ($self->param()) {
        if( ! ref( $self->{'_params'}->{$key} ) || $key eq 'session' || $key eq 'r' || $key eq 'config') { next; }
        if (!Scalar::Util::isweak($self->{'_params'}->{$key})) {
            #warn "[ WEAKEN ] ".$key;
            eval { Scalar::Util::weaken($self->{'_params'}->{$key}) };
        }
        delete $self->{'_params'}->{$key};
    }
}

1;

速度比較

Template-ToolkitからText::Xslateに変更することで、大よそ 150% ぐらい、リクエストの処理速度が向上しました。

今まで 100req/sec だったものが 150req/sec に高速化された形です。

気になった点、ハマッた点

  • mod_perlのプロセスサイズが 120%ぐらい増えました。 メモリが少ない環境では要注意かも。

ハマッた点

Docsにも書いてある点がほとんどなので、ちゃんと読め!という部分なのですが・・・

  • [% INCLUDE "header.inc" %] など、 "(ダブルクォーテーション)でファイル名を囲まないといけないです。
  • TTのオプションに存在する、"ABSOLUTE" が使えないので INCLUDE内のファイルに明示的に変数を渡さなければならないので

    [%- INCLUDE "header.inc" WITH
        FOO = 'ふー'
     -%]

    などとWITHで記述します。

  • TTでいう、PROCESS、INSERT は使えないです。
  • マクロを呼び出すときに、[% macro_func %] でなく、[% macro_func() %] と () を付ける必要があります。
  • render($file,$param) で指定する $fileは path で指定された以下のファイルパスを指定します。(絶対パスではないです)
  • TTの PRE_PROCESS は Text::Xslate の new時に渡す引数の header に相当します

まとめ

Template-Toolkitとほとんど同一の記法で、基本差し替えるだけで
Webアプリ全体の処理速度が向上しますので、是非ご利用されてみてはいかがでしょうか。

150%の処理速度向上が見込めましたので、次回プロジェクトでは試行してみたいと思います。

現在1件のコメントがあります | コメントの投稿はこちら

  1. Twitter Trackbacks for [perl] SledgeでText::Xslateを使う - ありんく tech-log [alink.co.jp] on Topsy.com  |  2010年9月1日 PM 2:37 #

    [...] [perl] SledgeでText::Xslateを使う - ありんく tech-log alink.co.jp/tech/blog/2010/09/01/perl-sledge%E3%81%A7textxslate%E3%82%92%E4%BD%BF%E3%81%86/ – view page – cached 今回はperlのフレームワーク "Sledge" でテンプ [...]

    Twitter Trackbacks for [perl] SledgeでText::Xslateを使う - ありんく tech-log [alink.co.jp] on Topsy.com - Gravatar

コメントを書き込む

コメント本文

※コメントのフォーム内で以下のタグがご利用いただけます
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

私はチーム・マイナス6%です