Template::Parser

Template::Parserでは、'[%'('%]')をデフォルトデリミタ(※)としてtt2ファイルをparseします。

parseの方針は、

  1. ファイル先頭もしくは'%]'から、次の'[%'までのテキストを $preとして保存。(プレシーディング部)
  2. '[%'から'%]'の内部のテキストを $dirとして保存。(ディレクティブ部)

のようです。

こうして順次parseしていき、区切られたもの毎にごにょごにょするわけです。

ごにょごにょ

区切られたものには、$preと$dirがあります。
次は、$dirを tokenize_directiveメソッドを使って、ディレクティブトークンを作成します。

[
  ディレクティブ部,  ($dir) 
  テンプレート内でディレクティブが存在した行数の番号, ( 12-13 )
  tokenise_directive($dir), 
]

という配列リファレンスが@tokens(ディレクティブトークン)にpushされていきます。

sub tokenise_directive

[% html_str | html | html_line_break %]

$var1 = [
  'IDENT',
  'html_str',
  'FILTER',
  '|',
  'IDENT',
  'html',
  'FILTER',
  '|',
  'IDENT',
  'html_line_break'
];
[% test_msg1 = 'test1'
   test_msg2 = 'test2' %]

$var1 = [
  'IDENT',
  'test_msg1',
  'ASSIGN',
  '=',
  'LITERAL',
  '\'test1\'',
  'IDENT',
  'test_msg2',
  'ASSIGN',
  '=',
  'LITERAL',
  '\'test2\'',
];


(※)デリミタとして使えるものは、
Template::Parser - LALR(1) parser for compiling template documents - metacpan.org
のTAG_STYLEを参照。
また、TAGSディレクティブでいつでも指定可能です。

(※)ディレクティブのparseデリミタ(デフォルトは'|')に関しては、
http://search.cpan.org/src/ABW/Template-Toolkit-2.15/lib/Template/Parser.pm
sub tokenise_directive メソッドを参照。

デバッグ時に良く使う、Data::Dumper
データ構造をうまく出力してくれて重宝ですが、
コードリファレンスはうまく表示してくれません。

$var1 = sub {"DUMMY"}

とか、出力されてしまいます...

コードリファレンスを表示したい時には、これ! B::Deparse !!
http://search.cpan.org/~nwclark/perl-5.8.8/ext/B/B/Deparse.pm

使い方

use B::Deparse;
$deparse = B::Deparse->new("-p", "-sC");
print $deparse->coderef2text(\&func);

とすると、&func内のコードを出力してくれます♪

先日、作成中のCatalystアプリにおいてメモリリークが観測されました。
原因は、

use Template::Plugin;
use base qw(Template::Plugin);

sub new {
    my ($class, $context, $args) = @_;

    my $self = {
      _CONTEXT => $context,
      _STASH => $context->stash,
    };

    bless $self , $class;
}

としているところの、

    _STASH => $context->stash

の部分で循環参照となってしまい、リークを起してしまったようです。

この問題によって、Catalyst::View::TTの、sub renderでの $tt->process()
で作成された$output(=画面に出力すべきHTML)が、
リクエスト終了後も開放されず、蓄積されていってしまったのでした。

循環参照の詳しい状況は再度追って報告します。

GTop

今回はGTopモジュールでメモリの推移を把握し、
ボトルネックを突き止めるアプローチを行ないました。

MyApp.pmに以下を追加します。

use GTop;
sub prepare {
    my ( $class, $args) = @_;

    my $gtop = GTop->new;
    my $memory_in_start = $gtop->mem($$)->size;
    my $c = $class->Super::prepare( $args );

    $c->stash->{gtop} = $gtop;
    $c->stash->{memory_in_start} = $memory_in_start;

    return $c;
}

sub finalize_body {
    my ( $class, $c ) = @_;

    $class->Super::finalize_body( $c );
    $c->stash->{memory_in_end} = $c->stash->{gtop}->mem($$)->size;
    $c->log->debug( "Diff:" . 
         GTop::size( $c->stash->{memory_in_end} - $c->stash->{memory_in_start}) );   
}

これで、リクエストに対する処理が終了するまでのメモリの推移が、ログに出力されます。

Catalyst,FCGIのメモリ推移

GTopモジュールで分かったことは、

  1. myapp_fcgi.pl起動後の1リクエスト目で、かなりのメモリ量を使用するということ。
  2. そして、次のリクエストからはほとんどメモリを消費しないということ。

この現象を理解したい。(誰か教えてw

(途中)

ブランチとは

trunk ----A
       |
        --B

branches ----A----my_A
          |
           --B----my_B

いきなりですが、
Subversionは、ブランチを、ブランチとは認識していません。
svn copyコマンドによって作成された、
同じレポジトリ内に共通の履歴を持ったディレクトリのコピーを、
人間がブランチとして認識しているに過ぎないのです。
(後で説明する タグ も全く同じです。)

ですので、ブランチを作るというよりも、
あるリビジョンをBASEとして、ディレクトリ(ファイル)をtrunkから派生させる、
と考えることが重要です。(結局、それはブランチの概念なんですが...w)

ブランチの作成方法

さて、作成方法ですが、2通りあります。

ブランチの作成方法(1)

svn copy trunk/A  branches/A/my_A
svn ci -m 'create branch of trunk/A'

ブランチの作成方法(2)

svn copy http://repo.com/trunk/A  http://repo.com/branches/A/my_A -m 'create ...'

(2)のほうが、svn co(checkout)しなくてよいので効率的です。
既にcheckoutしてある場合には、大した差はありません。

ブランチの運用

ここで、trunk/A/sample.txtと、branches/A/my_A/sample.txt 双方に変更を加え、
それぞれコミットをしたとします。

svn ci trunk/A/sample.text -m 'fix trunk sample.txt' 
... revision 3.

svn ci branches/A/my_A/sample.text -m 'fix branch my_A/sample.txt' 
... revision 4.

そして、以下を実行すると、

svn diff -r 3:4 trunk/A/sample.txt

trunk/A/sample.txtのdiffはbranchとtrunkとの差異の内容を表示してくれます。

このことからも、svn copyで作られたブランチは、
単なるハードリンク的なコピーでしかないことが分かると思います。

ブランチファイルの概念

トランクとブランチは全く同じファイルを参照していると言えます。
では、何が違うのか?

『ファイルのリビジョン管理のストリームが異なる』

        __________> branch
________|_________> trunk

あるファイルに、別のリビジョン管理ストリームを作成する。
これが、ブランチを作成する、という意味となります。
これまでの「リビジョン」という概念に、「ストリーム」という概念が加わるわけです。

作成されたストリームには名前が付けれます。
my-branch, release-1.0 などです。
Subversionでは、この名前をディレクトリ名とそのツリーとして実現しています。

また、このストリーム名をキーに、関連するツリーを取得することができます。

svn co http://example.com/branches/my_A

uri エスケープのお話

くえり%,$くえり,"くえり",く&えり,くえる@ ....

これらの文字はすべてuriエスケープ対象となるのでしょうか?

http://www.ietf.org/rfc/rfc2396.txt?number=2396

各モジュールで試してみましょう。

http://example.com/index.cgi?param=くえりー%

'%'がquery stringに含まれると、
400 Bad Requestでエラーになる。

TTであれば、
[% name | uri %]で防げます。

フォールト-トレンランス

fault-tolerance : 過失許容性 (goo辞書)

ユーザが過失を犯したとしても、システムをダウンさせないように対策をしておく設計思想。
作るだけじゃなくてね。って思想なんだけど、
大丈夫なのか、って思う程、全く考慮出来てない。
どこまでやるかも決まってないし(おぃ

この思想を実現する方法ものとして、