File Coverage

lib/Async/Template/Directive.pm
Criterion Covered Total %
statement 134 139 96.4
branch 33 46 71.7
condition 11 20 55.0
subroutine 21 21 100.0
pod 0 18 0.0
total 199 244 81.5


line stmt bran cond sub pod time code
1             package Async::Template::Directive;
2              
3             #! @file
4             #! @author: Serguei Okladnikov
5             #! @date 08.10.2012
6              
7 4     4   30 use strict;
  4         7  
  4         123  
8 4     4   22 use warnings;
  4         8  
  4         115  
9 4     4   28 use base 'Template::Directive';
  4         7  
  4         8045  
10              
11              
12             our $VERSION = 0.14;
13             our $DYNAMIC = 0 unless defined $DYNAMIC;
14              
15              
16              
17             sub event_proc {
18 175     175 0 386 my ( $self, $block ) = @_;
19 175         1167 return << "EOF";
20             sub {
21             my \$context = shift || die "template sub called without context\\n";
22             my \$stash = \$context->stash;
23             my \$out = \$context->event_output;
24             my \$_tt_error;
25             eval { BLOCK: {
26             $block
27             } };
28             if (\$@) {
29             \$_tt_error = \$context->catch(\$@, \$context->event_output);
30             if( \$_tt_error->type eq 'return' )
31             { \$context->do_return( \$\$out ); }
32             else
33             { die \$_tt_error; }
34             }
35             return '';
36             }
37             EOF
38             }
39              
40              
41             sub event_finalize {
42 105     105 0 704 return << "END";
43             \$context->event_done(\$out);
44             END
45             }
46              
47              
48             sub event_cb {
49 65     65 0 193 return << "END";
50             sub { \$context->event_done( \@_ == 1 ? \$_[0] : \\\@_ ) }
51             END
52             }
53              
54              
55             # TODO: remove this function after refactoring back $out to $output
56             sub return {
57 11     11 0 48 return "\$context->throw('return', '', \$out);";
58             }
59              
60              
61             sub ident_eventify {
62 67     67 0 310 my ( $self, $ident, $event_cb ) = @_;
63 67         86 my $last = $#{$ident};
  67         122  
64 67         117 my $params = $ident->[$last];
65 67 100       163 $params = '[]' if $params eq '0';
66 67 50       161 die 'event must be function call' unless ']' eq substr $params, -1;
67 67   66     188 my $cb = $event_cb || $self->event_cb;
68 67 100       368 my $comma = $params =~ /^\[\s*\]$/ ? '' : ',';
69 67         433 $params =~ s/.$/$comma $cb \]/;
70 67         248 $ident->[$last] = $params;
71             }
72              
73              
74             sub async_call {
75 2     2 0 15 my ( $self, $resvar, $ident ) = @_;
76 2         4 my ( $RES, $CB ) = (0,1);
77              
78 2 50       10 $resvar = '[' . join(', ', @$resvar) . ']' if $resvar;
79 2         9 $self->ident_eventify($ident, "\$async_cb");
80 2         8 my $expr = $self->ident( $ident );
81              
82 2         46 return << "END";
83              
84             my \$rescb = [ undef, undef ];
85             my \$async_cb = sub {
86             if( \$rescb->[$CB] )
87             { \$rescb->[$CB]->(\@_); }
88             else
89             { \$rescb->[$RES] = \\\@_ }
90             };
91             my \$await_cb = sub {
92             my \$cb = pop;
93             if( \$rescb->[$RES] )
94             { \$cb->( \@{\$rescb->[$RES]} ); }
95             else
96             { \$rescb->[$CB] = \$cb; }
97             };
98             \$stash->set($resvar, \$await_cb);
99             $expr;
100             END
101             }
102              
103              
104             #------------------------------------------------------------------------
105             # event_template($block)
106             #------------------------------------------------------------------------
107              
108             sub event_template {
109 37     37 0 180 my ($self, $block) = @_;
110             # $block = pad($block, 2) if $PRETTY;
111              
112 37 50       187 return "sub { return '' }" unless $block =~ /\S/;
113              
114 37         247 my $res = << "EOF" ;
115             $block
116             EOF
117              
118 37         92 return $self->event_proc($res);
119             }
120              
121              
122             #------------------------------------------------------------------------
123             # define_event($res,$expr,$block)
124             #------------------------------------------------------------------------
125              
126             sub define_event {
127 59     59 0 134 my ( $self, $resvar, $expr, $event ) = @_;
128 59 50       242 $resvar = '[' . join(', ', @$resvar) . ']' if $resvar;
129 59         129 $event = $self->event_proc( $event );
130 59         505 return << "END";
131            
132             # EVENT
133             my \$event = $event;
134             my \$ev = \$context->event_top();
135             \$context->event_push( {
136             resvar => $resvar,
137             event => \$event,
138             } );
139             $expr;
140             return '';
141             END
142             }
143              
144              
145             #------------------------------------------------------------------------
146             # include(\@nameargs) [% INCLUDE template foo = bar %]
147             # # => [ [ $file, ... ], \@args ]
148             #------------------------------------------------------------------------
149              
150             sub include {
151 5     5 0 33 my ($self, $nameargs, $event) = @_;
152 5         17 $self->process( $nameargs, $event, 'localize me!' );
153             }
154              
155              
156             #------------------------------------------------------------------------
157             # process(\@nameargs) [% PROCESS template foo = bar %]
158             # # => [ [ $file, ... ], \@args ]
159             #------------------------------------------------------------------------
160              
161             sub process {
162 15     15 0 71 my ($self, $nameargs, $event, $localize) = @_;
163 15         35 my ($file, $args) = @$nameargs;
164 15         28 my $hash = shift @$args;
165 15         61 $file = $self->filenames($file);
166 15 100       169 $file .= @$hash ? ', { ' . join(', ', @$hash) . ' }' : ', {}';
167 15   100     53 $localize ||= '';
168 15         38 $event = $self->event_proc( $event );
169 15         86 return << "EOF";
170              
171             # EVENT PROCESS
172             my \$event = $event;
173             \$context->event_push( {
174             event => \$event,
175             } );
176             \$context->process_enter($file,\'$localize\');
177             return '';
178             EOF
179             }
180              
181              
182             #------------------------------------------------------------------------
183             # event_wrapper(\@nameargs, $block, $tail, $is_blk_ev)
184             # \@nameargs => [ [ $file, ... ], \@args ] ]
185             # [% WRAPPER file1 + file2 foo=bar %]
186             # ...
187             # [% END %]
188             #------------------------------------------------------------------------
189              
190             sub event_wrapper {
191 2     2 0 17 my ($self, $nameargs, $block, $tail, $is_blk_ev) = @_;
192              
193 2         4 my ($files, $args) = @$nameargs;
194 2         6 my $hash = $args->[0];
195 2         5 push(@$hash, "'content'", '${$capture_output}');
196 2         8 my $inclargs .= '{ ' . join(', ', @$hash) . ' }';
197 2         6 my $name = '[' . join(', ', @$files) . ']';
198              
199 2 50       7 $block = pad($block, 1) if $Template::Directive::PRETTY;
200              
201 2 100       6 if( !$is_blk_ev ) {
202 1         4 $block .= $self->event_finalize;
203             }
204              
205 2         11 my $iteration = << "___EOF";
206             # WRAPPER LOOP
207             my \$capture_output = \$context->event_output;
208             my \$next_output = '';
209             \$context->set_event_output( \\\$next_output );
210             \$out = \$next_output;
211             if( scalar \@\$wrapper_files ) {
212             my \$file = pop \@\$wrapper_files;
213             \$context->event_push( {
214             event => \$iteration,
215             } );
216             \$context->process_enter(\$file, $inclargs, 'localize me');
217             } else {
218             my \$event_top = \$context->event_top();
219             my \$pop_output = \$event_top->{push_output};
220             \${\$pop_output} .= \${\$capture_output};
221             \$context->set_event_output( \$pop_output );
222             \$out = \$pop_output;
223             $tail
224             }
225             ___EOF
226              
227 2         7 $iteration = $self->event_proc( $iteration );
228              
229 2         9 my $capture = << "___EOF";
230             # WRAPPER CONTENT CAPTURE
231             my \$push_out = \$context->event_output;
232             my \$event_top = \$context->event_top();
233             \$event_top->{push_output} = \$push_out;
234             my \$capture_out = '';
235             \$context->set_event_output( \\\$capture_out );
236             \$out = \\\$capture_out;
237             \$context->event_push( {
238             resvar => undef,
239             event => \$iteration,
240             } );
241             $block
242             ___EOF
243              
244 2         52 return << "___EOF";
245             my \$wrapper_files = $name;
246             my \$iteration; \$iteration = $iteration;
247             $capture
248             ___EOF
249             }
250              
251              
252             #------------------------------------------------------------------------
253             # event_while($expr, $block, $tail, $label) [% WHILE x < 10 %]
254             # ...
255             # [% END %]
256             #------------------------------------------------------------------------
257              
258             sub event_while {
259 8     8 0 251 my ($self, $expr, $block, $tail, $label) = @_;
260             # $block = pad($block, 2) if $PRETTY;
261 8   50     32 $label ||= 'LOOP';
262              
263 8         17 my $while_max = $Template::Directive::WHILE_MAX;
264              
265 8         141 $block = << "EOF";
266             if( --\$context->event_top()->{failsafe} && ($expr) ) {
267             \$context->event_push( {
268             resvar => undef,
269             event => \$event,
270             } );
271             $block
272             } else {
273             die "WHILE loop terminated (> $while_max iterations)\\n"
274             unless \$context->event_top()->{failsafe};
275             $tail
276             }
277             EOF
278              
279 8         27 $block = $self->event_proc($block);
280              
281 8         125 return << "EOF";
282              
283             # EVENT $label DECLARE
284             my \$event;
285             \$event =
286             $block
287             ;
288              
289             # EVENT $label STARTUP
290             \$context->event_top()->{failsafe} = $while_max;
291             \$event->( \$context );
292             return '';
293             EOF
294             }
295              
296              
297             #------------------------------------------------------------------------
298             # event_for($target, $list, $args, $block, $tail)
299             # [% FOREACH x = [ foo bar ] %]
300             # ...
301             # [% END %]
302             #------------------------------------------------------------------------
303              
304             sub event_for {
305 8     8 0 363 my ($self, $target, $list, $args, $block, $tail, $label) = @_;
306             # $args is not used in original code
307 8   50     40 $label ||= 'LOOP';
308              
309             # vars: value, list, getnext, error, oldloop
310              
311 8         21 my ($loop_save, $loop_set, $loop_restore, $setiter);
312 8 50       21 if ($target) {
313 8         30 $loop_save = 'eval { $evtop->{oldloop} = ' . $self->ident(["'loop'"]) . ' }';
314 8         171 $loop_set = "\$stash->{'$target'} = \$evtop->{value}";
315 8         23 $loop_restore = "\$stash->set('loop', \$evtop->{oldloop})";
316             }
317             else {
318 0         0 $loop_save = '$stash = $context->localise()';
319             # $loop_set = "\$stash->set('import', \$evtop->{value}) "
320             # . "if ref \$value eq 'HASH'";
321 0         0 $loop_set = "\$stash->get(['import', [\$evtop->{value}]]) "
322             . "if ref \$evtop->{value} eq 'HASH'";
323 0         0 $loop_restore = '$stash = $context->delocalise()';
324             }
325             # $block = pad($block, 3) if $PRETTY;
326              
327 8         69 $block = << "EOF";
328             my \$evtop = \$context->event_top();
329             if( \$evtop->{getnext} ) {
330             (\$evtop->{value}, \$evtop->{error}) =
331             \$evtop->{list}->get_next();
332             } else {
333             \$evtop->{getnext} = 1;
334             }
335             if( ! \$evtop->{error} ) {
336             $loop_set;
337             \$context->event_push( {
338             resvar => undef,
339             event => \$event,
340             } );
341             do{
342             $block
343             };
344             } else {
345             $loop_restore;
346             \$evtop->{error} = 0
347             if \$evtop->{error} &&
348             \$evtop->{error} eq Template::Constants::STATUS_DONE;
349             die \$evtop->{error}
350             if \$evtop->{error};
351             $tail
352             }
353             EOF
354              
355 8         25 $block = $self->event_proc($block);
356              
357 8         129 return << "EOF";
358              
359             # EVENT $label DECLARE
360             my \$event;
361             \$event =
362             $block
363             ;
364              
365             # EVENT $label STARTUP
366             my \$evtop = \$context->event_top();
367             \$evtop->{list} = $list;
368             unless (UNIVERSAL::isa(\$evtop->{list}, 'Template::Iterator')) {
369             \$evtop->{list} =
370             Template::Config->iterator(\$evtop->{list})
371             || die \$Template::Config::ERROR, "\\n";
372             }
373             (\$evtop->{value}, \$evtop->{error}) = \$evtop->{list}->get_first();
374             $loop_save;
375             \$stash->set('loop', \$evtop->{list});
376             \$event->( \$context );
377             return '';
378             EOF
379              
380             }
381              
382              
383             #------------------------------------------------------------------------
384             # event_switch($expr, \@case) [% SWITCH %]
385             # [% CASE foo %]
386             # ...
387             # [% END %]
388             #------------------------------------------------------------------------
389              
390             sub event_switch {
391 6     6 0 41 my ($self, $expr, $case, $tail) = @_;
392 6         16 my @case = @$case;
393 6         11 my ($evented, $calltail,$pct, $match, $block, $default);
394 6         13 my $caseblock = '';
395              
396 6         11 $default = pop @case;
397              
398 6         9 $calltail = <
399             \$context->event_push( {
400             event => \$event_tail,
401             } );
402             EOF
403              
404 6         17 foreach $case (@case) {
405 20         31 $match = $case->[0];
406 20         39 $block = $case->[1];
407 20         26 $evented = $case->[2];
408             # $block = pad($block, 1) if $PRETTY;
409              
410 20 100       37 $pct = $evented ? \$calltail : \'';
411              
412 20         28 $caseblock .= <
413             \$_tt_match = $match;
414             \$_tt_match = [ \$_tt_match ] unless ref \$_tt_match eq 'ARRAY';
415             if (grep(/^\\Q\$_tt_result\\E\$/, \@\$_tt_match)) {
416 20         95 ${$pct} $block
417             last EVENTSWITCH;
418             }
419             EOF
420              
421             } # foreach
422              
423 6 100       20 if( defined $default ) {
424 4 50       16 if( 'ARRAY' eq ref $default ) {
425             #$default = 'my $event = ' . $self->event_proc( $default->[0] ) . ';';
426 4         23 $default = $default->[0];
427             }
428 4         32 $caseblock .= $calltail . $default
429             }
430 6         21 $tail = 'my $event_tail = ' . $self->event_proc( $tail ) . ';';
431             # $caseblock = pad($caseblock, 2) if $PRETTY;
432              
433 6         111 return <
434              
435             # EVENT SWITCH
436             $tail
437             do {
438             my \$_tt_result = $expr;
439             my \$_tt_match;
440             EVENTSWITCH: {
441             $caseblock
442             }
443             };
444            
445             \$event_tail->( \$context );
446             EOF
447             }
448              
449              
450             #------------------------------------------------------------------------
451             # event_if_directive($expr, $resvar, $evexpr, $expr, $tail)
452             #------------------------------------------------------------------------
453              
454             sub event_if_directive {
455 6     6 0 38 my ( $self, $resvar, $evexpr, $expr, $tail ) = @_;
456              
457 6 50       38 $resvar = '[' . join(', ', @$resvar) . ']' if $resvar;
458 6         14 $tail = $self->event_proc( $tail );
459              
460 6         30 return << "END";
461             my \$event_tail = $tail;
462             if( $expr ) {
463             $evexpr;
464             \$context->event_push( {
465             resvar => $resvar,
466             event => \$event_tail,
467             } );
468             } else {
469             \$event_tail->( \$context );
470             }
471             END
472              
473             }
474              
475              
476             #------------------------------------------------------------------------
477             # event_if($expr, $block, $else, $tail, $is_blk_ev)
478             #------------------------------------------------------------------------
479              
480             sub event_if {
481 27     27 0 179 my ($self, $expr, $block, $else, $tail, $is_blk_ev ) = @_;
482 27   50     104 my $label ||= 'IF';
483              
484 27 50       56 my @else = $else ? @$else : ();
485 27         39 $else = pop @else;
486             # $block = pad($block, 1) if $PRETTY;
487              
488 27         61 $tail = $self->event_proc( $tail );
489              
490 27         246 my $output = << "END";
491             my \$event_tail = $tail;
492             END
493              
494 27 100       62 if( $is_blk_ev ) {
495 13         33 $block = << "END";
496             \$context->event_push( {
497             event => \$event_tail,
498             } );
499             $block;
500             return '';
501             END
502             }
503              
504 27         93 $output .= "if ($expr) {\n$block\n}\n";
505              
506 27         51 foreach my $elsif (@else) {
507 18         42 ($expr, $block, $is_blk_ev) = @$elsif;
508 18 100       45 if( $is_blk_ev ) {
509 8         17 $block = << "END";
510             \$context->event_push( {
511             event => \$event_tail,
512             } );
513             $block;
514             return '';
515             END
516             }
517             # $block = pad($block, 1) if $PRETTY;
518 18         42 $output .= "elsif ($expr) {\n$block\n}\n";
519             }
520              
521 27 100       51 if (defined $else) {
522 12         18 $block = $else;
523 12 100 66     45 if( 'ARRAY' eq ref $else && 'ev' eq $else->[1] ) {
524 6         13 $block = $else->[0];
525 6         16 $block = << "END";
526             \$context->event_push( {
527             event => \$event_tail,
528             } );
529             $block;
530             return '';
531             END
532             }
533             # $else = pad($else, 1) if $PRETTY;
534 12         26 $output .= "else {\n$block\n}\n";
535             }
536              
537 27         45 $output .= << "END";
538             \$event_tail->( \$context );
539             END
540              
541 27         222 return $output;
542              
543             }
544              
545              
546             # WRNING: overloading only due to '${$out}' instead '$output'
547             #------------------------------------------------------------------------
548             # capture($name, $block)
549             #------------------------------------------------------------------------
550              
551             sub capture {
552 2     2 0 16 my ($self, $name, $block) = @_;
553              
554 2 50       9 if (ref $name) {
555 2 50 33     18 if (scalar @$name == 2 && ! $name->[1]) {
556 2         8 $name = $name->[0];
557             }
558             else {
559 0         0 $name = '[' . join(', ', @$name) . ']';
560             }
561             }
562             # $block = pad($block, 1) if $PRETTY;
563              
564 2         11 return <
565              
566             # CAPTURE
567             \$stash->set($name, do {
568             my \$output = ''; my \$out = \\\$output;
569             $block
570             \${\$out};
571             });
572             EOF
573              
574             }
575              
576              
577             #------------------------------------------------------------------------
578             # event_capture($name, $block)
579             #------------------------------------------------------------------------
580              
581             sub event_capture {
582 2     2 0 20 my ($self, $name, $block, $tail) = @_;
583              
584 2 50       10 if (ref $name) {
585 2 50 33     17 if (scalar @$name == 2 && ! $name->[1]) {
586 2         9 $name = $name->[0];
587             }
588             else {
589 0         0 $name = '[' . join(', ', @$name) . ']';
590             }
591             }
592             # $block = pad($block, 1) if $PRETTY;
593              
594             #$tail = $self->event_proc($tail);
595              
596 2         10 my $on_capture = << "EOF";
597             my \$event_top = \$context->event_top();
598             my \$capture_var = \$event_top->{capture_var};
599             my \$push_out = \$event_top->{push_output};
600             my \$capture_out = \$context->event_output;
601             \$context->set_event_output( \$push_out );
602             \$stash->set( \$capture_var, \$\$capture_out );
603             \$out = \$push_out;
604             #\$context->event_done();
605             #my \$tail =
606             $tail
607             ;
608             # \$tail->( \$context );
609             EOF
610              
611 2         51 $on_capture = $self->event_proc( $on_capture );
612              
613             return << "EOF"
614              
615             my \$push_out = \$context->event_output;
616             my \$capture_out = '';
617             \$context->set_event_output( \\\$capture_out );
618             \$out = \\\$capture_out;
619             my \$on_capture =
620             $on_capture;
621             my \$event_top = \$context->event_top();
622             \$event_top->{push_output} = \$push_out;
623             \$event_top->{capture_var} = $name;
624             \$context->event_push( {
625             resvar => undef,
626             event => \$on_capture,
627             } );
628              
629             $block
630             EOF
631 2         21 }
632              
633             1;