File Coverage

blib/lib/JSON/Schema/Modern/Vocabulary.pm
Criterion Covered Total %
statement 101 107 94.3
branch 14 20 70.0
condition n/a
subroutine 22 23 95.6
pod 8 8 100.0
total 145 158 91.7


line stmt bran cond sub pod time code
1 31     31   18045 use strict;
  31         113  
  31         1092  
2 31     31   232 use warnings;
  31         89  
  31         1536  
3             package JSON::Schema::Modern::Vocabulary;
4             # vim: set ts=8 sts=2 sw=2 tw=100 et :
5             # ABSTRACT: Base role for JSON Schema vocabulary classes
6              
7             our $VERSION = '0.570';
8              
9 31     31   554 use 5.020;
  31         142  
10 31     31   220 use Moo::Role;
  31         84  
  31         317  
11 31     31   18294 use strictures 2;
  31         264  
  31         1229  
12 31     31   6169 use stable 0.031 'postderef';
  31         523  
  31         184  
13 31     31   4469 use experimental 'signatures';
  31         123  
  31         168  
14 31     31   2675 use if "$]" >= 5.022, experimental => 're_strict';
  31         156  
  31         306  
15 31     31   2866 no if "$]" >= 5.031009, feature => 'indirect';
  31         126  
  31         267  
16 31     31   2048 no if "$]" >= 5.033001, feature => 'multidimensional';
  31         149  
  31         216  
17 31     31   1599 no if "$]" >= 5.033006, feature => 'bareword_filehandles';
  31         94  
  31         226  
18 31     31   1541 use Ref::Util 0.100 'is_plain_arrayref';
  31         693  
  31         2245  
19 31     31   255 use JSON::Schema::Modern::Utilities qw(jsonp assert_keyword_type abort);
  31         107  
  31         1801  
20 31     31   270 use Carp ();
  31         119  
  31         772  
21 31     31   235 use namespace::clean;
  31         94  
  31         238  
22              
23             our @CARP_NOT = qw(JSON::Schema::Modern);
24              
25             requires qw(vocabulary keywords);
26              
27 1     1 1 5 sub evaluation_order { 999 } # override, if needed
28              
29 0     0 1 0 sub traverse ($self, $schema, $state) {
  0         0  
  0         0  
  0         0  
  0         0  
30 0         0 $state->{evaluator}->_traverse_subschema($schema, $state);
31             }
32              
33 5567     5567 1 9538 sub traverse_subschema ($self, $schema, $state) {
  5567         8900  
  5567         8409  
  5567         8458  
  5567         7968  
34             $state->{evaluator}->_traverse_subschema($schema->{$state->{keyword}},
35 5567         62544 +{ %$state, schema_path => $state->{schema_path}.'/'.$state->{keyword} });
36             }
37              
38 2882     2882 1 5158 sub traverse_array_schemas ($self, $schema, $state) {
  2882         4985  
  2882         4482  
  2882         4830  
  2882         4062  
39 2882 50       8397 return if not assert_keyword_type($state, $schema, 'array');
40 2882 50       8761 return E($state, '%s array is empty', $state->{keyword}) if not $schema->{$state->{keyword}}->@*;
41              
42 2882         4814 my $valid = 1;
43 2882         10039 foreach my $idx (0 .. $schema->{$state->{keyword}}->$#*) {
44             $valid = 0 if not $state->{evaluator}->_traverse_subschema($schema->{$state->{keyword}}[$idx],
45 5156 100       61473 +{ %$state, schema_path => $state->{schema_path}.'/'.$state->{keyword}.'/'.$idx });
46             }
47 2882         9779 return $valid;
48             }
49              
50 4325     4325 1 7505 sub traverse_object_schemas ($self, $schema, $state) {
  4325         7085  
  4325         6501  
  4325         6605  
  4325         6039  
51 4325 100       12019 return if not assert_keyword_type($state, $schema, 'object');
52              
53 4322         8491 my $valid = 1;
54 4322         17404 foreach my $property (sort keys $schema->{$state->{keyword}}->%*) {
55             $valid = 0 if not $state->{evaluator}->_traverse_subschema($schema->{$state->{keyword}}{$property},
56 6324 100       39111 +{ %$state, schema_path => jsonp($state->{schema_path}, $state->{keyword}, $property) });
57             }
58 4322         14180 return $valid;
59             }
60              
61 1458     1458 1 2452 sub traverse_property_schema ($self, $schema, $state, $property) {
  1458         2444  
  1458         2208  
  1458         2054  
  1458         2240  
  1458         2021  
62 1458 50       3543 return if not assert_keyword_type($state, $schema, 'object');
63              
64             $state->{evaluator}->_traverse_subschema($schema->{$state->{keyword}}{$property},
65 1458         8443 +{ %$state, schema_path => jsonp($state->{schema_path}, $state->{keyword}, $property) });
66             }
67              
68 10685     10685 1 41782 sub eval ($self, $data, $schema, $state) {
  10685         17304  
  10685         16186  
  10685         16675  
  10685         15136  
  10685         14761  
69 10685         37879 $state->{evaluator}->_eval_subschema($data, $schema, $state);
70             }
71              
72 3793     3793 1 7389 sub eval_subschema_at_uri ($self, $data, $schema, $state, $uri) {
  3793         6471  
  3793         6373  
  3793         6072  
  3793         5670  
  3793         5531  
  3793         5854  
73 3793         14268 my $schema_info = $state->{evaluator}->_fetch_from_uri($uri);
74 3793 100       41937 abort($state, 'EXCEPTION: unable to find resource %s', $uri) if not $schema_info;
75              
76 3785         7450 my $vocabularies = $schema_info->{vocabularies};
77 3785 100       10460 if ($state->{validate_formats}) {
78             $vocabularies = [
79             map s/^JSON::Schema::Modern::Vocabulary::Format\KAnnotation$/Assertion/r, $state->{vocabularies}->@*
80 146         857 ];
81 146         933 require JSON::Schema::Modern::Vocabulary::FormatAssertion;
82             }
83              
84             return $state->{evaluator}->_eval_subschema($data, $schema_info->{schema},
85             +{
86             $schema_info->{configs}->%*,
87             %$state,
88             traversed_schema_path => $state->{traversed_schema_path}.$state->{schema_path}
89             .jsonp('', $state->{keyword}, exists $state->{_schema_path_suffix}
90             ? (is_plain_arrayref($state->{_schema_path_suffix}) ? $state->{_schema_path_suffix}->@* : $state->{_schema_path_suffix})
91             : ()),
92             initial_schema_uri => $schema_info->{canonical_uri},
93             document => $schema_info->{document},
94             document_path => $schema_info->{document_path},
95             spec_version => $schema_info->{specification_version},
96 3785 0       35348 schema_path => '',
    50          
97             vocabularies => $vocabularies,
98             });
99             }
100              
101             1;
102              
103             __END__
104              
105             =pod
106              
107             =encoding UTF-8
108              
109             =head1 NAME
110              
111             JSON::Schema::Modern::Vocabulary - Base role for JSON Schema vocabulary classes
112              
113             =head1 VERSION
114              
115             version 0.570
116              
117             =head1 SYNOPSIS
118              
119             package MyApp::Vocabulary::Awesome;
120             use Moo::Role;
121             with 'JSON::Schema::Modern::Vocabulary';
122              
123             =head1 DESCRIPTION
124              
125             This package is the role which all all vocabulary classes for L<JSON::Schema::Modern>
126             must compose, describing the basic structure expected of a vocabulary class.
127              
128             =head1 ATTRIBUTES
129              
130             =head1 METHODS
131              
132             =for stopwords schema subschema
133              
134             =head2 vocabulary
135              
136             Returns the canonical URI(s) describing the vocabulary for each draft specification version, as described in
137             L<JSON Schema Core Meta-specification, section 8.1.2|https://json-schema.org/draft/2020-12/json-schema-core.html#rfc.section.8.1.2>.
138             Must be implemented by the composing class.
139              
140             =head2 evaluation_order
141              
142             Returns a positive integer, used as a sort key for determining the evaluation order of this vocabulary. If
143             not overridden in a custom vocabulary class, its evaluation order will be after all built-in
144             vocabularies. You probably don't need to define this.
145              
146             =head2 keywords
147              
148             Returns the list of keywords defined by the vocabulary. Must be implemented by the composing class.
149              
150             =head2 traverse
151              
152             Traverses a subschema. Callers are expected to establish a new C<$state> scope.
153              
154             =head2 traverse_subschema
155              
156             Recursively traverses the schema at the current keyword, as in the C<not> keyword.
157              
158             =head2 traverse_array_schemas
159              
160             Recursively traverses the list of subschemas at the current keyword, as in the C<allOf> keyword.
161              
162             =head2 traverse_object_schemas
163              
164             Recursively traverses the (subschema) values of the object at the current keyword, as in the C<$defs> keyword.
165              
166             =head2 traverse_property_schema
167              
168             Recursively traverses the subschema under one property of the object at the current keyword.
169              
170             =head2 eval
171              
172             Evaluates a subschema. Callers are expected to establish a new C<$state> scope.
173              
174             =head2 eval_subschema_at_uri
175              
176             Resolves a URI to a subschema, then evaluates that subschema (essentially the C<$ref> keyword).
177              
178             =for stopwords OpenAPI
179              
180             =head1 SUPPORT
181              
182             Bugs may be submitted through L<https://github.com/karenetheridge/JSON-Schema-Modern/issues>.
183              
184             I am also usually active on irc, as 'ether' at C<irc.perl.org> and C<irc.libera.chat>.
185              
186             You can also find me on the L<JSON Schema Slack server|https://json-schema.slack.com> and L<OpenAPI Slack
187             server|https://open-api.slack.com>, which are also great resources for finding help.
188              
189             =head1 AUTHOR
190              
191             Karen Etheridge <ether@cpan.org>
192              
193             =head1 COPYRIGHT AND LICENCE
194              
195             This software is copyright (c) 2020 by Karen Etheridge.
196              
197             This is free software; you can redistribute it and/or modify it under
198             the same terms as the Perl 5 programming language system itself.
199              
200             =cut