File Coverage

blib/lib/Template/Plex.pm
Criterion Covered Total %
statement 140 173 80.9
branch 33 50 66.0
condition 17 33 51.5
subroutine 31 37 83.7
pod 15 21 71.4
total 236 314 75.1


line stmt bran cond sub pod time code
1             package Template::Plex;
2              
3 2     2   149056 use strict;
  2         7  
  2         58  
4 2     2   11 use warnings;
  2         3  
  2         103  
5              
6             our $VERSION = 'v0.6.2';
7 2     2   13 use feature qw;
  2         3  
  2         192  
8 2     2   14 no warnings "experimental";
  2         3  
  2         89  
9              
10              
11 2     2   3586 use Log::ger;
  2         102  
  2         10  
12 2     2   508 use Log::OK; #Allow control of logging from the command line
  2         3  
  2         13  
13              
14 2     2   18496 use Symbol qw;
  2         1664  
  2         125  
15              
16 2     2   13 use constant::more KEY_OFFSET=>0;
  2         4  
  2         10  
17              
18 2         20 use constant::more {plex_=>0, meta_=>1, args_=>2, sub_=>3,
19             package_=>4, init_done_flag_=>5, skip_=>6,
20             cache_=>7, slots_=>8, parent_=>9, default_result_=>10
21 2     2   254 };
  2         4  
22              
23 2     2   1229 use constant::more KEY_COUNT=>default_result_ - plex_ +1;
  2         5  
  2         7  
24              
25             #Template::Plex::Internal uses the field name constants so import it AFTER
26             #we define them
27 2     2   1111 use Template::Plex::Internal;
  2         6  
  2         56  
28              
29             our %top_level_cache;
30             sub new {
31 21     21 0 62 my ($package, $plex)=@_;
32 21         42 my $self=[];
33 21         47 $self->[plex_]=$plex;
34 21         46 $self->[cache_]={};
35 21         65 bless $self, $package;
36             }
37             sub get_cache {
38 0     0 0 0 $_[0][cache_];
39             }
40              
41             #Returns a template loaded and intialised
42             sub load {
43 21     21 1 3850 my ($self, $path, $vars, %opts)=@_;
44 21         34 my $template;
45 21 100       52 if(ref($self)){
46 6         10 Log::OK::TRACE and log_trace __PACKAGE__." instance load called for $path";
47 6         31 \my %fields=$self->args;
48              
49 6         17 my %options=$self->meta->%{qw}; #copy
50 6 100       42 $template=Template::Plex::Internal->new(\&Template::Plex::Internal::_prepare_template, $path, $vars?$vars:\%fields, %opts?%opts:%options);
    100          
51              
52             }
53             else{
54 15         24 Log::OK::TRACE and log_trace __PACKAGE__." class load called for $path";
55             #called on package
56 15         67 $template=Template::Plex::Internal->new(\&Template::Plex::Internal::_prepare_template, $path, $vars, %opts);
57              
58             }
59 19         79 $template->setup;
60 19         54 $template;
61             }
62              
63             #Returns a template which was already loaded can called from the callers position
64             #
65             #TODO: special case where the second argument is a hash ref or undef
66             # This indicates no id was specified so use implicit cache entry
67             # path must always be defined.
68             # eg
69             # cache undef, "path", .... ; #will use explicit cache key
70             # cache "path", {var}; #Use implicit cache key
71             # cache "path"; #Use implicit cache key
72             #
73             # This tidies up the common use case for cached templates
74             sub cache {
75 10     10 1 402 my $self=shift;
76 10         32 my @args=@_;
77              
78 10 50 33     58 if(@args ==1){
    50          
79             # Recalling implicit cache key with path only
80 0         0 unshift @args, undef;
81             }
82             elsif(defined($args[1]) and ref($args[1]) eq "HASH"){
83             # variables hash ref given, with implicit cache id
84 0         0 unshift @args, undef;
85             }
86             else{
87             # Expect explicit cache Id
88             }
89              
90 10         28 my ($id, $path, $vars, %opts)=@args;
91              
92             #my ($self, $id, $path, $vars, %opts)=@_;
93 10         16 Log::OK::TRACE and log_trace __PACKAGE__." cache: $path";
94 10   66     36 $id//=$path.join "", caller; #Set if undefined
95 10 100       24 if(ref($self)){
96 4 50       11 $self->[cache_]{$id} and return $self->[cache_]{$id};
97              
98 4         17 my $template=$self->load($path, $vars, %opts);
99 4   33     115 $self->[cache_]{$id}//=$template;
100             }
101             else{
102 6 100       24 $top_level_cache{$id} and return $top_level_cache{$id};
103              
104 5         16 my $template=$self->load($path, $vars, %opts);
105 5   33     33 $top_level_cache{$id}//=$template;
106             }
107             }
108              
109             #TODO: add parameter checking as per cache
110             sub immediate {
111 4     4 1 1713 Log::OK::TRACE and log_trace __PACKAGE__." immediate!!";
112            
113 4         8 my $self=shift;
114 4         12 my @args=@_;
115 4 50 33     34 if(@args ==1){
    50          
116             # Recalling implicit cache key with path only
117 0         0 unshift @args, undef;
118             }
119             elsif(defined($args[1]) and ref($args[1]) eq "HASH"){
120             # variables hash ref given, with implicit cache id
121 0         0 unshift @args, undef;
122             }
123             else{
124             # Expect explicit cache Id
125             }
126              
127 4         14 my ($id, $path, $vars, @opts)=@args;
128              
129              
130 4         5 Log::OK::TRACE and log_trace __PACKAGE__." immediate: $path";
131 4   33     35 $id//=$path.join "", caller; #Set if undefined
132            
133 4         14 my $template=$self->cache($id, $path, $vars, @opts);
134 4 50       16 return $template->render if $template;
135 0         0 "";
136              
137             }
138              
139             sub _plex_ {
140 0     0   0 $_[0][Template::Plex::plex_];
141             }
142              
143 8     8 1 34 sub meta :lvalue { $_[0][Template::Plex::meta_]; }
144              
145 9     9 1 24 sub args :lvalue{ $_[0][Template::Plex::args_]; }
146              
147 59     59 0 142 sub init_done_flag:lvalue{ $_[0][Template::Plex::init_done_flag_]; }
148              
149              
150             sub _render {
151             #sub in plex requires self as first argument
152 41     41   1082 return $_[0][sub_](@_);
153             }
154              
155             sub skip {
156 21     21 1 26 Log::OK::DEBUG and log_debug("Template::Plex: Skipping Template: ".$_[0]->meta->{file});
157 21         468 $_[0]->[skip_]->();
158             }
159              
160             #A call to this method will run the sub an preparation
161             #and immediately stop rendering the template
162             sub _init {
163 41     41   96 my ($self, $sub)=@_;
164            
165 41 100       410 return if $self->[init_done_flag_];
166 21         30 Log::OK::DEBUG and log_debug("Template::Plex: Initalising Template: ".$self->meta->{file});
167 21 50       119 unless($self->isa("Template::Plex")){
168             #if($self->[meta_]{package} ne caller){
169 0         0 Log::OK::ERROR and log_error("Template::Plex: init must only be called within a template: ".$self->meta->{file});
170 0         0 return;
171             }
172              
173 21         62 $self->pre_init;
174 21         448 $sub->();
175 21         57 $self->post_init;
176              
177 21         31 $self->[init_done_flag_]=1;
178 21         55 $self->skip;
179 0         0 ""; #Must return an empty string
180             }
181              
182       21 1   sub pre_init {
183              
184             }
185              
186       21 1   sub post_init {
187              
188             }
189       41 0   sub prefix {
190             }
191       19 0   sub postfix {
192             }
193              
194             #Execute the template in setup mode
195             sub setup {
196 21     21 0 56 my $self=shift;
197             #Test that the caller is not the template package
198 21         42 Log::OK::DEBUG and log_debug("Template::Plex: Setup Template: ".$self->meta->{file});
199 21 50       91 if($self->[meta_]{package} eq caller){
200             #Log::OK::ERROR and log_error("Template::Plex: setup must only be called outside a template: ".$self->meta->{file});
201             # return;
202             }
203 21         40 $self->[init_done_flag_]=undef;
204 21         73 $self->render(@_);
205            
206             #Check if an init block was used
207 21 50       56 unless($self->[init_done_flag_]){
208 0         0 Log::OK::WARN and log_warn "Template::Plex ignoring no \@{[init{...}]} block in template from ". $self->meta->{file};
209 0         0 $self->[init_done_flag_]=1;
210             }
211 21         37 "";
212             }
213              
214             # Slotting and Inheritance
215             #
216             #
217              
218             #Marks a slot in a parent template.
219             #A child template can fill this out by calling fill_slot on the parent
220             sub slot {
221 6     6 1 14 my ($self, $slot_name, $default_value)=@_;
222 6   100     17 $slot_name//="default"; #If no name assume default
223              
224 6         8 Log::OK::TRACE and log_trace __PACKAGE__.": Template called slot: $slot_name";
225 6         13 my $data=$self->[slots_]{$slot_name};
226 6         8 my $output="";
227            
228 6   66     15 $data//=$default_value;
229 6 100 66     36 if(defined($data) and $data->isa("Template::Plex")){
230             #render template
231 3 100       9 if($slot_name eq "default"){
232 2         4 Log::OK::TRACE and log_trace __PACKAGE__.": copy default slot";
233 2   50     8 $output.=$self->[default_result_]//"";
234             }
235             else {
236 1         2 Log::OK::TRACE and log_trace __PACKAGE__.": render non default template slot";
237 1         3 $output.=$data->render;
238             }
239             }
240             else {
241 3         6 Log::OK::TRACE and log_trace __PACKAGE__.": render non template slot";
242             #otherwise treat as text
243 3   50     19 $output.=$data//"";
244             }
245 6         107 $output
246             }
247              
248             sub fill_slot {
249 3     3 1 6 my ($self)=shift;
250 3         7 my $parent=$self->[parent_];
251 3 50       8 unless($parent){
252 0         0 Log::OK::WARN and log_warn __PACKAGE__.": No parent setup for: ". $self->meta->{file};
253 0         0 return;
254             }
255              
256 3 50       20 unless(@_){
257             #An unnamed fill spec implies the default slot
258 0         0 $parent->[slots_]{default}=$self;
259             }
260             else{
261             #5.36 multi element for loop
262             #disabled for backwards compatability
263             #
264             #for my ($k,$v)(@_){
265             # $parent->[slots_]{$k}=$v;
266             #}
267              
268 3         10 my %fillers=@_;
269 3         10 for (keys %fillers){
270 3         11 $parent->[slots_]{$_}=$fillers{$_};
271             }
272             }
273 3         88 "";
274             }
275              
276             sub append_slot {
277 0     0 1 0 my($self)=shift;
278 0         0 my $parent=$self->[parent_];
279 0 0       0 unless($parent){
280              
281 0         0 Log::OK::WARN and log_warn __PACKAGE__.": No parent setup for ". $self->meta->{file};
282             return
283 0         0 }
284             else{
285 0         0 my %fillers=@_;
286 0         0 for(keys %fillers){
287 0         0 $parent->[slots_]{$_}.=$fillers{$_};
288             }
289             }
290             }
291              
292             sub prepend_slot {
293 0     0 1 0 my($self)=shift;
294 0         0 my $parent=$self->[parent_];
295 0 0       0 unless($parent){
296              
297 0         0 Log::OK::WARN and log_warn __PACKAGE__.": No parent setup for ". $self->meta->{file};
298             return
299 0         0 }
300             else{
301 0         0 my %fillers=@_;
302 0         0 for(keys %fillers){
303 0         0 $parent->[slots_]{$_}=$fillers{$_}.$parent->[slots_]{$_};
304             }
305             }
306             }
307              
308              
309              
310             sub inherit {
311 2     2 1 5 my ($self, $path)=@_;
312 2         11 Log::OK::DEBUG and log_debug __PACKAGE__.": Inherit: $path";
313             #If any parent variables have be setup load the parent template
314              
315             #Setup the parent. Cached with path
316 2         8 my $p=$self->load($path, $self->args, $self->meta->%*);
317 2         6 $p->[slots_]={};
318              
319             #Add this template to the default slot
320 2         4 $p->[slots_]{default}=$self;
321 2         23 $self->[parent_]=$p;
322             }
323              
324             sub render {
325 59     59 1 1401 my ($self, $fields, $top_down)=@_;
326             #We don't call parent render if we are uninitialised
327              
328              
329            
330             #If the template uninitialized, we just do a first pass
331 59 100       109 unless($self->init_done_flag){
332              
333 21         50 return $self->_render;
334              
335             }
336              
337 38         57 Log::OK::TRACE and log_trace __PACKAGE__.": render :".$self->meta->{file}." flag: ".($top_down//"");
338              
339             #locate the 'top level' template and call downwards
340 38         52 my $p=$self;
341 38 100       86 if(!$top_down){
342 18         47 while($p->[parent_]){
343 2         6 $p=$p->[parent_];
344             }
345 18         43 $p->render($fields,1);
346             }
347             else{
348             #This is Normal template or top of hierarchy
349             #child has called parent and parent is the top
350             #
351             #Turn it around and call back down the chain
352             #
353              
354 20         32 Log::OK::TRACE and log_trace __PACKAGE__.": render: no parent bottom up. assume normal render";
355             #Check slots. Slots indicate we need to call the child first
356 20 100 66     55 if($self->[slots_] and $self->[slots_]->%*){
357 2         3 Log::OK::TRACE and log_trace __PACKAGE__.": render: rendering default slot";
358 2         6 $self->[default_result_]=$self->[slots_]{default}->render($fields,1);
359             }
360              
361             #now call render on self. This renders non hierarchial templates
362 20         37 Log::OK::TRACE and log_trace __PACKAGE__.": render: rendering body and sub templates";
363 20         100 my $total=$self->_render($fields); #Call down the chain with top_down flag
364 20         60 $self->[default_result_]=""; #Clear
365 20         87 return $total;
366             }
367             }
368              
369 0     0 1 0 sub parent {$_[0][parent_];}
370              
371              
372              
373              
374              
375              
376              
377             sub DESTROY {
378 0 0   0   0 delete_package $_[0][package_] if $_[0][package_];
379             }
380              
381             #Internal testing use only
382             sub __internal_test_proxy__ {
383 1     1   6 "PROXY";
384             }
385              
386             1;