File Coverage

blib/lib/JSON/Schema/Fit.pm
Criterion Covered Total %
statement 100 103 97.0
branch 37 44 84.0
condition 26 33 78.7
subroutine 26 27 96.3
pod 8 8 100.0
total 197 215 91.6


line stmt bran cond sub pod time code
1             package JSON::Schema::Fit;
2             $JSON::Schema::Fit::VERSION = '0.06';
3             # ABSTRACT: adjust data structure according to json-schema
4              
5              
6 2     2   184912 use 5.010;
  2         25  
7 2     2   10 use strict;
  2         5  
  2         42  
8 2     2   11 use warnings;
  2         4  
  2         50  
9 2     2   1068 use utf8;
  2         30  
  2         10  
10              
11 2     2   61 use Carp;
  2         19  
  2         118  
12              
13 2     2   14 use JSON;
  2         3  
  2         9  
14 2     2   186 use Scalar::Util qw/reftype/;
  2         5  
  2         99  
15 2     2   13 use List::Util qw/first/;
  2         3  
  2         183  
16 2     2   841 use Math::Round qw/round nearest/;
  2         15448  
  2         2766  
17              
18              
19 11     11 1 48 sub booleans { return _attr('booleans', @_); }
20              
21              
22 45     45 1 80 sub numbers { return _attr('numbers', @_); }
23              
24              
25              
26 26     26 1 51 sub round_numbers { return _attr('round_numbers', @_); }
27              
28              
29 26     26 1 48 sub clamp_numbers { return _attr('clamp_numbers', @_); }
30              
31              
32              
33 10     10 1 21 sub strings { return _attr('strings', @_); }
34              
35              
36 11     11 1 41 sub hash_keys { return _attr('hash_keys', @_); }
37              
38             # Store valid options as well as default values
39             my %valid_option =(
40             ( map { ($_ => 1) } qw!booleans numbers round_numbers strings hash_keys! ),
41             ( map { ($_ => 0) } qw!clamp_numbers! ),
42             );
43              
44             sub new {
45 10     10 1 4805 my ($class, %opts) = @_;
46 10         22 my $self = bless {}, $class;
47 10         26 for my $k (keys %opts) {
48 13 50       30 next unless exists $valid_option{$k};
49 13         38 _attr($k, $self, $opts{$k});
50             }
51 10         47 return $self
52             }
53              
54             sub _attr {
55 142     142   218 my $attr = shift;
56 142         199 my $self = shift;
57              
58 142 100       261 if (@_) {
59 13         36 return $self->{$attr} = shift;
60             } else {
61 129   100     639 return $self->{$attr} //= $valid_option{$attr};
62             }
63             }
64              
65              
66             sub get_adjusted {
67 66     66 1 42965 my ($self, $struc, $schema, $jpath) = @_;
68              
69 66 100 66     339 return $struc if !ref $schema || reftype $schema ne 'HASH';
70 63         176 my $method = $self->_adjuster_by_type($schema->{type});
71 63 50       123 return $struc if !$method;
72 63         151 return $self->$method($struc, $schema, $jpath);
73             }
74              
75              
76             sub _adjuster_by_type {
77 63     63   117 my ($self, $type) = @_;
78              
79 63 50       152 return if !$type;
80 63         138 my $method = "_get_adjusted_$type";
81 63 50       265 return $method if $self->can($method);
82 0         0 return;
83             }
84              
85              
86             sub _get_adjusted_boolean {
87 11     11   27 my ($self, $struc, $schema, $jpath) = @_;
88              
89 11 100       38 return $struc if !$self->booleans();
90 9 100       36 return JSON::true if $struc;
91 1         4 return JSON::false;
92             }
93              
94              
95             sub _get_adjusted_integer {
96 17     17   34 my ($self, $struc, $schema, $jpath) = @_;
97              
98 17 100       37 return $struc if !$self->numbers();
99 15         42 my $result = $self->_get_adjusted_number($struc, $schema, $jpath);
100 15         42 return round($result);
101             }
102              
103              
104             sub _get_adjusted_number {
105 28     28   52 my ($self, $struc, $schema, $jpath) = @_;
106              
107 28 100       54 return $struc if !$self->numbers();
108 26         69 my $result = 0+$struc;
109              
110 26 100       50 if ($self->round_numbers) {
111 24   66     82 my $quantum = $schema->{multipleOf} || $schema->{divisibleBy};
112 24 100       73 $result = nearest $quantum, $result if $quantum;
113             }
114              
115 26 100       251 if ($self->clamp_numbers) {
116 16 100 100     62 $result = $schema->{maximum} if exists $schema->{maximum} and $result > $schema->{maximum};
117 16 100 100     43 $result = $schema->{minimum} if exists $schema->{minimum} and $result < $schema->{minimum};
118             }
119              
120 26         67 return $result;
121             }
122              
123              
124             sub _get_adjusted_string {
125 10     10   23 my ($self, $struc, $schema, $jpath) = @_;
126              
127 10 100       27 return $struc if !$self->strings();
128 8         39 return "$struc";
129             }
130              
131              
132             sub _get_adjusted_array {
133 1     1   4 my ($self, $struc, $schema, $jpath) = @_;
134              
135 1 50       5 croak "Structure is not ARRAY at $jpath" if reftype $struc ne 'ARRAY';
136              
137 1         3 my $result = [];
138 1         4 for my $i ( 0 .. $#$struc ) {
139 3         33 push @$result, $self->get_adjusted($struc->[$i], $schema->{items}, $self->_jpath($jpath, $i));
140             }
141              
142 1         13 return $result;
143             }
144              
145              
146              
147             sub _get_adjusted_object {
148 11     11   25 my ($self, $struc, $schema, $jpath) = @_;
149              
150 11 50       39 croak "Structure is not HASH at $jpath" if reftype $struc ne 'HASH';
151              
152 11         21 my $result = {};
153 11         19 my $keys_re;
154              
155 11   50     29 my $properties = $schema->{properties} || {};
156 11   50     39 my $p_properties = $schema->{patternProperties} || {};
157              
158 11 100 100     29 if ($self->hash_keys() && exists $schema->{additionalProperties} && !$schema->{additionalProperties}) {
      66        
159             my $keys_re_text = join q{|}, (
160             keys %$p_properties,
161 8         47 map {quotemeta} keys %$properties,
  29         66  
162             );
163 8         111 $keys_re = qr{^$keys_re_text$}x;
164             }
165              
166 11         61 for my $key (keys %$struc) {
167 49 100 100     419 next if $keys_re && $key !~ $keys_re;
168              
169 41         70 my $subschema = $properties->{$key};
170 41 50 66 0   138 if (my $re_key = !$subschema && first {$key =~ /$_/x} keys %$p_properties) {
  0         0  
171 0         0 $subschema = $p_properties->{$re_key};
172             }
173              
174 41         102 $result->{$key} = $self->get_adjusted($struc->{$key}, $subschema, $self->_jpath($jpath, $key));
175             }
176              
177 11         106 return $result;
178             }
179              
180              
181             sub _jpath {
182 44     44   84 my ($self, $path, $key) = @_;
183 44   50     185 $path //= q{$};
184              
185 44 100       272 return "$path.$key" if $key =~ /^[_A-Za-z]\w*$/x;
186            
187 3         7 $key =~ s/(['\\])/\\$1/gx;
188 3         13 return $path . "['$key']";
189             }
190              
191              
192             1;
193              
194             __END__