File Coverage

blib/lib/Template/Plex.pm
Criterion Covered Total %
statement 143 176 81.2
branch 33 50 66.0
condition 17 33 51.5
subroutine 32 38 84.2
pod 15 21 71.4
total 240 318 75.4


line stmt bran cond sub pod time code
1             package Template::Plex;
2              
3 2     2   149035 use strict;
  2         9  
  2         58  
4 2     2   9 use warnings;
  2         4  
  2         47  
5              
6 2     2   9 use version; our $VERSION = version->declare('v0.6.0');
  2         4  
  2         12  
7 2     2   165 use feature qw;
  2         4  
  2         195  
8 2     2   12 no warnings "experimental";
  2         4  
  2         72  
9              
10              
11 2     2   3543 use Log::ger;
  2         104  
  2         22  
12 2     2   485 use Log::OK; #Allow control of logging from the command line
  2         4  
  2         11  
13              
14 2     2   17960 use Symbol qw;
  2         1604  
  2         124  
15              
16 2     2   13 use constant KEY_OFFSET=>0;
  2         4  
  2         151  
17 2         13 use enum ("plex_=0",qw
18              
19             cache_
20             slots_ parent_ default_result_
21 2     2   966 >);
  2         2229  
22              
23 2     2   1999 use constant KEY_COUNT=>default_result_ - plex_ +1;
  2         4  
  2         123  
24              
25             #Template::Plex::Internal uses the field name constants so import it AFTER
26             #we define them
27 2     2   860 use Template::Plex::Internal;
  2         5  
  2         4658  
28              
29             our %top_level_cache;
30             sub new {
31 21     21 0 53 my ($package, $plex)=@_;
32 21         36 my $self=[];
33 21         44 $self->[plex_]=$plex;
34 21         44 $self->[cache_]={};
35 21         62 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 3559 my ($self, $path, $vars, %opts)=@_;
44 21         35 my $template;
45 21 100       47 if(ref($self)){
46 6         10 Log::OK::TRACE and log_trace __PACKAGE__." instance load called for $path";
47 6         15 \my %fields=$self->args;
48              
49 6         18 my %options=$self->meta->%{qw}; #copy
50 6 100       78 $template=Template::Plex::Internal->new(\&Template::Plex::Internal::_prepare_template, $path, $vars?$vars:\%fields, %opts?%opts:%options);
    100          
51              
52             }
53             else{
54 15         21 Log::OK::TRACE and log_trace __PACKAGE__." class load called for $path";
55             #called on package
56 15         65 $template=Template::Plex::Internal->new(\&Template::Plex::Internal::_prepare_template, $path, $vars, %opts);
57              
58             }
59 19         89 $template->setup;
60 19         62 $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 348 my $self=shift;
76 10         22 my @args=@_;
77              
78 10 50 33     52 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         14 Log::OK::TRACE and log_trace __PACKAGE__." cache: $path";
94 10   66     43 $id//=$path.join "", caller; #Set if undefined
95 10 100       23 if(ref($self)){
96 4 50       31 $self->[cache_]{$id} and return $self->[cache_]{$id};
97              
98 4         16 my $template=$self->load($path, $vars, %opts);
99 4   33     104 $self->[cache_]{$id}//=$template;
100             }
101             else{
102 6 100       18 $top_level_cache{$id} and return $top_level_cache{$id};
103              
104 5         14 my $template=$self->load($path, $vars, %opts);
105 5   33     32 $top_level_cache{$id}//=$template;
106             }
107             }
108              
109             #TODO: add parameter checking as per cache
110             sub immediate {
111 4     4 1 1667 Log::OK::TRACE and log_trace __PACKAGE__." immediate!!";
112            
113 4         9 my $self=shift;
114 4         10 my @args=@_;
115 4 50 33     30 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         11 my ($id, $path, $vars, @opts)=@args;
128              
129              
130 4         6 Log::OK::TRACE and log_trace __PACKAGE__." immediate: $path";
131 4   33     32 $id//=$path.join "", caller; #Set if undefined
132            
133 4         11 my $template=$self->cache($id, $path, $vars, @opts);
134 4 50       17 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 33 sub meta :lvalue { $_[0][Template::Plex::meta_]; }
144              
145 9     9 1 25 sub args :lvalue{ $_[0][Template::Plex::args_]; }
146              
147 59     59 0 133 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   1074 return $_[0][sub_](@_);
153             }
154              
155             sub skip {
156 21     21 1 29 Log::OK::DEBUG and log_debug("Template::Plex: Skipping Template: ".$_[0]->meta->{file});
157 21         410 $_[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   87 my ($self, $sub)=@_;
164            
165 41 100       392 return if $self->[init_done_flag_];
166 21         25 Log::OK::DEBUG and log_debug("Template::Plex: Initalising Template: ".$self->meta->{file});
167 21 50       94 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         55 $self->pre_init;
174 21         449 $sub->();
175 21         59 $self->post_init;
176              
177 21         30 $self->[init_done_flag_]=1;
178 21         45 $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 64 my $self=shift;
197             #Test that the caller is not the template package
198 21         31 Log::OK::DEBUG and log_debug("Template::Plex: Setup Template: ".$self->meta->{file});
199 21 50       101 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         75 $self->render(@_);
205            
206             #Check if an init block was used
207 21 50       59 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         32 "";
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 15 my ($self, $slot_name, $default_value)=@_;
222 6   100     20 $slot_name//="default"; #If no name assume default
223              
224 6         6 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     16 $data//=$default_value;
229 6 100 66     34 if(defined($data) and $data->isa("Template::Plex")){
230             #render template
231 3 100       13 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         8 Log::OK::TRACE and log_trace __PACKAGE__.": render non template slot";
242             #otherwise treat as text
243 3   50     9 $output.=$data//"";
244             }
245 6         130 $output
246             }
247              
248             sub fill_slot {
249 3     3 1 16 my ($self)=shift;
250 3         6 my $parent=$self->[parent_];
251 3 50       7 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       8 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         9 my %fillers=@_;
269 3         9 for (keys %fillers){
270 3         11 $parent->[slots_]{$_}=$fillers{$_};
271             }
272             }
273 3         54 "";
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 9 my ($self, $path)=@_;
312 2         5 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         6 my $p=$self->load($path, $self->args, $self->meta->%*);
317 2         8 $p->[slots_]={};
318              
319             #Add this template to the default slot
320 2         5 $p->[slots_]{default}=$self;
321 2         6 $self->[parent_]=$p;
322             }
323              
324             sub render {
325 59     59 1 1361 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       105 unless($self->init_done_flag){
332              
333 21         48 return $self->_render;
334              
335             }
336              
337 38         48 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         54 my $p=$self;
341 38 100       76 if(!$top_down){
342 18         44 while($p->[parent_]){
343 2         5 $p=$p->[parent_];
344             }
345 18         42 $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         25 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     47 if($self->[slots_] and $self->[slots_]->%*){
357 2         3 Log::OK::TRACE and log_trace __PACKAGE__.": render: rendering default slot";
358 2         7 $self->[default_result_]=$self->[slots_]{default}->render($fields,1);
359             }
360              
361             #now call render on self. This renders non hierarchial templates
362 20         31 Log::OK::TRACE and log_trace __PACKAGE__.": render: rendering body and sub templates";
363 20         94 my $total=$self->_render($fields); #Call down the chain with top_down flag
364 20         47 $self->[default_result_]=""; #Clear
365 20         99 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   7 "PROXY";
384             }
385              
386             1;