File Coverage

blib/lib/Template/Plex/Internal.pm
Criterion Covered Total %
statement 274 285 96.1
branch 33 114 28.9
condition 16 119 13.4
subroutine 91 91 100.0
pod 0 5 0.0
total 414 614 67.4


line stmt bran cond sub pod time code
1             package Template::Plex::Internal;
2 8     8   60 use strict;
  8         16  
  8         271  
3 8     8   1570 use warnings;
  8         18  
  8         2179  
4              
5 8     8   58 use Template::Plex;
  8         21  
  8         468  
6 8     8   2366 use Error::Show;
  8         42588  
  8         373  
7              
8              
9 8     8   375 use feature qw;
  8         16  
  8         2608  
10 8     8   53 no warnings "experimental";
  8         16  
  8         345  
11              
12 8     8   2137 use File::Spec::Functions qw;
  8         4078  
  8         1369  
13              
14 7     7   5027 use Export::These qw;
  7         5612  
  7         128  
15              
16             my $Include=qr|\@\{\s*\[\s*include\s*\(\s*(.*?)\s*\)\s*\] \s* \}|x;
17             my $Init=qr|\@\{\s*\[\s*init\s*\{(?:.*?)\}\s*\] \s* \}|smx;
18              
19             # Match any curly bracket not contained withing a @{[ ]} block
20             #my $plain=qr/(?
21              
22              
23             sub new; #forward declare new;
24              
25             sub lexical{
26 27     27 0 57 my $href=shift;
27 27 50 33     113 die "NEED A HASH REF " unless ref $href eq "HASH" or !defined $href;
28 27   50     1801 $href//={};
29 27         70 \my %fields=$href;
30              
31 27         52 my $string="";
32 27         250 for my $k (keys %fields){
33 15         61 $string.= "\\my \$$k=\\\$fields{$k};\n";
34             }
35 27         75 $string;
36             }
37              
38             sub bootstrap{
39 27     27   630 my $plex=shift;
40 25         49 \my $_data_=\shift;
41 25         93 my $href=shift;
42 25         140 my %opts=@_;
43              
44 25   50     638 $href//={};
45 25         53 \my %fields=$href;
46              
47 25         681 my $out="package $opts{package} {
48             use Template::Plex::Internal qw;
49             no warnings qw;
50             ";
51              
52             #$out.='my $self=$plex;
53             #';
54              
55 25         76 $out.= ' \my %fields=$href;
56             ';
57 25 50       72 $out.=' my %options=%opts;
58             ' if keys %opts;
59 25         196 for($opts{use}->@*){
60 1         7 $out.="use $_;\n";
61             }
62 25         67 for($opts{inject}->@*){
63 1         123 $out.="$_\n";
64             }
65              
66 25 50       136 $out.=lexical($href) unless $opts{no_alias}; #add aliased variables from hash
67 25         322 $out.='
68             my %cache; #Stores code refs using caller as keys
69              
70             sub clear {
71             for (keys %cache){
72             my $t=delete $cache{$_};
73             $t->cleanup;
74             }
75             }
76              
77             sub skip{
78             goto _PLEX_SKIP;
79             }
80              
81             $plex->[Template::Plex::skip_]=\&skip;
82              
83              
84             sub init :prototype(&){
85             $self->_init(@_);
86             }
87              
88             sub parent {
89             $self->parent(@_);
90             }
91             sub slot {
92             $self->slot(@_);
93             }
94             sub fill_slot {
95             $self->fill_slot(@_);
96             }
97             sub append_slot {
98             $self->append_slot(@_);
99             }
100              
101             sub prepend_slot {
102             $self->prepend_slot(@_);
103             }
104              
105             sub fill_var{
106             my $name=shift;
107             no strict "refs";
108             $$name=shift;
109             "";
110             }
111              
112             sub append_var{
113             my $name=shift;
114             no strict "refs";
115             $$name .= shift;
116             "";
117              
118             }
119             sub prepend_var{
120             my $name=shift;
121             no strict "refs";
122             $$name = shift . $$name;
123             "";
124              
125             }
126              
127              
128             sub inherit {
129             $self->inherit(@_);
130             }
131              
132             sub load {
133             $self->load(@_);
134             }
135              
136             sub cache {
137             my @args=@_;
138             if(@args ==1){
139             # Recalling implicit cache key with path only
140             unshift @args, undef;
141             }
142             elsif(defined($args[1]) and ref($args[1]) eq "HASH"){
143             # variables hash ref given, with implicit cache id
144             unshift @args, undef;
145             }
146             else{
147             # Expect explicit cache Id
148             }
149              
150             my ($id, $path, $var, @opts)=@args;
151             #we want to cache based on the caller
152             $id//=$path.join "", caller;
153             #unshift @_, $id;
154             $self->cache($id,$path, $var,@opts);
155             }
156              
157             sub immediate {
158             my @args=@_;
159             if(@args ==1){
160             # Recalling implicit cache key with path only
161             unshift @args, undef;
162             }
163             elsif(defined($args[1]) and ref($args[1]) eq "HASH"){
164             # variables hash ref given, with implicit cache id
165             unshift @args, undef;
166             }
167             else{
168             # Expect explicit cache Id
169             }
170             my ($id, $path, $var, @opts)=@args;
171             #we want to cache based on the caller
172             $id//=$path.join "", caller;
173             my $template=$self->cache($id, $path, $var, @opts);
174             if($template){
175             return $template->render($var);
176             }
177             "";
178             }
179              
180              
181              
182             sub {
183             no warnings \'uninitialized\';
184             no strict;
185             #my $plex=shift;
186             my $self=shift;
187              
188             \\my %fields=shift//\\%fields;
189              
190              
191             ##__START
192             return $self->prefix.
193             qq
194             {'.
195             $_data_
196             . '}
197             .$self->postfix;
198             _PLEX_SKIP:
199             "";
200             }
201             ##__END
202             };';
203              
204             };
205              
206             # First argument the template string/text. This is any valid perl code
207             # Second argument is a hash ref to default or base level fields
208             # returns a code reference which when called renders the template with the values
209             sub _prepare_template{
210 7     7   9622 no warnings qw;
  7         18  
  7         2478  
211 25     27   857 my ($plex, undef, $href, %opts)=@_;
212 25   50     97 $href//={};
213 25         59 \my %fields=$href;
214 25         109 \my %meta=\%opts;
215              
216             #$plex now variable is now of base class
217 25   100     234 $plex=($opts{base}//"Template::Plex")->new($plex);
218              
219 25         76 $plex->[Template::Plex::meta_]=\%opts;
220 25         371 $plex->[Template::Plex::args_]=$href;
221              
222 25         55 my $self=$plex;
223 25         64 my $prog=&Template::Plex::Internal::bootstrap;
224 25         74 local $@;
225 7 0 0 7   66 my $ref=eval $prog;
  7 0 0 7   19  
  7 50 33 7   1733  
  7 50 33 7   1520  
  7 0 0 7   14  
  7 0 0 7   1861  
  7 0 33 7   43  
  7 0 33 1   13  
  7 0 0 1   928  
  7 0 0 1   41  
  7 0 0 2   19  
  7 0 0 1   377  
  7 0 33 1   1289  
  7 0 33 1   17  
  7 0 0 5   3302  
  7 0 0 10   50  
  7 0 0 7   37  
  7 0 0 5   483  
  7 0 0 5   93  
  7 0 0 4   14  
  7 0 0 7   1122  
  25 0 0 6   4883  
  1 0 0 4   10  
  1 0 0 3   667  
  1 0 0 2   10  
  1 0 0 3   2  
  1 0 0 3   46  
  1 0 0 2   6  
  1 0 0 1   2  
  1 50 0 1   205  
  1 50 33 1   9  
  1 50 33 1   3  
  1 0 0 1   30  
  2 0 0 1   498  
  2 0   1   14  
  1     1   588  
  1     1   9  
  2     1   5  
  2     1   102  
  5     1   16  
  1     1   3  
  1     1   130  
  1     1   8  
  1     1   3  
  1     1   601  
  1     1   10  
  1     1   3  
  1     1   53  
  1     1   6  
  1     1   2  
  2     1   223  
  10     1   40  
  6     1   21  
  5     1   42  
  5     1   547  
  3     1   10  
  3     1   608  
  3     1   44  
  6     1   54  
  4     1   204  
  1     1   3  
  1     1   101  
  1     1   7  
  1     1   3  
  1     1   705  
  1     1   9  
  1     1   2  
  1     1   49  
  1         6  
  1         2  
  1         228  
  1         29  
  1         3  
  1         30  
  1         522  
  1         3  
  1         582  
  1         9  
  1         3  
  1         97  
  1         8  
  1         2  
  1         103  
  1         7  
  1         2  
  1         628  
  1         9  
  1         2  
  1         49  
  1         5  
  1         3  
  1         208  
  1         8  
  1         2  
  1         28  
  1         441  
  1         2  
  1         619  
  1         9  
  1         4  
  1         105  
  1         7  
  1         3  
  1         95  
  1         8  
  1         1  
  1         676  
  1         10  
  1         3  
  1         50  
  1         7  
  1         2  
  1         175  
  1         9  
  1         3  
  1         28  
  1         499  
  1         2  
  1         567  
  1         10  
  1         3  
  1         151  
  1         10  
  1         2  
  1         104  
  1         7  
  2         7  
  2         636  
  1         10  
  1         2  
  2         56  
  2         17  
  2         6  
  2         262  
  1         5  
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
226 25         148 my $e=$@; #Save the error as require will nuke it
227 7     7   57 use Scalar::Util qw;
  7         20  
  7         5657  
228 25         573 weaken($self);
229 25 100       99 if($e){
230 3         15 my $context=Error::Show::context($e);
231             # Replace the pseudo filename with the file name if we have one
232 3         6117 my $filename=$meta{file};
233 3         71 $context=~s/(\(eval \d+\))/$filename/g;
234             # Rethrow the exception, translated context line numbers
235 3         35 die $context;
236             }
237 23         157 $plex->[Template::Plex::sub_]=$ref;
238 23         232 $plex;
239             }
240              
241             #a little helper to allow 'including' templates into each other
242             sub _munge {
243 7     5   51 my ($input, %options)=@_;
244              
245             #test for literals
246 7         16 my $path;
247 6 0       146 if($input =~ /^"(.*)"$/){
    0          
248             #literal
249 5         31 $path=$1;
250             }
251             elsif($input =~ /^'(.*)'$/){
252             #literal
253 4         22 $path=$1;
254             }
255             else {
256             #not supported?
257             #
258             }
259 4         690 Template::Plex::Internal->new(\&_prepare_template,$path,"",%options);
260             }
261              
262             sub _subst_inject {
263 28     31   126 \my $buffer=\(shift);
264 27         242 while($buffer=~s|$Include|_munge($1, @_)|e){
  2         193  
265             #TODO: Possible point for diagnostics?
266             };
267             }
268              
269             sub _block_fix {
270             #remove any new line immediately after a ]} pair
271 26     29   68 \my $buffer=\(shift);
272            
273 28         261 $buffer=~s/^(\s*\@\{\[.*?\]\})\n/$1/gms;
274             }
275              
276             sub _comment_strip {
277 3     2   235 \my $buffer=\(shift);
278 3         53 $buffer=~s/^\s*#.*?\n//gms;
279             }
280              
281              
282             sub _init_fix{
283 27     30   72 \my $buffer=\$_[0];
284             #Look for an init block
285             #unless($buffer=~/\@\[\{\s*init\s*\{
286 27 100       233 unless($buffer=~$Init){
287 10         563 $buffer="\@{[init{}]}".$buffer;
288             }
289             }
290              
291             my $prepare=\&_prepare_template;
292              
293             my %cache;
294              
295              
296             sub clear {
297 2     5 0 12 %cache=();
298             }
299              
300              
301             sub block :prototype(&) {
302 3     6 0 582 $_[0]->();
303 3         23 return "";
304             }
305             *pl=\*block;
306              
307              
308              
309             sub new{
310 25     27 0 68 my $plex=bless [], shift;
311 25         192 my ($prepare, $path, $args, %options)=@_;
312 25         86 my $root=$options{root};
313 25 50       78 die "Template::Plex::Internal first argument must be defined" unless defined $path;
314              
315 25         243 my $data=do {
316 25         123 local $/=undef;
317 25 100       121 if(ref($path) eq "GLOB"){
    100          
    100          
318             #file handle
319 1         674 $options{file}="$path";
320 1         9 <$path>;
321             }
322             elsif(ref($path) eq "ARRAY"){
323             #process as inline template
324 17         57 $options{file}="$path";
325 17         150 join "", @$path;
326             }
327             elsif(ref($path) eq "SCALAR"){
328             # Make relative to callers path
329 3         15 my $cpath=$options{caller}->meta->{file};
330 7     7   64 use File::Basename "dirname";
  7         13  
  7         4577  
331 3         311 my $rpath=dirname $cpath;
332 3         229 $rpath.="/".$$path;
333 3         15 $options{file}=$rpath;
334              
335 3         7 my $fh;
336 3 50       144 if(open $fh, "<", $rpath){
337             <$fh>
338 3         567 }
339             else {
340 1         3 die "Could not open file: $rpath $!";
341 1         514 "";
342             }
343             }
344             else{
345             #Assume a path
346             #Prepend the root if present and if not absolute
347            
348              
349             # only prepend root if relative path
350             #unless($path=~m|^/|){
351 7 100       35 unless(file_name_is_absolute($path)){
352             # Assume working dir if no root
353 8 50       81 $path=join "/", $root, $path if $root;
354             }
355 8         117 $options{file}=$path;
356              
357 7         17 my $fh;
358 7 100       410 if(open $fh, "<", $path){
359             <$fh>
360 8         530 }
361             else {
362 2         17 die "Could not open file: $path $!";
363 2         9 "";
364             }
365             }
366             };
367              
368 26   66     744 $args//={}; #set to empty hash if not defined
369            
370 26 50       127 chomp $data unless $options{no_eof_chomp};
371             # Perform inject substitution
372             #
373 25 50       120 _subst_inject($data, root=>$root) unless $options{no_include};
374             # Perform superfluous EOL removal
375             #
376 25 50       196 _block_fix($data) unless $options{no_block_fix};
377             #use feature ":all";
378             #say STDERR $options{file};
379             # Only do an init fix if the file has plex|plx in the name
380             # of if it is an array?
381 25 100       138 if($options{file}=~/\.plex|\.plx|^ARRAY/){
382             # Actually a template
383             #say STDERR "ACTUAL TEMPLATE $options{file}";
384 25 50       106 _init_fix($data) unless $options{no_init_fix};
385             }
386             else {
387             #say STDERR "NOT ACUTALLY A TEMPLATE $options{file}";
388             # Assume a raw file..
389             # enode into hex
390 1         202 $data='@{[pack "H*","'. unpack("H*", $data).'"]}';
391             }
392              
393 25 50       80 _comment_strip($data) if $options{use_comments};
394              
395 25 50       57 if($args){
396             #Only call this from top level call
397             #Returns the render sub
398              
399 25         92 state $package=0;
400 25         507 $package++;
401 25         77 $options{package}="Template::Plex::temp".$package; #force a unique package if non specified
402 25         814 $prepare->($plex, $data, $args, %options); #Prepare in the correct scope
403             }
404             else {
405 1         10 $data;
406             }
407             }
408              
409              
410             #Join map
411             sub jmap :prototype(&$@){
412 1     4 0 2 my ($sub,$delimiter)=(shift,shift); #block is first
413 1   0     105 $delimiter//=""; #delimiter is whats left
414 1         7 join $delimiter, map &$sub, @_;
415             }
416              
417              
418              
419             1;