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   17434 use strict;
  31         87  
  31         1020  
2 31     31   187 use warnings;
  31         77  
  31         1537  
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.572';
8              
9 31     31   532 use 5.020;
  31         127  
10 31     31   185 use Moo::Role;
  31         80  
  31         266  
11 31     31   17292 use strictures 2;
  31         224  
  31         1184  
12 31     31   5789 use stable 0.031 'postderef';
  31         494  
  31         195  
13 31     31   4436 use experimental 'signatures';
  31         91  
  31         180  
14 31     31   2513 use if "$]" >= 5.022, experimental => 're_strict';
  31         100  
  31         281  
15 31     31   2920 no if "$]" >= 5.031009, feature => 'indirect';
  31         86  
  31         231  
16 31     31   1661 no if "$]" >= 5.033001, feature => 'multidimensional';
  31         104  
  31         185  
17 31     31   1547 no if "$]" >= 5.033006, feature => 'bareword_filehandles';
  31         87  
  31         219  
18 31     31   1422 use Ref::Util 0.100 'is_plain_arrayref';
  31         599  
  31         2301  
19 31     31   304 use JSON::Schema::Modern::Utilities qw(jsonp assert_keyword_type abort);
  31         101  
  31         1751  
20 31     31   248 use Carp ();
  31         79  
  31         705  
21 31     31   181 use namespace::clean;
  31         82  
  31         230  
22              
23             our @CARP_NOT = qw(JSON::Schema::Modern);
24              
25             requires qw(vocabulary keywords);
26              
27 1     1 1 6 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 5635     5635 1 9724 sub traverse_subschema ($self, $schema, $state) {
  5635         9489  
  5635         8419  
  5635         8244  
  5635         7811  
34             $state->{evaluator}->_traverse_subschema($schema->{$state->{keyword}},
35 5635         63094 +{ %$state, schema_path => $state->{schema_path}.'/'.$state->{keyword} });
36             }
37              
38 2914     2914 1 5138 sub traverse_array_schemas ($self, $schema, $state) {
  2914         4898  
  2914         5028  
  2914         4509  
  2914         4196  
39 2914 50       8528 return if not assert_keyword_type($state, $schema, 'array');
40 2914 50       9300 return E($state, '%s array is empty', $state->{keyword}) if not $schema->{$state->{keyword}}->@*;
41              
42 2914         5386 my $valid = 1;
43 2914         10267 foreach my $idx (0 .. $schema->{$state->{keyword}}->$#*) {
44             $valid = 0 if not $state->{evaluator}->_traverse_subschema($schema->{$state->{keyword}}[$idx],
45 5188 100       62100 +{ %$state, schema_path => $state->{schema_path}.'/'.$state->{keyword}.'/'.$idx });
46             }
47 2914         9898 return $valid;
48             }
49              
50 4499     4499 1 7861 sub traverse_object_schemas ($self, $schema, $state) {
  4499         7720  
  4499         7072  
  4499         6818  
  4499         6426  
51 4499 100       12231 return if not assert_keyword_type($state, $schema, 'object');
52              
53 4496         8663 my $valid = 1;
54 4496         18501 foreach my $property (sort keys $schema->{$state->{keyword}}->%*) {
55             $valid = 0 if not $state->{evaluator}->_traverse_subschema($schema->{$state->{keyword}}{$property},
56 6528 100       41646 +{ %$state, schema_path => jsonp($state->{schema_path}, $state->{keyword}, $property) });
57             }
58 4496         15333 return $valid;
59             }
60              
61 1474     1474 1 2693 sub traverse_property_schema ($self, $schema, $state, $property) {
  1474         2459  
  1474         2453  
  1474         2172  
  1474         2304  
  1474         2311  
62 1474 50       3829 return if not assert_keyword_type($state, $schema, 'object');
63              
64             $state->{evaluator}->_traverse_subschema($schema->{$state->{keyword}}{$property},
65 1474         8458 +{ %$state, schema_path => jsonp($state->{schema_path}, $state->{keyword}, $property) });
66             }
67              
68 10785     10785 1 44406 sub eval ($self, $data, $schema, $state) {
  10785         19621  
  10785         18202  
  10785         16210  
  10785         16558  
  10785         15815  
69 10785         38067 $state->{evaluator}->_eval_subschema($data, $schema, $state);
70             }
71              
72 3877     3877 1 7774 sub eval_subschema_at_uri ($self, $data, $schema, $state, $uri) {
  3877         7171  
  3877         7258  
  3877         7537  
  3877         6281  
  3877         6793  
  3877         6397  
73 3877         16762 my $schema_info = $state->{evaluator}->_fetch_from_uri($uri);
74 3877 100       43528 abort($state, 'EXCEPTION: unable to find resource %s', $uri) if not $schema_info;
75              
76 3869         8275 my $vocabularies = $schema_info->{vocabularies};
77 3869 100       10680 if ($state->{validate_formats}) {
78             $vocabularies = [
79             map s/^JSON::Schema::Modern::Vocabulary::Format\KAnnotation$/Assertion/r, $state->{vocabularies}->@*
80 149         930 ];
81 149         960 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 3869 0       38015 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.572
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