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