File Coverage

blib/lib/Templ/Spec.pm
Criterion Covered Total %
statement 42 172 24.4
branch 0 68 0.0
condition 0 27 0.0
subroutine 14 34 41.1
pod 0 16 0.0
total 56 317 17.6


line stmt bran cond sub pod time code
1             package Templ::Spec;
2              
3 1     1   3 use strict;
  1         1  
  1         27  
4 1     1   2 use warnings;
  1         2  
  1         19  
5              
6 1     1   3 use Carp qw(carp croak confess);
  1         1  
  1         50  
7 1     1   4 use Scalar::Util qw(openhandle);
  1         1  
  1         55  
8 1     1   380 use IO::File;
  1         6478  
  1         88  
9 1     1   498 use Data::Dumper;
  1         6170  
  1         43  
10 1     1   466 use Class::ISA;
  1         1679  
  1         20  
11 1     1   4 use Templ;
  1         1  
  1         27  
12 1     1   4 use overload '""' => \&as_perl, '&{}' => \&as_sub;
  1         1  
  1         6  
13              
14             my $PKG = __PACKAGE__;
15              
16             sub _subclass_loaded ($) {
17 0     0     my $class = shift;
18 1     1   58 no strict 'refs';
  1         1  
  1         622  
19 0           my %pkg = %{$class.'::'};
  0            
20 0 0         return 1 if exists $pkg{'TEMPL_TAGS'};
21 0 0         return 1 if exists $pkg{'TEMPL_HEADERS'};
22 0           return 0;
23             }
24              
25             # Resolves, checks and loads a subclass of Templ::Spec
26             sub _load_subclass ($) {
27 0     0     my $class = shift;
28 0           my $caller = $class.'->'.caller();
29 0 0         defined($class)
30             || croak "Undefined class in call to $caller";
31 0 0 0       ref($class) && $class->isa($PKG)
32             && croak "Class method $caller called as object method";
33 0 0         ref($class)
34             && croak "Unknown parameter found in class position for $caller";
35 0 0         $class =~ m/^(\w+\:\:)*\w+$/
36             || croak "Invalid first string parameter: not a class for $caller";
37 0 0         $class eq $PKG
38             && croak "$PKG must be subclassed!";
39 0 0         unless ( _subclass_loaded($class) ) {
40 0           eval "require $class;";
41 0 0         unless ( _subclass_loaded($class) ) {
42 0           croak "Unable to load $PKG subclass $class (have you not added headers or tags?)";
43             }
44             }
45 0 0         $class->isa($PKG)
46             || croak "Class $class does not inherit from $PKG";
47 0           return $class;
48             }
49              
50             # Attempts to determine the non-Templ context from which a call came
51             sub _ext_caller () {
52 0     0     my $context = 0;
53 0           while ( $context <= 5 ) {
54 0           my @info = caller($context);
55 0 0         my $pkg = defined($info[0]) ? $info[0] : '';
56 0 0 0       next if !$pkg || $pkg eq 'Templ::Spec' || $pkg eq 'Templ';
      0        
57 0 0         return wantarray ? @info : $pkg;
58 0           } continue { ++$context }
59 0           croak "Deep frame stack for _ext_caller";
60             }
61              
62             sub _load_templ_code_from_file ($$) {
63 0     0     my $self = shift;
64 0           my $fh;
65 0 0         if (not ref $_[0]) {
66             # We were passed a filename
67 0           my $filename = shift;
68 0 0         croak "Unable to stat file '$filename'" unless -f $filename;
69 0           $fh = IO::File->new($filename, 'r');
70 0 0         defined($fh) || croak "Unable to open file ".$filename.": $!";
71             }
72             else {
73             # We were passed a filehandle
74 0           $fh = shift;
75             }
76 0 0 0       unless ( openhandle($fh) || eval { $fh->can('getline') } ) {
  0            
77 0           croak "$PKG filehandle parameter doesn't behave like a filehandle";
78             }
79 0           local $/ = undef;
80 0           $self->{'templ_code'} = <$fh>;
81 0           close $fh;
82             }
83              
84             ##############################################################################
85             # Class methods
86             #
87             # These use used by consumers of Templ::Spec's (packages and other code)
88             # in order to instantiate templates, or import methods which in turn
89             # instantiate templates
90              
91             sub templ {
92 0     0 0   my $class = _load_subclass(shift @_); # Might be a partial class if called from get()
93 0           my $self = bless {}, $class;
94 0 0         if (scalar(@_) == 1) {
    0          
95 0   0       my $source = shift || croak "Must provide source for ".caller();
96 0 0         if (ref $source) {
97 0           $self->_load_templ_code_from_file($source);
98             }
99             else {
100 0           $self->{'templ_code'} = $source;
101             }
102             }
103             elsif (scalar(@_) == 2) {
104 0   0       my $type = shift || croak "Must provide type for ".caller();
105 0   0       my $source = shift || croak "Must provide source for ".caller();
106 0 0         if ($type eq 'file') {
107 0           $self->_load_templ_code_from_file($source);
108             }
109             else {
110 0           croak "Unknown $PKG source type $type";
111             }
112             }
113             else {
114 0           croak "Incorrect new $class parameters: ".Dumper(\@_);
115             }
116 0           return $self;
117             }
118              
119             # Alias ->templ() to ->new()
120             *new = *templ;
121              
122             sub templ_method ($$$;$) {
123             #print Dumper(\@_);
124 0     0 0   my $class = _load_subclass(shift @_);
125 0           my $method_name = shift;
126 0           my $templ = $class->templ( @_ );
127 1     1   4 no strict 'refs';
  1         1  
  1         74  
128 0           *{ _ext_caller().'::'.$method_name } = $templ->as_method();
  0            
129 0           return $templ;
130             }
131              
132             sub templ_sub ($$$;$) {
133 0     0 0   my $class = _load_subclass(shift @_);
134 0           my $sub_name = shift;
135 0           my $templ = $class->templ( @_ );
136 1     1   3 no strict 'refs';
  1         1  
  1         78  
137 0           *{ _ext_caller().'::'.$sub_name } = $templ->as_sub();
  0            
138 0           return $templ;
139             }
140              
141             ##############################################################################
142             # Hybrid class/object methods
143              
144             sub tags {
145 0   0 0 0   my $class = ref( $_[0] ) || $_[0];
146 0           my @tags = ();
147 0           foreach my $this_class ( Class::ISA::self_and_super_path($class) ) {
148 1     1   3 no strict 'refs';
  1         1  
  1         81  
149 0 0         next unless scalar( @{ $this_class.'::TEMPL_TAGS' } );
  0            
150 0           push @tags, @{ $this_class.'::TEMPL_TAGS' };
  0            
151             }
152 0 0         return wantarray ? @tags : \@tags;
153             }
154              
155             sub header {
156 0   0 0 0   my $class = ref( $_[0] ) || $_[0];
157 0           my @headers = ();
158 0           foreach my $this_class ( Class::ISA::self_and_super_path($class) ) {
159 1     1   4 no strict 'refs';
  1         1  
  1         503  
160 0 0         next unless scalar( @{ $this_class.'::TEMPL_HEADERS' } );
  0            
161 0           push @headers, @{ $this_class.'::TEMPL_HEADERS' };
  0            
162             }
163 0           return join '', map {"$_\n"} @headers;
  0            
164             }
165              
166             ##############################################################################
167             # Object methods
168              
169             # Get the template contents of the object
170             sub templ_code {
171 0     0 0   my $self = shift;
172 0           return $self->{'templ_code'};
173             }
174              
175             # Returns an eval-able string perl block which returns the output of the
176             # template
177             sub as_perl {
178 0     0 0   my $self = shift;
179 0 0         if ( not defined $self->{'as_perl'} ) {
180 0           $self->{'as_perl'} = '{'
181             . Templ::Parser::Return->new()->parse($self)
182             . '}';
183             }
184 0           return $self->{'as_perl'};
185             }
186              
187             # Returns an eval-able string perl block which returns the output of the
188             # template, with newline-spanning strings split into multiple perl code lines
189             sub as_pretty_perl {
190 0     0 0   my $self = shift;
191 0 0         if ( not defined $self->{'as_pretty_perl'} ) {
192 0           $self->{'as_pretty_perl'} = '{'
193             . Templ::Parser::Return->new( 'prettyify' => 1 )->parse($self)
194             . '}';
195             }
196 0           return $self->{'as_pretty_perl'};
197             }
198              
199             # Returns a code reference to a block-based handler for the template
200             sub as_sub {
201 0     0 0   my $self = shift;
202 0 0         if ( not defined $self->{'as_sub'} ) {
203 0           my $sub;
204 0           eval '$sub = sub {'
205             . Templ::Parser::Return->new()->parse($self)
206             . '}';
207 0 0         $@ && croak $@;
208 0           $self->{'as_sub'} = $sub;
209             }
210 0           return $self->{'as_sub'};
211             }
212              
213             # Returns a code reference to a block-based handler for the template
214             sub as_method {
215 0     0 0   my $self = shift;
216 0 0         if ( not defined $self->{'as_method'} ) {
217 0           my $method;
218 0           eval '$method = sub {'
219             . 'my $self = shift; '
220             . Templ::Parser::Return->new()->parse($self)
221             . '}';
222 0 0         $@ && croak $@;
223 0           $self->{'as_method'} = $method;
224             }
225 0           return $self->{'as_method'};
226             }
227              
228             # Runs the print handler on the passed params... in other words it executes
229             # the template in such a way that the output is sent to the select()ed FH
230             sub as_print {
231 0     0 0   my $self = shift;
232 0 0         if ( not defined $self->{'as_print'} ) {
233 0           $self->{'as_print'} = '{'
234             . Templ::Parser::Print->new()->parse($self)
235             . '}';
236             }
237 0           return $self->{'as_print'};
238             }
239              
240             # Returns an eval-able string perl block which returns the output of the
241             # template, with newline-spanning strings split into multiple lines
242             sub as_pretty_print {
243 0     0 0   my $self = shift;
244 0 0         if ( not defined $self->{'as_pretty_print'} ) {
245 0           $self->{'as_pretty_print'} = '{'
246             . Templ::Parser::Print->new( 'prettyify' => 1 )->parse($self)
247             . '}';
248             }
249 0           return $self->{'as_pretty_print'};
250             }
251              
252             # Returns a code reference to a printing handler for this template
253             sub as_print_sub {
254 0     0 0   my $self = shift;
255 0 0         if ( not defined $self->{'as_print_sub'} ) {
256 0           my $sub;
257 0           eval '$sub = sub {'
258             . Templ::Parser::Print->new()->parse($self)
259             . '}';
260 0 0         $@ && croak $@;
261 0           $self->{'as_print_sub'} = $sub;
262             }
263 0           return $self->{'as_print_sub'};
264             }
265              
266             # Runs the return handler on the passed params... in other words, executes
267             # the template and returns the results
268             sub render {
269 0     0 0   my $self = shift;
270 0           $self->as_sub->(@_);
271             }
272              
273             # Prints the output of this template with the passed params
274             sub run {
275 0     0 0   my $self = shift;
276 0           $self->as_print_sub->(@_);
277             }
278              
279             sub dump {
280 0     0 0   my $self = shift;
281 0           local $Data::Dumper::Deparse = 1;
282 0           return Data::Dumper->Dump( [$self], ['template'] );
283             }
284              
285             1;