Template::Toolkitでファイルのタイムスタンプを付加

Railsだとimage_tagを使えば画像ファイルの更新日時をくっつけて

<img src="foo.jpg?1234567890" />

みたいにしてくれて、画像が更新されればブラウザは画像をキャッシュしていても新しくリクエストしてくれたりする。で、Catalyst/Template::Toolkitだとどうやるんだろう?ってことで書いてみた。

package MyApp::View::Plugin::Ts;
=head1 MyApp::View::Plugin::Ts
imgタグのsrc属性値やlinkタグのhref属性値に対象のファイルの更新時刻を付加することで
ファイルの更新が行われた際にブラウザがキャッシュを捨てて再度リクエストすることを促すためのTTプラグイン
=cut
use strict;
use warnings;
use base qw/Template::Plugin/;
use Encode;
my %cached_timestamps = ();
=head1 imgタグ
=head2 Usage
[% USE Ts %]
[% Ts.img_tag('/static/images/logo.jpg') %]
=> <img src="/static/images/logo.jpg?12345678" />
=cut
sub img_tag {
my ($self, $src, $alt) = @_;
return qq{<img src="@{[$self->ts($src)]}" alt="@{[$alt]}" />};
}
=head1 CSS用linkタグ
=head2 Usage
[% USE Ts %]
[% Ts.css_link_tag('/static/css/style.css') %]
=> <link rel="stylesheet" type="text/css" href="/static/css/style.css?1234890" />
=cut
sub css_link_tag {
my ($self, $href) = @_;
return qq{<link rel="stylesheet" type="text/css" href="@{[$self->ts($href)]}" />};
}
=head1 タイムスタンプ付きURLパスを返す
=head2 Usage
[% USE Ts %]
[% Ts.ts('/static/images/logo.jpg') %]
=>'/static/images/logo.jpg?12345678'
[% Ts.image('/static/images/foo/bar.jpg') %]
=> '/static/images/foo/bar.jpg?12345678'
=cut
sub ts {
my ($self, $source) = @_;
my $timestamp = $self->_get_timestamp($source);
return $source . '?' . $timestamp;
}
sub _get_timestamp {
my ($self, $source) = @_;
my $timestamp;
if (defined $cached_timestamps{$source}) {
$timestamp = $cached_timestamps{$source};
} else {
my $path = MyApp->config->{home} . '/root' . $source;
$timestamp = 86400 * (-M $path) + $^T;
$cached_timestamps{$source} = $timestamp;
}
return $timestamp;
}
1;
view raw ts.pm hosted with ❤ by GitHub

これをlib/MyApp/Views/Pluginに置いて、MyApp::View::TTとかに書かれている__PACKAGE__->configに

PLUGIN_BASE => 'TclandSp::View::Plugin',

を追加して各テンプレートで

[% USE Ts %]

すれば、例えば

[% Ts.ts('/static/images/logo.jpg') %]

/static/images/logo.jpg?1234567890

になる。これをimgタグと組み合わせて

<img src="[% Ts.ts('/static/images/logo.jpg') %]" />

とかするといい。

以下メモ:

やっぱりフルスタックのRailsはデカいけど便利だ。