HTML form management on Xslate


<追記>
@typesterさんの指摘を受けて再考中。確かに、気軽にrawを書くようになると、結局「うっかりraw」をする可能性が生じて安全性が下がります。テンプレートではrawを使うべきではない、ということを前提にドキュメントと記事を書き直しました。
参考:
@fujiwara:

うっかりraw付ける付けないのミスというより、化ける!→rawつけると化けない!→じゃあとりあえずrawつけとけばイイよね! みたいな流れがありがち。
@tokuhirom:
ていうか、T::MT の思想では、基本的に model とか controller 側で raw 属性つける、なんだよね。
Xslate上でフォーム処理をしようとしたらいろいろハマったのでCookbook書きました。

http://search.cpan.org/dist/HTML-Shakan=HTML::Shakanなどのフォームビルダーは、アプリケーションコードで出力の際にmark_raw()でraw stringにしてください。
HTML::FillInFormを使う場合がちょっと厄介で、簡単な方法がなかったので*1新しい構文を加えることにしました(0.1038から)。blockに対してその場でフィルタをかますことができるようになります。
Cookbookからのコピペですがこんな感じです。

#!perl -w
use strict;
use Text::Xslate qw(mark_raw);
use HTML::FillInForm;

sub fillinform {
    my($q) = @_;
    return sub {
        my($html) = @_;
        return mark_raw(HTML::FillInForm->fill(\$html, $q));
    };
}

my $tx  = Text::Xslate->new(
    function => {
        fillinform => \&fillinform,
    },
);

my %vars = (
    q => { foo => "<filled value>" },
);
print $tx->render_string(<<'T', \%vars);
FillInForm:
: block form | fillinform($q) | raw -> {
<form>
<input type="text" name="foo" />
</form>
: }
T
__END__

さらに、いちいちfillinform()を書くのがかったるい!という方向けに、HTML::FillInForm::Liteにはエクスポート可能なfillinform()関数を追加しました(1.09から)。fillinform($html, $data)とすれば$htmlにfill()した結果を返し、fillinform($data)とすれば上記のようなcurry化したサブルーチンを返します。
つまり、Text::Xslate->new( module => ['HTML::FillInForm::Lite' => [qw(fillinform)]] )とするだけでfillinform($q)フィルタが使えるので便利です。ただしrawフィルタが必要なのは変わりません。安全第一。

<追記>
テンプレート側でrawをするべきではない、という原則に従うと、上記のインターフェイスをそのまま使うのは奨励できません。関数インターフェイスを使う場合でも、以下のように一段ラッパーを書いたほうが安全です。

use Text::Xslate qw(mark_raw);
use HTML::FillInForm::Lite qw(fillinform);
my $tx = Text::Xslate->new(
    function => sub {
        my($q) = @_;
        return sub { mark_raw(fillinform($_[0], $q)) };
    },
);
# 後は同じ


*1:マクロを定義してそれを明示的に呼び出すしかありませんでした。