File Coverage

blib/lib/Boxer/World/Reclass.pm
Criterion Covered Total %
statement 171 190 90.0
branch 25 44 56.8
condition 17 31 54.8
subroutine 30 33 90.9
pod 0 3 0.0
total 243 301 80.7


line stmt bran cond sub pod time code
1             package Boxer::World::Reclass;
2              
3             =encoding UTF-8
4              
5             =head1 NAME
6              
7             Boxer::World::Reclass - software as serialized by reclass
8              
9             =cut
10              
11 6     6   21260 use v5.20;
  6         26  
12 6     6   34 use utf8;
  6         14  
  6         47  
13 6     6   674 use Role::Commons -all;
  6         26846  
  6         49  
14 6     6   39438 use feature 'signatures';
  6         13  
  6         754  
15 6     6   611 use namespace::autoclean 0.16;
  6         12230  
  6         45  
16 6     6   1027 use autodie;
  6         14086  
  6         59  
17              
18 6     6   39118 use YAML::XS;
  6         17797  
  6         364  
19 6     6   3214 use Hash::Merge qw(merge);
  6         44728  
  6         389  
20 6     6   49 use Try::Tiny;
  6         11  
  6         345  
21              
22 6     6   605 use Moo;
  6         2876  
  6         43  
23 6     6   4850 use MooX::StrictConstructor;
  6         2720  
  6         45  
24             extends qw(Boxer::World);
25              
26 6     6   31414 use Types::Standard qw( ArrayRef InstanceOf Maybe );
  6         72764  
  6         130  
27 6     6   6584 use Boxer::Types qw( ClassDir NodeDir Suite );
  6         16  
  6         60  
28              
29 6     6   6442 use Boxer::Part::Reclass;
  6         26  
  6         249  
30 6     6   2604 use Boxer::World::Flat;
  6         25  
  6         274  
31              
32 6     6   48 use strictures 2;
  6         57  
  6         235  
33 6     6   1063 no warnings "experimental::signatures";
  6         15  
  6         16423  
34              
35             =head1 VERSION
36              
37             Version v1.4.1
38              
39             =cut
40              
41             our $VERSION = "v1.4.1";
42              
43             =head1 DESCRIPTION
44              
45             Outside the box is a world of software.
46              
47             B<Boxer::World::Reclass> is a class describing a collection of software
48             available for installation into (or as) an operating system.
49              
50             =head1 SEE ALSO
51              
52             L<Boxer>.
53              
54             =cut
55              
56             has suite => (
57             is => 'ro',
58             isa => Suite,
59             required => 1,
60             );
61              
62             has classdir => (
63             is => 'lazy',
64             isa => ClassDir,
65             coerce => 1,
66             required => 1,
67             );
68              
69             sub _build_classdir ($self)
70 1     1   16 {
  1         2  
  1         2  
71 1 50       23 if ( $self->data ) {
72 1         28 return $self->data->child('classes');
73             }
74 0         0 return;
75             }
76              
77             has nodedir => (
78             is => 'lazy',
79             isa => NodeDir,
80             coerce => 1,
81             required => 1,
82             );
83              
84             sub _build_nodedir ($self)
85 1     1   29 {
  1         2  
  1         5  
86 1 50       25 if ( $self->data ) {
87 1         33 return $self->data->child('nodes');
88             }
89 0         0 return;
90             }
91              
92             has parts => (
93             is => 'lazy',
94             isa => ArrayRef [ InstanceOf ['Boxer::Part::Reclass'] ],
95             init_arg => undef,
96             );
97              
98             sub _build_parts ($self)
99 6     6   65225 {
  6         11  
  6         12  
100 1400         2261 my $classdata = $self->classdir->visit(
101 1400     1400   1924 sub ( $path, $state ) {
  1400         184577  
  1400         1870  
102 1400 100       3419 return if $path->is_dir;
103 920 50       17049 return unless ( $path->basename =~ /\.yml$/ );
104 920         27498 my $yaml = Load( $path->slurp_utf8 );
105 920         216756 my $class = $path->relative( $self->classdir ) =~ tr/\//./r
106             =~ s/\.yml$//r =~ s/\.init$//r;
107 920         297049 $state->{$class} = $yaml;
108             },
109 6         115 { recurse => 1 },
110             );
111 10         19 my $nodedata = $self->nodedir->visit(
112 10     10   23 sub ( $path, $state ) {
  10         3034  
  10         16  
113 10 50       30 return if $path->is_dir;
114 10 50       261 return unless ( $path->basename =~ /\.yml$/ );
115 10         337 my $yaml = Load( $path->slurp_utf8 );
116 10         2361 my $node = $path->basename(qr/\.yml$/);
117 10         359 $state->{$node} = $yaml;
118             },
119 6         724 );
120 6         445 my @parts;
121 6         19 for ( sort keys %{$nodedata} ) {
  6         53  
122 10   33     27520 my %params = $nodedata->{$_}{parameters} || ();
123 10         23 my @classes = @{ $nodedata->{$_}{classes} };
  10         53  
124 10         43 while ( my $next = shift @classes ) {
125 1270 50       244401 unless ( $classdata->{$next} ) {
126 0         0 $self->_logger->debug(
127             "Ignoring missing class $next for node $_.");
128 0         0 next;
129             }
130 1270 100 100     4622 if ( $classdata->{$next}{classes} and !$params{_seen}{$next} ) {
131 450         879 $params{_seen}{$next} = 1;
132 450         604 unshift @classes, @{ $classdata->{$next}{classes} }, $next;
  450         1291  
133 450         1076 next;
134             }
135 725         1911 %params = %{ merge( \%params, $classdata->{$next}{parameters} ) }
136 820 100       1886 if $classdata->{$next}{parameters};
137             }
138 10         5543 delete $params{_seen};
139 0         0 %params = %{ merge( \%params, $nodedata->{$_}{parameters} ) }
140 10 50       48 if $nodedata->{$_}{parameters};
141 10         389 push @parts,
142             Boxer::Part::Reclass->new(
143             id => $_,
144             epoch => $self->suite,
145             %params,
146             );
147             }
148 6         6360 return [@parts];
149             }
150              
151             sub list_parts ($self)
152 1     1 0 205 {
  1         4  
  1         2  
153 1         2 return map { $_->id } @{ $self->parts };
  2         17  
  1         24  
154             }
155              
156 10         23 sub get_part ( $self, $id )
157 10     10 0 224149 {
  10         23  
  10         15  
158 10 100       21 unless ( @{ $self->parts } ) {
  10         288  
159 1         28 $self->_logger->error("No parts exist.");
160 1         230 return;
161             }
162 9         309 foreach ( @{ $self->parts } ) {
  9         170  
163 11 100       142 if ( $_->id eq $id ) {
164 8         32 return $_;
165             }
166             }
167 1         27 $self->_logger->error("Part \"$id\" does not exist.");
168 1         441 return;
169             }
170              
171             my $pos = 1;
172             my @section_order = qw(
173             Administration
174             Service
175             Console
176             Desktop
177             Language
178             Framework
179             Task
180             Hardware
181             );
182             my %section_order = map { $_ => $pos++ } @section_order;
183              
184 6         12 sub map ( $self, $node_id, $nonfree )
  6         14  
185 6     6 0 17 {
  6         13  
  6         12  
186 6         24 my $node = $self->get_part($node_id);
187 6         40 my %desc;
188              
189             my @section_keys = sort {
190 126 0 50     481 ( $section_order{$a} // 1000 ) <=> ( $section_order{$b} // 1000 )
      50        
191             || $a cmp $b
192 6         14 } keys %{ $node->{doc} };
  6         57  
193              
194 6         23 foreach my $key (@section_keys) {
195 55   66     148 my $headline = $node->{doc}{$key}{headline}[0] || $key;
196 55 50 66     219 if (( $node->{pkg} and $node->{doc}{$key}{pkg} )
      33        
      0        
      66        
197             or ( $nonfree
198             and $node->{'pkg-nonfree'}
199             and $node->{doc}{$key}{'pkg-nonfree'} )
200             )
201             {
202 54         68 push @{ $desc{pkg} }, "# $headline";
  54         132  
203 54 50       113 if ( $node->{pkg} ) {
204 54         72 foreach ( @{ $node->{doc}{$key}{pkg} } ) {
  54         112  
205 223         265 push @{ $desc{pkg} }, "# * $_";
  223         518  
206             }
207             }
208 54 0 33     113 if ( $nonfree and $node->{'pkg-nonfree'} ) {
209 0         0 foreach ( @{ $node->{doc}{$key}{'pkg-nonfree'} } ) {
  0         0  
210 0         0 push @{ $desc{pkg} }, "# * [non-free] $_";
  0         0  
211             }
212             }
213             }
214 55 100 100     133 if ( $node->{tweak} and $node->{doc}{$key}{tweak} ) {
215 2         3 push @{ $desc{tweak} }, "# $headline";
  2         9  
216 2         4 foreach ( @{ $node->{doc}{$key}{tweak} } ) {
  2         7  
217 6         8 push @{ $desc{tweak} }, "# * $_";
  6         15  
218             }
219             }
220             }
221             my $pkgdesc
222             = defined( $desc{pkg} )
223 6 50       24 ? join( "\n", @{ $desc{pkg} } )
  6         351  
224             : '';
225             my $tweakdesc
226             = defined( $desc{tweak} )
227 6 100       29 ? join( "\n", @{ $desc{tweak} } )
  1         6  
228             : '';
229 6     6   410 my @pkg = try { @{ $node->{pkg} } }
  6         93  
230             catch {
231 0     0   0 $self->_logger->warning('No packages resolved');
232 0         0 return ();
233 6         83 };
234 6     6   227 my @pkgauto = try { @{ $node->{'pkg-auto'} } }
  6         30  
235             catch {
236 0     0   0 $self->_logger->warning('No package auto-markings resolved');
237 0         0 return ();
238 6         229 };
239 6     6   218 my @pkgavoid = try { @{ $node->{'pkg-avoid'} } }
  6         24  
240             catch {
241 0     0   0 $self->_logger->warning('No package avoidance resolved');
242 0         0 return ();
243 6         267 };
244 6     6   203 my @tweak = try { @{ $node->{tweak} } }
  6         74  
245             catch {
246 5     5   209 $self->_logger->warning('No tweaks resolved');
247 5         1349 return ();
248 6         114 };
249 6 50       58 if ($nonfree) {
250 0 0       0 push @pkg, @{ $node->{'pkg-nonfree'} } if ( $node->{'pkg-nonfree'} );
  0         0  
251 0         0 push @pkgauto, @{ $node->{'pkg-nonfree-auto'} }
252 0 0       0 if ( $node->{'pkg-nonfree-auto'} );
253             }
254 6         19 chomp(@tweak);
255              
256 6         145 return Boxer::World::Flat->new(
257             node => $node_id,
258             epoch => $node->epoch,
259             pkgs => \@pkg,
260             pkgs_auto => \@pkgauto,
261             pkgs_avoid => \@pkgavoid,
262             tweaks => \@tweak,
263             pkgdesc => $pkgdesc,
264             tweakdesc => $tweakdesc,
265             nonfree => $nonfree, # TODO: unset if none resolved
266             );
267             }
268              
269             =head1 AUTHOR
270              
271             Jonas Smedegaard C<< <dr@jones.dk> >>.
272              
273             =cut
274              
275             our $AUTHORITY = 'cpan:JONASS';
276              
277             =head1 COPYRIGHT AND LICENCE
278              
279             Copyright © 2013-2016 Jonas Smedegaard
280              
281             This is free software; you can redistribute it and/or modify it under
282             the same terms as the Perl 5 programming language system itself.
283              
284             =head1 DISCLAIMER OF WARRANTIES
285              
286             THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
287             WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
288             MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
289              
290             =cut
291              
292             1;