File Coverage

blib/lib/HiveJSO.pm
Criterion Covered Total %
statement 159 160 99.3
branch 71 80 88.7
condition 17 21 80.9
subroutine 25 25 100.0
pod 3 8 37.5
total 275 294 93.5


line stmt bran cond sub pod time code
1             package HiveJSO;
2             BEGIN {
3 6     6   159156 $HiveJSO::AUTHORITY = 'cpan:GETTY';
4             }
5             # ABSTRACT: HiveJSO Perl Implementation
6             $HiveJSO::VERSION = '0.010';
7 6     6   25778 use Moo;
  6         122193  
  6         40  
8 6     6   18493 use JSON::MaybeXS;
  6         63451  
  6         416  
9 6     6   4180 use HiveJSO::Error;
  6         26  
  6         276  
10 6     6   6991 use Digest::CRC qw( crc32 );
  6         35798  
  6         656  
11 6     6   53 use Carp qw( croak );
  6         15  
  6         16160  
12              
13             our %short_attributes = qw(
14             c checksum
15             d data
16             ma manufacturer
17             maw manufacturer_web
18             maf manufacturer_factory
19             mac manufacturer_country
20             n units
21             o command
22             p product_id
23             pr product
24             prw product_web
25             prt product_timestamp
26             q supports
27             r ranges
28             s sources
29             t timestamp
30             tz timestamp_timezone
31             u unit
32             w software
33             x error_code
34             xt error
35             );
36              
37             our @long_attributes = qw(
38             ok
39             path
40             );
41              
42             our @attributes = sort {
43             $a cmp $b
44             } ( @long_attributes, values %short_attributes );
45              
46             has [grep { $_ ne 'unit' } @attributes] => (
47             is => 'ro',
48             predicate => 1,
49             );
50              
51             has unit => (
52             is => 'ro',
53             required => 1,
54             );
55              
56             sub new_via_json {
57 25     25 0 21311 my ( $class, $json ) = @_;
58 25         38 my %obj = %{decode_json($json)};
  25         390  
59 23         575 return $class->new( %obj, original_json => $json );
60             }
61              
62             has unit_id => (
63             is => 'lazy',
64             init_arg => undef,
65             );
66              
67             sub _build_unit_id {
68 11     11   19326 my ( $self ) = @_;
69 11         73 my @unit_parts = split('/',$self->unit);
70 11         20 my $id_source = pop @unit_parts;
71 11         21 my $unit_id = pop @unit_parts;
72 11         77 return $unit_id+0;
73             }
74              
75             has id_source => (
76             is => 'lazy',
77             init_arg => undef,
78             );
79              
80             sub _build_id_source {
81 11     11   1338 my ( $self ) = @_;
82 11         64 my @unit_parts = split('/',$self->unit);
83 11         31 my $id_source = pop @unit_parts;
84 11         22 my $unit_id = pop @unit_parts;
85 11         122 return $id_source+0;
86             }
87              
88             has original_json => (
89             is => 'ro',
90             predicate => 1,
91             );
92              
93             sub BUILDARGS {
94 26     26 0 21470 my ( $class, @args ) = @_;
95 26         44 my %orig;
96 26 100       81 if (scalar @args == 1) {
97 1         2 %orig = %{$args[0]};
  1         5  
98             } else {
99 25         86 %orig = @args;
100             }
101 26         53 my %attr;
102              
103             #
104             # only accept allowed attributes
105             # only accept short or long not mixed
106             #
107 26         34 my ( $short, $long );
108 26         47 my $short_long_error = __PACKAGE__." you can't mix short HiveJSO attribute names with long HiveJSO attributes";
109 26         87 for my $k (keys %orig) {
110 80 100       456 if (defined $short_attributes{$k}) {
  1428 100       2273  
    100          
111 12 50       26 croak $short_long_error if $long; $short = 1;
  12         14  
112 12         27 $attr{$short_attributes{$k}} = $orig{$k};
113             } elsif (grep {
114             $k eq $short_attributes{$_}
115 82         174 } keys %short_attributes) {
116 27 50       69 croak $short_long_error if $short; $long = 1;
  27         36  
117 27         101 $attr{$k} = $orig{$k};
118             } elsif (grep { $_ eq $k } @long_attributes) {
119 18         61 $attr{$k} = $orig{$k};
120             } else {
121 23 100       51 if ($k eq 'original_json') {
122 22         43 $attr{$k} = $orig{$k}; delete $orig{$k};
  22         78  
123             } else {
124 1         168 croak __PACKAGE__." '".$k."' is not a valid HiveJSO attribute";
125             }
126             }
127             }
128              
129             #
130             # remove checksum now of input attributes
131             #
132 25   100     152 my $checksum = delete $orig{c} || delete $orig{checksum};
133              
134             #
135             # we need at least 2 attributes without checksum (unit and one other)
136             #
137 25 100       77 if (keys %orig < 2) {
138 1         243 croak __PACKAGE__." we need more attributes for a valid HiveJSO";
139             }
140              
141             #
142             # ok must be 1
143             #
144 24 100 100     142 if (defined $attr{ok} && $attr{ok} != 1) {
145 1         148 croak __PACKAGE__." ok attribute must be set to 1 for a valid HiveJSO";
146             }
147              
148             #
149             # data attribute validation
150             #
151 23 100       64 if (defined $attr{data}) {
152 5 50       17 croak __PACKAGE__." 'data' must be an array" unless ref $attr{data} eq 'ARRAY';
153 5         7 for my $data_set (@{$attr{data}}) {
  5         11  
154 7 100       316 croak __PACKAGE__." values inside the 'data' array must be arrays" unless ref $data_set eq 'ARRAY';
155 6 100       7 croak __PACKAGE__." array inside 'data' array needs at least one value" unless scalar @{$data_set};
  6         177  
156 5 100       263 croak __PACKAGE__." first value in array inside 'data' array must be positive integer above 0" unless $data_set->[0] > 0;
157             }
158             }
159              
160             #
161             # novell check
162             #
163 20 100       51 if (defined $attr{error_code}) {
164 1 50       193 croak __PACKAGE__." error_code must be positive integer above 0" unless $attr{error_code} > 0;
165             }
166              
167             #
168             # checksum check result is just an attribute, doesn't lead to failure
169             #
170 19 100       43 if ($checksum) {
171 6         23 my $calced_checksum = $class->calc_checksum(%orig);
172 6 100       29943 unless($calced_checksum == $checksum) {
173 1         277 croak __PACKAGE__." invalid HiveJSO checksum, should be '".$calced_checksum."'";
174             };
175             }
176 18         594 return { %attr };
177             }
178              
179             sub calc_checksum {
180 10     10 0 41 my ( $class, %obj ) = @_;
181 22         70 my $checksum_string = join(',',map {
182 13         35 '"'.$_.'"', $class->_get_value_checksum($obj{$_})
183 22 50       122 } sort { $a cmp $b } grep {
184 10         24 $_ ne 'checksum' && $_ ne 'c'
185             } keys %obj);
186 10         49 return crc32($checksum_string);
187             }
188              
189             sub _get_value_checksum {
190 28     28   38 my ( $class, $value ) = @_;
191 28 100       57 if (ref $value eq 'ARRAY') {
192 6         15 return '['.join(',',map {
193 3         5 $class->_get_value_checksum($_)
194 3         5 } @{$value}).']';
195             }
196 25         116 return '"'.$value.'"';
197             }
198              
199             has hivejso => (
200             is => 'lazy',
201             init_arg => undef,
202             );
203              
204             sub _build_hivejso {
205 1     1   5993 my ( $self ) = @_;
206 1         3 return encode_json({
207 1         2 %{$self->hivejso_data},
208             checksum => $self->hivejso_checksum,
209             });
210             }
211              
212             has hivejso_short => (
213             is => 'lazy',
214             init_arg => undef,
215             );
216              
217             sub _build_hivejso_short {
218 1     1   443 my ( $self ) = @_;
219 1         5 return encode_json({
220 1         2 %{$self->hivejso_data_short},
221             c => $self->hivejso_checksum_short,
222             });
223             }
224              
225             has hivejso_data => (
226             is => 'lazy',
227             init_arg => undef,
228             );
229              
230             sub _build_hivejso_data {
231 3     3   1146 my ( $self ) = @_;
232             return {
233 63 100       311 unit => $self->unit,
234             (map {
235 69 100 100     546 $self->can('has_'.$_)->($self) ? ( $_ => $self->$_ ) : ()
236             } grep {
237 3         18 $_ ne 'unit' && $_ ne 'checksum' && $_ ne 'c'
238             } @attributes),
239             };
240             }
241              
242             has hivejso_data_short => (
243             is => 'lazy',
244             init_arg => undef,
245             );
246              
247             sub _build_hivejso_data_short {
248 2     2   1026 my ( $self ) = @_;
249 42 100       215 my %short_data = (
250             u => $self->unit,
251             (map {
252 46 100 100     211 $self->can('has_'.$_)->($self) ? ( $_ => $self->$_ ) : ()
253             } grep {
254 2         14 $_ ne 'unit' && $_ ne 'checksum' && $_ ne 'c'
255             } @attributes),
256             );
257 2         16 for my $k (keys %short_attributes) {
258 42 50       98 if ($short_data{$short_attributes{$k}}) {
259 0         0 $short_data{$k} = delete $short_data{$short_attributes{$k}};
260             }
261             }
262 2         25 return { %short_data };
263             }
264              
265             has hivejso_checksum => (
266             is => 'lazy',
267             init_arg => undef,
268             );
269              
270             sub _build_hivejso_checksum {
271 2     2   1822 my ( $self ) = @_;
272 2         4 return $self->calc_checksum(%{$self->hivejso_data});
  2         22  
273             }
274              
275             has hivejso_checksum_short => (
276             is => 'lazy',
277             init_arg => undef,
278             );
279              
280             sub _build_hivejso_checksum_short {
281 1     1   367 my ( $self ) = @_;
282 1         3 return $self->calc_checksum(%{$self->hivejso_data_short});
  1         24  
283             }
284              
285             sub parse {
286 11     11 1 5668 my ( $class, $string ) = @_;
287 11         44 return $class->_parse(0,$string);
288             }
289              
290             sub parse_one {
291 3     3 1 6 my ( $class, $string ) = @_;
292 3         12 return $class->_parse(1,$string);
293             }
294              
295             sub parse_seek {
296 3     3 1 8 my ( $class, $string ) = @_;
297 3         12 my @parsed = $class->parse_one($string);
298 3         4 my ( $obj, $post );
299 3 100       11 if (ref $parsed[0]) {
    100          
300 1         2 $obj = shift @parsed;
301 1         3 $post = shift @parsed;
302             } elsif (scalar @parsed == 1) {
303 1         4 $post = shift @parsed;
304             } else {
305 1         2 shift @parsed;
306 1         3 $obj = shift @parsed;
307 1         2 $post = shift @parsed;
308             }
309 3         12 return ( $obj, $post );
310             }
311              
312             sub _parse_first {
313 15     15   29 my ( $class, $string, $current ) = @_;
314 15 100       53 my $start = defined $current
315             ? index($string,'{',$current)
316             : index($string,'{');
317 15 100       44 return if $start == -1;
318 14         25 my $end = index($string,'}',$start);
319 14 100       31 return if $end == -1;
320 10         27 my $test = substr($string,$start,$end-$start+1);
321 10         17 my $another_start = index($test,'{',1);
322 10 100       59 if ($another_start == -1) {
323 9 100       47 my @result = (
324             $start == 0 ? "" : substr($string,0,$start),
325             substr($string,$start,$end-$start+1),
326             substr($string,$end+1),
327             );
328 9         37 return @result;
329             } else {
330 1 50 33     8 return if defined $current && $another_start == $current; # TODO
331 1         9 return $class->_parse_first($string,$another_start);
332             }
333             }
334              
335             sub _parse {
336 14     14   25 my ( $class, $one, $string ) = @_;
337 14         17 my @results;
338 14         44 my @parse_first = $class->_parse_first($string);
339 14 100       34 if (@parse_first) {
340 9         16 my ( $pre, $obj, $post ) = @parse_first;
341 9 100 66     53 push @results, $pre if $pre && length($pre);
342 9         13 my $object;
343 9         11 eval {
344 9         29 $object = $class->new_via_json($obj);
345             };
346 9 100       325 if ($@) {
347 2         44 $object = HiveJSO::Error->new(
348             garbage => $obj,
349             error => $@,
350             );
351             }
352 9         1613 push @results, $object;
353 9 100 66     69 push @results, ( $one ? $post : $class->parse($post) ) if $post && length($post);
    100          
354             } else {
355 5         10 push @results, $string;
356             }
357 14         53 return @results;
358             }
359              
360             sub add {
361 1     1 0 53 my ( $self, %newattr ) = @_;
362 1         5 my %newobj = (
363 1         3 %{$self->hivejso_data},
364             %newattr,
365             );
366 1 50       42 return $self->new(
367             %newobj,
368             $self->has_checksum ? ( checksum => $self->calc_checksum(%newobj) ) : (),
369             );
370             }
371              
372             sub add_short {
373 1     1 0 64 my ( $self, %newattr ) = @_;
374 1         7 my %newobj = (
375 1         3 %{$self->hivejso_data_short},
376             %newattr,
377             );
378 1 50       12 return $self->new(
379             %newobj,
380             $self->has_checksum ? ( c => $self->calc_checksum(%newobj) ) : (),
381             );
382             }
383              
384             1;
385              
386             __END__