File Coverage

blib/lib/JSONSchema/Validator/JSONPointer.pm
Criterion Covered Total %
statement 85 89 95.5
branch 29 40 72.5
condition 24 29 82.7
subroutine 19 19 100.0
pod 0 12 0.0
total 157 189 83.0


line stmt bran cond sub pod time code
1             package JSONSchema::Validator::JSONPointer;
2              
3             # ABSTRACT: JSON Pointer with URI resolving
4              
5 6     6   41 use strict;
  6         13  
  6         172  
6 6     6   28 use warnings;
  6         13  
  6         165  
7 6     6   29 use URI;
  6         21  
  6         163  
8 6     6   52 use Carp 'croak';
  6         17  
  6         302  
9              
10 6     6   38 use Scalar::Util 'weaken';
  6         12  
  6         513  
11              
12             use overload
13 983     983   1698 'bool' => sub { $_[0]->value },
14 6     6   42 fallback => 1;
  6         11  
  6         55  
15              
16             our @ISA = 'Exporter';
17             our @EXPORT_OK = qw(json_pointer);
18              
19             sub json_pointer {
20 5849     5849 0 12458 return __PACKAGE__;
21             }
22              
23             sub append {
24 5707     5707 0 10765 my ($class, $path, @values) = @_;
25 5707         9480 my $suffix = join('/', map { $class->escape($_) } @values);
  5707         9512  
26 5707 100       22729 return $path =~ m!/$!
27             ? $path . $suffix
28             : $path . '/' . $suffix;
29             }
30              
31             sub join {
32 61     61 0 108 my ($class, @parts) = @_;
33 61         101 return '/' . join('/', map { $class->escape($_) } @parts);
  61         97  
34             }
35              
36             sub escape {
37 5768     5768 0 9025 my ($class, $value) = @_;
38 5768         10302 $value =~ s!~!~0!g;
39 5768         7621 $value =~ s!/!~1!g;
40 5768         14446 return $value;
41             }
42              
43             sub unescape {
44 373     373 0 639 my ($class, $value) = @_;
45 373         599 $value =~ s!~1!/!g;
46 373         544 $value =~ s!~0!~!g;
47 373         846 return $value;
48             }
49              
50             sub new {
51 1013     1013 0 2727 my ($class, %params) = @_;
52              
53 1013         2218 my ($scope, $value, $validator) = @params{qw/scope value validator/};
54              
55 1013 50       1963 croak 'JSONPointer: scope is required' unless defined $scope;
56 1013 50       1722 croak 'JSONPointer: validator is required' unless $validator;
57              
58 1013         2437 weaken($validator);
59              
60 1013         2493 my $self = {
61             scope => $scope,
62             value => $value,
63             validator => $validator
64             };
65              
66 1013         1713 bless $self, $class;
67              
68 1013         3394 return $self;
69             }
70              
71 2845     2845 0 6043 sub validator { shift->{validator} }
72 1046     1046 0 2092 sub scope { shift->{scope} }
73 2348     2348 0 4802 sub value { shift->{value} }
74              
75             sub xget {
76 829     829 0 1604 my ($self, @parts) = @_;
77              
78 829         1448 my $current_scope = $self->scope;
79 829         1368 my $current_value = $self->value;
80              
81 829   66     3073 while (ref $current_value eq 'HASH' && $current_value->{'$ref'}) {
82 0         0 my $ref = URI->new($current_value->{'$ref'});
83 0 0       0 $ref = $ref->abs($current_scope) if $current_scope;
84 0         0 ($current_scope, $current_value) = $self->validator->resolver->resolve($ref);
85             }
86              
87 829 100 100     2047 if (ref $current_value eq 'HASH' && $self->validator->using_id_with_ref) {
88 114         221 my $id = $current_value->{$self->validator->ID_FIELD};
89 114 100 66     387 if ($id && !ref $id) {
90 89 50       306 $current_scope = $current_scope
91             ? URI->new($id)->abs($current_scope)->as_string
92             : $id;
93             }
94             }
95              
96 829         8135 for my $part (@parts) {
97 1082 100 100     4522 if (ref $current_value eq 'HASH' && exists $current_value->{$part}) {
    100 66        
      66        
98 852         1445 $current_value = $current_value->{$part};
99             } elsif (ref $current_value eq 'ARRAY' && $part =~ m/^\d+$/ && scalar(@$current_value) > $part) {
100 61         119 $current_value = $current_value->[$part];
101             } else {
102 169         251 $current_value = undef;
103 169         277 last;
104             }
105              
106 913   100     2726 while (ref $current_value eq 'HASH' && $current_value->{'$ref'}) {
107 34         116 my $ref = URI->new($current_value->{'$ref'});
108 34 100       1537 $ref = $ref->abs($current_scope) if $current_scope;
109 34         475 ($current_scope, $current_value) = $self->validator->resolver->resolve($ref);
110             }
111              
112 913 100 100     2820 if (ref $current_value eq 'HASH' && $self->validator->using_id_with_ref) {
113 230         523 my $id = $current_value->{$self->validator->ID_FIELD};
114 230 100 66     616 if ($id && !ref $id) {
115 2 50       11 $current_scope = $current_scope
116             ? URI->new($id)->abs($current_scope)->as_string
117             : $id;
118             }
119             }
120             }
121              
122 829         1491 return __PACKAGE__->new(
123             value => $current_value,
124             scope => $current_scope,
125             validator => $self->validator
126             )
127             }
128              
129             sub get {
130             # pointer is string which is already urldecoded and utf8-decoded
131 203     203 0 390 my ($self, $pointer) = @_;
132 203 50       470 return $self unless $pointer;
133              
134 203 50       1015 croak "Invalid JSON Pointer $pointer" unless $pointer =~ s!^/!!;
135              
136             my @parts = length $pointer
137 203 100       986 ? map { $self->unescape($_) } split(/\//, $pointer, -1)
  373         707  
138             : ('');
139              
140 203         504 return $self->xget(@parts);
141             }
142              
143             sub keys {
144 51     51 0 106 my ($self, %params) = @_;
145 51   100     181 my $raw = $params{raw} // 0;
146              
147 51 100       117 if (ref $self->value eq 'HASH') {
148 26 50       41 return map { $raw ? $_ : $self->join($_) } keys %{$self->value};
  33         160  
  26         41  
149             }
150              
151 25 50       50 if (ref $self->value eq 'ARRAY') {
152 25 50       44 return map { $raw ? $_ : $self->join($_) } 0 .. $#{$self->value};
  61         148  
  25         41  
153             }
154              
155 0           return;
156             }
157              
158             1;
159              
160             __END__