« リンク先情報の取得テスト | メイン | 東方名前録 »
2006年02月17日
PerlでELの使えるテンプレート [Perl]
JSPのEL(式言語)は非常に効率的で便利な代物だと思う。これに慣れてしまうと、地道にテンプレートの平文キーワードに値をバインドさせていくのがかなり面倒に思えてくる。
PerlのテンプレートでもELを使えないだろうか。Template::ToolKitを使えばかなり高度なことができるが、あくまでPurePerlで実現したい。そんなわけで、自作のテンプレートモジュール MjView に式言語もどきを組み込んでみることにした。
<h2>${ Title }</h2>
こんな感じの単純な置換はこれまでも出来ていたが、
<p>${ list.user.name }</p>
のように、とりあえず階層的な構造を持つデータにアクセスできるようにするのが目標だ。
しかし、ここでひとつ問題が上がってきた。 ${ user.name } という式を解析するとき、 $user->{'name'} を単純に呼び出せばいいのか、$user->name とメソッド呼び出しをするのか、それとも $user->get('name') なのかが分からないのだ。ここらへんはJSPで自動的でやってくれる(というか制約上に収められている)が、Perlのようなおおらかな言語では様々なかたちで実装されているので、なかなか汎用的な設計をされているとは限らない。色々試してみたが、PerlにはJavaScriptのように外部からメソッドの実装の有無を調べる手段は(基本的に)無いので、残念ながら変数セパレータを全てドット(".") で解決するのは諦めざるを得なかった。
解決策として取ったのは、ドット(".")なら変数展開し、シャープ("#")ならメソッド呼び出し、カンマ(",")ならgetメソッドを通すという方法だ。例えば
${ party.userList,mjrk#name }
という式なら、
$party->{'userList'}->get('mjrk')->name()
を呼び出すようにする。やや強引だが、他に思いつかなかったので仕方ない。ブランケット("[]")を使うとか、もうちょっとうまいやり方もあったかもしれない。このへんはもうすこし改良の余地アリだ。
残る課題は、ループ、条件分岐、インクルードのタグを作ることだ。現状ではループやインクルードなどの処理は MjView クラスのメソッドをやりくりすることでやっつけているが、これがテンプレート中に組み込めるようになればますます View の分離が図りやすくなるはずだ。やはり目指すところは、JSPのELとカスタムタグだろう。
今回作った変換処理のコア部分を参考程度にのっけてみる。
#-----------------------------------------#
# テンプレート変換(実体)
#-----------------------------------------#
sub _transTemplate{
my $this = shift;
my($name, $list) = @_;
my $template;
my $cache = \$this->{'home'}->{'temporary'}->{$name};
if(!$$cache){ # キャッシュ無し
if(-f $name){
sysopen(TMP,$name,O_RDONLY);
local($/) = undef;
$template = <TMP>;
close(TMP);
$$cache = $template;
}
}
# キャッシュを利用
else{ $template = $$cache; }
# 変換します
$template =~ s/\$\{\s*([\w\.\#\,]+\s*)\}/&parseEL($list,$1)/eg;
return $template;
}
# ELを解析
sub parseEL{
my($data, $str) = @_;
$str =~ m/^([\#\.\,]?)(\w+)([\#\.\,])?(.*)$/;
if($1 eq '.' || !$1){ $data = $data->{$2}; }
elsif($1 eq ','){ $data = $data->get($2); }
elsif($1 eq '#'){ $data = $data->$2; }
return (!$3) ? $data : &parseEL($data, $3.$4);
}
なお、エラー処理とかのコードは冗長なので省いてある。
投稿者 : 09:50 | コメント (0) | トラックバック (0)
トラックバック
このエントリーのトラックバックURL:
http://totora.jpn.org/mt/mt-tb.cgi/110