File Coverage

blib/lib/Template/Plugin/Next.pm
Criterion Covered Total %
statement 60 68 88.2
branch 11 18 61.1
condition 5 10 50.0
subroutine 10 12 83.3
pod 4 4 100.0
total 90 112 80.3


line stmt bran cond sub pod time code
1             package Template::Plugin::Next;
2              
3 2     2   162209 use warnings;
  2         7  
  2         75  
4 2     2   13 use strict;
  2         4  
  2         80  
5              
6 2     2   13 use base qw( Template::Plugin );
  2         9  
  2         2916  
7 2     2   8136 use Template::Plugin ();
  2         6  
  2         36  
8 2     2   1162 use Template::Exception ();
  2         3974  
  2         40  
9              
10 2     2   15 use File::Spec ();
  2         3  
  2         35  
11              
12 2     2   62 use 5.006;
  2         6  
  2         1893  
13              
14             =head1 NAME
15              
16             Template::Plugin::Next - include the 'next' template file with identical relative path
17              
18             =head1 VERSION
19              
20             Version 0.03
21              
22             =cut
23              
24             our $VERSION = '0.03';
25              
26              
27             =head1 SYNOPSIS
28              
29             This is a plugin for the Template Toolkit distribution that allows the inclusion of template files with identical relative paths like the present template. Those templates are 'hidden' by the present template, because their respective INCLUDE_PATH entries are dominated by the one of the including template.
30              
31             The functionality provided by this plugin might come handy in multi skin situations where default templates are selectively redefined by a skin using a dominating INCLUDE_PATH entry for the skin and identical relative paths to the templates as with the default templates - thus hiding them.
32              
33             The Next-plugin allows to include the dominated default templates from inside the hiding template in order to decorate the default template or include a parameterized version of it.
34              
35             Example:
36              
37             # We assume: INCLUDE_PATH='/templates/c:/templates/b:/templates/a'
38              
39             # template a/test.tt (note this template accepts a "parameter" called 'repeat'):
40             [% repeat = repeat || 1; 'a' | repeat(repeat) %]
41            
42             # template b/test.tt:
43             b
44             [% USE Next; Next.include( repeat => 3 ); %]
45             b
46            
47             # template c/test.tt:
48             c
49             [% USE Next; Next.include(); %]
50             c
51              
52             # a call to template test.tt
53             [% INCLUDE test.tt %]
54            
55             # would yield something like the following (with POST_CHOMP set to 1):
56            
57             c
58             b
59             aaa
60             b
61             c
62              
63              
64             =head1 EXPORT
65              
66             Exported stash variable: Next_
67              
68             =head1 METHODS
69              
70             =cut
71              
72             sub new {
73             # called as Template::Plugin::Next->new($context)
74 4     4 1 35436 my ($class, $context, %params) = @_;
75              
76 4         21 my $self =
77             bless {
78             _CONTEXT => $context
79             }, $class;
80              
81 4         13 $self;
82             }
83              
84             sub error {
85 0     0 1 0 my $proto = shift;
86 0 0       0 die( ref( $_[0] ) ? @_ : do { $proto->SUPER::error(@_); Template::Exception->new( 'Next', $proto->SUPER::error ) } );
  0         0  
  0         0  
87             }
88              
89             =head2 process
90              
91             Includes the 'next' dominated template with an identical relative path like the one this plugin method is called from. It accepts named parameters like its TT directive counterpart PROCESS that will result in stash variables.
92              
93             =cut
94              
95             sub process {
96 2     2 1 77 my $self = shift;
97 2         3 my $params = shift;
98 2   50     15 my $localise = shift || 0;
99 2         13 my $context = $self->{_CONTEXT};
100 2         8 my $stash = $context->stash();
101              
102 2         50 my $name = $stash->get( 'component.name' ); # template file path not file name
103              
104 2   33     44 my $providers = $context->{ PREFIX_MAP }->{ default } || $context->{ LOAD_TEMPLATES };
105              
106 2         5 foreach my $provider ( grep { ref( $_ ) eq 'Template::Provider' } @$providers ) {
  2         9  
107             # we know only how to handle the standard behaviour of providers
108              
109 2         4 my $rel_path = $name;
110 2         3 my @include_paths = @{$provider->paths};
  2         9  
111             # include paths are returned in a list even if
112             # multiple include paths have been specified via a colon separated scalar
113              
114 2 50       71 $self->error( 'Not applicable. There is no second include path!' ) if scalar(@include_paths) == 1;
115            
116 2 100       29 if ( File::Spec->file_name_is_absolute( my $abs_path = $rel_path ) ) {
117             # this is for subsequent calls to NEXT
118 1 50 33     35 ( my( $include_path ), $rel_path ) =
119             @{
120 1         2 ( ( $stash->get( 'Next_' ) || $self->error( 'Could not find Next_ stash entry!' ) )->{$abs_path}
121             ||
122             $self->error( "Could not find abs path $abs_path in Next_ map!" )
123             )
124             };
125              
126 1         4 while ( @include_paths ) {
127 3 100       12 last if ( shift @include_paths ) eq $include_path;
128             }
129 1         3 $rel_path =~ s/^\///;
130              
131             } else {
132             # this is for the initial call to NEXT
133 1         5 while ( @include_paths ) {
134 2 100       7 last if scalar stat( _concat_path( (shift @include_paths ), $rel_path ) );
135             }
136             }
137              
138 2         7 foreach my $include_path ( @include_paths ) {
139 2         6 my $path = _concat_path( $include_path, $rel_path );
140              
141 2 50       57 if( scalar stat( $path ) ) {
142 2         3 my $template;
143             {
144             # make abs. template paths local to this template retrieval
145 2         3 local $provider->{ABSOLUTE} = 1;
  2         7  
146 2         10 $template = $context->template($path);
147             }
148 2 50       6885 defined $template || $self->error( $context->error );
149 2   100     35 my $map = $stash->get( 'Next_' ) || {};
150 2         22 $map->{$path} = [ $include_path, $rel_path ];
151 2         19 $stash->set( 'Next_' => $map );
152 2         22 return $context->process( $template, $params, $localise );
153             }
154             }
155             }
156              
157 0         0 $self->error( "No 'Next' template!" );
158             }
159              
160             =head2 include
161              
162             Same as process() but with stash localisation.
163              
164             =cut
165              
166             sub include {
167 0     0 1 0 my $self = shift;
168 0         0 $self->process( @_, 1 );
169             }
170              
171             sub _concat_path {
172 4     4   9 my ( $base_path, $append_dirs ) = @_;
173             # $base_dir: base path (no filename) as string
174             # $append_dirs: directories to append as string or an array reference
175            
176 4         32 my ($base_volume, $base_directories, $base_file) = File::Spec->splitpath( $base_path, 1 );
177 0           File::Spec->catpath(
178             $base_volume,
179             File::Spec->catdir(
180             File::Spec->splitdir( $base_directories ),
181 4 50       147 ( ref($append_dirs) ? @{$append_dirs} : File::Spec->splitdir( $append_dirs ) )
182             )
183             ,
184             $base_file
185             );
186             }
187              
188             =head1 CAVEATS
189              
190             =over 4
191              
192             =item * INCLUDE_PATH stability is required during a single call to Template::process().
193              
194             =item * Only template providers of class Template::Provider are supported.
195              
196             =back
197              
198             =head1 AUTHOR
199              
200             Alexander Kühne, C<< >>
201              
202             =head1 BUGS
203              
204             Please report any bugs or feature requests to C, or through
205             the web interface at L. I will be notified, and then you'll
206             automatically be notified of progress on your bug as I make changes.
207              
208              
209              
210              
211             =head1 SUPPORT
212              
213             You can find documentation for this module with the perldoc command.
214              
215             perldoc Template::Plugin::Next
216              
217              
218             You can also look for information at:
219              
220             =over 4
221              
222             =item * RT: CPAN's request tracker
223              
224             L
225              
226             =item * AnnoCPAN: Annotated CPAN documentation
227              
228             L
229              
230             =item * CPAN Ratings
231              
232             L
233              
234             =item * Search CPAN
235              
236             L
237              
238             =back
239              
240             =head1 COPYRIGHT & LICENSE
241              
242             Copyright 2010 Alexander Kühne, all rights reserved.
243              
244             This program is free software; you can redistribute it and/or modify it
245             under the same terms as Perl itself.
246              
247              
248             =cut
249              
250             1; # End of Template::Plugin::Next