line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Catalyst::Plugin::VersionedURI; |
2
|
|
|
|
|
|
|
our $AUTHORITY = 'cpan:YANICK'; |
3
|
|
|
|
|
|
|
# ABSTRACT: add version component to uris |
4
|
|
|
|
|
|
|
$Catalyst::Plugin::VersionedURI::VERSION = '1.1.2'; |
5
|
|
|
|
|
|
|
|
6
|
4
|
|
|
4
|
|
4379452
|
use 5.10.0; |
|
4
|
|
|
|
|
15
|
|
7
|
|
|
|
|
|
|
|
8
|
4
|
|
|
4
|
|
23
|
use strict; |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
109
|
|
9
|
4
|
|
|
4
|
|
24
|
use warnings; |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
133
|
|
10
|
|
|
|
|
|
|
|
11
|
4
|
|
|
4
|
|
23
|
use Moose::Role; |
|
4
|
|
|
|
|
9
|
|
|
4
|
|
|
|
|
47
|
|
12
|
4
|
|
|
4
|
|
22142
|
use URI::QueryParam; |
|
4
|
|
|
|
|
10
|
|
|
4
|
|
|
|
|
99
|
|
13
|
4
|
|
|
4
|
|
5008
|
use Path::Tiny; |
|
4
|
|
|
|
|
38048
|
|
|
4
|
|
|
|
|
2999
|
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
our @uris; |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
sub initialize_uri_regex { |
18
|
4
|
|
|
4
|
0
|
11
|
my $self = shift; |
19
|
|
|
|
|
|
|
|
20
|
4
|
0
|
33
|
|
|
20
|
if ( not exists $self->config->{'Plugin::VersionedURI'} |
21
|
|
|
|
|
|
|
and exists $self->config->{'VersionedURI'} ) { |
22
|
0
|
|
|
|
|
0
|
warn <<'END_DEPRECATION'; |
23
|
|
|
|
|
|
|
Catalyst::Plugin::VersionedURI configuration set under 'VersionedURI' is deprecated |
24
|
|
|
|
|
|
|
Please move your configuration to 'Plugin::VersionedURI' |
25
|
|
|
|
|
|
|
END_DEPRECATION |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
$self->config->{'Plugin::VersionedURI'} |
28
|
0
|
|
|
|
|
0
|
= $self->config->{'VersionedURI'}; |
29
|
|
|
|
|
|
|
} |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
my $conf = $self->config->{'Plugin::VersionedURI'}{uri} |
33
|
4
|
|
50
|
|
|
362
|
|| '/static'; |
34
|
|
|
|
|
|
|
|
35
|
4
|
50
|
|
|
|
392
|
@uris = ref($conf) ? @$conf : ( $conf ); |
36
|
4
|
|
|
|
|
25
|
s#^/## for @uris; |
37
|
4
|
|
|
|
|
34
|
s#(?<!/)$#/# for @uris; |
38
|
|
|
|
|
|
|
|
39
|
4
|
|
|
|
|
17
|
return join '|', @uris; |
40
|
|
|
|
|
|
|
} |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
sub versioned_uri_regex { |
43
|
13
|
|
|
13
|
0
|
29
|
my $self = shift; |
44
|
13
|
|
|
|
|
38
|
state $uris_re = $self->initialize_uri_regex; |
45
|
13
|
|
|
|
|
79
|
return $uris_re; |
46
|
|
|
|
|
|
|
} |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
sub uri_version { |
49
|
8
|
|
|
8
|
0
|
17
|
my ( $self, $uri ) = @_; |
50
|
|
|
|
|
|
|
|
51
|
8
|
|
|
|
|
87
|
state $app_version = $self->VERSION; |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
return $app_version |
54
|
8
|
100
|
|
|
|
39
|
unless state $mtime = $self->config->{'Plugin::VersionedURI'}{mtime}; |
55
|
|
|
|
|
|
|
|
56
|
2
|
|
|
|
|
93
|
state %cache; # Would be nice to make this shared across processes |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
# Return the cached value if there is one |
59
|
2
|
50
|
|
|
|
8
|
return $cache{$uri} if defined $cache{$uri}; |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
# Strip off the request base, so we can find the file referenced |
62
|
2
|
|
|
|
|
18
|
( my $file = $uri ) =~ s/^\Q@{[ $self->req->base ]}\E//; |
|
2
|
|
|
|
|
7
|
|
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
# Search the include_path(s) provided in config or the |
65
|
|
|
|
|
|
|
# project root if no include_path was specified |
66
|
|
|
|
|
|
|
state $include_paths = |
67
|
|
|
|
|
|
|
$self->config->{'Plugin::VersionedURI'}{include_path} // |
68
|
2
|
|
50
|
|
|
211
|
[ $self->config->{root} ]; |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
# Return/cache the file's mtime |
71
|
2
|
|
|
|
|
167
|
for my $path ( map { path( $_, $file ) } @$include_paths ) { |
|
2
|
|
|
|
|
11
|
|
72
|
2
|
100
|
|
|
|
252
|
return $cache{$uri} = $path->stat->mtime if -f $path; |
73
|
|
|
|
|
|
|
} |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
# No file was found. Store and return the application's version as |
76
|
|
|
|
|
|
|
# a fallback. |
77
|
1
|
|
|
|
|
91
|
return $cache{$uri} = $app_version; |
78
|
|
|
|
|
|
|
} |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
around uri_for => sub { |
81
|
|
|
|
|
|
|
my ( $code, $self, @args ) = @_; |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
my $uri = $self->$code(@args); |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
my $uris_re = $self->versioned_uri_regex |
86
|
|
|
|
|
|
|
or return $uri; |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
my $base = $self->req->base; |
89
|
|
|
|
|
|
|
$base =~ s#(?<!/)$#/#; # add trailing '/' |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
return $uri unless $$uri =~ m#(^\Q$base\E$uris_re)#; |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
my $version = $self->uri_version( $uri, @args ); |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
if ( state $in_path = $self->config->{'Plugin::VersionedURI'}{in_path} ) { |
96
|
|
|
|
|
|
|
$$uri =~ s#(^\Q$base\E$uris_re)#${1}v$version/#; |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
else { |
99
|
|
|
|
|
|
|
state $version_name = $self->config->{'Plugin::VersionedURI'}{param} || 'v'; |
100
|
|
|
|
|
|
|
$uri->query_param( $version_name => $version ); |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
return $uri; |
104
|
|
|
|
|
|
|
}; |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
1; |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
__END__ |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
=pod |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
=encoding UTF-8 |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
=head1 NAME |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
Catalyst::Plugin::VersionedURI - add version component to uris |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
=head1 VERSION |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
version 1.1.2 |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
=head1 SYNOPSIS |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
In your config file: |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
<Plugin::VersionedURI> |
127
|
|
|
|
|
|
|
uri static/ |
128
|
|
|
|
|
|
|
mtime 0 |
129
|
|
|
|
|
|
|
</Plugin::VersionedURI> |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
In C<MyApp.pm>: |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
package MyApp; |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
use Catalyst qw/ VersionedURI /; |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
In the Apache config: |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
<Directory /home/myapp/static> |
140
|
|
|
|
|
|
|
ExpiresActive on |
141
|
|
|
|
|
|
|
ExpiresDefault "access plus 1 year" |
142
|
|
|
|
|
|
|
</Directory> |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
=head1 DESCRIPTION |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
C<Catalyst::Plugin::VersionedURI> adds a versioned component |
147
|
|
|
|
|
|
|
to uris returned by C<uri_for()> matching a given set of regular expressions provided in |
148
|
|
|
|
|
|
|
the configuration file. E.g., |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
$c->uri_for( '/static/images/foo.png' ); |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
will, with the configuration used in the L<SYNOPSIS> return |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
/static/images/foo.png?v=1.2.3 |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
This can be useful, mainly, to have the |
157
|
|
|
|
|
|
|
static files of a site magically point to a new location upon new |
158
|
|
|
|
|
|
|
releases of the application, and thus bypass previously set expiration times. |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
The versioned component of the uri resolves to the version of the application. |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
=head1 CONFIGURATION |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
=head2 uri |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
The plugin's accepts any number of C<uri> configuration elements, which are |
167
|
|
|
|
|
|
|
taken as regular expressions to be matched against the uris. The regular |
168
|
|
|
|
|
|
|
expressions are implicitly anchored at the beginning of the uri, and at the |
169
|
|
|
|
|
|
|
end by a '/'. If not given, defaults to C</static>. |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
=head2 mtime |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
If set to a true value, the plugin will use the file's modification time for |
174
|
|
|
|
|
|
|
versioning instead of the application's version. The modification time is |
175
|
|
|
|
|
|
|
checked only once for each file. If a file is changed after the application is |
176
|
|
|
|
|
|
|
started, the old version number will continue to be used. Checking the |
177
|
|
|
|
|
|
|
modification time on each uri, each time it is served, would result in |
178
|
|
|
|
|
|
|
considerable additional overhead. |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
=head2 include_path |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
A list of directories to search for files if you specify the C<mtime> flag. |
183
|
|
|
|
|
|
|
If no file is found, the application version is used. Defaults to |
184
|
|
|
|
|
|
|
C<MyApp->config->{root}>. |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
=head2 in_path |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
If true, add the versioned element as part of the path (right after the |
189
|
|
|
|
|
|
|
matched uri). If false, the versioned element is added as a query parameter. |
190
|
|
|
|
|
|
|
For example, if we match on '/static', the base uri '/static/foo.png' will resolve to |
191
|
|
|
|
|
|
|
'/static/v1.2.3/foo.png' if 'in_path' is I<true>, and '/static/foo.png?v=1.2.3' |
192
|
|
|
|
|
|
|
if I<false>. |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
Defaults to false. |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
=head2 param |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
Name of the parameter to be used for the versioned element. Defaults to 'v'. |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
Not used if I<in_path> is set to I<true>. |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
=head1 WEB SERVER-SIDE CONFIGURATION |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
Of course, the redirection to a versioned uri is a sham |
205
|
|
|
|
|
|
|
to fool the browsers into refreshing their cache. If the path is |
206
|
|
|
|
|
|
|
modified because I<in_path> is set to I<true>, it's typical to |
207
|
|
|
|
|
|
|
configure the front-facing web server to point back to |
208
|
|
|
|
|
|
|
the same back-end directory. |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
=head2 Apache |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
To munge the paths back to the base directory, the Apache |
213
|
|
|
|
|
|
|
configuration can look like: |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
<Directory /home/myapp/static> |
216
|
|
|
|
|
|
|
RewriteEngine on |
217
|
|
|
|
|
|
|
RewriteRule ^v[0123456789._]+/(.*)$ /myapp/static/$1 [PT] |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
ExpiresActive on |
220
|
|
|
|
|
|
|
ExpiresDefault "access plus 1 year" |
221
|
|
|
|
|
|
|
</Directory> |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
=head1 YOU BROKE MY DEVELOPMENT SERVER, YOU INSENSITIVE CLOD! |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
If I<in_path> is set to I<true>, while the plugin is working fine with a web-server front-end, it's going to seriously cramp |
226
|
|
|
|
|
|
|
your style if you use, for example, the application's standalone server, as |
227
|
|
|
|
|
|
|
now all the newly-versioned uris are not going to resolve to anything. |
228
|
|
|
|
|
|
|
The obvious solution is, well, fairly obvious: remove the VersionedURI |
229
|
|
|
|
|
|
|
configuration stanza from your development configuration file. |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
If, for whatever reason, you absolutly want your application to deal with the versioned |
232
|
|
|
|
|
|
|
paths with or without the web server front-end, you can use |
233
|
|
|
|
|
|
|
L<Catalyst::Controller::VersionedURI>, which will undo what |
234
|
|
|
|
|
|
|
C<Catalyst::Plugin::VersionedURI> toiled to shoe-horn in. |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
=head1 THANKS |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
Mark Grimes, Alexander Hartmaier. |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
=head1 SEE ALSO |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
=over |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
=item Blog entry introducing the module: L<http://babyl.dyndns.org/techblog/entry/versioned-uri>. |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
=item L<Catalyst::Controller::VersionedURI> |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
=back |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
=head1 AUTHOR |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
Yanick Champoux <yanick@babyl.dyndns.org> |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
This software is copyright (c) 2011 by Yanick Champoux. |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
259
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
=cut |