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   95503 use strict;
  2         4  
  2         45  
4 2     2   6 use warnings;
  2         4  
  2         65  
5              
6             our $VERSION = '0.03';
7              
8 2     2   884 use Moonshine::Element;
  2         37207  
  2         57  
9 2     2   14 use Ref::Util qw/:all/;
  2         2  
  2         403  
10 2     2   945 use Hash::Merge qw/merge/;
  2         3715  
  2         119  
11              
12             our @ISA;
13 2     2   77 BEGIN { @ISA = ('UNIVERSAL::Object') }
14              
15             our %HAS;
16              
17             BEGIN {
18 2     2   1201 %HAS = ( base_element => sub { undef } );
  0         0  
19             }
20              
21             sub BUILD {
22 13     13 1 21275 my ( $self, $build_args ) = @_;
23              
24 13   50     81 my $config = $self->_merge_configs( $build_args->{config} // {} );
25              
26             my $base_element = $self->add_base_element( $build_args->{base_element}
27 13   66     3710 // delete $config->{base_element} );
28              
29 13 50       2401 if ( defined $config ) {
30 13         37 $config = $self->_process_config( $config, $base_element );
31             }
32              
33 13 100       48 die "build_html is not defined" unless $self->can('build_html');
34              
35 12 100       23 if ( is_blessed_ref($base_element) ) {
36 7         19 $self->{base_element} = $base_element;
37             }
38              
39             $base_element =
40 12         28 $self->build_html( $self->_return_base_element($base_element) );
41              
42 12         9245 $self->{base_element} = $base_element;
43              
44 12         27 return;
45             }
46              
47             sub add_base_element {
48 18     18 0 36 my ( $self, $base_element_args ) = @_;
49              
50 18 100       56 if ( $self->can('base_element') ) {
51 7         15 $base_element_args = merge( $self->base_element, $base_element_args );
52             }
53              
54 18 100       252 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     53 : Moonshine::Element->new($base_element_args);
60             }
61              
62 5         8 return undef;
63             }
64              
65             sub render {
66 10     10 0 4234 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   18 my ( $self, $build_config ) = ( shift, shift );
80 13   33     73 my $base_config = $self->can('config') && $self->config // {};
      50        
81 13         35 return merge( $build_config, $base_config );
82             }
83              
84             sub _process_config {
85 13     13   15 my ( $self, $config, $element ) = @_;
86              
87 13         29 my $action_config = $self->_config_to_actions($config);
88              
89 13         13 for ( @{$action_config} ) {
  13         21  
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         10 for ( keys %{$config} ) {
  13         17  
112 0         0 _make_shine( $_, $config );
113             }
114              
115 13         17 return $config;
116             }
117              
118             sub _config_to_actions {
119 13     13   11 my ( $self, $config ) = @_;
120              
121 13         18 my @configs = ();
122 13         12 my @keys = keys %{$config};
  13         23  
123 13         11 my $previous;
124 13         24 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         21 return \@configs;
159             }
160              
161             sub _make_shine {
162 0     0   0 my ( $key, $config ) = @_;
163              
164             {
165 2     2   10 no strict 'refs';
  2         3  
  2         51  
  0         0  
166 2     2   7 no warnings 'redefine';
  2         2  
  2         582  
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   34 return $_[1]->{base_element} ? $_[1]->{base_element} : $_[1];
198             }
199              
200             1;
201              
202             __END__