Commit | Line | Data |
7adfd53f |
1 | package Reaction::UI::RenderingContext::TT; |
2 | |
3 | use Reaction::Class; |
4 | use aliased 'Reaction::UI::RenderingContext'; |
5 | use aliased 'Template::View'; |
6 | |
7 | class 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 | |
86 | package Reaction::UI::Renderer::TT::Iter; |
87 | |
88 | use overload ( |
89 | q{""} => 'stringify', |
90 | fallback => 1 |
91 | ); |
92 | |
93 | sub pos { shift->{pos} } |
94 | |
95 | sub new { |
96 | my ($class, $cr, $rctx) = @_; |
97 | bless({ rctx => $rctx, cr => $cr, pos => 0 }, $class); |
98 | } |
99 | |
100 | sub 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 | |
108 | sub all { |
109 | my $self = shift; |
110 | my @all; |
111 | while (my $e = $self->next) { |
112 | push(@all, $e); |
113 | } |
114 | \@all; |
115 | } |
116 | |
117 | sub stringify { |
118 | my $self = shift; |
119 | my $res = ''; |
120 | foreach my $e (@{$self->all}) { |
121 | $res .= $e->(); |
122 | } |
123 | $res; |
124 | } |
125 | |
126 | 1; |