File Coverage

blib/lib/JSV/Reference.pm
Criterion Covered Total %
statement 73 79 92.4
branch 22 30 73.3
condition 21 30 70.0
subroutine 15 16 93.7
pod 0 6 0.0
total 131 161 81.3


line stmt bran cond sub pod time code
1             package JSV::Reference;
2              
3 48     48   25483 use strict;
  48         86  
  48         1270  
4 48     48   235 use warnings;
  48         85  
  48         1253  
5              
6 48     48   238 use Carp;
  48         83  
  48         2766  
7 48     48   2029 use Clone qw(clone);
  48         4107  
  48         2060  
8 48     48   40738 use Data::Walk;
  48         53838  
  48         2942  
9 48     48   41689 use JSON::Pointer;
  48         492255  
  48         2055  
10 48     48   393 use Scalar::Util qw(weaken);
  48         99  
  48         4194  
11 48     48   44894 use URI;
  48         132179  
  48         1794  
12 48     48   36951 use URI::Split qw(uri_split uri_join);
  48         28245  
  48         42414  
13              
14             my %memo;
15              
16             sub new {
17 63     63 0 145 my $class = shift;
18 63 50       271 my $args = ref $_[0] ? $_[0] : { @_ };
19              
20 63         365 %$args = (
21             registered_schema_map => {},
22             max_recursion => 10,
23             %$args,
24             );
25              
26 63         1571 bless $args => $class;
27             }
28              
29             sub resolve {
30 79     79 0 13328 my ($self, $ref, $opts) = @_;
31 79 50       239 die 'ref value should be hash' unless ref $ref eq 'HASH';
32 79 50       316 die '$ref not found' unless exists $ref->{'$ref'};
33 79         392 my $ref_uri = URI->new($ref->{'$ref'});
34              
35 79 100 66     28908 if ( ! $ref_uri->scheme && $opts->{base_uri} ) {
36 4         141 $ref_uri = $ref_uri->abs($opts->{base_uri});
37             }
38            
39 79 50 100     2674 die '$ref format invalid' unless $ref_uri->scheme || $ref_uri->fragment || $ref_uri->as_string eq "#";
      66        
40              
41 79         1434 my $ref_obj = $self->get_schema($ref_uri, $opts);
42              
43 77 100 33     340 if ( ref $ref_obj eq 'HASH' && exists $ref_obj->{'$ref'} ) {
44 5         23 $self->resolve($ref_obj, $opts);
45             }
46              
47 77         392 %$ref = %$ref_obj;
48              
49             ### TODO: Does this weaken have means?
50 77         273 weaken($ref_obj);
51              
52 77         258 $ref->{id} = $ref_uri->as_string;
53             }
54              
55             sub get_schema {
56 79     79 0 178 my ($self, $uri, $opts) = @_;
57              
58 79         204 my ($normalized_uri, $fragment) = $self->normalize_uri($uri);
59 79   66     343 my $schema = $self->{registered_schema_map}{$normalized_uri} || $opts->{root};
60 79 100       214 unless (ref $schema eq 'HASH') {
61 1         6 die sprintf("cannot resolve reference: uri = %s", $uri);
62             }
63              
64 78 50 66     290 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       177 if ( $fragment ) {
69 69         119 eval {
70 69         305 $schema = JSON::Pointer->get($schema, $fragment, 1);
71             };
72 69 100       10468 if (my $e = $@ ) {
    50          
73 1         14 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       217 unless (ref $schema eq 'HASH') {
81 0         0 die sprintf("cannot resolve reference: uri = %s", $uri);
82             }
83              
84 77         157 return $schema;
85             }
86              
87             sub register_schema {
88 9     9 0 136 my ($self, $uri, $schema) = @_;
89 9         40 my $normalized_uri = $self->normalize_uri($uri);
90 9         575 my $cloned_schema = clone($schema);
91              
92             ### recursive reference resolution
93             walkdepth(+{
94             wanted => sub {
95 410 50 100 410   25144 if (
      66        
      66        
      66        
96             defined $Data::Walk::type &&
97             $Data::Walk::type eq "HASH" &&
98             exists $_->{'$ref'} &&
99             !ref $_->{'$ref'} &&
100             keys %$_ == 1
101             ) {
102 28         114 my $ref_uri = URI->new($_->{'$ref'});
103 28 100       19218 return if $ref_uri->scheme;
104 27         545 $_->{'$ref'} = $ref_uri->abs($normalized_uri)->as_string;
105             }
106             },
107 9         115 }, $cloned_schema);
108              
109 9         405 $self->{registered_schema_map}{$normalized_uri} = $cloned_schema;
110             }
111              
112             sub unregister_schema {
113 0     0 0 0 my ($self, $uri) = @_;
114 0         0 my $normalized_uri = $self->normalize_uri($uri);
115 0         0 delete $self->{registered_schema_map}{$normalized_uri};
116             }
117              
118             sub normalize_uri {
119 88     88 0 147 my ($self, $uri) = @_;
120 88         129 my %parts;
121              
122 88         360 @parts{qw/scheme authority path query fragment/} = uri_split($uri);
123 88         1462 my $fragment = $parts{fragment};
124 88         151 $parts{fragment} = undef;
125              
126 88         311 my $normalized_uri = uri_join(@parts{qw/scheme authority path query fragment/});
127              
128 88 100       1970 return wantarray ? ($normalized_uri, $fragment) : $normalized_uri;
129             }
130              
131             1;
132              
133             __END__