File Coverage

lib/Async/Template/Context.pm
Criterion Covered Total %
statement 101 106 95.2
branch 25 38 65.7
condition 8 16 50.0
subroutine 16 16 100.0
pod 0 10 0.0
total 150 186 80.6


line stmt bran cond sub pod time code
1             package Async::Template::Context;
2              
3             #! @file
4             #! @author: Serguei Okladnikov
5             #! @date 01.10.2012
6              
7             #! This source file have functions `process_enter()` and `process_leave()`.
8             #! Code parts of them taken from function `process()` of template toolkit
9             #! library and substantially enhanced, the asynchronous processing
10             #! is introduced by Serguei Okladnikov
11             #! Author of that original code parts is Andy Wardley
12              
13              
14 4     4   29 use strict;
  4         8  
  4         135  
15 4     4   24 use warnings;
  4         20  
  4         154  
16 4     4   23 use base 'Template::Context';
  4         7  
  4         2851  
17 4     4   21357 use Scalar::Util 'blessed';
  4         32  
  4         357  
18              
19              
20             our $VERSION = 0.14;
21             our $DYNAMIC = 0 unless defined $DYNAMIC;
22              
23             #use constant DOCUMENT => Template::Context::DOCUMENT;
24 4     4   27 use constant DOCUMENT => 'Async::Template::Document';
  4         9  
  4         4421  
25              
26              
27             sub event_output {
28 385     385 0 90211 $_[0]->{_event_output};
29             }
30              
31             sub set_event_output {
32 13     13 0 309 $_[0]->{_event_output} = $_[1];
33             }
34              
35             sub event_clear {
36 32     32 0 167 $_[0]->{event_stack} = [];
37             }
38              
39             sub event_done {
40 231     231 0 6397124 my ( $self, $res ) = @_;
41 231         486 my $ev = $self->event_pop();
42 231 100       574 if( $ev->{resvar} ) {
43 82         313 $self->stash->set( $ev->{resvar}, $res );
44             }
45              
46             # TODO: here exeptions not handled, and not reevented
47              
48 231         5169 my $output = $ev->{event}->( $self, \$res );
49             }
50              
51             sub event_push {
52 254     254 0 4388 push @{ $_[0]->{event_stack} }, $_[1];
  254         4020  
53             }
54              
55             sub event_pop {
56 231     231 0 291 pop @{ $_[0]->{event_stack} };
  231         569  
57             }
58              
59             sub event_top {
60 273     273 0 10488 return $_[0]->{event_stack}->[ $#{ $_[0]->{event_stack} } ];
  273         3830  
61             }
62              
63             sub do_return {
64 11     11 0 998 my ( $self, $res ) = @_;
65 11         33 my $ev = $self->{event_stack}->[0];
66 11         64 $ev->{event}->( $self, $res );
67             }
68              
69              
70             #------------------------------------------------------------------------
71             # process_enter and process_leave event implementation modified from:
72             #
73             # process($template, \%params) [% PROCESS template var=val ... %]
74             # process($template, \%params, $local) [% INCLUDE template var=val ... %]
75             #
76             # Processes the template named or referenced by the first parameter.
77             # The optional second parameter may reference a hash array of variable
78             # definitions. These are set before the template is processed by
79             # calling update() on the stash. Note that, unless the third parameter
80             # is true, the context is not localised and these, and any other
81             # variables set in the template will retain their new values after this
82             # method returns. The third parameter is in place so that this method
83             # can handle INCLUDE calls: the stash will be localized.
84             #
85             # Returns the output of processing the template. Errors are thrown
86             # as Template::Exception objects via die().
87             #------------------------------------------------------------------------
88              
89             sub process_enter {
90 22     22 0 203 my ($self, $template, $params, $localize) = @_;
91 22         65 my $context = $self;
92 22         55 my ($trim, $blocks) = @$self{ qw( TRIM BLOCKS ) };
93 22         33 my ($stash, $name, $tblocks, $tmpout);
94 22         34 my $output = '';
95            
96 22         37 my $ev = $self->event_top;
97              
98 22         40 $ev->{localize} = $localize;
99 22         41 $ev->{template} = $template;
100 22 100       95 $ev->{template} = [ $template ] unless ref $template eq 'ARRAY';
101              
102 0         0 $self->debug("process([ ", join(', '), @{$ev->{template}}, ' ], ',
103             defined $params ? $params : '', ', ',
104             $localize ? '' : '', ')')
105 22 0       61 if $self->{ DEBUG };
    0          
    50          
106              
107             # fetch compiled template for each name specified
108 22         28 foreach $name (@{$ev->{template}}) {
  22         49  
109 25         79 push(@{$ev->{compiled}}, $self->template($name));
  25         79  
110             }
111              
112 22 100       47377 if ($localize) {
113             # localise the variable stash with any parameters passed
114 13         87 $stash = $self->{ STASH } = $self->{ STASH }->clone($params);
115             } else {
116             # update stash with any new parameters passed
117 9         40 $self->{ STASH }->update($params);
118 9         81 $stash = $self->{ STASH };
119             }
120              
121 22         667 my $event; $event = sub {
122 45   50 45   121 my $context = shift || die "template sub called without context\n";
123 45         117 my $stash = $context->stash;
124 45         172 my $out = $context->event_output;
125 45         54 my $component;
126 45         80 my $ev = $self->event_top;
127              
128             # save current component
129 45         69 eval { $component = $stash->get('component') };
  45         306  
130              
131 45         63 foreach my $compiled ( @{$ev->{compiled_entered}} ) {
  45         116  
132 10         29 $compiled->process_leave( $context );
133             }
134              
135 45 100       58 unless( @{$ev->{template}} ) {
  45         122  
136 20         55 $context->process_leave;
137 20         45 $context->event_done( $out );
138 20         1088 return '';
139             }
140              
141 25         52 $ev->{compiled_entered} = [];
142 25         91 $context->event_push( {
143             event => $event,
144             } );
145              
146 25         39 my $name = shift @{$ev->{template}};
  25         50  
147              
148 25         36 do {
149 25         29 my $compiled = shift @{$ev->{compiled}};
  25         41  
150 25 50       113 my $element = ref $compiled eq 'CODE'
    100          
151             ? { (name => (ref $name ? '' : $name), modtime => time()) }
152             : $compiled;
153              
154 25 100 66     151 if (blessed($component) && $component->isa(DOCUMENT)) {
155 8         26 $element->{ caller } = $component->{ name };
156 8   50     146 $element->{ callers } = $component->{ callers } || [];
157 8         20 push(@{$element->{ callers }}, $element->{ caller });
  8         22  
158             }
159              
160 25         169 $stash->set('component', $element);
161            
162 25 100       52 unless ($localize) {
163             # merge any local blocks defined in the Template::Document
164             # into our local BLOCKS cache
165 12 0 33     48 @$blocks{ keys %$tblocks } = values %$tblocks
      33        
166             if (blessed($compiled) && $compiled->isa(DOCUMENT))
167             && ($tblocks = $compiled->blocks);
168             }
169            
170 25 100       90 if (ref $compiled eq 'CODE') {
    50          
171 14         359 $tmpout = &$compiled($self);
172             }
173             elsif (ref $compiled) {
174             # attention, do not change sequence of this two lines
175 11         14 push @{$ev->{compiled_entered}}, $compiled; # first
  11         25  
176 11         101 $tmpout = $compiled->process_enter($self); # second
177             }
178             else {
179 0         0 $self->throw('file',
180             "invalid template reference: $compiled");
181             }
182            
183 25 50       952 if ($trim) {
184 0         0 for ($tmpout) {
185 0         0 s/^\s+//;
186 0         0 s/\s+$//;
187             }
188             }
189             # $output .= $tmpout;
190              
191             # pop last item from callers.
192             # NOTE - this will not be called if template throws an
193             # error. The whole issue of caller and callers should be
194             # revisited to try and avoid putting this info directly into
195             # the component data structure. Perhaps use a local element
196             # instead?
197              
198 25 100 66     121 pop(@{$element->{ callers }})
  8         24  
199             if (blessed($component) && $component->isa(DOCUMENT));
200             };
201 25         604 $stash->set('component', $component);
202 22         215 };
203              
204 22         59 $event->( $context );
205            
206             }
207              
208              
209             sub process_leave {
210 20     20 0 28 my $self = shift;
211 20         23 my ( $error );
212 20         34 my $ev = $self->event_top;
213              
214 20         31 $error = $@;
215            
216 20 100       51 if ($ev->{localize}) {
217             # ensure stash is delocalised before dying
218 12         43 $self->{ STASH } = $self->{ STASH }->declone();
219             }
220            
221 20 0       87 $self->throw(ref $error
    50          
222             ? $error : (Template::Constants::ERROR_FILE, $error))
223             if $error;
224            
225 20         27 return '';
226             }
227              
228              
229             1;