2011/12/18

Text::Xslate で HTML 吐くときに human readable に dump する

dump 関数に decoded を吐かせると、、

テンプレに渡されるパラメータを、ダンプして確認する機会というのは多いと思うのです。Text::Xsalte にはもちろんビルトインで dump 関数が用意されています。

<: $foo | dump :>

しかし、この dump 関数は Data::Dumper でダンプする(だけ)という素直な実装になっています。したがって、通常 decoded な値をテンプレートに渡すと思うのですが、そうした値がエスケープして表示され、人間には読めないのですね。

例えば、こんな風です。

{ foo => "\x{fffd}\x{fffd}\x{fffd}\x{142}\x{fffd}\x{fffd}H\x{fffd}\x{5c2}\x{fffd}\x{fffd}\x{fffd}" }

元は

use utf8;
+{ foo => 'おでん食べたい' };

こんなんです。

やっぱり、ダンプ結果もそのまま次のようになって欲しいですよね。

{ foo => "おでん食べたい" }

というわけで、dump 対象の変数を再帰的に全て encoded にしてやろう、となるわけです。

edump

簡易的に以下のような Bridge モジュールを書いて、edump という関数を用意してみます。encoded 処理は Data::Recursive::Encode に丸投げです。簡単ですね。

package Text::Xslate::Bridge::Dumper;
use strict;
use warnings;
use parent qw/Text::Xslate::Bridge/;

__PACKAGE__->bridge(
    function => +{
        edump => \&edump,
    },
);

sub _p {
    require 'Data/Dumper.pm';
    my $dd = Data::Dumper->new(\@_);
    return $dd->Dump() if defined wantarray;
    print $dd->Dump();
}

sub edump {
    my ($ref, $enc) = @_;
    require 'Data/Recursive/Encode.pm';
    _p( Data::Recursive::Encode->encode($enc || 'utf8', $ref) );
}

1;

試してみます。

<: $foo | edump :>

edump すると、

{ foo => "おでん食べたい" }

読めるようになりました!

でも、これだけでは、次のような場合、ちょっと読みにくいのです。

+{
    foo => 'おでん食べたい',
    bar => 'もつ鍋食べたい',
    baz => '<a href="http://www.google.co.jp/search?q=%E5%9C%9F%E4%BD%90%E9%B6%B4&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla:ja:official&hl=ja&client=firefox-a">土佐鶴</a>',
},

上のパラメータを edump してみると、

{ bar => 'もつ鍋食べたい', baz => '<a href="http://www.google.co.jp/search?q=%E5%9C%9F%E4%BD%90%E9%B6%B4&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla:ja:official&hl=ja&client=firefox-a">土佐鶴</a>', foo => 'おでん食べたい' }

こうなっちゃうんですよね。読めるといえば読めますが、1行表示され、パラメータ数の増加にともなって読みにくくなるのが目に見えて悲しいです。一応この時点でも、ソースで見れば、ちゃんと改行されてきれいに表示されますが、いちいちソース表示したくないですよね、普通。しかもソースだとエスケープされてるし。なので、あと一声、気をつかいたい。

predump

というわけで、結論を言ってしまうと edump の結果を、pre タグ付で吐き出す、predump という関数も増やしてみました。この場合、エスケープ済みとエスケープしてないものを連結する場合の挙動がいまいちわからなかったので、手動エスケープ(_recursive_escape は Data::Recursive::Encode の _apply 関数あたりのコピペです)してみた。

sub predump {
    mark_raw('<pre>' . edump(_recursive_escape(\@_)) . '</pre>');
}

これを使うと、

{
  bar => 'もつ鍋食べたい',
  baz => '<a href="http://www.google.co.jp/search?q=%E5%9C%9F%E4%BD%90%E9%B6%B4&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla:ja:official&hl=ja&client=firefox-a">土佐鶴</a>',
  foo => 'おでん食べたい'
}

やった、読みやすい!

というわけで、HTML 出力する場合に人間が読める dump の出来上がり。

余談

  • 取り急ぎは utf8 決めうちで書いた
  • Data::Dumper::AutoEncode はこれから書く(けどいつになるやら) Data::Dumper::AutoEncode 書いた。
  • エスケープ済みとエスケープしてないものの連結の挙動がよくわからん><
  • ダンプ結果を Perltidy で整形しようとしたら exeption 、、
  • もつ鍋ほんとに食べたい
サイト内検索