File Coverage

blib/lib/Template/Magic.pm
Criterion Covered Total %
statement 273 295 92.5
branch 85 112 75.8
condition 48 79 60.7
subroutine 61 67 91.0
pod 19 28 67.8
total 486 581 83.6


line stmt bran cond sub pod time code
1             package Template::Magic ;
2             $VERSION = 1.40 ;
3 31     31   4547462 use strict ;
  31         87  
  31         1316  
4 31     31   883 use 5.006_001 ;
  31         107  
  31         1827  
5              
6             # This file uses the "Perlish" coding style
7             # please read http://perl.4pro.net/perlish_coding_style.html
8              
9             ; use Carp
10 31     31   193 ; $Carp::Internal{+__PACKAGE__}++
  31         127  
  31         5467  
11             ; use warnings::register
12 31     31   186 ; use Template::Magic::Zone
  31         58  
  31         6583  
13 31     31   53786 ; use IO::Util
  31         99  
  31         933  
14 31     31   61230 ; use Class::Util
  31         639419  
  31         250  
15 31     31   50725 ; use File::Spec
  31         31736  
  31         245  
16              
17 31     31   6122 ; sub NEXT_HANDLER () { 0 }
  31         74  
  31         47431  
18             ; sub LAST_HANDLER () { 1 }
19              
20             ; sub import
21 34     34   498 { my ($pkg, $pragma) = @_
22 34 50 66     266 ; if ( $pragma
23             && $pragma eq '-compile'
24             )
25 0 0       0 { carp "The -compile pragma has no effect since version 1.39"
26             if warnings::enabled
27             }
28             else
29             { require Exporter
30 34         598 ; our @ISA = 'Exporter'
  34         733  
31 34         135 ; our @EXPORT_OK = qw| NEXT_HANDLER
32             LAST_HANDLER
33             |
34 34         60157 ; $pkg->export_to_level(1, @_)
35             }
36             }
37              
38             ; sub new
39 37     37 1 7396 { my ($c) = shift
40 37         88 ; my ($s) = @_
41 37 50       226 ; $s = { @_ } # passing hash backward compatibility
42             unless ref $s eq 'HASH'
43 37         187 ; foreach ( keys %$s ) # passing -flag backward compatibility
44 20 50       151 { $$s{$_} = delete $$s{-$_}
45             if s/^-//
46             }
47 37         134 ; foreach ( values %$s ) # each value should be an ARRAY ref
48 20 100       127 { $_ = [ $_ ]
49             unless ref eq 'ARRAY'
50             }
51 37         118 ; bless $s, $c
52 37   66     1870 ; $$s{markers} ||= $s->DEFAULT_MARKERS
53 37   33     303 ; $$s{output_handlers} ||= $s->DEFAULT_PRINT_HANDLERS
54 37   33     339 ; $$s{text_handlers} ||= $s->DEFAULT_TEXT_HANDLERS
      33        
55             || $$s{output_handlers}
56 37   66     257 ; $$s{zone_handlers} ||= $s->DEFAULT_ZONE_HANDLERS
57 37   66     277 ; $$s{value_handlers} ||= $s->DEFAULT_VALUE_HANDLERS
58 37   33     321 ; $$s{post_handlers} ||= $s->DEFAULT_POST_HANDLERS
59 37   100     354 ; $$s{lookups} ||= [ (caller)[0] ]
60 37   33     340 ; $$s{options} ||= $s->DEFAULT_OPTIONS
61 37         302 ; $$s{options} = { map { /^(no_)*(.+)$/
  37         153  
62 37 50       5583 ; $2 => $1 ? 0 : 1
63             }
64 37         67 @{$$s{options}}
65             }
66 37         156 ; foreach my $n (qw| zone value text output post |)
67 185   100     962 { $$s{$n.'_handlers'}
68             &&= [ $s->_Hload( $$s{$n.'_handlers'}
69             , $n
70             )
71             ]
72             }
73 37         145 ; $s
74             }
75              
76             ; sub _Hload
77 117     117   1280 { my ($s, $arr, $n) = @_
78             ; map
79 117 100       204 { if ( ref eq 'CODE' )
  304 50       730  
80 286         952 { $_
81             }
82             elsif ( not ref )
83 18   33     165 { my $C = $s->can($_)
84             || $s->can( join ( '_'
85             , $_
86             , uc $n
87             , 'HANDLERS'
88             )
89             )
90             || croak qq(Unknown handler "$_")
91 18         56 ; my $ref = $s->$C
92 18 100       91 ; if ( ref $ref eq 'ARRAY' )
    50          
93 1         14 { $s->_Hload( $ref, $n )
94             }
95             elsif ( ref $ref eq 'CODE' )
96 17         133 { $ref
97             }
98             }
99             }
100             @$arr
101             }
102              
103             ; sub _re
104 56     56   243 { my ($s) = @_
105 56 100       249 ; unless ( $$s{_re} ) # execute it just the first time AND if it has to parse
106 36 100       69 { unless ( @{$$s{markers}} == 3 )
  36         178  
107 31         61117 { no strict 'refs'
108 31     31   249 ; my $m = $$s{markers}[0]
  31         85  
  4         10  
109 4   33     57 ; my $M = $s->can($m)
110             || $s->can($m.'_MARKERS') # backward compatibility
111             || croak qq(Unknown markers "$m")
112 4         12 ; $$s{markers} = $s->$M
113             }
114 180         2373 ; $$s{markers} = [ map { qr/$_/s
  36         179  
115             }
116 36         81 ( @{$$s{markers}}
117             , '(?:(?!' .$$s{markers}[2]. ').)*'
118             , '\w+'
119             )
120             ]
121 36         104 ; my ($S, $I, $E, $A, $ID) = @{$$s{markers}}
  36         119  
122 36         783 ; $$s{_re}{label} = qr/$S$I*$ID$A$E/s
123 36         766 ; $$s{_re}{start_label} = qr/$S($ID)($A)$E/s
124 36         531 ; $$s{_re}{end_label} = qr/$S$I($ID)$E/s
125 36         964 ; $$s{_re}{include_label} = qr/$S\bINCLUDE_TEMPLATE\b($A)$E/s
126             }
127              
128             ; wantarray
129 56 50       278 ? @{$$s{markers}}
  0         0  
130             : $$s{_re}
131             }
132            
133             ; sub find_file
134 9     9 1 23 { my ($s, $t) = @_
135 18     18   348 ; my $find = sub{(grep -s, @_)[0]}
136 9         37 ; File::Spec->file_name_is_absolute($t)
137             ? $find->($t)
138             : ( $ENV{TEMPLATE_MAGIC_ROOT}
139             && $find->( File::Spec->catfile( $ENV{TEMPLATE_MAGIC_ROOT}
140             , $t
141             )
142             )
143             || $find->( map File::Spec->catfile( $_
144             , $t
145             )
146             , @{$$s{paths}}
147             )
148             || $ENV{TEMPLATE_MAGIC_ROOT}
149             && $find->( map File::Spec->catfile( $ENV{TEMPLATE_MAGIC_ROOT}
150             , $_
151             , $t
152             )
153 9 50 33     146 , @{$$s{paths}}
154             )
155             || $find->($t)
156             )
157             }
158            
159             ; sub output
160 46     46 1 11052 { my $s = shift
161 46         77 ; my $args
162 46         148 ; $$args{template} = shift
163 46 100       324 ; $$args{lookups} = [ @_ ] if @_
164 46     46   1624 ; IO::Util::capture { $s->_process( $args ) }
165 46         393 }
166              
167             ; sub print
168 0     0 1 0 { my $s = shift
169 0         0 ; my $args
170 0         0 ; $$args{template} = shift
171 0 0       0 ; $$args{lookups} = [ @_ ] if @_
172 0         0 ; $s->_process( $args )
173             }
174              
175             ; sub noutput
176 1     1 1 1041 { my ($s, %args) = @_
177 1 50       8 ; $args{lookups} = [ $args{lookups} ]
178             unless ref $args{lookups} eq 'ARRAY'
179 1     1   29 ; IO::Util::capture { $s->_process( \%args ) }
180 1         7 }
181              
182             ; sub nprint
183 0     0 1 0 { my ($s, %args) = @_
184 0 0       0 ; $args{lookups} = [ $args{lookups} ]
185             unless ref $args{lookups} eq 'ARRAY'
186 0         0 ; $s->_process( \%args )
187             }
188              
189             ; sub _process
190 47     47   274 { my ($s, $args) = @_
191 47 100       236 ; $$s{_temp_lookups} = $$args{lookups} if exists $$args{lookups}
192 47         73 ; my $t
193 47 100 100     227 ; if ( $t = $$args{container_template}
194             || ${$$s{container_template}}[0]
195             )
196 4         10 { $$s{_included_template} = $$args{template}
197             }
198             else
199 43         101 { $t = $$args{template}
200             }
201 47         258 ; my $z = $s->load( $t )
202 47         142 ; $$z{tm} = $s
203 47         244 ; $z->content_process
204 47         591 ; delete $$z{tm} # to avoid tm object caching
205 47         652 ; delete @$s{qw|_included_template _temp_lookups _NOT_lookup|}
206             }
207              
208             ; sub load
209 55     55 1 100 { my ($s, $t) = @_
210 55         93 ; my $main_zone
211 55 100       186 ; if ( not ref $t )
212 9 50       30 { $t = $s->find_file($t)
213             or croak qq(Template file "$t" empty or not found)
214 9 50       36 ; if ( $$s{options}{cache} )
215 9         40 { $main_zone = IO::Util::_get_parsing_cache('magic_zone', $t)
216 9 50       786 ; return $main_zone if $main_zone
217             }
218             }
219 55 100       258 ; my $content = ref $t eq 'SCALAR' ? $t : IO::Util::slurp $t
220 55         1479 ; $main_zone = $s->_parse( $content )
221 55 100 66     10343 ; $$s{options}{cache} &&! ref($t) # set cache
222             && IO::Util::_set_parsing_cache 'magic_zone', $t, $main_zone
223 55         673 ; $main_zone
224             }
225              
226             ; sub purge_cache
227 0     0 1 0 { $_[0] = 'magic_zone'
228 0         0 ; goto &IO::Util::_purge_parsing_cache
229             }
230              
231             ; sub _parse
232 55     55   95 { my ($s, $content_ref) = @_
233 55         334 ; my $re = $s->_re
234             ; my @temp
235 55         1438 = map { [ $_
236 413         551 , do { /$$re{end_label}/ && $1
237 413 100 100     8162 || /$$re{include_label}/ && do{ (my $t = $1) =~ s/^\s+//
  7   66     45  
      66        
      66        
238 7 100       45 ; $t
239             ? $s->load($t)
240             : 'CONTAINER_INCLUDE'
241             }
242             || /$$re{start_label}/ && { id => $1
243             , attributes => $2
244             }
245             }
246             ]
247             }
248             split /($$re{label})/ , $$content_ref
249            
250 55         510 ; for ( my $i = $#temp # find end
251             ; $i >= 0
252             ; $i --
253             )
254 413         650 { my $id = $temp[$i][1]
255 413 100 100     2098 ; next if ( ref $id or not $id )
256 61         416 ; for ( ( my $ii = $i-1 # find THE start
257             , my $l = 0
258             )
259             ; $ii >= 0 # condition
260             ; ( $ii --
261             , $l ++
262             )
263             )
264 260         407 { my $the_start = $temp[$ii][1]
265 260 100       951 ; next unless ref($the_start) eq 'HASH' # next if not start
266 118 100       8305 ; next unless $$the_start{id} eq $id # next if not THE start
267 57         353 ; $$the_start{_s} = $ii + 1
268 57         131 ; $$the_start{_e} = $ii + $l
269             ; last
270 57         186 }
271             }
272             # allows to set protected props from outside class
273 55         110 ; local $Class::props::force = 1
274 55         852 ; Template::Magic::Zone->new( _s => 0
275             , _e => $#temp
276             , _t => \@temp
277             , is_main => 1
278             )
279             }
280            
281              
282             ############################# STANDARD HANDLERS #############################
283              
284             # override these DEFAULT subs in subclasses to change defaults
285              
286             ; sub DEFAULT_ZONE_HANDLERS
287 32     32 0 104 {
288             }
289            
290             ; sub DEFAULT_POST_HANDLERS
291 37     37 0 129 {
292             }
293            
294             ; sub DEFAULT_TEXT_HANDLERS
295 37     37 0 255 {
296             }
297              
298             ; sub DEFAULT_VALUE_HANDLERS
299 31     31 0 80 { my ($s, @args) = @_
300 31         124 ; [ $s->SCALAR
301             , $s->REF
302             , $s->CODE(@args)
303             , $s->ARRAY
304             , $s->HASH
305             , $s->OBJECT
306             ]
307             }
308              
309             ; sub DEFAULT_PRINT_HANDLERS
310             { [ sub
311 368 50   368   1923 { print "$_[1]" if defined $_[1]
312 368         6898 ; NEXT_HANDLER
313             }
314 37     37 0 307 ]
315             }
316            
317 31         24792 ; { no warnings 'once'
318 31     31   249 ; *DEFAULT_OUTPUT_HANDLERS = \&DEFAULT_PRINT_HANDLER # deprecated
  31         62  
319             }
320            
321             ; sub DEFAULT_OPTIONS
322 37     37 0 193 { [ qw| cache | ]
323             }
324            
325             ; sub DEFAULT_MARKERS
326 29     29 1 178 { [ qw| { / } | ]
327             }
328            
329             ; sub HTML_MARKERS
330 7     7 1 35 { [ qw| | ]
331             }
332              
333             ; sub CODE_MARKERS
334 0     0 1 0 { [ qw| <- / -> | ]
335             }
336              
337             ; sub HTML_VALUE_HANDLERS # value handler
338 3     3 0 10 { my ($s, @args) = @_
339 3         27 ; [ $s->SCALAR
340             , $s->REF
341             , $s->CODE(@args)
342             , $s->TableTiler
343             , $s->ARRAY
344             , $s->HASH
345             , $s->FillInForm
346             , $s->OBJECT
347             ]
348             }
349            
350             ; sub SCALAR # value handler
351             { sub
352 184     184   273 { my ($z) = @_
353 184         452 ; my $v = $z->value
354 184 100       2246 ; if ( not ref $v ) # if it's a plain string
355 109         356 { $z->output($v) # set output
356 109         5713 ; $z->output_process( $v ) # process output (requires string)
357 109         696 ; LAST_HANDLER
358             }
359             }
360 37     37   370 }
361              
362             ; sub REF # value handler
363             { sub
364 75     75   110 { my ($z) = @_
365 75         199 ; my $v = $z->value
366 75 50       1200 ; if (ref($v) =~ /^(SCALAR|REF)$/) # if it's a reference
367 0         0 { $z->value($$v) # dereference
368 0         0 ; $z->value_process # process the new value
369 0         0 ; LAST_HANDLER
370             }
371             }
372 37     37 1 360 }
373            
374             ; sub ARRAY # value handler
375             { sub
376 57     57   85 { my ($z) = @_
377 57 100       146 ; if (ref $z->value eq 'ARRAY') # if it's an ARRAY
378 9         122 { my ($i, $attr, $val_key, $ix_key, $named) = 0
379 9 100       30 ; if ( $attr = $z->attributes )
380 3         79 { $attr =~ s/^\s*(OF\s)*\s*//i
381 3         11 ; ($val_key, $ix_key, $i) = split /\s+/, $attr
382 3         6 ; $named = 1
383             }
384 9         179 ; foreach my $item ( @{$z->value} ) # for each value in the array
  9         28  
385 26 100       204 { $z->value( $named # set the value for the zone
    100          
386             ? { $val_key => $item
387             , $ix_key ? ($ix_key => $i ++) : ()
388             }
389             : $item
390             )
391 26         319 ; $z->value_process # process it
392             }
393 9         47 ; LAST_HANDLER
394             }
395             }
396 36     36 1 443 }
397            
398             ; sub HASH # value handler
399             { sub
400 48     48   78 { my ($z) = @_
401 48 100       140 ; if (ref $z->value eq 'HASH') # if it's a HASH
402 43         754 { $z->content_process # start again the process
403 43         712 ; LAST_HANDLER
404             }
405             }
406 36     36 1 495 }
407            
408             ; sub CODE # value handler
409 35     35 1 135 { my ( undef, @args ) = @_
410             ; sub
411 74     74   103 { my ($z) = @_
412 74         200 ; my $v = $z->value
413 74 100       1028 ; if ( ref $v eq 'CODE' )
414 16         46 { my $l = $z->location
415             ; my $nv = Class::Util::blessed($l)
416 31 100       46808 ? do { no strict 'refs'
  16         198  
417 31 50   31   213 ; $l->$v( ${ref($l).'::no_template_magic_zone'}
  31         77  
  5         59  
  5         43  
418             ? ()
419             : $z
420             , @args
421             )
422             }
423             : $v->( $z , @args )
424 16 50 100     320 ; if ( $v ne ($nv||'') ) # avoid infinite loop
425 16         50 { $z->value($nv)
426 16         201 ; $z->value_process
427             }
428 16         114 ; LAST_HANDLER
429             }
430             }
431 35         364 }
432              
433             ; sub OBJECT
434             { sub
435 2     2   7 { my ($z) = @_
436 2 50       7 ; if ( Class::Util::blessed($z->value) )
437 2         80 { $z->content_process # process content
438 2         32 ; LAST_HANDLER
439             }
440             }
441 34     34 1 353 }
442            
443             ; sub ID_list
444 1     1 1 7 { my ($s, $indent, $end) = @_
445 1   50     3 ; $indent ||= ' ' x 4
446 1   50     11 ; $end ||= '/'
447 1         3 ; my $re = $s->_re
448 1     9   5 ; $$s{text_handlers} = [ sub{} ] # does not print any text
  9         59  
449             ; $$s{zone_handlers}
450             = [ sub # takes control of the whole process
451 6     6   8 { my ($z) = @_
452 6         19 ; $z->output_process( $indent x $z->level
453             . $z->id
454             . ":\n"
455             )
456 6         40 ; $z->content_process
457 6         60 ; my $cont = $z->content
458 6 100 66     47 ; if ( $z->_e # if it is a block
459             && $cont =~ /$$re{label}/ # and contains labels
460             )
461 2         37 { $z->output_process( $indent x $z->level # print the end
462             . $end
463             . $z->id
464             . ":\n"
465             )
466             }
467 6         89 ; LAST_HANDLER
468             }
469 1         6 ]
470             }
471            
472             # START AutoLoaded handlers
473              
474             # 'sub' must be at start of line to be found by AutoSplit
475             # no fancy coding here :-(
476              
477             sub _EVAL_ # zone handler
478             { sub
479 2     2   4 { my ($z) = @_;
480 2 100       6 ; if ( $z->id eq '_EVAL_' )
481 1         26 { $z->value( eval $z->content )
482             }
483 2         46 ; NEXT_HANDLER
484             # lookup is skipped by the defined $z->value
485             # value_process is entered by default
486             }
487 1     1   17 }
488              
489             sub _EVAL_ATTRIBUTES_ # zone handler
490             { sub
491 2     2   3 { my ($z) = @_
492 2 100       6 ; if ( $z->attributes )
493 1         58 { $z->param( eval $z->attributes )
494             }
495 2         45 ; NEXT_HANDLER
496             # $z->attributes should be a ref to a structure
497             }
498 1     1   4 }
499              
500             sub TRACE_DELETIONS # zone handler
501             { sub
502 5     5   7 { my ($z) = @_
503             # do lookup and value processes as usual
504 5         14 ; $z->lookup_process
505 5         58 ; $z->value_process
506             # if they fail to find a true output trace the deletion
507 5 100       15 ; if ( not defined $z->output )
    100          
508 2 100       43 { $z->output_process ( '<<' . $z->id . ' not found>>' )
509             unless ref $z->value eq 'HASH'
510             }
511             elsif ( not $z->output )
512 1         48 { $z->output_process ( '<<' . $z->id . ' found but empty>>' )
513             }
514 5         172 ; LAST_HANDLER
515             }
516 1     1 1 5 }
517              
518             sub INCLUDE_TEXT # zone handler
519             { sub
520 2     2   3 { my ($z) = @_
521 2 100       11 ; if ( $z->id eq 'INCLUDE_TEXT' )
522 1         22 { my $file = $z->attributes
523 1 50       76 ; open my $itxt, $file
524             or croak qq(Error opening text file "$file": $^E)
525 1         35 ; $z->text_process($_) while <$itxt>
526 1         10 ; close $itxt
527 1         13 ; LAST_HANDLER
528             }
529             }
530 1     1 1 11 }
531              
532             ############### HTML HANDLERS ##############
533              
534             sub TableTiler # value handler
535             { eval
536 4     4 0 7 { local $SIG{__DIE__}
  4         21  
537             ; require HTML::TableTiler
538 4         3381 ; return $HTML::TableTiler::VERSION >= 1.14
  4         35012  
539             }
540 4 50       20 ; if ( $@ )
541 0         0 { carp qq("HTML::TableTiler" is not installed on this system or it is not current\n)
542 0     0   0 ; return sub {} # no action
543 0         0 }
544             else
545             { sub # normal handler
546 6     6   11 { my ($z) = @_
547 6         15 ; my $v = $z->value
548 6 100 66     97 ; if ( ref($v) eq 'ARRAY'
549             && HTML::TableTiler::is_matrix($v) # if matrix
550             )
551             { $z->value
552 2         66 ( do { my $cont = $z->content
  2         8  
553 2   50     14 ; HTML::TableTiler::tile_table( $v
554             , $cont && \$cont
555             , $z->attributes
556             , 1
557             )
558             }
559             )
560 2         940 ; $z->value_process
561 2         10 ; LAST_HANDLER
562             }
563             }
564 4         67 }
565             }
566              
567             sub FillInForm # value handler
568             { eval
569 4     4 0 10 { local $SIG{__DIE__}
  4         16  
570             ; require HTML::FillInForm
571 4         2883 }
572 4 50       13246 ; if ( $@ )
573 0         0 { carp qq("HTML::FillInForm" is not installed on this system\n)
574 0     0   0 ; sub {}
575 0         0 }
576             else
577             { sub
578 2     2   4 { my ($z) = @_
579 2         5 ; my $v = $z->value
580 2 50 33     39 ; if ( ref($v)
581             && defined UNIVERSAL::can( $v , 'param' )
582             )
583 2         48 { my $cont = IO::Util::capture { $z->content_process }
584 2         22 ; my $attr = $z->attributes
  2         37  
585 2         52 ; my ($list) = $attr =~ /ignore_fields\s*=>\s*\[(.*)\]/
586 2   100     26 ; my @if = map /(?:'|")(.+)(?:'|")/ #'
587             , split /\s*,\s*/
588             , $list||''
589 2         16 ; $z->value( HTML::FillInForm
590             ->new
591             ->fill( scalarref => $cont
592             , fobject => $v
593             , ignore_fields => \@if
594             )
595             )
596 2         1277 ; $z->value_process
597 2         11 ; LAST_HANDLER
598             }
599             }
600 4         67 }
601             }
602            
603             __END__