File Coverage

blib/lib/Template/Lace/Renderer.pm
Criterion Covered Total %
statement 59 73 80.8
branch 22 40 55.0
condition 2 10 20.0
subroutine 10 13 76.9
pod 2 8 25.0
total 95 144 65.9


line stmt bran cond sub pod time code
1             package Template::Lace::Renderer;
2              
3 1     1   726 use Moo;
  1         3  
  1         10  
4 1     1   545 use Scalar::Util;
  1         4  
  1         1361  
5              
6             has [qw(model dom components)] => (is=>'ro', required=>1);
7              
8             sub call {
9 4     4 1 24 my ($self, $proto, @args) = @_;
10 4 100 100     32 if(ref($proto)||'' eq 'CODE') {
    50          
11 1         9 return $proto->($self->model, $self->dom, @args);
12             } elsif($proto) {
13 3         21 return $self->model->$proto($self->dom, @args);
14             }
15             }
16              
17             sub call_at {
18 0     0 1 0 my ($self, $css, $proto, @args) = @_;
19 0         0 my $dom = $self->dom->at($css);
20 0 0 0     0 if(ref($proto)||'' eq 'CODE') {
    0          
21 0         0 return $proto->($self->model, $dom, @args);
22             } elsif($proto) {
23 0         0 return $self->model->$proto($dom, @args);
24             }
25             }
26              
27             sub render {
28 2     2 0 10 my $self = shift;
29 2         11 my $rendered_dom = $self->get_processed_dom
30             ->to_string;
31 2         1362 return $rendered_dom;
32             }
33              
34             sub get_processed_dom {
35 6     6 0 15 my $self = shift;
36 6         22 my $dom = $self->dom;
37 6         31 $self->process_components($dom);
38 6 100       1252 $self->model->process_dom($dom)
39             if $self->model->can('process_dom');
40 6         20328 return $dom;
41             }
42              
43             sub process_components {
44 6     6 0 18 my ($self, $dom) = @_;
45 6         13 my @ordered_keys = @{$self->components->ordered_component_keys};
  6         32  
46 6         18 my %constructed_components = ();
47 6         16 foreach my $id(@ordered_keys) {
48 6 50       1495 next unless $self->components->handlers->{$id}; # might skip if 'static' handler
49 6 100       33 next unless my $local_dom = $dom->at("[uuid='$id']");
50             my $constructed_component = $self->process_component(
51             $local_dom,
52             $self->components->handlers->{$id},
53             \%constructed_components,
54 5         4108 %{$self->components->component_info->{$id}});
  5         43  
55 5 50       31 $constructed_components{$id} = $constructed_component
56             if $constructed_component;
57             }
58             # Now post process.. We do this so that parents can have access to
59             # children for transforming dom.
60 6         19 foreach my $id(@ordered_keys) {
61 6 100       6811 next unless $constructed_components{$id};
62 5         25 my $processed_component = $constructed_components{$id}->get_processed_dom;
63              
64             #=head1 comment
65              
66             # Move all the scripts, styles and links to the head area
67             # TODO this probably doesn't work if the stuff is in a component
68             # inside a component.
69             $processed_component->find('link:not(head link)')->each(sub {
70 0 0 0 0   0 return unless $_->attr('id') || $_->attr('href');
71 0         0 $dom->append_link_uniquely($_);
72 0         0 $_->remove;
73 5         188 }); # href
74             $processed_component->find('style:not(head style)')->each(sub {
75 3 50   3   1163 return unless $_->attr('id');
76 3         64 $dom->append_style_uniquely($_);
77 3         18 $_->remove;
78 5         1824 }); #id
79             $processed_component->find('script:not(head script)')->each(sub {
80 0     0   0 my ($e, $num) = @_;
81 0 0 0     0 return unless $e->attr('id') || $e->attr('src');
82 0         0 $dom->append_script_uniquely($e);
83 0         0 $_->remove;
84 5         1162 }); #id or src
85              
86             #=cut
87              
88 5         1504 $dom->at("[uuid='$id']")->replace($processed_component);
89             }
90             }
91              
92             sub prepare_component_attrs {
93 5     5 0 23 my ($self, $dom, $model, %component_info) = @_;
94             my %attrs = (
95 5         12 $self->process_attrs($model, $dom, %{$component_info{attrs}}),
  5         26  
96             content=>$dom->content,
97             model=>$model);
98 5         769 return %attrs;
99             }
100              
101             sub process_component {
102 5     5 0 31 my ($self, $dom, $component, $constructed_components, %component_info) = @_;
103 5         28 my %attrs = $self->prepare_component_attrs($dom, $self->model, %component_info);
104 5 50       23 if(my $container_id = $component_info{current_container_id}) {
105             # Its possible if the compoent was a 'on_component_add' type that
106             # its been removed from the DOM and a Child might still have it as
107             # a container by mistake. Possible a TODO to have a better idea.
108             $attrs{container} = $constructed_components->{$container_id}->model
109 5 100       25 if $constructed_components->{$container_id};
110             }
111              
112 5 50       26 if(Scalar::Util::blessed $component) {
    0          
113 5         10 my $constructed_component;
114 5 100       13 if($attrs{container}) {
115 2 50       17 if($attrs{container}->can('create_child')) {
116 0         0 $constructed_component = $attrs{container}->create_child($component, %attrs);
117             } else {
118 2         12 $constructed_component = $component->create(%attrs);
119             $attrs{container}->add_child($constructed_component) if
120 2 50       23 $attrs{container}->can('add_child');
121             }
122             } else {
123 3         19 $constructed_component = $component->create(%attrs);
124             }
125 5         25 return $constructed_component;
126             } elsif(ref($component) eq 'CODE') {
127 0         0 die "Component not an object";
128             #my $new_dom = $component->($dom->content, %attrs);
129             #warn $new_dom;
130             #$dom->replace($new_dom);
131             #warn $dom;
132             #return;
133             }
134             }
135              
136             sub process_attrs {
137 6     6 0 36 my ($self, $ctx, $dom, %attrs) = @_;
138             return map {
139 6         22 my $proto = $attrs{$_};
  23         57  
140 23 100       73 my $value = ref($proto) ? $proto->($ctx, $dom) : $proto;
141 23         2351 $_ => $value;
142             } keys %attrs;
143             }
144              
145             1;
146              
147             =head1 NAME
148              
149             Template::Lace::Renderer
150              
151             =head1 SYNOPSIS
152              
153             TBD
154              
155             =head1 DESCRIPTION
156              
157             Renderer for the model. Not really end user aimed. See L<Template::Lace>
158             for main overview.
159              
160             =head1 METHODS
161              
162             This class defines the following public methods
163              
164             =head2 call
165              
166             Allows you to have the renderer 'call' a method into the model, with the current
167             DOM. Takes either a coderef or a string (that must be the name of a method in the
168             model. Example:
169              
170             $renderer->call(sub {
171             my ($model, $dom) = @_;
172             $model->add_debug($dom);
173             }, @args);
174              
175             $renderer->call('add_debug', @args);
176              
177             Are both the same as
178              
179             $renderer
180             ->model
181             ->add_debug($renderer->dom, @args);
182              
183             You might find this is a useful shortcut (or not).
184              
185             =head2 call_at
186              
187             Basically similar to L</call> except allows you to specify a CSS match to
188             set the DOM.
189              
190             $renderer->call_at('#debug','add_debug', @args);
191              
192             Is basically a shortcut for:
193              
194             my $dom = $renderer->dom->at('#debug);
195             $renderer->model->add_debug($dom, @args);
196              
197             =head1 SEE ALSO
198            
199             L<Template::Lace>.
200              
201             =head1 AUTHOR
202              
203             Please See L<Template::Lace> for authorship and contributor information.
204            
205             =head1 COPYRIGHT & LICENSE
206            
207             Please see L<Template::Lace> for copyright and license information.
208              
209             =cut