line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# |
2
|
|
|
|
|
|
|
# This file is part of Template-Plugin-TwoStage |
3
|
|
|
|
|
|
|
# |
4
|
|
|
|
|
|
|
# This software is copyright (c) 2014 by Alexander Kühne. |
5
|
|
|
|
|
|
|
# |
6
|
|
|
|
|
|
|
# This is free software; you can redistribute it and/or modify it under |
7
|
|
|
|
|
|
|
# the same terms as the Perl 5 programming language system itself. |
8
|
|
|
|
|
|
|
# |
9
|
|
|
|
|
|
|
package Template::Plugin::TwoStage; |
10
|
|
|
|
|
|
|
# ABSTRACT: two stage processing of template blocks with first stage caching |
11
|
|
|
|
|
|
|
$Template::Plugin::TwoStage::VERSION = '0.07'; # TRIAL |
12
|
|
|
|
|
|
|
|
13
|
4
|
|
|
4
|
|
24
|
use warnings; |
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
139
|
|
14
|
4
|
|
|
4
|
|
19
|
use strict; |
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
109
|
|
15
|
|
|
|
|
|
|
|
16
|
4
|
|
|
4
|
|
20
|
use base qw( Template::Plugin Class::Data::Inheritable ); |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
2380
|
|
17
|
4
|
|
|
4
|
|
6462
|
use Template 2.01 (); |
|
4
|
|
|
|
|
120
|
|
|
4
|
|
|
|
|
93
|
|
18
|
4
|
|
|
4
|
|
23
|
use Template::Plugin (); |
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
69
|
|
19
|
4
|
|
|
4
|
|
2528
|
use Template::Parser (); |
|
4
|
|
|
|
|
132786
|
|
|
4
|
|
|
|
|
151
|
|
20
|
4
|
|
|
4
|
|
90
|
use Template::Exception (); |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
119
|
|
21
|
4
|
|
|
4
|
|
21
|
use Template::Provider (); |
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
163
|
|
22
|
|
|
|
|
|
|
|
23
|
4
|
|
|
4
|
|
21
|
use File::Path qw( rmtree mkpath ); |
|
4
|
|
|
|
|
4
|
|
|
4
|
|
|
|
|
501
|
|
24
|
4
|
|
|
4
|
|
25
|
use File::Spec (); |
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
96
|
|
25
|
4
|
|
|
4
|
|
3142
|
use Digest::SHA1 qw( sha1_hex ); |
|
4
|
|
|
|
|
3354
|
|
|
4
|
|
|
|
|
278
|
|
26
|
4
|
|
|
4
|
|
2423
|
use Encode (); |
|
4
|
|
|
|
|
37736
|
|
|
4
|
|
|
|
|
182
|
|
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
# declare constants one by one - as opposed to a multiple constants declaration - |
29
|
|
|
|
|
|
|
# in order to be compatible with constant.pm version 1.02 shipped with perl 5.6 |
30
|
4
|
|
50
|
4
|
|
33
|
use constant DEBUG => $ENV{TWOSTAGE_DEBUG} || 0; |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
370
|
|
31
|
4
|
|
|
4
|
|
25
|
use constant UNSAFE => '^A-Za-z0-9_'; |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
227
|
|
32
|
4
|
|
|
4
|
|
21
|
use constant CACHE_DIR_NAME => 'TT_P_TwoStage'; |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
556
|
|
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
BEGIN { |
35
|
4
|
|
|
4
|
|
6
|
eval { |
36
|
4
|
|
|
|
|
1984
|
require URI::Escape::XS; |
37
|
4
|
|
|
|
|
10463
|
URI::Escape::XS->import( qw( uri_escape ) ); |
38
|
|
|
|
|
|
|
}; |
39
|
|
|
|
|
|
|
|
40
|
4
|
50
|
|
|
|
20
|
if ($@) { |
41
|
0
|
|
|
|
|
0
|
print STDERR "URI::Escape::XS not available ($@)...\n" if DEBUG; |
42
|
0
|
|
|
|
|
0
|
require URI::Escape; |
43
|
0
|
|
|
|
|
0
|
URI::Escape->import( qw( uri_escape ) ); |
44
|
|
|
|
|
|
|
} else { |
45
|
4
|
|
|
|
|
3675
|
print STDERR "URI::Escape::XS available ...\n" if DEBUG; |
46
|
|
|
|
|
|
|
} |
47
|
|
|
|
|
|
|
}; |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
my $TAG_STYLE_unquotemeta = { |
50
|
|
|
|
|
|
|
map { |
51
|
|
|
|
|
|
|
my @tags = @{$Template::Parser::TAG_STYLE->{$_}}; |
52
|
|
|
|
|
|
|
( $_, [ map { $_ =~ s/\\([^A-Za-z_0-9]{1})/$1/g; $_ } @tags ] ) |
53
|
|
|
|
|
|
|
} keys %$Template::Parser::TAG_STYLE |
54
|
|
|
|
|
|
|
}; |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
# declare options here |
57
|
|
|
|
|
|
|
my @options = qw( caching_dir dev_mode namespace ttl dir_keys runtime_tag_style tt_cache_size ); |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
__PACKAGE__->mk_classdata( caching_dir => File::Spec->tmpdir ); |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
__PACKAGE__->mk_classdata( dev_mode => 0 ); |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
__PACKAGE__->mk_classdata( ttl => 0 ); |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
__PACKAGE__->mk_classdata( dir_keys => undef ); |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
__PACKAGE__->mk_classdata( namespace => undef ); |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
__PACKAGE__->mk_classdata( runtime_tag_style => 'star' ); |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
__PACKAGE__->mk_classdata( precompile_tag_style => undef ); # is always the configured tag style of the Template object |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
__PACKAGE__->mk_classdata( tt_cache_size => undef ); |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
sub extend_keys { |
85
|
57
|
|
|
57
|
1
|
81
|
my $self = shift; |
86
|
57
|
|
|
|
|
130
|
my $context = $self->{CONTEXT}; |
87
|
57
|
|
|
|
|
150
|
my $stash = $context->stash(); |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
# hook method for adding standard keys - return the keys => value -hash by reference! |
90
|
57
|
|
|
|
|
294
|
{}; |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
# TT2 PLUGIN HOOK METHODS |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
sub load { |
98
|
5
|
|
|
5
|
1
|
104002
|
my ($class, $context) = @_; |
99
|
|
|
|
|
|
|
|
100
|
5
|
|
|
|
|
43
|
my $config = $class->compile_options( $context ); |
101
|
|
|
|
|
|
|
|
102
|
5
|
|
|
|
|
22
|
my $caching_dir = $config->{ caching_dir }; |
103
|
5
|
|
|
|
|
10
|
eval { mkpath( $caching_dir, 0, 0700 ) }; |
|
5
|
|
|
|
|
1769
|
|
104
|
5
|
50
|
|
|
|
27
|
$class->error( "Couldn't create directory: $caching_dir. Error message: $@" ) if $@; |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
# We choose to have a specific provider for the plugin, because we do not want |
107
|
|
|
|
|
|
|
# to make any assumptions about which provider class is used by the user. |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
# make include path |
110
|
|
|
|
|
|
|
|
111
|
5
|
|
|
|
|
49
|
my ($volume, $directories, $file) = File::Spec->splitpath( $caching_dir, 1 ); |
112
|
|
|
|
|
|
|
# Strip off the class name from the caching directory |
113
|
|
|
|
|
|
|
# (which itself contains the class name as the last directory). |
114
|
|
|
|
|
|
|
# The class name will be part of the template's relative path when calling process(). |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
my $inc_path = |
117
|
|
|
|
|
|
|
File::Spec->catpath( |
118
|
|
|
|
|
|
|
$volume, |
119
|
|
|
|
|
|
|
File::Spec->catdir( |
120
|
5
|
|
|
|
|
13
|
do { my @dirs = File::Spec->splitdir( $directories ); pop @dirs; @dirs } |
|
5
|
|
|
|
|
45
|
|
|
5
|
|
|
|
|
13
|
|
|
5
|
|
|
|
|
125
|
|
121
|
|
|
|
|
|
|
), |
122
|
|
|
|
|
|
|
$file |
123
|
|
|
|
|
|
|
); |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
my $p = Template::Provider->new( |
126
|
5
|
|
|
|
|
45
|
{ %{$context->{ CONFIG }}, |
127
|
|
|
|
|
|
|
INCLUDE_PATH => $inc_path, |
128
|
|
|
|
|
|
|
CACHE_SIZE => $config->{ tt_cache_size }, |
129
|
5
|
|
|
|
|
13
|
COMPILE_EXT => '.ttc', |
130
|
|
|
|
|
|
|
COMPILE_DIR => _concat_path( $inc_path, 'tt_compiled' ) |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
); |
133
|
5
|
|
|
|
|
2534
|
push @{$context->{ LOAD_TEMPLATES }}, $p; |
|
5
|
|
|
|
|
19
|
|
134
|
|
|
|
|
|
|
|
135
|
5
|
|
|
|
|
23
|
$context->{ PREFIX_MAP }->{ twostage } = [ $p ]; |
136
|
|
|
|
|
|
|
|
137
|
5
|
|
|
|
|
8
|
print STDERR "$class:\nwe use caching dir: $caching_dir\n" if DEBUG; |
138
|
|
|
|
|
|
|
|
139
|
5
|
|
|
|
|
20
|
$class; |
140
|
|
|
|
|
|
|
} |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
sub new { |
144
|
40
|
|
|
40
|
1
|
215754
|
my ($class, $context, @params) = @_; |
145
|
|
|
|
|
|
|
|
146
|
40
|
|
|
|
|
58
|
print STDERR "new $class\n" if DEBUG; |
147
|
40
|
|
|
|
|
220
|
$class->create($context, @params); |
148
|
|
|
|
|
|
|
} |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
sub error { |
151
|
0
|
|
|
0
|
1
|
0
|
my $proto = shift; |
152
|
0
|
0
|
|
|
|
0
|
die( ref( $_[0] ) ? @_ : do { $proto->SUPER::error(@_); Template::Exception->new( 'TwoStage', $proto->SUPER::error ) } ); |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
153
|
|
|
|
|
|
|
} |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
sub create { |
156
|
40
|
|
|
40
|
0
|
66
|
my ($class, $context, $params) = @_; |
157
|
|
|
|
|
|
|
|
158
|
40
|
|
|
|
|
57
|
print STDERR "create \n" if DEBUG; |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
# let parameters overwrite a selected set of the compiled options |
161
|
|
|
|
|
|
|
bless { |
162
|
|
|
|
|
|
|
CONTEXT => $context, |
163
|
|
|
|
|
|
|
CONFIG => { |
164
|
40
|
|
|
|
|
115
|
%{$class->compiled_options( $context )}, |
165
|
|
|
|
|
|
|
precompile_tag_style => ( $class->precompile_tag_style || $context->{CONFIG}->{TAG_STYLE} || 'default' ), |
166
|
|
|
|
|
|
|
( defined $params ? |
167
|
|
|
|
|
|
|
# specify invalid options for plugin construction |
168
|
40
|
100
|
50
|
|
|
69
|
do { delete @$params{ qw( caching_dir tt_cache_size ) }; %$params } : |
|
3
|
|
|
|
|
52
|
|
|
3
|
|
|
|
|
42
|
|
169
|
|
|
|
|
|
|
() |
170
|
|
|
|
|
|
|
) |
171
|
|
|
|
|
|
|
} |
172
|
|
|
|
|
|
|
}, $class; |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
sub compile_options { |
176
|
5
|
|
|
5
|
0
|
25
|
my ($class, $context) = @_; |
177
|
|
|
|
|
|
|
|
178
|
5
|
|
|
|
|
9
|
my %config; |
179
|
5
|
|
|
|
|
13
|
@config{ @options } = map { $class->$_ } @options; |
|
35
|
|
|
|
|
468
|
|
180
|
|
|
|
|
|
|
|
181
|
5
|
|
|
|
|
84
|
$config{ extend_keys } = \&Template::Plugin::TwoStage::extend_keys; |
182
|
|
|
|
|
|
|
|
183
|
5
|
100
|
100
|
|
|
106
|
if ( $class eq __PACKAGE__ && ( my $c = $context->{ CONFIG }->{ TwoStage } ) ) { |
|
|
100
|
|
|
|
|
|
184
|
|
|
|
|
|
|
|
185
|
1
|
|
|
|
|
4
|
my @ack_opts = grep { scalar grep /^$_$/, @options } keys %$c; |
|
1
|
|
|
|
|
86
|
|
186
|
|
|
|
|
|
|
# slurp in all options from TT2 main configuration hash |
187
|
1
|
|
|
|
|
5
|
@config{ @ack_opts } = @$c{ @ack_opts }; |
188
|
1
|
|
|
|
|
2
|
my $xk = $c->{ extend_keys }; |
189
|
1
|
50
|
33
|
|
|
5
|
if ( defined $xk && ref $xk eq 'CODE' ) { |
190
|
|
|
|
|
|
|
# xk() as configuration option in TT2 main configuration hash |
191
|
0
|
|
|
|
|
0
|
$config{ extend_keys } = $xk; |
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
} elsif ( $class ne __PACKAGE__ ) { |
195
|
|
|
|
|
|
|
|
196
|
4
|
|
|
4
|
|
31
|
no strict 'refs'; |
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
9801
|
|
197
|
2
|
|
|
|
|
16
|
my $meth_name = "${class}::extend_keys"; |
198
|
2
|
50
|
|
|
|
5
|
if ( defined &{$meth_name} ) { |
|
2
|
|
|
|
|
16
|
|
199
|
|
|
|
|
|
|
# xk() as redefined callback method in derived class |
200
|
0
|
|
|
|
|
0
|
$config{ extend_keys } = \&{$meth_name}; |
|
0
|
|
|
|
|
0
|
|
201
|
|
|
|
|
|
|
} |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
} |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
$config{ caching_dir } = |
206
|
5
|
|
|
|
|
13
|
&_concat_path( $config{ caching_dir }, [ CACHE_DIR_NAME, do { uri_escape( $class, UNSAFE ) } ] ); |
|
5
|
|
|
|
|
36
|
|
207
|
|
|
|
|
|
|
|
208
|
5
|
|
|
|
|
13
|
print STDERR join( ', ', ( %config ) )."\n" if DEBUG; |
209
|
|
|
|
|
|
|
|
210
|
5
|
|
|
|
|
41
|
$context->{ CONFIG }->{ _TwoStage }->{ compiled_options }->{ $class } = \%config; |
211
|
|
|
|
|
|
|
} |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
sub compiled_options { |
214
|
145
|
|
|
145
|
0
|
406
|
my $proto = shift; |
215
|
145
|
|
66
|
|
|
488
|
my $class = ref $proto || $proto; |
216
|
145
|
100
|
|
|
|
389
|
my $context = ref $proto ? $proto->{ CONTEXT } : shift; |
217
|
145
|
|
|
|
|
188
|
my $name = shift; |
218
|
|
|
|
|
|
|
|
219
|
145
|
|
|
|
|
297
|
my $c = $context->{ CONFIG }->{ _TwoStage }->{ compiled_options }->{ $class }; |
220
|
145
|
100
|
|
|
|
887
|
defined $name ? $c->{ $name } : $c; |
221
|
|
|
|
|
|
|
} |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
sub dump_options { |
224
|
4
|
|
|
4
|
0
|
1311
|
my $self = shift; |
225
|
|
|
|
|
|
|
|
226
|
4
|
|
|
|
|
8
|
my $options_dump = ''; |
227
|
|
|
|
|
|
|
map { |
228
|
36
|
100
|
|
|
|
65
|
if ( $_ ne 'extend_keys' ) { |
229
|
32
|
100
|
|
|
|
139
|
$options_dump.= "$_: ".( defined $self->{CONFIG}->{$_} ? $self->{CONFIG}->{$_} : '' )."\n" |
230
|
|
|
|
|
|
|
} |
231
|
4
|
|
|
|
|
5
|
} sort keys %{$self->{CONFIG}}; |
|
4
|
|
|
|
|
52
|
|
232
|
|
|
|
|
|
|
|
233
|
4
|
|
|
|
|
20
|
$options_dump; |
234
|
|
|
|
|
|
|
} |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
sub process { |
238
|
57
|
|
|
57
|
1
|
4021274
|
my( $self, $params, $localize ) = @_; |
239
|
57
|
|
100
|
|
|
290
|
$localize ||= 0; |
240
|
57
|
|
|
|
|
120
|
my $context = $self->{CONTEXT}; |
241
|
57
|
|
|
|
|
175
|
my $stash = $context->stash(); |
242
|
|
|
|
|
|
|
|
243
|
57
|
50
|
|
|
|
254
|
exists( $params->{template} ) || $self->error( "Pass template => \$name !" ); |
244
|
57
|
|
|
|
|
153
|
$self->{prec_template} = {}; # store for properties of current template processed |
245
|
57
|
|
|
|
|
157
|
$self->{params} = $params; # parameters handed to process() |
246
|
57
|
|
100
|
|
|
411
|
$self->{params}->{keys} = $self->_complement_keys( $params->{keys} || {} ); |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
# make the config options local to this call |
249
|
|
|
|
|
|
|
local $self->{CONFIG} = |
250
|
|
|
|
|
|
|
{ |
251
|
57
|
|
|
|
|
202
|
%{$self->{CONFIG}}, |
252
|
57
|
|
|
|
|
1715
|
do { |
253
|
57
|
|
|
|
|
213
|
my %p = %$params; |
254
|
|
|
|
|
|
|
# specify invalid options as parameters to process()/include() |
255
|
57
|
|
|
|
|
139
|
delete @p{ qw( caching_dir tt_cache_size ) }; |
256
|
57
|
|
|
|
|
404
|
%p |
257
|
|
|
|
|
|
|
} |
258
|
|
|
|
|
|
|
}; |
259
|
|
|
|
|
|
|
|
260
|
57
|
50
|
|
|
|
525
|
if ( $stash->get( 'TwoStage_precompile_mode') ) { |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
# don't do runtime phase processing if the template is called in precompilation mode |
263
|
0
|
|
|
|
|
0
|
print STDERR "$params->{template}: precompile_mode ack..." if DEBUG; |
264
|
0
|
|
|
|
|
0
|
return $context->process( $params->{template}, {}, 1 ); |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
print STDERR |
269
|
|
|
|
|
|
|
"try using cached version of component ($params->{template}) ".$self->_signature."\n" |
270
|
|
|
|
|
|
|
."dev_mode: ".$self->{CONFIG}->{dev_mode}."\n" |
271
|
|
|
|
|
|
|
."INCLUDE_PATH: ".join( ' : ', @{$context->{CONFIG}->{INCLUDE_PATH}} )."\n" |
272
|
57
|
|
|
|
|
367
|
."keys: \n".( join "\n", map { "$_ -> $self->{params}->{keys}->{$_}" } keys %{$self->{params}->{keys}} )."\n\n" |
273
|
|
|
|
|
|
|
if DEBUG; |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
# stat() the cached precompiled version to play safely with negative |
276
|
|
|
|
|
|
|
# caching of TT2 introduced in recent versions! |
277
|
|
|
|
|
|
|
# Else requesting for a not yet existing precompiled version |
278
|
|
|
|
|
|
|
# would lead to an immediate decline of a future request for the same precompiled template without |
279
|
|
|
|
|
|
|
# further stat() checks by the provider - even if it has been created on disk in the meantime. |
280
|
|
|
|
|
|
|
|
281
|
57
|
|
|
|
|
170
|
my @stat = stat( $self->_file_path ); |
282
|
|
|
|
|
|
|
|
283
|
57
|
|
|
|
|
114
|
print STDERR "template.modtime: ".$stash->get( 'template.modtime' )." - ttl: $self->{CONFIG}->{ttl} ".time()." <= ".( $stat[9] + $self->{CONFIG}->{ttl})."\n" |
284
|
|
|
|
|
|
|
if DEBUG && scalar( @stat ); |
285
|
|
|
|
|
|
|
|
286
|
57
|
100
|
66
|
|
|
950
|
if ( scalar( @stat ) |
|
|
|
100
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
66
|
|
|
|
|
287
|
|
|
|
|
|
|
&& |
288
|
|
|
|
|
|
|
$stash->get( 'template.modtime' ) <= $stat[9] # cached version outdated? |
289
|
|
|
|
|
|
|
&& |
290
|
|
|
|
|
|
|
!$self->{CONFIG}->{dev_mode} # forces in cases of nested TwoStage processed templates a refresh also for modified inner templates |
291
|
|
|
|
|
|
|
&& |
292
|
|
|
|
|
|
|
( !$self->{CONFIG}->{ttl} || time() <= ($stat[9] + $self->{CONFIG}->{ttl}) ) |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
) { |
295
|
|
|
|
|
|
|
|
296
|
24
|
|
|
|
|
491
|
print STDERR "file ".$self->_file_path." successfully stat()ed\n" if DEBUG; |
297
|
|
|
|
|
|
|
|
298
|
24
|
|
|
|
|
30
|
my $output; |
299
|
24
|
|
|
|
|
35
|
eval { |
300
|
|
|
|
|
|
|
$output = |
301
|
|
|
|
|
|
|
$context->process( |
302
|
|
|
|
|
|
|
'twostage:' # prefix for provider selection |
303
|
|
|
|
|
|
|
.uri_escape( ref($self), UNSAFE ).'/' |
304
|
24
|
100
|
|
|
|
121
|
.( do { my $dirs = join( '/', @{$self->_dynamic_dir_segments} ); $dirs ? $dirs.'/' : '' } ) |
|
24
|
|
|
|
|
799
|
|
|
24
|
|
|
|
|
63
|
|
|
24
|
|
|
|
|
108
|
|
305
|
|
|
|
|
|
|
.$self->_signature, |
306
|
|
|
|
|
|
|
{}, |
307
|
|
|
|
|
|
|
$localize |
308
|
|
|
|
|
|
|
); |
309
|
|
|
|
|
|
|
}; |
310
|
|
|
|
|
|
|
|
311
|
24
|
50
|
|
|
|
28113
|
$self->error( "Retrieval though stat()'ed successfully (".$self->_file_path."): FAILED ($@)\n" ) if $@; |
312
|
24
|
|
|
|
|
34
|
print STDERR "Using cached output:\n\n $output\n\n" if DEBUG; |
313
|
|
|
|
|
|
|
|
314
|
24
|
|
|
|
|
213
|
return $output; |
315
|
|
|
|
|
|
|
} |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
# process precompiled component |
318
|
33
|
|
|
|
|
249
|
$context->process( $self->_precompile, {}, $localize ); |
319
|
|
|
|
|
|
|
} |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
sub include { |
322
|
2
|
|
|
2
|
1
|
66
|
(shift)->process( @_, 1 ); |
323
|
|
|
|
|
|
|
} |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
sub purge { |
327
|
11
|
|
|
11
|
1
|
520
|
my $self = shift; |
328
|
11
|
|
|
|
|
23
|
my $class = ref($self); |
329
|
|
|
|
|
|
|
|
330
|
11
|
|
|
|
|
19
|
my $CACHE_DIR_NAME = CACHE_DIR_NAME; |
331
|
11
|
|
|
|
|
43
|
my $caching_dir = $self->compiled_options( 'caching_dir' ); |
332
|
|
|
|
|
|
|
|
333
|
11
|
100
|
33
|
|
|
19
|
if ( |
|
|
|
66
|
|
|
|
|
|
|
|
66
|
|
|
|
|
334
|
11
|
|
|
|
|
47
|
do { my $class_ue = uri_escape($class, UNSAFE ); $caching_dir =~ /$class_ue/; } && |
|
11
|
|
|
|
|
914
|
|
335
|
|
|
|
|
|
|
$caching_dir =~ /${CACHE_DIR_NAME}/ && |
336
|
|
|
|
|
|
|
-e $caching_dir && |
337
|
|
|
|
|
|
|
-d $caching_dir # kind of paranoia |
338
|
|
|
|
|
|
|
) { |
339
|
10
|
|
|
|
|
20
|
eval { rmtree( $caching_dir, 0, 1 ) }; |
|
10
|
|
|
|
|
8778
|
|
340
|
10
|
50
|
|
|
|
49
|
if ( $@ ) { |
341
|
0
|
|
|
|
|
0
|
$class->error( "Couldn't remove directory tree: $caching_dir. Error message: $@" ); |
342
|
|
|
|
|
|
|
} |
343
|
|
|
|
|
|
|
} |
344
|
|
|
|
|
|
|
|
345
|
11
|
|
|
|
|
87
|
''; |
346
|
|
|
|
|
|
|
} |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
sub _complement_keys { |
350
|
57
|
|
|
57
|
|
85
|
my $self = shift; |
351
|
57
|
|
|
|
|
64
|
my $keys = shift; |
352
|
|
|
|
|
|
|
|
353
|
57
|
|
|
|
|
166
|
my $callers = $self->{CONTEXT}->stash->get( 'component.callers' ); |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
+{ |
356
|
57
|
|
|
|
|
174
|
%{ $self->{CONFIG}->{extend_keys}->( $self ) }, |
357
|
57
|
|
|
|
|
378
|
%{$keys}, |
358
|
|
|
|
|
|
|
'_file_scope' => |
359
|
4
|
|
|
|
|
20
|
( ref($callers) ? join( '\\', @{$callers} ) : '' ) |
360
|
57
|
100
|
|
|
|
2137
|
.$self->{CONTEXT}->stash->get( 'component.name' ) |
361
|
|
|
|
|
|
|
# For making BLOCK name in template file scoped we need a unique identifier: |
362
|
|
|
|
|
|
|
# component.callers + component.name |
363
|
|
|
|
|
|
|
# This approach introduces the drawback that a BLOCK defined in a template being |
364
|
|
|
|
|
|
|
# included in different other templates as an "intra" is cached for each call stack |
365
|
|
|
|
|
|
|
# path seperately! But it is a feasable workaround as we don't know how to figure |
366
|
|
|
|
|
|
|
# out the name of the template the BLOCK was defined in. |
367
|
|
|
|
|
|
|
}; |
368
|
|
|
|
|
|
|
} |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
sub _precompile { |
371
|
33
|
|
|
33
|
|
44
|
my $self = shift; |
372
|
33
|
|
|
|
|
62
|
my $context = $self->{CONTEXT}; |
373
|
33
|
|
|
|
|
94
|
my $stash = $context->stash(); |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
my $TAGS_tag = |
376
|
|
|
|
|
|
|
$TAG_STYLE_unquotemeta->{ $self->{CONFIG}->{precompile_tag_style} }->[0] |
377
|
|
|
|
|
|
|
.' TAGS '.$self->{CONFIG}->{runtime_tag_style}.' ' |
378
|
33
|
|
|
|
|
287
|
.$TAG_STYLE_unquotemeta->{ $self->{CONFIG}->{precompile_tag_style} }->[1]."\n"; |
379
|
|
|
|
|
|
|
|
380
|
33
|
|
|
|
|
38
|
print STDERR "We are using tag style: $self->{CONFIG}->{precompile_tag_style}\n" if DEBUG; |
381
|
|
|
|
|
|
|
|
382
|
33
|
|
|
|
|
33
|
my $template; |
383
|
33
|
|
|
|
|
38
|
eval { |
384
|
33
|
|
|
|
|
219
|
$template = $context->process( $self->{params}->{template}, { TwoStage_precompile_mode => 1 }, 1 ); |
385
|
|
|
|
|
|
|
}; |
386
|
|
|
|
|
|
|
|
387
|
33
|
50
|
|
|
|
5731
|
if ( $@ ) { |
388
|
0
|
|
|
|
|
0
|
print STDERR "\tFAILED ($@)\n" if DEBUG; |
389
|
0
|
0
|
|
|
|
0
|
$self->error( ref($@) ? $@ : "Precompilation of module $self->{params}->{template}: $@ \n" ); |
390
|
|
|
|
|
|
|
} |
391
|
|
|
|
|
|
|
|
392
|
33
|
|
|
|
|
38
|
print STDERR "storing ".$self->_signature."\n\n" if DEBUG; |
393
|
|
|
|
|
|
|
|
394
|
33
|
|
|
|
|
37
|
eval { mkpath( $self->_file_dir, 0, 0700 ) }; |
|
33
|
|
|
|
|
112
|
|
395
|
33
|
50
|
|
|
|
111
|
if ($@) { |
396
|
0
|
|
|
|
|
0
|
$self->error( "Couldn't create ".$self->_file_dir.": $@" ); |
397
|
|
|
|
|
|
|
} |
398
|
|
|
|
|
|
|
|
399
|
33
|
50
|
|
|
|
213
|
open( my $fh, "> ", $self->_file_path ) || $self->error( "Could not get a filehandle! Error: $!" ); |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
my $out = |
402
|
|
|
|
|
|
|
$TAGS_tag |
403
|
|
|
|
|
|
|
.( $self->{CONFIG}->{dev_mode} |
404
|
|
|
|
|
|
|
&& |
405
|
|
|
|
|
|
|
$TAG_STYLE_unquotemeta->{ $self->{CONFIG}->{runtime_tag_style} }->[0] |
406
|
|
|
|
|
|
|
."# This precompiled template ( $self->{params}->{template} ) is stored together with the following keys:\n\t" |
407
|
|
|
|
|
|
|
.join( "\n\t", map { "$_ => ".( defined $self->{params}->{keys}->{$_} ? $self->{params}->{keys}->{$_} : 'undef' ) } keys %{$self->{params}->{keys}} )."\n " |
408
|
33
|
|
100
|
|
|
348
|
.$TAG_STYLE_unquotemeta->{ $self->{CONFIG}->{runtime_tag_style} }->[1]."\n" |
409
|
|
|
|
|
|
|
|| |
410
|
|
|
|
|
|
|
'' |
411
|
|
|
|
|
|
|
) |
412
|
|
|
|
|
|
|
.$template; |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
|
415
|
33
|
100
|
|
|
|
190
|
if ( Encode::is_utf8( $template ) ) { |
416
|
|
|
|
|
|
|
|
417
|
1
|
|
|
|
|
8
|
print STDERR "_precompile: encode\n" if DEBUG; |
418
|
1
|
|
|
|
|
8
|
$out = Encode::decode_utf8( "\x{ef}\x{bb}\x{bf}" ).$out; # utf8 bom is stripped off again on load by Template::Provider |
419
|
1
|
|
|
|
|
74
|
binmode( $fh ); # turn off crlf io layer!? |
420
|
1
|
|
|
1
|
|
50
|
binmode( $fh, ':encoding(utf8)' ); |
|
1
|
|
|
|
|
9
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
12
|
|
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
} else { |
423
|
|
|
|
|
|
|
|
424
|
32
|
|
|
|
|
32
|
print STDERR "_precompile: octets\n" if DEBUG; |
425
|
32
|
|
|
|
|
107
|
binmode( $fh ); |
426
|
|
|
|
|
|
|
} |
427
|
|
|
|
|
|
|
|
428
|
33
|
|
|
|
|
1660
|
print $fh $out; |
429
|
33
|
|
|
|
|
1323
|
close $fh; |
430
|
|
|
|
|
|
|
|
431
|
33
|
|
|
|
|
373
|
return \($TAGS_tag.$template); |
432
|
|
|
|
|
|
|
} |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
sub _signature { |
435
|
81
|
|
|
81
|
|
180
|
my $self = shift; |
436
|
|
|
|
|
|
|
# produce signature |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
$self->{prec_template}->{signature} |
439
|
|
|
|
|
|
|
||= |
440
|
|
|
|
|
|
|
sha1_hex( |
441
|
|
|
|
|
|
|
join( |
442
|
|
|
|
|
|
|
':', |
443
|
|
|
|
|
|
|
( |
444
|
|
|
|
|
|
|
$self->{params}->{template}, |
445
|
81
|
|
100
|
|
|
371
|
map { "$_=".( $self->{params}->{keys}->{$_} || '' ) } sort keys %{$self->{params}->{keys}} |
|
69
|
|
66
|
|
|
974
|
|
|
57
|
|
|
|
|
258
|
|
446
|
|
|
|
|
|
|
) |
447
|
|
|
|
|
|
|
) |
448
|
|
|
|
|
|
|
).'.tt'; |
449
|
|
|
|
|
|
|
} |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
sub _dynamic_dir_segments { |
452
|
108
|
|
|
108
|
|
137
|
my $self = shift; |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
$self->{prec_template}->{dynamic_dir_segments} |
455
|
|
|
|
|
|
|
||= |
456
|
|
|
|
|
|
|
[ |
457
|
|
|
|
|
|
|
# include a possible namespace |
458
|
|
|
|
|
|
|
( $self->{CONFIG}->{namespace} ? $self->{CONFIG}->{namespace} : () ), |
459
|
|
|
|
|
|
|
# include dir_keys - we offer this feature only in testing mode! |
460
|
|
|
|
|
|
|
( $self->{CONFIG}->{dev_mode} && $self->{CONFIG}->{dir_keys} |
461
|
|
|
|
|
|
|
? |
462
|
|
|
|
|
|
|
( |
463
|
|
|
|
|
|
|
$self->{params}->{template}, |
464
|
|
|
|
|
|
|
map { uri_escape( $_, UNSAFE ), |
465
|
4
|
|
|
|
|
86
|
uri_escape( 'value-'.$self->{params}->{keys}->{$_}, UNSAFE ) |
466
|
|
|
|
|
|
|
} |
467
|
|
|
|
|
|
|
( ref( $self->{CONFIG}->{dir_keys} ) |
468
|
|
|
|
|
|
|
? |
469
|
6
|
|
|
|
|
15
|
grep( { exists $self->{params}->{keys}->{$_} } @{$self->{CONFIG}->{dir_keys}} ) |
|
2
|
|
|
|
|
9
|
|
470
|
|
|
|
|
|
|
: |
471
|
108
|
100
|
66
|
|
|
1575
|
keys %{$self->{params}->{keys}} |
|
0
|
50
|
100
|
|
|
0
|
|
|
|
100
|
|
|
|
|
|
472
|
|
|
|
|
|
|
) |
473
|
|
|
|
|
|
|
) |
474
|
|
|
|
|
|
|
: |
475
|
|
|
|
|
|
|
() |
476
|
|
|
|
|
|
|
) |
477
|
|
|
|
|
|
|
]; |
478
|
|
|
|
|
|
|
} |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
sub _rel_file_path { |
481
|
57
|
|
|
57
|
|
69
|
my $self = shift; |
482
|
|
|
|
|
|
|
|
483
|
57
|
|
33
|
|
|
302
|
$self->{prec_template}->{rel_file_path} ||= &_concat_path( $self->_rel_file_dir, $self->_signature ); |
484
|
|
|
|
|
|
|
} |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
sub _file_path { |
487
|
90
|
|
|
90
|
|
100
|
my $self = shift; |
488
|
|
|
|
|
|
|
|
489
|
90
|
|
66
|
|
|
117930
|
$self->{prec_template}->{file_path} ||= &_concat_path( $self->compiled_options( 'caching_dir' ), $self->_rel_file_path ); |
490
|
|
|
|
|
|
|
} |
491
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
sub _rel_file_dir { |
493
|
90
|
|
|
90
|
|
88
|
my $self = shift; |
494
|
|
|
|
|
|
|
|
495
|
90
|
|
100
|
|
|
255
|
$self->{prec_template}->{rel_file_dir} ||= File::Spec->catdir( @{$self->_dynamic_dir_segments} ); |
|
84
|
|
|
|
|
178
|
|
496
|
|
|
|
|
|
|
} |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
sub _file_dir { |
499
|
33
|
|
|
33
|
|
42
|
my $self = shift; |
500
|
|
|
|
|
|
|
|
501
|
33
|
|
33
|
|
|
169
|
$self->{prec_template}->{file_dir} ||= &_concat_path( $self->compiled_options( 'caching_dir' ), $self->_rel_file_dir ); |
502
|
|
|
|
|
|
|
} |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
# helpers |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
sub _concat_path { |
507
|
165
|
|
|
165
|
|
656
|
my ( $base_path, $append_dirs ) = @_; |
508
|
|
|
|
|
|
|
# $base_dir: base path (no filename) as string |
509
|
|
|
|
|
|
|
# $append_dirs: directories to append as string or an array reference |
510
|
|
|
|
|
|
|
|
511
|
165
|
|
|
|
|
1188
|
my ($base_volume, $base_directories, $base_file) = File::Spec->splitpath( $base_path, 1 ); |
512
|
|
|
|
|
|
|
File::Spec->catpath( |
513
|
|
|
|
|
|
|
$base_volume, |
514
|
|
|
|
|
|
|
File::Spec->catdir( |
515
|
|
|
|
|
|
|
File::Spec->splitdir( $base_directories ), |
516
|
165
|
100
|
|
|
|
8729
|
( ref($append_dirs) ? @{$append_dirs} : File::Spec->splitdir( $append_dirs ) ) |
|
13
|
|
|
|
|
725
|
|
517
|
|
|
|
|
|
|
) |
518
|
|
|
|
|
|
|
, |
519
|
|
|
|
|
|
|
$base_file |
520
|
|
|
|
|
|
|
); |
521
|
|
|
|
|
|
|
} |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
1; # End of Template::Plugin::TwoStage |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
__END__ |