root of componentUI renders
[catagits/Reaction.git] / lib / Reaction / UI / RenderingContext / TT.pm
CommitLineData
7adfd53f 1package Reaction::UI::RenderingContext::TT;
2
3use Reaction::Class;
4use aliased 'Reaction::UI::RenderingContext';
5use aliased 'Template::View';
6
7class TT is RenderingContext, which {
8
7adfd53f 9 has 'iter_class' => (
10 is => 'ro', required => 1,
11 default => sub { 'Reaction::UI::Renderer::TT::Iter'; },
12 );
13
f2fef590 14 our $body;
15
16 implements 'dispatch' => as {
17 my ($self, $render_tree, $args) = @_;
18#warn "-- dispatch start\n";
19 local $body = '';
20 my %args_copy = %$args;
21 foreach my $to_render (@$render_tree) {
22 my ($type, @to) = @$to_render;
23 if ($type eq '-layout') {
24 my ($lset, $fname, $next) = @to;
25 local $args_copy{call_next} =
26 (@$next
27 ? sub { $self->dispatch($next, $args); }
28 : '' # no point running internal dispatch if nothing -to- dispatch
29 );
30 $self->render($lset, $fname, \%args_copy);
31 } elsif ($type eq '-render') {
32 my ($widget, $fname, $over) = @to;
33 #warn "@to";
34 if (defined $over) {
35 $over->each(sub {
36 local $args_copy{_} = $_[0];
37 $body .= $widget->render($fname, $self, \%args_copy);
38 });
39 } else {
40 $body .= $widget->render($fname, $self, \%args_copy);
41 }
42 }
43 }
44#warn "-- dispatch end, body: ${body}\n-- end body\nbacktrace: ".Carp::longmess()."\n-- end trace\n";
45 return $body;
46 };
47
7adfd53f 48 implements 'render' => as {
d8c7a86e 49 my ($self, $lset, $fname, $args) = @_;
f2fef590 50
51 confess "\$body not in scope" unless defined($body);
7adfd53f 52
53 # foreach non-_ prefixed key in the args
54 # build a subref for this key that passes self so the generator has a
55 # rendering context when [% key %] is evaluated by TT as $val->()
56 # (assuming it's a subref - if not just pass through)
57
58 my $tt_args = {
59 map {
60 my $arg = $args->{$_};
f2fef590 61 ($_ => (ref $arg eq 'CODE' ? sub { $arg->($self, $args) } : $arg))
7adfd53f 62 } grep { !/^_/ } keys %$args
63 };
64
65 # if there's an _ key that's our current topic (decalarative syntax
66 # sees $_ as $_{_}) so build an iterator around it.
67
68 # There's possibly a case for making everything an iterator but I think
69 # any fragment should only have a single multiple arg
70
71 # we also create a 'pos' shortcut to content.pos for brevity
72
73 if (my $topic = $args->{_}) {
74 my $iter = $self->iter_class->new(
75 $topic, $self
76 );
77 $tt_args->{content} = $iter;
78 $tt_args->{pos} = sub { $iter->pos };
79 }
f2fef590 80 $body .= $lset->tt_view->include($fname, $tt_args);
81#warn "rendered ${fname}, body length now ".length($body)."\n";
7adfd53f 82 };
83
84};
85
86package Reaction::UI::Renderer::TT::Iter;
87
88use overload (
89 q{""} => 'stringify',
90 fallback => 1
91);
92
93sub pos { shift->{pos} }
94
95sub new {
96 my ($class, $cr, $rctx) = @_;
97 bless({ rctx => $rctx, cr => $cr, pos => 0 }, $class);
98}
99
100sub next {
101 my $self = shift;
102 $self->{pos}++;
103 my $next = $self->{cr}->();
104 return unless $next;
105 return sub { $next->($self->{rctx}) };
106}
107
108sub all {
109 my $self = shift;
110 my @all;
111 while (my $e = $self->next) {
112 push(@all, $e);
113 }
114 \@all;
115}
116
117sub stringify {
118 my $self = shift;
119 my $res = '';
120 foreach my $e (@{$self->all}) {
121 $res .= $e->();
122 }
123 $res;
124}
125
1261;