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   18326 use strict;
  31         114  
  31         1105  
2 31     31   230 use warnings;
  31         111  
  31         1586  
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.571';
8              
9 31     31   572 use 5.020;
  31         123  
10 31     31   228 use Moo::Role;
  31         79  
  31         320  
11 31     31   18245 use strictures 2;
  31         268  
  31         1189  
12 31     31   5767 use stable 0.031 'postderef';
  31         573  
  31         197  
13 31     31   4451 use experimental 'signatures';
  31         136  
  31         157  
14 31     31   2777 use if "$]" >= 5.022, experimental => 're_strict';
  31         106  
  31         288  
15 31     31   3054 no if "$]" >= 5.031009, feature => 'indirect';
  31         109  
  31         251  
16 31     31   1808 no if "$]" >= 5.033001, feature => 'multidimensional';
  31         150  
  31         212  
17 31     31   1786 no if "$]" >= 5.033006, feature => 'bareword_filehandles';
  31         124  
  31         282  
18 31     31   1645 use Ref::Util 0.100 'is_plain_arrayref';
  31         754  
  31         2363  
19 31     31   291 use JSON::Schema::Modern::Utilities qw(jsonp assert_keyword_type abort);
  31         96  
  31         1927  
20 31     31   284 use Carp ();
  31         99  
  31         880  
21 31     31   191 use namespace::clean;
  31         121  
  31         227  
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 9721 sub traverse_subschema ($self, $schema, $state) {
  5635         9417  
  5635         8571  
  5635         8450  
  5635         7972  
34             $state->{evaluator}->_traverse_subschema($schema->{$state->{keyword}},
35 5635         66561 +{ %$state, schema_path => $state->{schema_path}.'/'.$state->{keyword} });
36             }
37              
38 2914     2914 1 5634 sub traverse_array_schemas ($self, $schema, $state) {
  2914         4962  
  2914         4565  
  2914         4674  
  2914         4140  
39 2914 50       8411 return if not assert_keyword_type($state, $schema, 'array');
40 2914 50       9298 return E($state, '%s array is empty', $state->{keyword}) if not $schema->{$state->{keyword}}->@*;
41              
42 2914         5322 my $valid = 1;
43 2914         10070 foreach my $idx (0 .. $schema->{$state->{keyword}}->$#*) {
44             $valid = 0 if not $state->{evaluator}->_traverse_subschema($schema->{$state->{keyword}}[$idx],
45 5188 100       65178 +{ %$state, schema_path => $state->{schema_path}.'/'.$state->{keyword}.'/'.$idx });
46             }
47 2914         10376 return $valid;
48             }
49              
50 4499     4499 1 8464 sub traverse_object_schemas ($self, $schema, $state) {
  4499         7492  
  4499         7174  
  4499         7646  
  4499         6468  
51 4499 100       12790 return if not assert_keyword_type($state, $schema, 'object');
52              
53 4496         9372 my $valid = 1;
54 4496         19505 foreach my $property (sort keys $schema->{$state->{keyword}}->%*) {
55             $valid = 0 if not $state->{evaluator}->_traverse_subschema($schema->{$state->{keyword}}{$property},
56 6528 100       42100 +{ %$state, schema_path => jsonp($state->{schema_path}, $state->{keyword}, $property) });
57             }
58 4496         15040 return $valid;
59             }
60              
61 1474     1474 1 2592 sub traverse_property_schema ($self, $schema, $state, $property) {
  1474         2649  
  1474         2375  
  1474         2352  
  1474         2334  
  1474         2176  
62 1474 50       4173 return if not assert_keyword_type($state, $schema, 'object');
63              
64             $state->{evaluator}->_traverse_subschema($schema->{$state->{keyword}}{$property},
65 1474         9595 +{ %$state, schema_path => jsonp($state->{schema_path}, $state->{keyword}, $property) });
66             }
67              
68 10785     10785 1 44574 sub eval ($self, $data, $schema, $state) {
  10785         17914  
  10785         17007  
  10785         15919  
  10785         15287  
  10785         15293  
69 10785         40634 $state->{evaluator}->_eval_subschema($data, $schema, $state);
70             }
71              
72 3877     3877 1 7905 sub eval_subschema_at_uri ($self, $data, $schema, $state, $uri) {
  3877         7020  
  3877         6337  
  3877         6236  
  3877         6257  
  3877         6428  
  3877         6046  
73 3877         15966 my $schema_info = $state->{evaluator}->_fetch_from_uri($uri);
74 3877 100       44526 abort($state, 'EXCEPTION: unable to find resource %s', $uri) if not $schema_info;
75              
76 3869         8601 my $vocabularies = $schema_info->{vocabularies};
77 3869 100       10879 if ($state->{validate_formats}) {
78             $vocabularies = [
79             map s/^JSON::Schema::Modern::Vocabulary::Format\KAnnotation$/Assertion/r, $state->{vocabularies}->@*
80 149         986 ];
81 149         997 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       37590 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.571
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