File Coverage

blib/lib/Moonshine/Template.pm
Criterion Covered Total %
statement 64 121 52.8
branch 11 52 21.1
condition 5 34 14.7
subroutine 17 22 77.2
pod 1 5 20.0
total 98 234 41.8


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