File Coverage

blib/lib/Template/Nest.pm
Criterion Covered Total %
statement 229 232 98.7
branch 88 112 78.5
condition 27 36 75.0
subroutine 30 30 100.0
pod 15 17 88.2
total 389 427 91.1


line stmt bran cond sub pod time code
1             package Template::Nest;
2              
3 9     9   1338876 use strict;
  9         22  
  9         395  
4 9     9   48 use warnings;
  9         16  
  9         597  
5 9     9   49 use File::Spec;
  9         18  
  9         261  
6 9     9   116 use Carp;
  9         39  
  9         801  
7 9     9   5957 use Data::Dumper;
  9         100212  
  9         33451  
8              
9             our $VERSION = '0.13';
10              
11             sub new{
12 10     10 1 2076357 my ($class,%opts) = @_;
13              
14              
15             # defaults:
16 10         171 my $self = {
17             comment_delims => [ '' ],
18             token_delims => [ '<%','%>' ],
19             name_label => 'NAME',
20             template_dir => '',
21             template_ext => '.html',
22             show_labels => 0,
23             defaults => {},
24             defaults_namespace_char => '.',
25             fixed_indent => 0,
26             die_on_bad_params => 1,
27             escape_char => "\\",
28             token_placeholder => '',
29             file_encoding => undef, # UTF-8, latin1
30             };
31              
32 10         37 bless $self,$class;
33              
34 10 100       226 if ( %opts ){
35 9         52 for my $k (keys %opts){
36 48 50       259 confess "$k is not a valid option" unless defined $self->can($k);
37 48         222 $self->$k( $opts{$k} );
38             }
39             }
40              
41 10         107 return $self;
42             }
43              
44              
45             sub template_dir{
46 98     98 1 4044 my($self,$dir) = @_;
47 98 50 66     343 confess "Expected a scalar directory name but got a ".ref($dir) if $dir && ref($dir);
48 98 100       255 $self->{template_dir} = $dir if $dir;
49 98         329 return $self->{template_dir};
50             }
51              
52              
53             sub template_hash{
54 4     4 0 1104 my ($self,$template_hash) = @_;
55              
56 4 100       15 $self->{template_hash} = $template_hash if $template_hash;
57 4         22 return $self->{template_hash};
58             }
59              
60              
61             sub defaults{
62 7     7 1 5308 my ($self,$defaults) = @_;
63              
64 7 100       31 if ( $defaults ){
65              
66 5 50       24 confess "defaults should be a hashref" unless ref $defaults eq ref {};
67 5         21 $self->{defaults} = $defaults;
68              
69             }
70              
71 7         27 return $self->{defaults};
72             }
73              
74             sub token_placeholder{
75 4     4 1 4872 my ($self,$token) = @_;
76              
77 4 50       15 if (defined $token){
78 4         14 $self->{token_placeholder} = $token;
79             }
80 4         11 return $self->{token_placeholder};
81             }
82              
83             sub file_encoding {
84 1     1 1 4 my ($self, $enc) = @_;
85 1 50       17 if (defined $enc) {
86 1         3 $self->{file_encoding} = $enc;
87             }
88 1         4 return $self->{file_encoding};
89             }
90              
91             sub defaults_namespace_char{
92 30     30 1 1526 my ($self,$char) = @_;
93              
94 30 100       94 if ( defined $char ){
95 10 100       40 if ( $char eq '' ){
96 7         26 $self->{defaults_namespace_char} = '';
97             } else {
98 3 50       18 confess "defaults_namespace_char should be a single character or ''" unless $char =~ /./;
99 3         11 $self->{defaults_namespace_char} = $char;
100             }
101             }
102              
103 30         115 return $self->{defaults_namespace_char};
104             }
105              
106              
107             sub comment_delims{
108 3     3 1 7031 my ($self,$delim1,$delim2) = @_;
109 3 100       31 if (defined $delim1 ){
110 2   50     9 $delim2 = $delim2 || '';
111 2         8 $self->{'comment_delims'} = [ $delim1, $delim2 ];
112             }
113 3         16 return $self->{'comment_delims'};
114             }
115              
116              
117             sub token_delims{
118 63     63 1 9842 my ($self,$delim1,$delim2) = @_;
119              
120 63 100       202 if (defined $delim1 ){
121              
122 10 100       57 if ( ref $delim1 eq ref [] ){
123 9         32 ($delim1,$delim2) = @$delim1;
124             }
125              
126 10   50     42 $delim2 ||= '';
127 10         66 $self->{'token_delims'} = [ $delim1, $delim2 ];
128             }
129 63         206 return $self->{'token_delims'};
130             }
131              
132              
133              
134             sub show_labels{
135 87     87 1 634 my ($self,$show) = @_;
136 87 100 66     475 confess "Expected a boolean but got $show" if $show && ! ( $show == 0 || $show == 1 );
      100        
137 86 100       203 $self->{show_labels} = $show if defined $show;
138 86         256 return $self->{show_labels};
139             }
140              
141              
142             sub template_ext{
143 98     98 1 3763 my ($self,$ext) = @_;
144 98 50 66     342 confess "Expected a scalar extension name but got a ".ref($ext) if defined $ext && ref($ext);
145 98 100       224 $self->{template_ext} = $ext if defined $ext;
146 98         1230 return $self->{template_ext};
147             }
148              
149              
150             sub name_label{
151 268     268 1 2728 my ($self,$label) = @_;
152 268 50 66     653 confess "Expected a scalar name label but got a ".ref($label) if defined $label && ref($label);
153 268 100       560 $self->{name_label} = $label if $label;
154 268         801 return $self->{name_label};
155             }
156              
157              
158             sub fixed_indent{
159 9     9 1 8056 my ($self,$indent) = @_;
160              
161 9 100       26 if ( defined $indent ){
162 7 100 100     367 confess "Expected 0 or 1 but got $indent" unless $indent == 0 or $indent == 1;
163 6         15 $self->{fixed_indent} = $indent;
164             }
165              
166 8         28 return $self->{fixed_indent};
167             }
168              
169              
170             sub die_on_bad_params{
171 8     8 1 1558 my ($self,$should_die) = @_;
172              
173 8 100       26 if ( defined $should_die ){
174 6 100 100     288 confess "Expected 0 or 1 but got $should_die" unless $should_die == 0 or $should_die == 1;
175 5         16 $self->{die_on_bad_params} = $should_die;
176             }
177              
178 7         27 return $self->{die_on_bad_params};
179             }
180              
181              
182              
183             sub escape_char{
184 7     7 1 1549 my ($self,$char) = @_;
185              
186 7 100       26 if (defined $char){
187 5 50 66     45 confess "escape_char should be a single character or ''" unless $char eq '' or $char =~ /./;
188 5         13 $self->{escape_char} = $char;
189             }
190              
191 7         23 return $self->{escape_char};
192             }
193              
194              
195              
196             sub render{
197 145     145 1 8049 my ($self,$comp) = @_;
198              
199 145         254 my $html;
200 145 100       671 if ( ref($comp) =~ /array/i ){
    100          
201 15         51 $html = $self->_render_array( $comp );
202             } elsif( ref( $comp ) =~ /hash/i ){
203 86         328 $html = $self->_render_hash( $comp );
204             } else {
205 44         99 $html = $comp;
206             }
207              
208 139         612 return $html;
209             }
210              
211              
212              
213             sub _render_hash{
214 86     86   159 my ($self,$h) = @_;
215              
216 86 50       311 confess "Expected a hashref. Instead got a ".ref($h) unless ref($h) =~ /hash/i;
217              
218 86         235 my $template_name = $h->{ $self->name_label };
219              
220 86 50       229 confess 'Encountered hash with no name_label ("'.$self->name_label.'"): '.Dumper( $h ) unless $template_name;
221              
222 86         144 my $param = {};
223              
224 86         235 foreach my $k ( keys %$h ){
225 172 100       381 next if $k eq $self->name_label;
226 87         256 $param->{$k} = $self->render( $h->{$k} );
227             }
228              
229 84         230 my $template = $self->_get_template( $template_name );
230 84         326 my $html = $self->_fill_in( $template_name, $template, $param );
231              
232 81 100       246 if ( $self->show_labels ){
233              
234 8         11 my $ca = $self->{comment_delims}->[0];
235 8         10 my $cb = $self->{comment_delims}->[1];
236              
237 8         18 $html = "$ca BEGIN $template_name $cb\n$html\n$ca END $template_name $cb\n";
238             }
239              
240 81         334 return $html;
241              
242             }
243              
244              
245              
246              
247             sub _render_array{
248              
249 15     15   34 my ($self, $arr, $delim) = @_;
250 15 50       65 confess "Expected an array. Instead got a ".ref($arr) unless ref($arr) =~ /array/i;
251 15         30 my $html = '';
252 15         37 foreach my $comp (@$arr){
253 29 50 33     84 $html.= $delim if ($delim && $html);
254 29         90 $html.= $self->render( $comp );
255             }
256 14         34 return $html;
257              
258             }
259              
260              
261              
262             sub _get_template{
263 90     90   208 my ($self,$template_name) = @_;
264              
265 90         173 my $template = '';
266 90 100       244 if ( $self->{template_hash} ){
267 3         5 $template = $self->{template_hash}{$template_name};
268             } else {
269              
270 87         261 my $filename = File::Spec->catdir(
271             $self->template_dir,
272             $template_name.$self->template_ext
273             );
274              
275 87         214 my $fh;
276 87 100       257 if ($self->{file_encoding}) {
277 1 50   1   65 open $fh, "<:encoding($self->{file_encoding})", $filename
  1         1233  
  1         22  
  1         6  
278             or confess "Could not open file $filename: $!";
279             } else {
280 86 50       5148 open $fh, '<', $filename
281             or confess "Could not open file $filename: $!";
282             }
283              
284 87         2100 my $text = '';
285 87         2217 while( my $line = <$fh> ){
286 360         2484 $template.=$line;
287             }
288              
289             }
290              
291 90         635 $template =~ s/\n$//;
292 90         325 return $template;
293             }
294              
295              
296              
297              
298             sub params{
299 6     6 0 4484 my ($self,$template_name) = @_;
300              
301 6         15 my $esc = $self->{escape_char};
302 6         18 my $template = $self->_get_template( $template_name );
303 6         111 my @frags = split( /\Q$esc$esc\E/, $template );
304 6         19 my $tda = $self->{token_delims}[0];
305 6         14 my $tdb = $self->{token_delims}[1];
306              
307 6         11 my %rem;
308 6         22 for my $i (0..$#frags){
309 6         106 my @f = $frags[$i] =~ m/(?
310 6         14 for my $f ( @f ){
311 14         44 $f =~ s/^\s*//;
312 14         75 $f =~ s/\s*$//;
313 14         46 $rem{$f} = 1;
314             }
315             }
316              
317 6         32 my @params = sort(keys %rem);
318 6         48 return \@params;
319             }
320              
321              
322              
323             sub _token_regex{
324 203     203   458 my ($self,$param_name) = @_;
325              
326 203         375 my $esc = $self->{escape_char};
327 203         448 my $tda = $self->{token_delims}[0];
328 203         392 my $tdb = $self->{token_delims}[1];
329              
330 203 100       491 $param_name = '.*?' unless defined $param_name;
331              
332 203         9113 my $token_regex = qr/\Q$tda\E\s+$param_name\s+\Q$tdb\E/;
333 203 50       735 if ( $esc ){
334 203         8708 $token_regex = qr/(?
335             }
336 203         787 return $token_regex;
337             }
338              
339              
340             sub _fill_in{
341 84     84   265 my ($self,$template_name,$template,$params) = @_;
342              
343 84         188 my $esc = $self->{escape_char};
344 84         156 my @frags;
345              
346 84 50       215 if ( $esc ){
347 84         672 @frags = split( /\Q$esc$esc\E/, $template );
348             } else {
349 0         0 @frags = ( $template );
350             }
351              
352 84         290 foreach my $param_name (keys %$params){
353              
354 83         193 my $param_val = $params->{$param_name};
355              
356 83         131 my $replaced = 0;
357              
358 83 100       224 if ( $self->{fixed_indent} ){ #if fixed_indent we need to add spaces during the replacement
359 10         35 for my $i (0..$#frags){
360 10         25 my $rx = $self->_token_regex( $param_name );
361 10         933 my @spaces_repl = $frags[$i] =~ m/([^\S\r\n]*)$rx/g;
362              
363 10         47 while(@spaces_repl){
364 10         24 my $sp = shift @spaces_repl;
365 10         47 my $repl = shift @spaces_repl;
366 10         22 my $param_out = $param_val;
367 10         63 $param_out =~ s/\n/\n$sp/g;
368              
369 10 50       29 if ( $esc ){
370 10 50       262 $replaced = 1 if $frags[$i] =~ s/(?
371             } else {
372 0 0       0 $replaced = 1 if $frags[$i] =~ s/\Q$repl\E/$param_out/;
373             }
374             }
375             }
376             } else {
377 73         221 for my $i (0..$#frags){
378 81         221 my $rx = $self->_token_regex( $param_name );
379 81 100       1222 $replaced = 1 if $frags[$i] =~ s/$rx/$param_val/g;
380             }
381             }
382              
383 83 100 100     531 if ( $self->{die_on_bad_params} && $replaced == 0 ){
384 3         1464 confess "Could not replace template param '$param_name': token does not exist in template '$template_name'";
385             }
386             }
387              
388 81         283 for my $i (0..$#frags){
389              
390 85 100       131 if ( %{$self->{defaults}} ){
  85         262  
391 18         60 my @rem = $self->_params_in( $frags[$i] );
392 18         60 my $char = $self->defaults_namespace_char;
393 18         44 for my $name ( @rem ){
394 14         37 my @parts = ( $name );
395 14 100       83 @parts = split( /\Q$char\E/, $name ) if $char;
396              
397 14         70 my $val = $self->_get_default_val( $self->{defaults}, @parts );
398 14         57 my $rx = $self->_token_regex( $name );
399 14         222 $frags[$i] =~ s/$rx/$val/g;
400             }
401             }
402              
403             # Handle unmatched parameters, if token_placeholder is set then
404             # we replace these parameters with the placeholder.
405 85 100       218 if ($self->{token_placeholder}) {
406 8         24 my $param_rx = $self->_token_regex("param_name");
407              
408 8         35 my @rem = $self->_params_in( $frags[$i] );
409 8         21 for my $name ( @rem ) {
410 5         14 my @parts = ( $name );
411              
412 5         12 my $placeholder = $self->{token_placeholder};
413 5         63 $placeholder =~ s/$param_rx/$name/g;
414              
415 5         18 my $rx = $self->_token_regex( $name );
416 5         92 $frags[$i] =~ s/$rx/$placeholder/g;
417             }
418             }
419              
420 85         224 my $rx = $self->_token_regex;
421 85         659 $frags[$i] =~ s/$rx//g;
422             }
423              
424 81 50       214 if ( $esc ){
425 81         220 for my $i (0..$#frags){
426 85         497 $frags[$i] =~ s/\Q$esc\E//gs;
427             }
428             }
429              
430 81 50       349 my $text = $esc? join($esc,@frags): $frags[0];
431 81         284 return $text;
432             }
433              
434              
435             sub _params_in{
436 26     26   109 my ( $self, $text ) = @_;
437              
438 26         57 my $esc = $self->{escape_char};
439 26         79 my $tda = $self->token_delims->[0];
440 26         84 my $tdb = $self->token_delims->[1];
441              
442 26         45 my @rem;
443 26 50       75 if ( $esc ){
444 26         467 @rem = $text =~ m/(?
445             } else {
446 0         0 @rem = $text =~ m/\Q$tda\E\s+(.*?)\s+\Q$tdb\E/g;
447             }
448              
449 26         57 my %rem;
450 26         60 for my $name (@rem){
451 19         61 $rem{$name} = 1
452             }
453              
454 26         115 return keys %rem;
455             }
456              
457              
458              
459             sub _get_default_val{
460 25     25   81 my ($self,$ref,@parts) = @_;
461              
462 25 100       86 if ( @parts == 1 ){
463 12   100     45 my $val = $ref->{$parts[0]} || '';
464 12         52 return $val;
465             } else {
466 13         23 my $ref_name = shift @parts;
467 13         29 my $new_ref = $ref->{ $ref_name };
468 13 100       36 return '' unless $new_ref;
469 11         31 return $self->_get_default_val( $new_ref, @parts );
470             }
471             }
472              
473              
474              
475             1;
476             __END__