File Coverage

blib/lib/Moonshine/Template.pm
Criterion Covered Total %
statement 59 116 50.8
branch 11 52 21.1
condition 5 34 14.7
subroutine 16 21 76.1
pod 1 5 20.0
total 92 228 40.3


line stmt bran cond sub pod time code
1             package Moonshine::Template;
2              
3 2     2   43967 use strict;
  2         3  
  2         59  
4 2     2   7 use warnings;
  2         4  
  2         77  
5              
6             our $VERSION = '0.04';
7              
8 2     2   1367 use Moonshine::Element;
  2         59026  
  2         87  
9 2     2   18 use Ref::Util qw/:all/;
  2         3  
  2         527  
10 2     2   1194 use Hash::Merge qw/merge/;
  2         4992  
  2         157  
11              
12             our @ISA;
13 2     2   111 BEGIN { @ISA = ('UNIVERSAL::Object') }
14              
15             our %HAS;
16              
17             BEGIN {
18 2     2   1701 %HAS = ( base_element => sub { undef } );
  0         0  
19             }
20              
21             sub BUILD {
22 13     13 1 38710 my ( $self, $build_args ) = @_;
23              
24 13   50     113 my $config = $self->_merge_configs( $build_args->{config} // {} );
25              
26             my $base_element = $self->add_base_element( $build_args->{base_element}
27 13   66     4675 // delete $config->{base_element} );
28              
29 13 50       5281 if ( defined $config ) {
30 13         60 $config = $self->_process_config( $config, $base_element );
31             }
32              
33 13 100       71 die "build_html is not defined" unless $self->can('build_html');
34              
35 12 100       40 if ( is_blessed_ref($base_element) ) {
36 7         32 $self->{base_element} = $base_element;
37             }
38              
39             $base_element =
40 12         57 $self->build_html( $self->_return_base_element($base_element) );
41              
42 12         20774 $self->{base_element} = $base_element;
43              
44 12         49 return;
45             }
46              
47             sub add_base_element {
48 18     18 0 57 my ( $self, $base_element_args ) = @_;
49              
50 18 100       79 if ( $self->can('base_element') ) {
51 7         21 $base_element_args = merge( $self->base_element, $base_element_args );
52             }
53              
54 18 100       356 if ( is_hashref($base_element_args) ) {
55             return
56             defined $base_element_args->{template}
57             ? $base_element_args->{template}
58             ->new( $base_element_args->{template_args} // {} )
59 13 50 0     71 : Moonshine::Element->new($base_element_args);
60             }
61              
62 5         10 return undef;
63             }
64              
65             sub render {
66 10     10 0 7585 return $_[0]->{base_element}->render;
67             }
68              
69             sub children {
70 0     0 0 0 my $element = $_[0]->_return_base_element( $_[0]->{base_element} );
71 0         0 return $element->{children};
72             }
73              
74             sub element {
75 0     0 0 0 return $_[0]->_return_base_element( $_[0]->{base_element} );
76             }
77              
78             sub _merge_configs {
79 13     13   24 my ( $self, $build_config ) = ( shift, shift );
80 13   33     123 my $base_config = $self->can('config') && $self->config // {};
      50        
81 13         55 return merge( $build_config, $base_config );
82             }
83              
84             sub _process_config {
85 13     13   22 my ( $self, $config, $element ) = @_;
86              
87 13         47 my $action_config = $self->_config_to_actions($config);
88              
89 13         18 for ( @{$action_config} ) {
  13         32  
90 0         0 my $key = ( keys %{$_} )[0];
  0         0  
91 0         0 my $value = $_->{$key};
92              
93             my $processed_element =
94 0 0       0 $self->add_base_element( $value->{build} ? $value->{build} : $value );
95              
96 0 0       0 if ( is_blessed_ref($processed_element) ) {
97 0 0       0 if ( defined $value->{target} ) {
98             my $target =
99             $value->{target} eq 'base_element'
100             ? $self->_return_base_element($element)
101             : $self->_return_base_element(
102 0 0       0 $config->{ $value->{target} } );
103 0   0     0 my $action = $value->{action} // 'add_child';
104 0         0 $target->$action(
105             $self->_return_base_element($processed_element) );
106             }
107 0         0 $config->{$key} = $processed_element;
108             }
109             }
110              
111 13         15 for ( keys %{$config} ) {
  13         26  
112 0         0 _make_shine( $_, $config );
113             }
114              
115 13         26 return $config;
116             }
117              
118             sub _config_to_actions {
119 13     13   16 my ( $self, $config ) = @_;
120              
121 13         26 my @configs = ();
122 13         14 my @keys = keys %{$config};
  13         36  
123 13         20 my $previous;
124 13         42 while (@keys) {
125 0         0 my $key = shift @keys;
126 0         0 my $value = $config->{$key};
127              
128 0 0       0 grep { defined $value->{$_} } qw/action target template tag build/
  0         0  
129             or next;
130              
131 0 0 0     0 $previous && $previous eq $key
      0        
132             and die "$key target - $value->{target} does not exist in the spec"
133             or $previous = $key;
134              
135             my $target = $value->{target}
136 0 0 0     0 or unshift @configs, { $key => $value }
137             and next;
138              
139 0 0 0     0 $target eq 'base_element'
140             and unshift @configs, { $key => $value }
141             and next;
142              
143 0         0 my $success = 0;
144 0 0       0 if ( my $config_count = scalar @configs ) {
145 0         0 for ( my $index = 0 ; $index < $config_count ; $index++ ) {
146 0 0       0 if ( my $target_found = $configs[$index]->{$target} ) {
147 0         0 splice @configs, $index + 1, 0, { $key => $value };
148 0         0 $success = 1;
149 0         0 last;
150             }
151             }
152             }
153 0 0       0 unless ($success) {
154 0         0 push @keys, $key;
155             }
156             }
157              
158 13         30 return \@configs;
159             }
160              
161             sub _make_shine {
162 0     0   0 my ( $key, $config ) = @_;
163              
164             {
165 2     2   14 no strict 'refs';
  2         3  
  2         78  
  0         0  
166 2     2   11 no warnings 'redefine';
  2         2  
  2         839  
167             {
168 0         0 *{"has_$key"} = sub {
  0         0  
169 0     0   0 my $val = $config->{$key};
170 0 0       0 defined $val or return undef;
171 0 0       0 is_arrayref($val) and return scalar @{$val};
  0         0  
172 0         0 is_hashref($val) and return map { $_; }
173 0 0       0 sort { $a <=> $b or $a cmp $b }
174 0 0       0 keys %{$val};
  0         0  
175 0         0 return 1;
176             }
177 0         0 };
178             {
179 0         0 *{"$key"} = sub {
  0         0  
180 0     0   0 my $val = $config->{$key};
181 0 0       0 defined $_[1] or return $val;
182             is_arrayref($val) && not is_arrayref( $_[1] )
183 0 0 0     0 and return push @{$val}, $_[1];
  0         0  
184             is_hashref($val) and ( is_hashref( $_[1] )
185             and return
186 0         0 map { $config->{$_} = $_[1]->{$_} } keys %{ $_[1] } )
  0         0  
187 0 0 0     0 or ( is_scalarref( \$_[1] ) and return $val->{ $_[1] } );
      0        
      0        
188 0 0       0 $config->{$key} = $_[1] and return;
189             }
190 0         0 };
191             };
192              
193 0         0 return 1;
194             }
195              
196             sub _return_base_element {
197 12 50   12   58 return $_[1]->{base_element} ? $_[1]->{base_element} : $_[1];
198             }
199              
200             1;
201              
202             __END__