| 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; |