File Coverage

blib/lib/Templ/Template.pm
Criterion Covered Total %
statement 152 176 86.3
branch 25 48 52.0
condition 5 15 33.3
subroutine 36 40 90.0
pod 0 18 0.0
total 218 297 73.4


line stmt bran cond sub pod time code
1             package Templ::Template;
2              
3 1     1   4 use Exporter;
  1         2  
  1         53  
4             push @ISA, 'Exporter';
5             @EXPORT = qw(add_header add_tag);
6              
7 1     1   4 use strict;
  1         2  
  1         20  
8 1     1   4 use warnings;
  1         2  
  1         25  
9              
10 1     1   5 use Carp qw(carp croak confess);
  1         7  
  1         64  
11 1     1   4 use Scalar::Util qw(blessed openhandle);
  1         2  
  1         91  
12 1     1   762 use File::Spec::Functions qw(rel2abs);
  1         810  
  1         60  
13 1     1   765 use IO::File;
  1         10044  
  1         137  
14 1     1   975 use Data::Dumper;
  1         10079  
  1         63  
15 1     1   589 use Templ::Parser::Print;
  1         2  
  1         25  
16 1     1   475 use Templ::Parser::Return;
  1         2  
  1         25  
17 1     1   5 use Templ::Util qw(expand_isa);
  1         1  
  1         45  
18 1     1   5 use Digest::MD5 qw(md5);
  1         1  
  1         65  
19 1     1   5 use overload '""' => \&as_perl, '&{}' => \&as_sub;
  1         1  
  1         7  
20              
21             my %templ_cache = ();
22              
23             ##############################################################################
24             # Subclass import functions
25              
26             sub add_header ($) {
27              
28 1     1 0 3 my $target_class = caller; # The class name of the template we are
29             # adding a header to
30 1         2 my $header = shift; # Header to associate with the template
31              
32 1     1   94 no strict 'refs';
  1         2  
  1         25  
33 1     1   5 no warnings 'once';
  1         1  
  1         80  
34 1         1 push @{ $target_class . '::TEMPL_HEADERS' }, $header;
  1         7  
35              
36             }
37              
38             sub add_tag ($$;%) {
39              
40 6     6 0 10 my $target_class = caller; # The class name of the template we are
41             # adding a tag to
42 6         9 my $char = shift; # Character to associate with the tag
43 6         9 my $tag_class = shift; # The tag's class name we're adding to
44             # the calling template
45              
46 1     1   5 no strict 'refs';
  1         1  
  1         276  
47 6         356 eval "
48             require Templ::Tag::$tag_class;
49             require $tag_class;
50             ";
51 6 50       40 if ( scalar keys %{ 'Templ::Tag::' . $tag_class . '::' } ) {
  6 0       28  
52 6         12 $tag_class = 'Templ::Tag::' . $tag_class;
53             }
54 0         0 elsif ( not scalar keys %{ $tag_class . '::' } ) {
55 0         0 croak "Unable to resolve Templ::Tag class $tag_class";
56             }
57              
58 6         9 push @{ $target_class . '::TEMPL_TAGS' },
  6         51  
59             $tag_class->new( 'char' => $char, @_ );
60              
61             }
62              
63             ##############################################################################
64             # Class methods
65              
66             sub new {
67              
68 4     4 0 27 my $class = shift; # Might be a partial class if called from get()
69 4 0 33     16 if ( not defined $class || ref $class || $class !~ m/^(\w+\:\:)*\w+$/ ) {
      33        
70 0         0 croak "Can only be called as __PACKAGE__->new";
71             }
72 4         273 eval "require Templ::Template::$class; require $class;";
73 1     1   6 no strict 'refs';
  1         2  
  1         132  
74 4 100       16 if ( scalar keys %{ 'Templ::Template::' . $class . '::' } ) {
  4 50       23  
75 3         8 $class = 'Templ::Template::' . $class;
76             }
77 1         7 elsif ( not scalar keys %{ $class . '::' } ) {
78 0         0 croak "Unable to resolve Templ::Template class $class";
79             }
80              
81 4         12 my $self = bless {}, $class;
82 4         27 $self->resolve_source( @_ );
83              
84 4         24 return $self;
85             }
86              
87             # Creates a new Templ::Template* object of the passed type
88             sub get {
89 3     3 0 4 my $class = shift;
90 1     1   4 no warnings 'uninitialized';
  1         2  
  1         246  
91 3         41 my $id = md5( join "\n", ( caller(1), @_ ) );
92 3 50       13 if ( not defined $templ_cache{$id} ) { $templ_cache{$id} = new( @_ ); }
  3         87  
93 3         13 return $templ_cache{$id};
94             }
95              
96             ##############################################################################
97             # Hybrid class/object methods
98              
99             sub tags {
100 4   33 4 0 13 my $class = ref( $_[0] ) || $_[0];
101 4         8 my @tags = ();
102 4         11 foreach my $this_class ( expand_isa($class) ) {
103 1     1   6 no strict 'refs';
  1         2  
  1         122  
104 15 100       18 next unless scalar( @{ $this_class . '::TEMPL_TAGS' } );
  15         57  
105 7         10 push @tags, @{ $this_class . '::TEMPL_TAGS' };
  7         27  
106             }
107 4 50       20 return wantarray ? @tags : \@tags;
108             }
109              
110             sub header {
111 4   33 4 0 16 my $class = ref( $_[0] ) || $_[0];
112 4         9 my @headers = ();
113 4         12 foreach my $this_class ( expand_isa($class) ) {
114 1     1   4 no strict 'refs';
  1         2  
  1         982  
115 15 100       17 next unless scalar( @{ $this_class . '::TEMPL_HEADERS' } );
  15         69  
116 3         5 push @headers, @{ $this_class . '::TEMPL_HEADERS' };
  3         10  
117             }
118 4         12 return join '', map {"$_\n"} @headers;
  3         17  
119             }
120              
121             ##############################################################################
122             # Object methods
123              
124             # Get the template contents of the object
125             sub templ_code {
126 4     4 0 8 my $self = shift;
127 4         15 return $self->{'templ_code'};
128             }
129              
130             sub resolve_source {
131            
132 4     4 0 7 my $self = shift;
133 4         8 my $source = shift;
134            
135 4 50       9 return unless defined $source;
136            
137 4         21 $self->clear;
138              
139 4 100 33     25 if (not ref $source) {
    50          
    100          
    50          
140 2         6 $self->{'templ_code'} = $source;
141             }
142             elsif (ref($source) eq 'ARRAY') {
143 0         0 $self->{'templ_code'} = join '', @{$source};
  0         0  
144             }
145             elsif ( ref($source) eq 'SCALAR' ) {
146 1 50       31 croak "Unable to stat file '$source'" unless -f $$source;
147 1         11 my $fh = IO::File->new($$source, 'r');
148 1 50       128 defined($fh) || croak "Unable to open file ".$$source.": $!";
149 1         33 $self->{'templ_code'} = join '', $fh->getlines;
150 1         76 $fh->close;
151             }
152 0         0 elsif ( openhandle($source) || eval { $source->can('getline') }) {
153 1         5 local $/ = undef;
154 1         23 $self->{'templ_code'} = <$source>;
155 1         24 close $source;
156             }
157             else {
158 0         0 croak "Unrecognized Templ source parameter: ".Dumper($source);
159             }
160             }
161              
162             # Returns an eval-able string perl block which returns the output of the
163             # template
164             sub as_perl {
165 1     1 0 2 my $self = shift;
166 1 50       5 if ( not defined $self->{'as_perl'} ) {
167 1         6 $self->{'as_perl'} = '{'
168             . Templ::Parser::Return->new()->parse($self)
169             . '}';
170             }
171 1         57 return $self->{'as_perl'};
172             }
173              
174             # Returns an eval-able string perl block which returns the output of the
175             # template, with newline-spanning strings split into multiple perl code lines
176             sub as_pretty_perl {
177 0     0 0 0 my $self = shift;
178 0 0       0 if ( not defined $self->{'as_pretty_perl'} ) {
179 0         0 $self->{'as_pretty_perl'} = '{'
180             . Templ::Parser::Return->new( 'prettyify' => 1 )->parse($self)
181             . '}';
182             }
183 0         0 return $self->{'as_pretty_perl'};
184             }
185              
186             # Returns a code reference to a block-based handler for the template
187             sub as_sub {
188 3     3 0 4 my $self = shift;
189 3 100       13 if ( not defined $self->{'as_sub'} ) {
190 2         4 my $sub;
191 1     1   7 eval '$sub = sub {' . Templ::Parser::Return->new()->parse($self) . '}';
  1         1  
  1         163  
  2         17  
192 2 50       15 $@ && croak $@;
193 2         74 $self->{'as_sub'} = $sub;
194             }
195 3         86 return $self->{'as_sub'};
196             }
197              
198             # Runs the return handler on the passed params... in other words, executes
199             # the template and returns the results
200             sub render {
201 3     3 0 1581 my $self = shift;
202 3         14 $self->as_sub->(@_);
203             }
204              
205             # Runs the print handler on the passed params... in other words it executes
206             # the template in such a way that the output is sent to the select()ed FH
207             sub as_print {
208 0     0 0 0 my $self = shift;
209 0 0       0 if ( not defined $self->{'as_print'} ) {
210 0         0 $self->{'as_print'} = '{'
211             . Templ::Parser::Print->new()->parse($self)
212             . '}';
213             }
214 0         0 return $self->{'as_print'};
215             }
216              
217             # Returns an eval-able string perl block which returns the output of the
218             # template, with newline-spanning strings split into multiple lines
219             sub as_pretty_print {
220 0     0 0 0 my $self = shift;
221 0 0       0 if ( not defined $self->{'as_pretty_print'} ) {
222 0         0 $self->{'as_pretty_print'} = '{'
223             . Templ::Parser::Print->new( 'prettyify' => 1 )->parse($self)
224             . '}';
225             }
226 0         0 return $self->{'as_pretty_print'};
227             }
228              
229             # Returns a code reference to a printing handler for this template
230             sub as_print_sub {
231 1     1 0 3 my $self = shift;
232 1 50       6 if ( not defined $self->{'as_print_sub'} ) {
233 1         3 my $sub;
234 1     1   616 eval '$sub = sub {' . Templ::Parser::Print->new()->parse($self) . '}';
  1         3  
  1         132  
  1         15  
235 1 50       15 $@ && croak $@;
236 1         73 $self->{'as_print_sub'} = $sub;
237             }
238 1         30 return $self->{'as_print_sub'};
239             }
240              
241             # Prints the output of this template with the passed params
242             sub run {
243 1     1 0 2142 my $self = shift;
244 1         7 $self->as_print_sub->(@_);
245             }
246              
247             sub clear {
248 4     4 0 7 my $self = shift;
249 4         7 delete $self->{$_} foreach grep { m/^as_/ } keys %{$self};
  0         0  
  4         147  
250             }
251              
252             sub dump {
253 0     0 0   my $self = shift;
254 0           local $Data::Dumper::Deparse = 1;
255 0           return Data::Dumper->Dump( [$self], ['template'] );
256             }
257              
258             1;