File Coverage

blib/lib/JSV/Reference.pm
Criterion Covered Total %
statement 73 79 92.4
branch 22 30 73.3
condition 18 27 66.6
subroutine 15 16 93.7
pod 0 6 0.0
total 128 158 81.0


line stmt bran cond sub pod time code
1             package JSV::Reference;
2              
3 48     48   15264 use strict;
  48         650  
  48         1028  
4 48     48   127 use warnings;
  48         50  
  48         774  
5              
6 48     48   125 use Carp;
  48         34  
  48         1915  
7 48     48   511 use Clone qw(clone);
  48         1767  
  48         1400  
8 48     48   20090 use Data::Walk;
  48         40324  
  48         2166  
9 48     48   20597 use JSON::Pointer;
  48         280606  
  48         1305  
10 48     48   221 use Scalar::Util qw(weaken);
  48         50  
  48         2602  
11 48     48   22558 use URI;
  48         83636  
  48         1357  
12 48     48   18153 use URI::Split qw(uri_split uri_join);
  48         19100  
  48         26359  
13              
14             my %memo;
15              
16             sub new {
17 63     63 0 99 my $class = shift;
18 63 50       206 my $args = ref $_[0] ? $_[0] : { @_ };
19              
20 63         262 %$args = (
21             registered_schema_map => {},
22             max_recursion => 10,
23             %$args,
24             );
25              
26 63         1123 bless $args => $class;
27             }
28              
29             sub resolve {
30 79     79 0 10441 my ($self, $ref, $opts) = @_;
31 79 50       139 die 'ref value should be hash' unless ref $ref eq 'HASH';
32 79 50       125 die '$ref not found' unless exists $ref->{'$ref'};
33 79         199 my $ref_uri = URI->new($ref->{'$ref'});
34              
35 79 100 66     17183 if ( ! $ref_uri->scheme && $opts->{base_uri} ) {
36 4         68 $ref_uri = $ref_uri->abs($opts->{base_uri});
37             }
38            
39 79 50 100     1655 die '$ref format invalid' unless $ref_uri->scheme || $ref_uri->fragment || $ref_uri->as_string eq "#";
      66        
40              
41 79         851 my $ref_obj = $self->get_schema($ref_uri, $opts);
42              
43 77 100 33     231 if ( ref $ref_obj eq 'HASH' && exists $ref_obj->{'$ref'} ) {
44 5         15 $self->resolve($ref_obj, $opts);
45             }
46              
47 77         251 %$ref = %$ref_obj;
48              
49             ### TODO: Does this weaken have means?
50 77         150 weaken($ref_obj);
51              
52 77         193 $ref->{id} = $ref_uri->as_string;
53             }
54              
55             sub get_schema {
56 79     79 0 78 my ($self, $uri, $opts) = @_;
57              
58 79         114 my ($normalized_uri, $fragment) = $self->normalize_uri($uri);
59 79   66     221 my $schema = $self->{registered_schema_map}{$normalized_uri} || $opts->{root};
60 79 100       155 unless (ref $schema eq 'HASH') {
61 1         4 die sprintf("cannot resolve reference: uri = %s", $uri);
62             }
63              
64 78 50 66     163 if (exists $schema->{'$ref'} && $schema->{'$ref'} eq $normalized_uri) {
65 0         0 die sprintf("cannot resolve reference: uri = %s", $uri);
66             }
67              
68 78 100       116 if ( $fragment ) {
69 69         70 eval {
70 69         176 $schema = JSON::Pointer->get($schema, $fragment, 1);
71             };
72 69 100       6432 if (my $e = $@ ) {
    50          
73 1         10 die sprintf("cannot resolve reference fragment: uri = %s, msg = %s", $uri, $e);
74             }
75             elsif (!$schema) {
76 0         0 die sprintf("cannot resolve reference fragment: uri = %s, msg = %s", $uri);
77             }
78             }
79              
80 77 50       130 unless (ref $schema eq 'HASH') {
81 0         0 die sprintf("cannot resolve reference: uri = %s", $uri);
82             }
83              
84 77         123 return $schema;
85             }
86              
87             sub register_schema {
88 9     9 0 86 my ($self, $uri, $schema) = @_;
89 9         21 my $normalized_uri = $self->normalize_uri($uri);
90 9         331 my $cloned_schema = clone($schema);
91              
92             ### recursive reference resolution
93             walkdepth(+{
94             wanted => sub {
95 410 50 66 410   15459 if (
      66        
      66        
96             ref $_ eq "HASH" &&
97             exists $_->{'$ref'} &&
98             !ref $_->{'$ref'} &&
99             keys %$_ == 1
100             ) {
101 28         72 my $ref_uri = URI->new($_->{'$ref'});
102 28 100       12178 return if $ref_uri->scheme;
103 27         314 $_->{'$ref'} = $ref_uri->abs($normalized_uri)->as_string;
104             }
105             },
106 9         64 }, $cloned_schema);
107              
108 9         217 $self->{registered_schema_map}{$normalized_uri} = $cloned_schema;
109             }
110              
111             sub unregister_schema {
112 0     0 0 0 my ($self, $uri) = @_;
113 0         0 my $normalized_uri = $self->normalize_uri($uri);
114 0         0 delete $self->{registered_schema_map}{$normalized_uri};
115             }
116              
117             sub normalize_uri {
118 88     88 0 78 my ($self, $uri) = @_;
119 88         80 my %parts;
120              
121 88         150 @parts{qw/scheme authority path query fragment/} = uri_split($uri);
122 88         915 my $fragment = $parts{fragment};
123 88         87 $parts{fragment} = undef;
124              
125 88         184 my $normalized_uri = uri_join(@parts{qw/scheme authority path query fragment/});
126              
127 88 100       1277 return wantarray ? ($normalized_uri, $fragment) : $normalized_uri;
128             }
129              
130             1;
131              
132             __END__