File Coverage

blib/lib/Template/Plex/Internal.pm
Criterion Covered Total %
statement 224 250 89.6
branch 21 44 47.7
condition 10 18 55.5
subroutine 81 88 92.0
pod 0 5 0.0
total 336 405 82.9


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