line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Catalyst::Controller::AutoAssets::Handler; |
2
|
4
|
|
|
4
|
|
2490
|
use strict; |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
109
|
|
3
|
4
|
|
|
4
|
|
13
|
use warnings; |
|
4
|
|
|
|
|
4
|
|
|
4
|
|
|
|
|
95
|
|
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
# VERSION |
6
|
|
|
|
|
|
|
|
7
|
4
|
|
|
4
|
|
16
|
use Moose::Role; |
|
4
|
|
|
|
|
3
|
|
|
4
|
|
|
|
|
33
|
|
8
|
4
|
|
|
4
|
|
8932
|
use namespace::autoclean; |
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
28
|
|
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
requires qw( |
11
|
|
|
|
|
|
|
asset_request |
12
|
|
|
|
|
|
|
write_built_file |
13
|
|
|
|
|
|
|
); |
14
|
|
|
|
|
|
|
|
15
|
4
|
|
|
4
|
|
273
|
use Cwd; |
|
4
|
|
|
|
|
4
|
|
|
4
|
|
|
|
|
266
|
|
16
|
4
|
|
|
4
|
|
16
|
use Path::Class 0.32 qw( dir file ); |
|
4
|
|
|
|
|
82
|
|
|
4
|
|
|
|
|
171
|
|
17
|
4
|
|
|
4
|
|
16
|
use Fcntl qw( :DEFAULT :flock ); |
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
1470
|
|
18
|
4
|
|
|
4
|
|
19
|
use Carp; |
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
210
|
|
19
|
4
|
|
|
4
|
|
17
|
use File::stat qw(stat); |
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
47
|
|
20
|
4
|
|
|
4
|
|
229
|
use Catalyst::Utils; |
|
4
|
|
|
|
|
4
|
|
|
4
|
|
|
|
|
87
|
|
21
|
4
|
|
|
4
|
|
12
|
use Time::HiRes qw(gettimeofday tv_interval); |
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
34
|
|
22
|
4
|
|
|
4
|
|
363
|
use Storable qw(store retrieve); |
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
247
|
|
23
|
4
|
|
|
4
|
|
16
|
use Try::Tiny; |
|
4
|
|
|
|
|
3
|
|
|
4
|
|
|
|
|
168
|
|
24
|
4
|
|
|
4
|
|
1699
|
use Data::Dumper::Concise 'Dumper'; |
|
4
|
|
|
|
|
19630
|
|
|
4
|
|
|
|
|
10022
|
|
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
require Digest::SHA1; |
27
|
|
|
|
|
|
|
require MIME::Types; |
28
|
|
|
|
|
|
|
require Module::Runtime; |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
has 'Controller' => ( |
31
|
|
|
|
|
|
|
is => 'ro', required => 1, |
32
|
|
|
|
|
|
|
isa => 'Catalyst::Controller::AutoAssets', |
33
|
|
|
|
|
|
|
handles => [qw(type _app action_namespace unknown_asset _build_params _module_version)], |
34
|
|
|
|
|
|
|
); |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
# Directories to include |
37
|
|
|
|
|
|
|
has 'include', is => 'ro', isa => 'ScalarRef|Str|ArrayRef[ScalarRef|Str]', required => 1; |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
# Optional regex to require files to match to be included |
40
|
|
|
|
|
|
|
has 'include_regex', is => 'ro', isa => 'Maybe[Str]', default => undef; |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
# Optional regex to exclude files |
43
|
|
|
|
|
|
|
has 'exclude_regex', is => 'ro', isa => 'Maybe[Str]', default => undef; |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
# Whether or not to use qr/$regex/i or qr/$regex/ |
46
|
|
|
|
|
|
|
has 'regex_ignore_case', is => 'ro', isa => 'Bool', default => 0; |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
# Whether or not to make the current asset available via 307 redirect to the |
49
|
|
|
|
|
|
|
# real, current checksum/fingerprint asset path |
50
|
|
|
|
|
|
|
has 'current_redirect', is => 'ro', isa => 'Bool', default => 1; |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
# What string to use for the 'current' redirect |
53
|
|
|
|
|
|
|
has 'current_alias', is => 'ro', isa => 'Str', default => 'current'; |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
# Whether or not to make the current asset available via a static path |
56
|
|
|
|
|
|
|
# with no benefit of caching |
57
|
|
|
|
|
|
|
has 'allow_static_requests', is => 'ro', isa => 'Bool', default => 0; |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
# What string to use for the 'static' path |
60
|
|
|
|
|
|
|
has 'static_alias', is => 'ro', isa => 'Str', default => 'static'; |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
# Extra custom response headers for current/static requests |
63
|
|
|
|
|
|
|
has 'current_response_headers', is => 'ro', isa => 'HashRef', default => sub {{}}; |
64
|
|
|
|
|
|
|
has 'static_response_headers', is => 'ro', isa => 'HashRef', default => sub {{}}; |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
# Whether or not to set 'Etag' response headers and check 'If-None-Match' request headers |
67
|
|
|
|
|
|
|
# Very useful when using 'static' paths |
68
|
|
|
|
|
|
|
has 'use_etags', is => 'ro', isa => 'Bool', default => 0; |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
# Max number of seconds before recalculating the fingerprint (sha1 checksum) |
71
|
|
|
|
|
|
|
# regardless of whether or not the mtime has changed. 0 means infinite/disabled |
72
|
|
|
|
|
|
|
has 'max_fingerprint_calc_age', is => 'ro', isa => 'Int', default => sub {0}; |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
# Max number of seconds to wait to obtain a lock (to be thread safe) |
75
|
|
|
|
|
|
|
has 'max_lock_wait', is => 'ro', isa => 'Int', default => 120; |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
has 'cache_control_header', is => 'ro', isa => 'Str', |
78
|
|
|
|
|
|
|
default => sub { 'public, max-age=31536000, s-max-age=31536000' }; # 31536000 = 1 year |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
# Whether or not to use stored state data across restarts to avoid rebuilding. |
81
|
|
|
|
|
|
|
has 'persist_state', is => 'ro', isa => 'Bool', default => sub{0}; |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
# Optional shorter checksum |
84
|
|
|
|
|
|
|
has 'sha1_string_length', is => 'ro', isa => 'Int', default => sub{40}; |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
# directory to use for relative includes (defaults to the Catalyst home dir); |
87
|
|
|
|
|
|
|
# TODO: coerce from Str |
88
|
|
|
|
|
|
|
has 'include_relative_dir', isa => 'Path::Class::Dir', is => 'ro', lazy => 1, default => sub { |
89
|
|
|
|
|
|
|
my $self = shift; |
90
|
|
|
|
|
|
|
my $home = $self->_app->config->{home}; |
91
|
|
|
|
|
|
|
$home = $home && -d $home ? $self->_app->config->{home} : cwd(); |
92
|
|
|
|
|
|
|
return dir( $home ); |
93
|
|
|
|
|
|
|
}; |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
###################################### |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
|
100
|
|
|
|
0
|
0
|
|
sub BUILD {} |
101
|
|
|
|
|
|
|
before BUILD => sub { |
102
|
|
|
|
|
|
|
my $self = shift; |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
# optionally initialize state data from the copy stored on disk for fast |
105
|
|
|
|
|
|
|
# startup (avoids having to always rebuild after every app restart): |
106
|
|
|
|
|
|
|
$self->_restore_state if($self->persist_state); |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
# init includes |
109
|
|
|
|
|
|
|
$self->includes; |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
Catalyst::Exception->throw("Must include at least one file/directory") |
112
|
|
|
|
|
|
|
unless (scalar @{$self->includes} > 0); |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
# if the user picks something lower than 5 it is probably a mistake (really, anything |
115
|
|
|
|
|
|
|
# lower than 8 is probably not a good idea. But the full 40 is probably way overkill) |
116
|
|
|
|
|
|
|
Catalyst::Exception->throw("sha1_string_length must be between 5 and 40") |
117
|
|
|
|
|
|
|
unless ($self->sha1_string_length >= 5 && $self->sha1_string_length <= 40); |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
# init work_dir: |
120
|
|
|
|
|
|
|
$self->work_dir->mkpath($self->_app->debug); |
121
|
|
|
|
|
|
|
$self->work_dir->resolve; |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
$self->prepare_asset; |
124
|
|
|
|
|
|
|
}; |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
# Main code entry point: |
127
|
|
|
|
|
|
|
sub request { |
128
|
18
|
|
|
18
|
0
|
221
|
my ( $self, $c, @args ) = @_; |
129
|
18
|
|
|
|
|
29
|
my $sha1 = $args[0]; |
130
|
|
|
|
|
|
|
|
131
|
18
|
100
|
|
|
|
76
|
return $self->current_request($c, @args) if ( |
132
|
|
|
|
|
|
|
$self->is_current_request_arg(@args) |
133
|
|
|
|
|
|
|
); |
134
|
|
|
|
|
|
|
|
135
|
14
|
50
|
33
|
|
|
506
|
return $self->static_request($c, @args) if ( |
136
|
|
|
|
|
|
|
$self->allow_static_requests |
137
|
|
|
|
|
|
|
&& $self->static_alias eq $sha1 |
138
|
|
|
|
|
|
|
); |
139
|
|
|
|
|
|
|
|
140
|
14
|
|
|
|
|
70
|
return $self->handle_asset_request($c, @args); |
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
sub is_current_request_arg { |
144
|
5
|
|
|
5
|
0
|
7
|
my ($self, $arg) = @_; |
145
|
5
|
100
|
|
|
|
172
|
return $arg eq $self->current_alias ? 1 : 0; |
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
sub current_request { |
149
|
4
|
|
|
4
|
0
|
8
|
my ( $self, $c, $arg, @args ) = @_; |
150
|
|
|
|
|
|
|
my %headers = ( |
151
|
|
|
|
|
|
|
'Cache-Control' => 'no-cache', |
152
|
4
|
|
|
|
|
9
|
%{$self->current_response_headers} |
|
4
|
|
|
|
|
143
|
|
153
|
|
|
|
|
|
|
); |
154
|
4
|
|
|
|
|
83
|
$c->response->header( $_ => $headers{$_} ) for (keys %headers); |
155
|
4
|
|
|
|
|
1027
|
$c->response->redirect(join('/',$self->asset_path,@args), 307); |
156
|
4
|
|
|
|
|
695
|
return $c->detach; |
157
|
|
|
|
|
|
|
} |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
sub static_request { |
160
|
0
|
|
|
0
|
0
|
0
|
my ( $self, $c, $arg, @args ) = @_; |
161
|
|
|
|
|
|
|
my %headers = ( |
162
|
|
|
|
|
|
|
'Cache-Control' => 'no-cache', |
163
|
0
|
|
|
|
|
0
|
%{$self->static_response_headers} |
|
0
|
|
|
|
|
0
|
|
164
|
|
|
|
|
|
|
); |
165
|
0
|
|
|
|
|
0
|
$c->response->header( $_ => $headers{$_} ) for (keys %headers); |
166
|
|
|
|
|
|
|
# Simulate a request to the current sha1 checksum: |
167
|
0
|
|
|
|
|
0
|
return $self->handle_asset_request($c, $self->asset_name, @args); |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
sub handle_asset_request { |
172
|
14
|
|
|
14
|
0
|
27
|
my ( $self, $c, $arg, @args ) = @_; |
173
|
|
|
|
|
|
|
|
174
|
14
|
|
|
|
|
49
|
$self->prepare_asset(@args); |
175
|
|
|
|
|
|
|
|
176
|
14
|
50
|
33
|
|
|
489
|
if($self->use_etags && $self->client_current_etag($c, $arg, @args)) { |
177
|
|
|
|
|
|
|
# Set 304 Not Modified: |
178
|
0
|
|
|
|
|
0
|
$c->response->status(304); |
179
|
|
|
|
|
|
|
} |
180
|
|
|
|
|
|
|
else { |
181
|
14
|
|
|
|
|
59
|
$self->asset_request($c, $arg, @args); |
182
|
|
|
|
|
|
|
} |
183
|
12
|
|
|
|
|
257
|
return $c->detach; |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
sub client_current_etag { |
187
|
0
|
|
|
0
|
0
|
0
|
my ( $self, $c, $arg, @args ) = @_; |
188
|
|
|
|
|
|
|
|
189
|
0
|
|
|
|
|
0
|
my $etag = $self->etag_value(@args); |
190
|
0
|
|
|
|
|
0
|
$c->response->header( Etag => $etag ); |
191
|
0
|
|
|
|
|
0
|
my $client_etag = $c->request->headers->{'if-none-match'}; |
192
|
0
|
0
|
0
|
|
|
0
|
return ($client_etag && $client_etag eq $etag) ? 1 : 0; |
193
|
|
|
|
|
|
|
} |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
sub etag_value { |
196
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
197
|
0
|
|
|
|
|
0
|
return '"' . join('/',$self->asset_name,@_) . '"'; |
198
|
|
|
|
|
|
|
} |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
############################ |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
has 'work_dir', is => 'ro', isa => 'Path::Class::Dir', lazy => 1, default => sub { |
205
|
|
|
|
|
|
|
my $self = shift; |
206
|
|
|
|
|
|
|
my $c = $self->_app; |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
my $tmpdir = Catalyst::Utils::class2tempdir($c) |
209
|
|
|
|
|
|
|
|| Catalyst::Exception->throw("Can't determine tempdir for $c"); |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
return dir($tmpdir, "AutoAssets", $self->action_namespace($c)); |
212
|
|
|
|
|
|
|
}; |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
has 'built_file', is => 'ro', isa => 'Path::Class::File', lazy => 1, default => sub { |
215
|
|
|
|
|
|
|
my $self = shift; |
216
|
|
|
|
|
|
|
my $filename = 'built_file'; |
217
|
|
|
|
|
|
|
return file($self->work_dir,$filename); |
218
|
|
|
|
|
|
|
}; |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
has 'scratch_dir', is => 'ro', isa => 'Path::Class::Dir', lazy => 1, default => sub { |
221
|
|
|
|
|
|
|
my $self = shift; |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
my $Dir = dir($self->work_dir,'_scratch'); |
224
|
|
|
|
|
|
|
$Dir->rmtree if (-d $Dir); |
225
|
|
|
|
|
|
|
$Dir->mkpath; |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
return $Dir |
228
|
|
|
|
|
|
|
}; |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
has 'fingerprint_file', is => 'ro', isa => 'Path::Class::File', lazy => 1, default => sub { |
231
|
|
|
|
|
|
|
my $self = shift; |
232
|
|
|
|
|
|
|
return file($self->work_dir,'fingerprint'); |
233
|
|
|
|
|
|
|
}; |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
has 'lock_file', is => 'ro', isa => 'Path::Class::File', lazy => 1, default => sub { |
236
|
|
|
|
|
|
|
my $self = shift; |
237
|
|
|
|
|
|
|
return file($self->work_dir,'lockfile'); |
238
|
|
|
|
|
|
|
}; |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
has 'includes', is => 'ro', isa => 'ArrayRef', lazy => 1, default => sub { |
242
|
|
|
|
|
|
|
my $self = shift; |
243
|
|
|
|
|
|
|
my $rel = $self->include_relative_dir; |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
my @list = ((ref $self->include)||'') eq 'ARRAY' ? @{$self->include} : $self->include; |
246
|
|
|
|
|
|
|
my $i = 0; |
247
|
|
|
|
|
|
|
return [ map { |
248
|
|
|
|
|
|
|
my $inc; $i++; |
249
|
|
|
|
|
|
|
if((ref($_)||'') eq 'SCALAR') { |
250
|
|
|
|
|
|
|
# New support for ScalarRef includes ... we pre-dump them to a temp file |
251
|
|
|
|
|
|
|
$inc = file( $self->scratch_dir, join('','_generated_include_file_',$i) ); |
252
|
|
|
|
|
|
|
$inc->spew(iomode => '>:raw', $$_); |
253
|
|
|
|
|
|
|
} |
254
|
|
|
|
|
|
|
else { |
255
|
|
|
|
|
|
|
$inc = file($_); |
256
|
|
|
|
|
|
|
} |
257
|
|
|
|
|
|
|
$inc = $rel->file($inc) unless ($inc->is_absolute); |
258
|
|
|
|
|
|
|
$inc = dir($inc) if (-d $inc); #<-- convert to Path::Class::Dir |
259
|
|
|
|
|
|
|
$inc->resolve |
260
|
|
|
|
|
|
|
} @list ]; |
261
|
|
|
|
|
|
|
}; |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
sub get_include_files { |
267
|
18
|
|
|
18
|
0
|
23
|
my $self = shift; |
268
|
|
|
|
|
|
|
|
269
|
18
|
|
|
|
|
31
|
my @excluded = (); |
270
|
18
|
|
|
|
|
29
|
my @files = (); |
271
|
18
|
|
|
|
|
25
|
for my $inc (@{$self->includes}) { |
|
18
|
|
|
|
|
629
|
|
272
|
18
|
100
|
|
|
|
109
|
if($inc->is_dir) { |
273
|
|
|
|
|
|
|
$inc->recurse( |
274
|
|
|
|
|
|
|
preorder => 1, |
275
|
|
|
|
|
|
|
depthfirst => 1, |
276
|
|
|
|
|
|
|
callback => sub { |
277
|
41
|
|
|
41
|
|
6634
|
my $child = shift; |
278
|
41
|
100
|
|
|
|
89
|
$self->_valid_include_file($child) |
279
|
|
|
|
|
|
|
? push @files, $child->absolute |
280
|
|
|
|
|
|
|
: push @excluded, $child->absolute; |
281
|
|
|
|
|
|
|
} |
282
|
12
|
|
|
|
|
130
|
); |
283
|
|
|
|
|
|
|
} |
284
|
|
|
|
|
|
|
else { |
285
|
6
|
50
|
|
|
|
37
|
$self->_valid_include_file($inc) |
286
|
|
|
|
|
|
|
? push @files, $inc->absolute |
287
|
|
|
|
|
|
|
: push @excluded, $inc->absolute; |
288
|
|
|
|
|
|
|
} |
289
|
|
|
|
|
|
|
} |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
# Some handlers (like Directory) need to know about excluded files |
292
|
18
|
|
|
|
|
1149
|
$self->_record_excluded_files(\@excluded); |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
# force consistent ordering of files: |
295
|
18
|
|
|
|
|
74
|
return [sort @files]; |
296
|
|
|
|
|
|
|
} |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
# optional hook for excluded files: |
299
|
|
|
|
17
|
|
|
sub _record_excluded_files {} |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
has '_include_regexp', is => 'ro', isa => 'Maybe[RegexpRef]', |
303
|
|
|
|
|
|
|
lazy => 1, init_arg => undef, default => sub { |
304
|
|
|
|
|
|
|
my $self = shift; |
305
|
|
|
|
|
|
|
my $str = $self->include_regex or return undef; |
306
|
|
|
|
|
|
|
return $self->regex_ignore_case ? qr/$str/i : qr/$str/; |
307
|
|
|
|
|
|
|
}; |
308
|
|
|
|
|
|
|
has '_exclude_regexp', is => 'ro', isa => 'Maybe[RegexpRef]', |
309
|
|
|
|
|
|
|
lazy => 1, init_arg => undef, default => sub { |
310
|
|
|
|
|
|
|
my $self = shift; |
311
|
|
|
|
|
|
|
my $str = $self->exclude_regex or return undef; |
312
|
|
|
|
|
|
|
return $self->regex_ignore_case ? qr/$str/i : qr/$str/; |
313
|
|
|
|
|
|
|
}; |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
sub _valid_include_file { |
316
|
47
|
|
|
47
|
|
53
|
my ($self, $file) = @_; |
317
|
|
|
|
|
|
|
return ( |
318
|
47
|
100
|
33
|
|
|
84
|
$file->is_dir |
319
|
|
|
|
|
|
|
|| ($self->include_regex && ! ($file =~ $self->_include_regexp)) |
320
|
|
|
|
|
|
|
|| ($self->exclude_regex && $file =~ $self->_exclude_regexp) |
321
|
|
|
|
|
|
|
) ? 0 : 1; |
322
|
|
|
|
|
|
|
} |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
has 'last_fingerprint_calculated', is => 'rw', isa => 'Maybe[Int]', default => sub{undef}; |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
has 'built_mtime', is => 'rw', isa => 'Maybe[Str]', default => sub{undef}; |
327
|
|
|
|
|
|
|
sub get_built_mtime { |
328
|
37
|
|
|
37
|
0
|
54
|
my $self = shift; |
329
|
37
|
100
|
|
|
|
1317
|
return -f $self->built_file ? $self->built_file->stat->mtime : undef; |
330
|
|
|
|
|
|
|
} |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
# inc_mtimes are the mtime(s) of the include files. For directory assets |
333
|
|
|
|
|
|
|
# this is *only* the mtime of the top directory (see subfile_meta below) |
334
|
|
|
|
|
|
|
has 'inc_mtimes', is => 'rw', isa => 'Maybe[Str]', default => undef; |
335
|
|
|
|
|
|
|
sub get_inc_mtime_concat { |
336
|
29
|
|
|
29
|
0
|
45
|
my $self = shift; |
337
|
29
|
|
|
|
|
40
|
my $list = shift; |
338
|
29
|
|
|
|
|
46
|
return join('-', map { $_->stat->mtime } @$list ); |
|
40
|
|
|
|
|
1764
|
|
339
|
|
|
|
|
|
|
} |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
sub calculate_fingerprint { |
343
|
8
|
|
|
8
|
0
|
11
|
my $self = shift; |
344
|
8
|
|
|
|
|
11
|
my $list = shift; |
345
|
|
|
|
|
|
|
# include both the include (source) and built (output) in the fingerprint: |
346
|
8
|
|
|
|
|
284
|
my $sha1 = $self->file_checksum(@$list,$self->built_file); |
347
|
8
|
50
|
|
|
|
318
|
$self->last_fingerprint_calculated(time) if ($sha1); |
348
|
8
|
|
|
|
|
22
|
return $sha1; |
349
|
|
|
|
|
|
|
} |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
sub current_fingerprint { |
352
|
26
|
|
|
26
|
0
|
36
|
my $self = shift; |
353
|
26
|
100
|
|
|
|
914
|
return undef unless (-f $self->fingerprint_file); |
354
|
22
|
|
|
|
|
1733
|
my $fingerprint = $self->fingerprint_file->slurp(iomode => '<:raw'); |
355
|
22
|
|
|
|
|
4941
|
return $fingerprint; |
356
|
|
|
|
|
|
|
} |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
sub save_fingerprint { |
359
|
4
|
|
|
4
|
0
|
8
|
my $self = shift; |
360
|
4
|
50
|
|
|
|
11
|
my $fingerprint = shift or die "Expected fingerprint/checksum argument"; |
361
|
4
|
|
|
|
|
136
|
return $self->fingerprint_file->spew(iomode => '>:raw', $fingerprint); |
362
|
|
|
|
|
|
|
} |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
sub calculate_save_fingerprint { |
365
|
4
|
|
|
4
|
0
|
7
|
my $self = shift; |
366
|
4
|
50
|
|
|
|
17
|
my $fingerprint = $self->calculate_fingerprint(@_) or return 0; |
367
|
4
|
|
|
|
|
15
|
return $self->save_fingerprint($fingerprint); |
368
|
|
|
|
|
|
|
} |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
sub fingerprint_calc_current { |
371
|
25
|
|
|
25
|
0
|
29
|
my $self = shift; |
372
|
25
|
50
|
|
|
|
872
|
my $last = $self->last_fingerprint_calculated or return 0; |
373
|
25
|
50
|
|
|
|
808
|
return 1 if ($self->max_fingerprint_calc_age == 0); # <-- 0 means infinite |
374
|
0
|
0
|
|
|
|
0
|
return 1 if (time - $last < $self->max_fingerprint_calc_age); |
375
|
0
|
|
|
|
|
0
|
return 0; |
376
|
|
|
|
|
|
|
} |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
# ----- |
379
|
|
|
|
|
|
|
# Quick and dirty state persistence for faster startup |
380
|
|
|
|
|
|
|
has 'persist_state_file', is => 'ro', isa => 'Path::Class::File', lazy => 1, default => sub { |
381
|
|
|
|
|
|
|
my $self = shift; |
382
|
|
|
|
|
|
|
return file($self->work_dir,'state.dat'); |
383
|
|
|
|
|
|
|
}; |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
has '_persist_attrs', is => 'ro', isa => 'ArrayRef', default => sub{[qw( |
386
|
|
|
|
|
|
|
built_mtime |
387
|
|
|
|
|
|
|
inc_mtimes |
388
|
|
|
|
|
|
|
last_fingerprint_calculated |
389
|
|
|
|
|
|
|
)]}; |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
sub _persist_state { |
392
|
4
|
|
|
4
|
|
6
|
my $self = shift; |
393
|
4
|
50
|
|
|
|
135
|
return undef unless ($self->persist_state); |
394
|
0
|
|
|
|
|
0
|
my $data = { map { $_ => $self->$_ } @{$self->_persist_attrs} }; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
395
|
0
|
|
|
|
|
0
|
$data->{_module_version} = $self->_module_version; |
396
|
0
|
|
|
|
|
0
|
$data->{_build_params} = $self->_build_params; |
397
|
0
|
|
|
|
|
0
|
store $data, $self->persist_state_file; |
398
|
0
|
|
|
|
|
0
|
return $data; |
399
|
|
|
|
|
|
|
} |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
sub _restore_state { |
402
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
403
|
0
|
0
|
|
|
|
0
|
return 0 unless (-f $self->persist_state_file); |
404
|
0
|
|
|
|
|
0
|
my $data; |
405
|
|
|
|
|
|
|
try { |
406
|
0
|
|
|
0
|
|
0
|
$data = retrieve $self->persist_state_file; |
407
|
0
|
0
|
|
|
|
0
|
if($self->_valid_state_data($data)) { |
408
|
0
|
|
|
|
|
0
|
$self->$_($data->{$_}) for (@{$self->_persist_attrs}); |
|
0
|
|
|
|
|
0
|
|
409
|
|
|
|
|
|
|
} |
410
|
|
|
|
|
|
|
} |
411
|
|
|
|
|
|
|
catch { |
412
|
0
|
|
|
0
|
|
0
|
$self->clear_asset; #<-- make sure no partial state data is used |
413
|
0
|
|
|
|
|
0
|
$self->_app->log->warn( |
414
|
|
|
|
|
|
|
'Failed to restore state from ' . $self->persist_state_file |
415
|
|
|
|
|
|
|
); |
416
|
0
|
|
|
|
|
0
|
}; |
417
|
0
|
|
|
|
|
0
|
return $data; |
418
|
|
|
|
|
|
|
} |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
sub _valid_state_data { |
421
|
0
|
|
|
0
|
|
0
|
my ($self, $data) = @_; |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
# Make sure the version and config params hasn't changed |
424
|
|
|
|
|
|
|
return ( |
425
|
|
|
|
|
|
|
$self->_module_version eq $data->{_module_version} |
426
|
|
|
|
|
|
|
&& Dumper($self->_build_params) eq Dumper($data->{_build_params}) |
427
|
0
|
0
|
0
|
|
|
0
|
) ? 1 : 0; |
428
|
|
|
|
|
|
|
} |
429
|
|
|
|
|
|
|
# ----- |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
# force rebuild on next request/prepare_asset |
433
|
|
|
|
|
|
|
sub clear_asset { |
434
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
435
|
0
|
|
|
|
|
0
|
$self->inc_mtimes(undef); |
436
|
|
|
|
|
|
|
} |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
sub _build_required { |
439
|
29
|
|
|
29
|
|
39
|
my ($self, $d) = @_; |
440
|
|
|
|
|
|
|
return ( |
441
|
|
|
|
|
|
|
$self->inc_mtimes && $self->built_mtime && |
442
|
|
|
|
|
|
|
$d->{inc_mtimes} && $d->{built_mtime} && |
443
|
|
|
|
|
|
|
$self->inc_mtimes eq $d->{inc_mtimes} && |
444
|
|
|
|
|
|
|
$self->built_mtime eq $d->{built_mtime} && |
445
|
29
|
100
|
33
|
|
|
970
|
$self->fingerprint_calc_current |
446
|
|
|
|
|
|
|
) ? 0 : 1; |
447
|
|
|
|
|
|
|
} |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
# Gets the data used throughout the prepare_asset process: |
451
|
|
|
|
|
|
|
sub get_prepare_data { |
452
|
17
|
|
|
17
|
0
|
24
|
my $self = shift; |
453
|
|
|
|
|
|
|
|
454
|
17
|
|
|
|
|
49
|
my $files = $self->get_include_files; |
455
|
17
|
|
|
|
|
427
|
my $inc_mtimes = $self->get_inc_mtime_concat($files); |
456
|
17
|
|
|
|
|
2696
|
my $built_mtime = $self->get_built_mtime; |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
return { |
459
|
17
|
|
|
|
|
1843
|
files => $files, |
460
|
|
|
|
|
|
|
inc_mtimes => $inc_mtimes, |
461
|
|
|
|
|
|
|
built_mtime => $built_mtime |
462
|
|
|
|
|
|
|
}; |
463
|
|
|
|
|
|
|
} |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
17
|
0
|
|
sub before_prepare_asset {} |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
sub prepare_asset { |
468
|
29
|
|
|
29
|
0
|
44
|
my $self = shift; |
469
|
29
|
|
|
|
|
131
|
my $start = [gettimeofday]; |
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
# Optional hook: |
472
|
29
|
|
|
|
|
124
|
$self->before_prepare_asset(@_); |
473
|
|
|
|
|
|
|
|
474
|
29
|
|
|
|
|
132
|
my $opt = $self->get_prepare_data; |
475
|
29
|
100
|
|
|
|
93
|
return 1 unless $self->_build_required($opt); |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
#### ----- |
478
|
|
|
|
|
|
|
#### The code above this line happens on every request and is designed |
479
|
|
|
|
|
|
|
#### to be as fast as possible |
480
|
|
|
|
|
|
|
#### |
481
|
|
|
|
|
|
|
#### The code below this line is (comparatively) expensive and only |
482
|
|
|
|
|
|
|
#### happens when a rebuild is needed which should be rare--only when |
483
|
|
|
|
|
|
|
#### content is modified, or on app startup (unless 'persist_state' is set) |
484
|
|
|
|
|
|
|
#### ----- |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
### Do a rebuild: |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
# --- Blocks for up to max_lock_wait seconds waiting to get an exclusive lock |
489
|
|
|
|
|
|
|
# The lock is held until it goes out of scope. |
490
|
|
|
|
|
|
|
# If we fail to get the lock, we just continue anyway in hopes that the second |
491
|
|
|
|
|
|
|
# build won't corrupt the first, which is arguably better than killing the |
492
|
|
|
|
|
|
|
# request. |
493
|
4
|
|
|
4
|
|
59
|
my $lock= try { $self->_get_lock($self->lock_file, $self->max_lock_wait); }; |
|
4
|
|
|
|
|
281
|
|
494
|
|
|
|
|
|
|
# --- |
495
|
|
|
|
|
|
|
|
496
|
4
|
|
|
|
|
79
|
$self->build_asset($opt); |
497
|
|
|
|
|
|
|
|
498
|
4
|
50
|
|
|
|
705
|
$self->_app->log->debug( |
499
|
|
|
|
|
|
|
"Built asset: " . $self->base_path . '/' . $self->asset_name . |
500
|
|
|
|
|
|
|
' in ' . sprintf("%.3f", tv_interval($start) ) . 's' |
501
|
|
|
|
|
|
|
) if ($self->_app->debug); |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
# Release the lock and return: |
504
|
4
|
|
|
|
|
187
|
$self->_persist_state; |
505
|
4
|
|
|
|
|
61
|
return 1; |
506
|
|
|
|
|
|
|
} |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
sub build_asset { |
509
|
4
|
|
|
4
|
0
|
9
|
my ($self, $opt) = @_; |
510
|
|
|
|
|
|
|
|
511
|
4
|
|
33
|
|
|
21
|
my $files = $opt->{files} || $self->get_include_files; |
512
|
4
|
|
33
|
|
|
15
|
my $inc_mtimes = $opt->{inc_mtimes} || $self->get_inc_mtime_concat($files); |
513
|
4
|
|
33
|
|
|
29
|
my $built_mtime = $opt->{built_mtime} || $self->get_built_mtime; |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
# Check the fingerprint to see if we can avoid a full rebuild (if mtimes changed |
516
|
|
|
|
|
|
|
# but the actual content hasn't by comparing the fingerprint/checksum): |
517
|
4
|
|
|
|
|
302
|
my $fingerprint = $self->calculate_fingerprint($files); |
518
|
4
|
|
|
|
|
13
|
my $cur_fingerprint = $self->current_fingerprint; |
519
|
4
|
50
|
33
|
|
|
234
|
if($fingerprint && $cur_fingerprint && $cur_fingerprint eq $fingerprint) { |
|
|
|
33
|
|
|
|
|
520
|
|
|
|
|
|
|
# If the mtimes changed but the fingerprint matches we don't need to regenerate. |
521
|
|
|
|
|
|
|
# This will happen if another process just built the files while we were waiting |
522
|
|
|
|
|
|
|
# for the lock and on the very first time after the application starts up |
523
|
0
|
|
|
|
|
0
|
$self->inc_mtimes($inc_mtimes); |
524
|
0
|
|
|
|
|
0
|
$self->built_mtime($built_mtime); |
525
|
0
|
|
|
|
|
0
|
$self->_persist_state; |
526
|
0
|
|
|
|
|
0
|
return 1; |
527
|
|
|
|
|
|
|
} |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
### Ok, we really need to do a full rebuild: |
530
|
|
|
|
|
|
|
|
531
|
4
|
50
|
|
|
|
184
|
my $fd = $self->built_file->openw or die $!; |
532
|
4
|
|
|
|
|
634
|
binmode $fd; |
533
|
4
|
|
|
|
|
29
|
$self->write_built_file($fd,$files); |
534
|
4
|
100
|
|
|
|
390
|
$fd->close if ($fd->opened); |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
# Update the fingerprint (global) and cached mtimes (specific to the current process) |
537
|
4
|
|
|
|
|
391
|
$self->inc_mtimes($opt->{inc_mtimes}); |
538
|
4
|
|
|
|
|
10
|
$self->built_mtime($self->get_built_mtime); |
539
|
|
|
|
|
|
|
# we're calculating the fingerprint again because the built_file, which was just |
540
|
|
|
|
|
|
|
# regenerated, is included in the checksum data. This could probably be optimized, |
541
|
|
|
|
|
|
|
# however, this only happens on rebuild which rarely happens (should never happen) |
542
|
|
|
|
|
|
|
# in production so an extra second is no big deal in this case. |
543
|
4
|
|
|
|
|
104
|
$self->calculate_save_fingerprint($opt->{files}); |
544
|
|
|
|
|
|
|
} |
545
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
sub file_checksum { |
547
|
8
|
|
|
8
|
0
|
13
|
my $self = shift; |
548
|
8
|
50
|
|
|
|
26
|
my $files = ref $_[0] eq 'ARRAY' ? $_[0] : \@_; |
549
|
|
|
|
|
|
|
|
550
|
8
|
|
|
|
|
78
|
my $Sha1 = Digest::SHA1->new; |
551
|
8
|
|
|
|
|
16
|
foreach my $file ( grep { -f $_ } @$files ) { |
|
26
|
|
|
|
|
680
|
|
552
|
22
|
50
|
|
|
|
435
|
my $fh = $file->openr or die "$! : $file\n"; |
553
|
22
|
|
|
|
|
2731
|
binmode $fh; |
554
|
22
|
|
|
|
|
831
|
$Sha1->addfile($fh); |
555
|
22
|
|
|
|
|
81
|
$fh->close; |
556
|
|
|
|
|
|
|
} |
557
|
|
|
|
|
|
|
|
558
|
8
|
|
|
|
|
431
|
return substr $Sha1->hexdigest, 0, $self->sha1_string_length; |
559
|
|
|
|
|
|
|
} |
560
|
|
|
|
|
|
|
|
561
|
8
|
|
|
8
|
0
|
515
|
sub asset_name { (shift)->current_fingerprint } |
562
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
sub base_path { |
564
|
8
|
|
|
8
|
0
|
10
|
my $self = shift; |
565
|
8
|
|
50
|
8
|
|
47
|
my $pfx = try{RapidApp->active_request_context->mount_url} || ''; |
|
8
|
|
|
|
|
259
|
|
566
|
8
|
|
|
|
|
127
|
return join('/',$pfx,$self->action_namespace($self->_app)); |
567
|
|
|
|
|
|
|
} |
568
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
# this is just used for some internal optimization to avoid calling stat |
570
|
|
|
|
|
|
|
# duplicate times. It is basically me being lazy, adding an internal extra param |
571
|
|
|
|
|
|
|
# to asset_path() without changing its public API/arg list |
572
|
|
|
|
|
|
|
has '_asset_path_skip_prepare', is => 'rw', isa => 'Bool', default => 0; |
573
|
|
|
|
|
|
|
before asset_path => sub { |
574
|
|
|
|
|
|
|
my $self = shift; |
575
|
|
|
|
|
|
|
$self->prepare_asset(@_) unless ($self->_asset_path_skip_prepare); |
576
|
|
|
|
|
|
|
}; |
577
|
|
|
|
|
|
|
sub asset_path { |
578
|
8
|
|
|
8
|
0
|
18
|
my $self = shift; |
579
|
8
|
|
|
|
|
25
|
return $self->base_path . '/' . $self->asset_name; |
580
|
|
|
|
|
|
|
} |
581
|
|
|
|
|
|
|
|
582
|
0
|
|
|
0
|
0
|
0
|
sub html_head_tags { undef } |
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
# This locks a file or dies trying, and on success, returns a "lock object" |
585
|
|
|
|
|
|
|
# which will release the lock if it goes out of scope. At the moment, this |
586
|
|
|
|
|
|
|
# "object" is just a file handle. |
587
|
|
|
|
|
|
|
# |
588
|
|
|
|
|
|
|
# This lock is specifically *not* inherited by child processes (thanks to |
589
|
|
|
|
|
|
|
# fcntl(FL_CLOEXEC), and in fact, this design principle gives it |
590
|
|
|
|
|
|
|
# cross-platform compatibility that most lock module sdon't have. |
591
|
|
|
|
|
|
|
# |
592
|
|
|
|
|
|
|
sub _get_lock { |
593
|
4
|
|
|
4
|
|
8
|
my ($self, $fname, $timeout)= @_; |
594
|
4
|
|
|
|
|
5
|
my $fh; |
595
|
4
|
50
|
33
|
|
|
37
|
sysopen($fh, $fname, O_RDWR|O_CREAT|O_EXCL, 0644) |
596
|
|
|
|
|
|
|
or sysopen($fh, $fname, O_RDWR) |
597
|
|
|
|
|
|
|
or croak "Unable to create or open $fname"; |
598
|
|
|
|
|
|
|
|
599
|
4
|
|
|
4
|
|
179
|
try { fcntl($fh, F_SETFD, FD_CLOEXEC) } |
600
|
4
|
50
|
|
|
|
113117
|
or carp "Failed to set close-on-exec for $fname (see BUGS in Catalyst::Controller::AutoAssets)"; |
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
# Try to get lock until timeout. We poll because there isn't a sensible |
603
|
|
|
|
|
|
|
# way to wait for the lock. (I don't consider SIGALRM to be very sensible) |
604
|
4
|
|
|
|
|
89
|
my $deadline= Time::HiRes::time() + $timeout; |
605
|
4
|
|
|
|
|
8
|
my $locked= 0; |
606
|
4
|
|
|
|
|
7
|
while (1) { |
607
|
4
|
50
|
|
|
|
48
|
last if flock($fh, LOCK_EX|LOCK_NB); |
608
|
0
|
0
|
|
|
|
0
|
croak "Can't get lock on $fname after $timeout seconds" if Time::HiRes::time() >= $deadline; |
609
|
0
|
|
|
|
|
0
|
Time::HiRes::sleep(0.4); |
610
|
|
|
|
|
|
|
} |
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
# Succeeded in getting the lock, so write our pid. |
613
|
4
|
|
|
|
|
25
|
my $data= "$$"; |
614
|
4
|
50
|
|
|
|
248
|
syswrite($fh, $data, length($data)) or croak "Failed to write pid to $fname"; |
615
|
4
|
50
|
|
|
|
155
|
truncate($fh, length($data)) or croak "Failed to resize $fname"; |
616
|
|
|
|
|
|
|
|
617
|
4
|
|
|
|
|
25
|
return $fh; |
618
|
|
|
|
|
|
|
} |
619
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
1; |
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
__END__ |
623
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
=pod |
625
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
=head1 NAME |
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
Catalyst::Controller::AutoAssets::Handler - Handler type Role and default namespace |
629
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
=head1 DESCRIPTION |
631
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
This is the base Role for C<Catalyst::Controller::AutoAssets> Handler classes and is |
633
|
|
|
|
|
|
|
where the majority of the work is done for the AutoAssets module. The Handler class is |
634
|
|
|
|
|
|
|
specified in the 'type' config param and is relative to this namespace. Absolute class |
635
|
|
|
|
|
|
|
names can also be specified with the '+' prefix, so the following are equivalent: |
636
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
type => 'Directory' |
638
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
type => '+Catalyst::Controller::AutoAssets::Handler::Directory' |
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
Custom Handler classes can be written and used as long as they consume this Role. For examples |
642
|
|
|
|
|
|
|
of how to write custom Handlers, see the existing Handlers below for reference. |
643
|
|
|
|
|
|
|
|
644
|
|
|
|
|
|
|
=head1 TYPE HANDLERS |
645
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
These are the current built in handler classes: |
647
|
|
|
|
|
|
|
|
648
|
|
|
|
|
|
|
=over |
649
|
|
|
|
|
|
|
|
650
|
|
|
|
|
|
|
=item L<Catalyst::Controller::AutoAssets::Handler::Directory> |
651
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
=item L<Catalyst::Controller::AutoAssets::Handler::CSS> |
653
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
=item L<Catalyst::Controller::AutoAssets::Handler::JS> |
655
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
=item L<Catalyst::Controller::AutoAssets::Handler::ImageSet> |
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
=item L<Catalyst::Controller::AutoAssets::Handler::IconSet> |
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
=back |
661
|
|
|
|
|
|
|
|
662
|
|
|
|
|
|
|
=head1 AUTHOR |
663
|
|
|
|
|
|
|
|
664
|
|
|
|
|
|
|
Henry Van Styn <vanstyn@cpan.org> |
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
667
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
This software is copyright (c) 2013 by IntelliTree Solutions llc. |
669
|
|
|
|
|
|
|
|
670
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
671
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
672
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
=cut |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
|