File Coverage

blib/lib/HTML/Template/Set.pm
Criterion Covered Total %
statement 28 220 12.7
branch 0 128 0.0
condition 0 45 0.0
subroutine 9 21 42.8
pod 3 3 100.0
total 40 417 9.5


line stmt bran cond sub pod time code
1             # HTML::Template::Set
2             #
3             # description: Filter/Wrapper for HTML::Template to include TMPL_SET tags
4             # that will set params. There is also functionality to associate the
5             # environment variables (%ENV) to TMPL_VAR tags prefixed with 'ENV_', I
6             # figured adding this here made sense since the module name is ::Set and
7             # it is 'setting' the paramaters.
8             #
9             # author: David J Radunz
10             #
11             # $Id: Set.pm,v 1.9 2004/05/01 08:26:56 dj Exp $
12              
13             package HTML::Template::Set;
14              
15 1     1   7258 use strict;
  1         3  
  1         41  
16 1     1   7 use warnings;
  1         2  
  1         49  
17              
18             # BEGIN BLOCK {{{
19             BEGIN {
20             ## Modules
21             # CPAN
22 1     1   1764 use HTML::Template;
  1         17819  
  1         42  
23 1     1   13 use Carp qw(croak confess carp);
  1         2  
  1         84  
24              
25             # Base
26 1     1   5 use base 'HTML::Template';
  1         2  
  1         145  
27              
28             ## Constants
29 1     1   5 use constant TRUE => 1;
  1         2  
  1         90  
30 1     1   5 use constant FALSE => 0;
  1         1  
  1         45  
31              
32             ## Variables
33 1     1   4 use vars(qw($VERSION));
  1         2  
  1         79  
34              
35 1     1   2 $VERSION = do { my @r=(q$Revision: 1.9 $=~/\d+/g); sprintf "%d."."%03d"x$#r,@r };
  1         5  
  1         9  
36 1         2799 $HTML::Template::Set::VERSION = $VERSION;
37             }
38             # }}}
39              
40              
41             # Init Functions {{{
42             sub new {
43 0     0 1   my $pkg = shift;
44              
45             # check hashworthyness
46 0 0         if (@_ % 2) {
47 0           croak("HTML::Template::Set->new() called with odd number of option ".
48             "parameters - should be of the form option => value");
49             }
50              
51             # set defaults for our internal options
52 0           my %default_options = (
53             set_order_bottom_up => FALSE,
54             associate_env => FALSE,
55             env_names_to_lower_case => FALSE,
56             chomp_after_set => TRUE
57             );
58              
59 0           my %options = _merge_opts(\%default_options, {@_});
60              
61             # check for unsupported options file_cache and shared_cache
62 0 0 0       if ($options{file_cache} or $options{shared_cache}) {
63 0           croak("HTML::Template::Set->new() : sorry, this module won't work with ".
64             "file_cache or shared_cache modes. This will hopefully be fixed in an ".
65             "upcoming version.");
66             }
67              
68             # push on our filter, one way or another.
69 0 0         if (exists $options{filter}) {
70             # CODE => ARRAY
71 0 0         $options{filter} = [ { 'sub' => $options{filter},
72             'format' => 'scalar' } ]
73             if ref($options{filter}) eq 'CODE';
74              
75             # HASH => ARRAY
76 0 0         $options{filter} = [ $options{filter} ]
77             if ref($options{filter}) eq 'HASH';
78              
79 0 0         unless (ref($options{filter}) eq 'ARRAY') {
80             # unrecognized
81 0           croak("HTML::Template::Set->new() : bad format for filter argument. ".
82             "Please check the HTML::Template docs for the allowed forms.");
83             }
84             }
85              
86             # push onto ARRAY
87 0           my %set_params = ();
88 0           push(@{$options{filter}}, {
89 0     0     'sub' => sub { _set_filter(\%set_params, \%options, @_); },
90 0           'format' => 'scalar'
91             });
92              
93             # default global_vars on
94 0 0         unless (exists $options{global_vars}) {
95 0           $options{global_vars} = 1;
96             }
97              
98             # create an HTML::Template object, catch the results to keep error
99             # message line-numbers helpful.
100 0           my $self;
101 0           eval {
102 0           $self = $pkg->SUPER::new(%options,
103             _set_params => \%set_params
104             );
105             };
106 0 0         if ($@) {
107 0           croak("HTML::Template::Set->new() : Error creating HTML::Template object".
108             " : ". $@);
109             }
110              
111 0 0         unless (exists $set_params{___loaded_set_params}) {
112             # pull set_params out of the parse_stack for cache mode.
113 0 0         if ($self->{options}->{cache}) {
114 0           my $parse_stack = $self->{parse_stack};
115 0           my $set_params = ${$parse_stack}[-1];
  0            
116              
117 0 0 0       if (defined $set_params and ref $set_params eq 'HASH') {
118 0           $self->{options}->{_set_params} = $set_params;
119 0 0         if ($self->{options}->{debug}) {
120 0           print STDERR "### HTML::Template::Set Debug ### loaded set params ".
121             "from cache\n";
122             }
123             }
124             }
125             }
126              
127             # merge the TMPL_SET params as VAR's
128 0           $self->_merge_set_params();
129              
130 0           return $self;
131             }
132              
133             sub _merge_opts {
134 0     0     my ($defaults, $args) = @_;
135              
136 0 0 0       return unless ((defined $defaults && ref $defaults eq 'HASH')
      0        
      0        
137             or (defined $args and ref $args eq 'HASH'));
138              
139 0           my %opts = %$defaults;
140              
141 0           foreach my $key (keys %$args) {
142 0           $opts{$key} = $args->{$key};
143             }
144              
145 0           return %opts;
146             }
147             # }}}
148              
149              
150             # Set Filter Function {{{
151             sub _set_filter {
152 0     0     my ($set_params, $options, $text) = @_;
153              
154             # return unless there is some text to parse
155 0 0         return unless ($$text);
156              
157             # the rtext is the text that is returned after sucking out the set tags
158 0           my @rtext = ();
159              
160             # the setstack is a temporary stack containing pending sets
161             # waiting for a /set.
162 0           my @setstack = ();
163              
164             # the setparamstack is a temorary stack containing all sets for this
165             # template, it is used later to set the real set_params (depending on the
166             # set_order_*)
167 0           my $setparamstack = {};
168              
169             # all the tags that need NAMEs:
170 0           my %need_names = map { $_ => 1 }
  0            
171             qw(TMPL_SET);
172              
173             # initilize the lineno counter
174 0           my $lineno = 1;
175              
176             # now split up template on '<', leaving them in
177 0           my @chunks = split(m/(?=<)/, $$text);
178              
179             # loop through chunks, filling up pstack
180 0           my $last_chunk = $#chunks;
181 0           for (my $chunk_number = 0;
182             $chunk_number <= $last_chunk;
183             $chunk_number++) {
184 0 0         next unless defined $chunks[$chunk_number];
185 0           my $chunk = $chunks[$chunk_number];
186              
187             # a general regex to match any and all TMPL_* tags
188 0 0         if ($chunk =~ /^<
189             (?:!--\s*)?
190             (
191             \/?[Tt][Mm][Pp][Ll]_
192             (?:
193             (?:[Ss][Ee][Tt])
194             )
195             ) # $1 => $which - start of the tag
196              
197             \s*
198              
199             # NAME attribute
200             (?:
201             (?:
202             [Nn][Aa][Mm][Ee]
203             \s*=\s*
204             )?
205             (?:
206             "([^">]*)" # $2 => double-quoted NAME value "
207             |
208             '([^'>]*)' # $3 => single-quoted NAME value
209             |
210             ([^\s=>]*) # $4 => unquoted NAME value
211             )
212             )?
213              
214             \s*
215              
216             (?:--)?>
217             (.*) # $5 => $post - text that comes after the tag
218             $/sx) {
219              
220 0           my $which = uc($1); # which tag is it
221              
222             # what name for the tag? undef for a /tag at most, one of the
223             # following three will be defined
224 0 0         my $name = defined $2 ? $2 : defined $3 ? $3 : defined $4 ? $4 : undef;
    0          
    0          
225              
226 0           my $post = $5; # what comes after on the line
227              
228             # allow mixed case in filenames, otherwise flatten
229 0 0 0       $name = lc($name)
230             unless (not defined $name or $options->{case_sensitive});
231              
232             # croak if we need a name and didn't get one
233 0 0 0       if ($need_names{$which} and (not defined $name or not length $name)) {
      0        
234 0           croak("HTML::Template::Set->new() : No NAME given to a $which at".
235             "line ". $lineno. "\n");
236             }
237              
238             # parse tags
239 0 0         if ($which eq 'TMPL_SET') {
    0          
240 0 0         if ($options->{debug}) {
241 0           print STDERR "### HTML::Template::Set Debug ### line $lineno : ".
242             "$which start\n";
243             }
244              
245 0 0         if (@setstack > 0) {
246 0           croak("HTML::Template::Set->new() : Sorry, currently nested ".
247             "TMPL_SET tags are not permitted: at line ". $lineno);
248             }
249              
250 0 0         if (exists $setparamstack->{$name}) {
251 0           croak("HTML::Template::Set->new() : found duplicate TMPL_SET for ".
252             $name. " at line ". $lineno);
253             }
254              
255             # initilize the paramater
256 0           $setparamstack->{$name} = undef;
257              
258 0           push(@setstack, $name);
259              
260             # trim space before the TMPL_SET tag
261 0 0         if (@rtext > 0) {
262 0           $rtext[-1] =~ s/([^ \t]*)[ \t]*$/$1/;
263             }
264             } elsif ($which eq '/TMPL_SET') {
265 0 0         if ($options->{debug}) {
266 0           print STDERR "### HTML::Template::Set Debug ### line $lineno : $which end\n";
267             }
268              
269 0           $name = pop(@setstack);
270              
271 0 0         unless ($name) {
272 0           croak("HTML::Template::Set->new() : found with no ".
273             "matching at line ". $lineno);
274             }
275              
276 0 0         if ($post) {
277 0 0         if ($options->{chomp_after_set}) {
278 0           $post =~ s/^\s*(\n|\r\n)//;
279             } else {
280 0           $post =~ s/^[ ]*(\n|\r\n)//;
281             }
282             }
283             }
284              
285             # either add post to the setparamstack or the rtext
286 0 0         if (defined($post)) {
287 0           my ($set_name) = @setstack;
288 0 0         if ($set_name) {
289 0           $setparamstack->{$set_name} .= $post;
290             } else {
291 0           push(@rtext, $post);
292             }
293             }
294             } else { # just your ordinary markup
295             # either add the chunk to the setparamstack or the rtext
296             # push the rest and get next chunk
297 0 0         if (defined($chunk)) {
298 0           my ($set_name) = @setstack;
299 0 0         if ($set_name) {
300 0           $setparamstack->{$set_name} .= $chunk;
301             } else {
302 0           push(@rtext, $chunk);
303             }
304             }
305             }
306              
307             # count newlines in chunk and advance line count
308 0           $lineno += scalar(@{[$chunk =~ m/(\n)/g]});
  0            
309             } # next CHUNK
310              
311             # Set the text to our parsed text
312 0           $$text = join('', @rtext);
313              
314             # merge the setparamstack back to the main set_params
315 0 0         if (%{$setparamstack}) {
  0            
316 0           foreach my $param (keys %{$setparamstack}) {
  0            
317 0 0         if (exists $set_params->{$param}) {
318 0 0         if ($options->{set_order_bottom_up}) {
319 0           $set_params->{$param} = $setparamstack->{$param};
320             }
321             } else {
322 0           $set_params->{$param} = $setparamstack->{$param};
323             }
324             }
325             }
326              
327 0 0         unless (exists $set_params->{___loaded_set_params}) {
328 0           $set_params->{___loaded_set_params} = TRUE;
329             }
330              
331 0           return;
332             }
333             # }}}
334              
335              
336             # Merge Functions {{{
337             sub _merge_set_params {
338 0     0     my ($self) = @_;
339 0           my $set_params = $self->{options}->{_set_params};
340 0           my $param_map = $self->{param_map};
341              
342 0 0 0       return unless (defined $set_params and ref $set_params eq 'HASH'
      0        
343             and %$set_params);
344              
345 0           foreach my $key (keys %{$set_params}) {
  0            
346 0 0         next if ($key eq '___loaded_set_params');
347              
348 0           my $value = $set_params->{$key};
349 0           my $name = $self->_initilize_set_param($key);
350              
351             # Check the TMPL_SET tag for TMPL_VAR's initilizing them and replacing
352             # them with a smaller string [#SVAR #].
353 0           $value =~ s//$self->_initilize_set_param($1, 1)/ge;
  0            
354              
355             # Set the VAR (this will not overload any $tmpl->param calls in the
356             # script as its run before any of them are made.
357 0           ${$param_map->{$name}} = $value;
  0            
358             }
359             }
360              
361             sub _initilize_set_param {
362 0     0     my ($self, $param, $tag) = @_;
363 0           my $param_map = $self->{param_map};
364              
365 0 0         if (exists $param_map->{$param}) {
366 0           my $var = $param_map->{$param};
367 0 0         unless (ref $var eq 'HTML::Template::VAR') {
368 0           croak("HTML::Template->new() : Already used param name ". $param.
369             " as a TMPL_LOOP, found in a TMPL_SET");
370             }
371             } else {
372             # Make a new VAR
373 0           $param_map->{$param} = HTML::Template::VAR->new();
374             }
375              
376 0 0         return ($tag) ?
377             '[#SVAR '. $param. '#]' : $param;
378             }
379              
380             sub _merge_env_params {
381 0     0     my ($self) = @_;
382 0           my $param_map = $self->{param_map};
383              
384 0           foreach my $key (keys %ENV) {
385 0           my $name;
386 0 0 0       if (exists $self->{options}->{case_sensitive}
387             and $self->{options}->{case_sensitive}) {
388 0 0 0       $name = (exists $self->{options}->{env_names_to_lower_case}
389             and $self->{options}->{env_names_to_lower_case}) ?
390             'env_'. lc($key) : 'ENV_'. $key;
391             } else {
392 0           $name = 'env_'. lc($key);
393             }
394 0 0         if (exists $param_map->{$name}) {
395 0           my $var = $param_map->{$name};
396 0 0         if (ref $var eq 'HTML::Template::VAR') {
397 0           ${$param_map->{$name}} = $ENV{$key};
  0            
398             } else {
399 0           croak("HTML::Template->new() : Already used param name ". $name.
400             " as a TMPL_LOOP, while associating ENV");
401             }
402             }
403             }
404             }
405             # }}}
406              
407              
408             # Overloaded Param Function {{{
409             sub param {
410 0     0 1   my ($self, @args) = @_;
411              
412 0 0         if (@args == 1) {
413 0           return $self->_get_translated_set_tag($args[0]);
414             } else {
415 0           return $self->SUPER::param(@args);
416             }
417             }
418              
419             sub _get_translated_set_tag {
420 0     0     my ($self, $param) = @_;
421 0           my $options = $self->{options};
422 0           my $set_params = $options->{_set_params};
423              
424 0 0 0       unless (exists $options->{case_sensitive} and $options->{case_sensitive}) {
425 0           $param = lc($param);
426             }
427              
428 0           my $value = $self->SUPER::param($param);
429              
430             # dont bother translating anything but set params
431 0 0         return $value unless (exists $set_params->{$param});
432              
433             # translate the VAR tags in the SET
434 0 0         if ($value) {
435 0           $value =~
436 0           s/\Q[#SVAR \E(.+?)\Q#]\E/$self->_get_param_in_set($param, $1)/ge;
437             }
438              
439 0           return $value;
440             }
441              
442             sub _get_param_in_set {
443 0     0     my ($self, $set_name, $param) = @_;
444 0           my $options = $self->{options};
445 0           my $param_map = $self->{param_map};
446              
447 0 0         return undef unless ($param);
448              
449 0 0         if ($set_name eq $param) {
450 0           croak("HTML::Template::Set : Cannot have TMPL_VAR within ".
451             "TMPL_SET of the same name: ". $set_name);
452             }
453              
454 0 0         if (exists $param_map->{$param}) {
455 0 0         return (${$param_map->{$param}}) ? ${$param_map->{$param}} : '';
  0            
  0            
456             } else {
457 0 0 0       if ($options->{associate_env} and $param =~ /^env_(.+?)$/i) {
458 0           my $env = $1;
459 0 0         if ($env) {
460 0           return $ENV{uc($env)};
461             }
462             }
463 0           croak("HTML::Template::Set : Tried to set non-existent ".
464             "TMPL_VAR: ". $param. " in TMPL_SET: ". $set_name.
465             " (this should never occur.. hmmm)");
466             }
467             }
468             # }}}
469              
470              
471             # Overloaded Output Function {{{
472             # for filling in TMPL_VAR's in params (put there via TMPL_SET)
473             sub output {
474 0     0 1   my ($self, @args) = @_;
475 0           my $parse_stack = $self->{parse_stack};
476 0           my $options = $self->{options};
477 0           my $set_params = $options->{_set_params};
478              
479             # pull set_params out of the parse_stack for cache mode so HTML::Template
480             # doesnt try and process them.
481 0 0         if ($options->{cache}) {
482 0           pop @$parse_stack;
483             }
484              
485             # merge the ENV hash params as VAR's
486 0 0 0       if (exists $self->{options}->{associate_env} and
487             $self->{options}->{associate_env}) {
488 0           $self->_merge_env_params();
489             }
490              
491 0 0         if (ref $set_params eq 'HASH') {
492 0           foreach my $name (keys %{$set_params}) {
  0            
493 0 0         next if ($name eq '___loaded_set_params');
494              
495             # looks silly, but sub param is overloaded to translate vars in the
496             # set tag :)
497 0           $self->SUPER::param( $name => $self->param($name) );
498             }
499             }
500              
501 0           my $output = $self->SUPER::output(@args);
502              
503 0 0         if ($options->{cache}) {
504 0           push @$parse_stack, $set_params;
505             }
506              
507 0           return $output;
508             }
509              
510              
511             # }}}
512              
513              
514             # Overloaded Cache Function {{{
515             sub _commit_to_cache {
516 0     0     my ($self, @args) = @_;
517 0           my $parse_stack = $self->{parse_stack};
518 0           my $options = $self->{options};
519              
520 0           push @$parse_stack, $options->{_set_params};
521              
522 0 0         if ($options->{debug}) {
523 0           print STDERR "### HTML::Template::Set Debug ### commited set params to ".
524             "cache\n";
525             }
526              
527 0           return $self->SUPER::_commit_to_cache(@args);
528             }
529             # }}}
530              
531             1;
532              
533             __END__