この前 XslateのテンプレートをCSSインライン化する - $shibayu36->blog;という記事をかいたのだけど、その時にgfxさんにコメントで
gfx
BKっぽい気もしますが他にいい方法は思いつかないし、最近 pre_process_handler という機能も足したので、まあ許容範囲じゃないすかね。
と言われた。
pre_process_handlerというものがあったことを知らなかったので、試しに利用してみた。
pre_process_handlerを使ってtemplateに対してCSSインライン化する
さてやりたかったことを振り返ると
- メールを一通一通CSSインライン化するのではなく、先にtemplateをインライン化しておいて、それを利用してrenderしたい
ということでした。
例えば以下のようなtemplateを用意します。
<html> <head> <style type="text/css">body { margin: 0px; } #a { padding: 0px; }</style> </head> <body> [% IF visible %] <p id="a">hoge</p> [% END # IF visible %] <p id="b">[% text %]</p> </body> </html>
そして次のようにpre_process_handlerを使ってファイルロード時にCSSインライン化されるようにしてみます。
my $counter = 0; my $xslate = Text::Xslate->new( syntax => 'TTerse', pre_process_handler => sub { my $text = shift; $counter++; my $inlined_text = inline_css($text); return $inlined_text; }, ); print $xslate->render('./template.html', { visible => 1, text => 'hoge', }); print $xslate->render('./template.html', { visible => 0, text => 'fuga', }); print "inline count: " . $counter; sub inline_css { my ($template) = @_; # [% %]のXslateのsyntaxを[mail_template_compiler_placeholder:1] # のようなプレースホルダーに置き換える my $placeholder_to_syntax = {}; my $placeholder_count = 0; $template =~ s{(\[%.+?%\])}{ my $syntax = $1; my $placeholder = "[mail_template_compiler_placeholder:$placeholder_count]"; $placeholder_to_syntax->{$placeholder} = $syntax; $placeholder_count++; $placeholder; }gse; # CSSインライン化 my $inliner = CSS::Inliner->new; $inliner->read({ html => $template }); $template = $inliner->inlinify; # placeholderをXslateのsyntaxに戻す $template =~ s{(\[mail_template_compiler_placeholder:\d+\])}{ my $placeholder = $1; my $syntax = $placeholder_to_syntax->{$placeholder}; }gse; return $template; }
これを実行してみると、以下のようになり、二回renderしているけどインライン化はただ一回しか行われていません。さらにrenderの結果もうまくいっているようです。
<html> <head> </head> <body style="margin: 0px;"> <p id="a" style="padding: 0px;">hoge</p> <p id="b">hoge</p> </body> </html> <html> <head> </head> <body style="margin: 0px;"> <p id="b">fuga</p> </body> </html> inline count: 1
このようにすれば、最初に一度だけメールテンプレートのコンパイルが行えるので、キャッシュが有効な限りは、一度だけのCSSインライン化で何通もメールを送ることができます。やりたいことが出来てますね。便利。
キャッシュとの関係
ちょっとだけsourceを読んでみたら、pre_process_handlerは_load_sourceというメソッドで実行されているようです。
また_load_sourceはload_fileするときにコンパイルされたものが無ければ呼ばれるようです。
と言うことはキャッシュが有効でないと「一度だけインライン化する」というのはできなそうです。試しに以下のようにキャッシュオフにすると、
my $xslate = Text::Xslate->new( cache => 0, syntax => 'TTerse', pre_process_handler => sub { my $text = shift; $counter++; my $inlined_text = inline_css($text); return $inlined_text; }, );
inline count: 2
となり、やはりrenderごとにインライン化が呼ばれています。
この点は一応気をつけておきたいですね。
まとめ
前回のCSSインライン化の話をXslateのpre_process_handlerの機能を使って実装してみました。少しハマりどころもありそうだけど、便利に使えそうですね。