File Coverage

blib/lib/Template/Plex/Internal.pm
Criterion Covered Total %
statement 322 381 84.5
branch 30 126 23.8
condition 14 77 18.1
subroutine 102 107 95.3
pod 0 5 0.0
total 468 696 67.2


line stmt bran cond sub pod time code
1             package Template::Plex::Internal;
2 3     3   21 use strict;
  3         7  
  3         78  
3 3     3   222 use warnings;
  3         12  
  3         331  
4              
5 3     3   19 use Template::Plex;
  3         6  
  3         124  
6              
7              
8 3     3   22 use feature qw;
  3         6  
  3         190  
9 3     3   16 no warnings "experimental";
  3         7  
  3         475  
10              
11             #use File::Spec::Functions qw;
12              
13 3     3   1059 use Export::These qw;
  3         1260  
  3         45  
14              
15             my $Include=qr|\@\{\s*\[\s*include\s*\(\s*(.*?)\s*\)\s*\] \s* \}|x;
16             my $Init=qr|\@\{\s*\[\s*init\s*\{(?:.*?)\}\s*\] \s* \}|smx;
17              
18              
19             sub new; #forward declare new;
20              
21             sub lexical{
22 22     22 0 59 my $href=shift;
23 22 50 33     76 die "NEED A HASH REF " unless ref $href eq "HASH" or !defined $href;
24 22   50     66 $href//={};
25 22         272 \my %fields=$href;
26              
27 22         36 my $string="";
28 22         352 for my $k (keys %fields){
29 12         40 $string.= "\\my \$$k=\\\$fields{$k};\n";
30             }
31 22         59 $string;
32             }
33              
34             sub bootstrap{
35 22     22   106 my $plex=shift;
36 22         40 \my $_data_=\shift;
37 22         34 my $href=shift;
38 22         121 my %opts=@_;
39              
40 22   50     59 $href//={};
41 22         36 \my %fields=$href;
42              
43 22         443 my $out="package $opts{package} {
44             use Template::Plex::Internal qw;
45             no warnings qw;
46             ";
47              
48 22         58 $out.='my $self=$plex;
49             ';
50              
51 22         38 $out.= ' \my %fields=$href;
52             ';
53 22 50       99 $out.=' my %options=%opts;
54             ' if keys %opts;
55 22         75 for($opts{use}->@*){
56 1         3 $out.="use $_;\n";
57             }
58 22         197 for($opts{inject}->@*){
59 1         8 $out.="$_\n";
60             }
61              
62 22 50       69 $out.=lexical($href) unless $opts{no_alias}; #add aliased variables from hash
63 22         194 $out.='
64             my %cache; #Stores code refs using caller as keys
65              
66             sub clear {
67             %cache=();
68             }
69              
70             sub skip{
71             goto _PLEX_SKIP;
72             }
73              
74             $plex->[Template::Plex::skip_]=\&skip;
75              
76              
77             sub init :prototype(&){
78             $self->_init(@_);
79             }
80              
81             sub slot {
82             $self->slot(@_);
83             }
84             sub fill_slot {
85             $self->fill_slot(@_);
86             }
87             sub append_slot {
88             $self->append_slot(@_);
89             }
90              
91             sub prepend_slot {
92             $self->prepend_slot(@_);
93             }
94              
95             sub fill_var{
96             my $name=shift;
97             no strict "refs";
98             $$name=shift;
99             "";
100             }
101              
102             sub append_var{
103             my $name=shift;
104             no strict "refs";
105             $$name .= shift;
106             "";
107              
108             }
109             sub prepend_var{
110             my $name=shift;
111             no strict "refs";
112             $$name = shift . $$name;
113             "";
114              
115             }
116              
117              
118             sub inherit {
119             $self->inherit(@_);
120             }
121              
122             sub load {
123             $self->load(@_);
124             }
125              
126             sub cache {
127             my @args=@_;
128             if(@args ==1){
129             # Recalling implicit cache key with path only
130             unshift @args, undef;
131             }
132             elsif(defined($args[1]) and ref($args[1]) eq "HASH"){
133             # variables hash ref given, with implicit cache id
134             unshift @args, undef;
135             }
136             else{
137             # Expect explicit cache Id
138             }
139              
140             my ($id, $path, $var, @opts)=@args;
141             #we want to cache based on the caller
142             $id=$path.join "", caller;
143             #unshift @_, $id;
144             $self->cache($id,$path, $var,@opts);
145             }
146              
147             sub immediate {
148             my @args=@_;
149             if(@args ==1){
150             # Recalling implicit cache key with path only
151             unshift @args, undef;
152             }
153             elsif(defined($args[1]) and ref($args[1]) eq "HASH"){
154             # variables hash ref given, with implicit cache id
155             unshift @args, undef;
156             }
157             else{
158             # Expect explicit cache Id
159             }
160             my ($id, $path, $var, @opts)=@args;
161             #we want to cache based on the caller
162             $id=$path.join "", caller;
163             my $template=$self->cache($id, $path,$var, @opts);
164             if($template){
165             return $template->render;
166             }
167             "";
168             }
169              
170              
171             sub {
172             no warnings \'uninitialized\';
173             no strict;
174             #my $plex=shift;
175             my $self=shift;
176              
177             \\my %fields=shift//\\%fields;
178              
179              
180             ##__START
181             return $self->prefix.
182             qq
183             {'.
184             $_data_
185             . '}
186             .$self->postfix;
187             _PLEX_SKIP:
188             "";
189             }
190             ##__END
191             };';
192              
193             };
194              
195             # First argument the template string/text. This is any valid perl code
196             # Second argument is a hash ref to default or base level fields
197             # returns a code reference which when called renders the template with the values
198             sub _prepare_template{
199 3     3   2379 no warnings qw;
  3         6  
  3         2719  
200 22     22   315 my ($plex, undef, $href, %opts)=@_;
201 22   50     53 $href//={};
202 22         340 \my %fields=$href;
203 22         43 \my %meta=\%opts;
204              
205             #$plex now variable is now of base class
206 22   100     147 $plex=($opts{base}//"Template::Plex")->new($plex);
207              
208 22         134 $plex->[Template::Plex::meta_]=\%opts;
209 22         39 $plex->[Template::Plex::args_]=$href;
210              
211 22         45 my $prog=&Template::Plex::Internal::bootstrap;
212 3 0 0 3   24 my $ref=eval $prog;
  3 0 0 3   7  
  3 0 33 3   89  
  3 0 0 3   728  
  3 50 0 3   7  
  3 50 0 3   919  
  3 0 33 3   21  
  3 0 0 3   6  
  3 0 0 3   237  
  3 0 0 3   19  
  3 0 0 3   6  
  3 0 33 3   193  
  3 0 0 3   17  
  3 0 33 3   6  
  3 50 0 3   1238  
  3 50 0 3   24  
  3 50 0 3   8  
  3 0 0 3   90  
  3 0 0 3   15  
  3 0   3   5  
  3 0   3   595  
  3 50   3   23  
  3 50   3   7  
  3 50   3   86  
  3 0   3   710  
  3 0   3   9  
  3 0   3   917  
  3 50   3   29  
  3 50   1   5  
  3 50   1   202  
  3 0   1   19  
  3 0   1   13  
  3 0   1   189  
  3 0   1   18  
  3 0   1   14  
  3 0   1   1193  
  3 0   1   23  
  3 0   1   6  
  3 0   2   98  
  3 0   1   16  
  3 0   1   10  
  3 0   1   631  
  3 0   1   21  
  3 0   1   6  
  3 0   1   60  
  3     4   691  
  3     1   7  
  3     1   894  
  3     1   22  
  3     1   5  
  3     1   206  
  3     1   20  
  3     1   25  
  3     1   224  
  3     2   18  
  3     1   12  
  3     1   1177  
  3     2   21  
  3     1   8  
  3     2   99  
  3     5   25  
  3     4   7  
  3     4   503  
  3     6   23  
  3     4   6  
  3     3   74  
  3     3   673  
  3     3   7  
  3     5   943  
  3     3   22  
  3     1   9  
  3     3   248  
  3     4   20  
  3     3   12  
  3     3   258  
  3     3   18  
  3     5   6  
  3     3   1257  
  3     4   29  
  3     3   10  
  3     8   99  
  3     1   16  
  3     2   14  
  3     2   428  
  22     0   2365  
  1     0   8  
  1     0   2  
  1     0   20  
  1         229  
  1         2  
  1         287  
  1         17  
  1         3  
  1         70  
  1         7  
  1         2  
  1         62  
  1         7  
  1         3  
  1         383  
  1         8  
  1         2  
  1         40  
  1         6  
  1         3  
  1         188  
  1         9  
  1         2  
  1         19  
  1         229  
  1         4  
  1         285  
  1         7  
  1         2  
  1         66  
  2         10  
  2         11  
  1         73  
  1         7  
  2         5  
  2         421  
  2         13  
  1         2  
  1         28  
  1         15  
  1         3  
  1         150  
  1         7  
  1         3  
  1         19  
  1         233  
  1         2  
  1         291  
  1         8  
  4         12  
  1         120  
  1         7  
  1         2  
  1         83  
  1         14  
  1         4  
  1         400  
  1         7  
  1         3  
  1         29  
  1         5  
  1         2  
  1         154  
  1         9  
  1         2  
  1         19  
  1         232  
  1         2  
  1         276  
  1         15  
  1         4  
  1         71  
  1         7  
  1         2  
  1         63  
  1         6  
  1         2  
  1         380  
  1         8  
  1         3  
  1         29  
  1         6  
  1         2  
  1         158  
  2         11  
  2         14  
  1         18  
  1         216  
  2         8  
  2         360  
  2         13  
  2         7  
  2         93  
  1         6  
  1         2  
  1         82  
  1         8  
  1         2  
  1         421  
  1         8  
  1         12  
  1         34  
  1         6  
  1         2  
  1         217  
  2         10  
  1         3  
  2         101  
  5         251  
  3         10  
  4         302  
  5         25  
  4         9  
  3         77  
  3         18  
  3         8  
  5         80  
  3         12  
  1         2  
  3         421  
  4         16  
  3         12  
  3         53  
  3         13  
  3         8  
  1         160  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  2         17  
  2         17  
  3         28  
  2         27  
  7         32  
  1         9  
  2         13  
  2         17  
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
213 22 100 66     131 if($@ and !$ref){
214 3         9 my $e=$@; #Save the error as require will nuke it
215 3         1163 require Error::Show;
216 3         7153 my $context=Error::Show::context(error=>$e, program=>$prog,
217             start_mark=>'##__START',
218             end_mark=>'##__END',
219             start_offset=>2,
220             end_offset=>5,
221             limit=>1
222             );
223             # Replace the sudo filename with the file name if we have one
224 3         1334 my $filename=$meta{file};
225 3         46 $context=~s/(\(eval \d+\))/$filename/g;
226             # Rethrow the exception, translated context line numbers
227 3         27 die $context;
228             }
229 20         42 $plex->[Template::Plex::sub_]=$ref;
230 20         264 $plex;
231             }
232              
233             #a little helper to allow 'including' templates into each other
234             sub _munge {
235 1     1   10 my ($input, %options)=@_;
236              
237             #test for literals
238 1         9 my $path;
239 1 0       9 if($input =~ /^"(.*)"$/){
    0          
240             #literal
241 1         10 $path=$1;
242             }
243             elsif($input =~ /^'(.*)'$/){
244             #literal
245 1         10 $path=$1;
246             }
247             else {
248             #not supported?
249             #
250             }
251 1         9 Template::Plex::Internal->new(\&_prepare_template,$path,"",%options);
252             }
253              
254             sub _subst_inject {
255 21     22   47 \my $buffer=\(shift);
256 22         146 while($buffer=~s|$Include|_munge($1, @_)|e){
  0         0  
257             #TODO: Possible point for diagnostics?
258             };
259             }
260              
261             sub _block_fix {
262             #remove any new line immediately after a ]} pair
263 21     22   37 \my $buffer=\(shift);
264            
265 21         166 $buffer=~s/^(\s*\@\{\[.*?\]\})\n/$1/gms;
266             }
267              
268             sub _comment_strip {
269 0     1   0 \my $buffer=\(shift);
270 0         0 $buffer=~s/^\s*#.*?\n//gms;
271             }
272              
273              
274             sub _init_fix{
275 21     22   36 \my $buffer=\$_[0];
276             #Look for an init block
277             #unless($buffer=~/\@\[\{\s*init\s*\{
278 22 100       133 unless($buffer=~$Init){
279 7         37 $buffer="\@{[init{}]}".$buffer;
280             }
281             }
282              
283             my $prepare=\&_prepare_template;
284              
285             my %cache;
286              
287              
288             sub clear {
289 0     1 0 0 %cache=();
290             }
291              
292              
293             sub block :prototype(&) {
294 1     1 0 21 $_[0]->();
295 2         13 return "";
296             }
297             *pl=\*block;
298              
299              
300              
301             sub new{
302 22     22 0 61 my $plex=bless [], shift;
303 22         58 my ($prepare, $path, $args, %options)=@_;
304 22         42 my $root=$options{root};
305 22 50       58 die "Template::Plex::Internal first argument must be defined" unless defined $path;
306              
307 21         44 my $data=do {
308 21         87 local $/=undef;
309 21 50       72 if(ref($path) eq "GLOB"){
    100          
310             #file handle
311 0         0 $options{file}="$path";
312 0         0 <$path>;
313             }
314             elsif(ref($path) eq "ARRAY"){
315             #process as inline template
316 14         43 $options{file}="$path";
317 14         61 join "", @$path;
318             }
319             else{
320             #Assume a path
321             #Prepend the root if present
322 7         16 $options{file}=$path;
323             #$path=catfile $root, $path if $root;
324 7 50       27 $path=join "/", $root, $path if $root;
325 7         10 my $fh;
326 7 50       334 if(open $fh, "<", $path){
327             <$fh>
328 8         408 }
329             else {
330 1         11 die "Could not open file: $path $!";
331 0         0 "";
332             }
333             }
334             };
335              
336 21   66     83 $args//={}; #set to empty hash if not defined
337            
338 22 50       68 chomp $data unless $options{no_eof_chomp};
339             # Perform inject substitution
340             #
341 22 50       86 _subst_inject($data, root=>$root) unless $options{no_include};
342             # Perform superfluous EOL removal
343             #
344 22 50       79 _block_fix($data) unless $options{no_block_fix};
345 22 50       90 _init_fix($data) unless $options{no_init_fix};
346 22 50       100 _comment_strip($data) if $options{use_comments};
347              
348 21 50       51 if($args){
349             #Only call this from top level call
350             #Returns the render sub
351              
352 21         30 state $package=0;
353 21         38 $package++;
354 21         68 $options{package}="Template::Plex::temp".$package; #force a unique package if non specified
355 21         75 $prepare->($plex, $data, $args, %options); #Prepare in the correct scope
356             }
357             else {
358 0           $data;
359             }
360             }
361              
362              
363             #Join map
364             sub jmap :prototype(&$@){
365 0     0 0   my ($sub,$delimiter)=(shift,shift); #block is first
366 0   0       $delimiter//=""; #delimiter is whats left
367 0           join $delimiter, map &$sub, @_;
368             }
369              
370              
371              
372             1;