File Coverage

blib/lib/JSV/Context.pm
Criterion Covered Total %
statement 96 104 92.3
branch 40 50 80.0
condition 1 3 33.3
subroutine 15 15 100.0
pod 0 6 0.0
total 152 178 85.3


line stmt bran cond sub pod time code
1             package JSV::Context;
2              
3 47     47   1044 use strict;
  47         90  
  47         1236  
4 47     47   237 use warnings;
  47         80  
  47         2743  
5              
6             use Class::Accessor::Lite (
7 47         595 new => 1,
8             rw => [qw/
9             keywords
10             reference
11             original_schema
12             throw_error
13             throw_immediate
14             enable_history
15             enable_format
16             formats
17             history
18             json
19             loose_type
20             /],
21             ro => [qw/
22             errors
23             current_type
24             current_keyword
25             current_pointer
26             current_instance
27             current_schema
28             current_schema_pointer
29             schema_pointer_history
30             /],
31 47     47   237 );
  47         86  
32              
33 47     47   13344 use Carp qw(croak);
  47         120  
  47         2332  
34 47     47   253 use JSON;
  47         74  
  47         343  
35 47     47   6450 use JSV::Keyword qw(:constants);
  47         81  
  47         6392  
36 47     47   25699 use JSV::Util::Type qw(detect_instance_type detect_instance_type_loose);
  47         161  
  47         3108  
37 47     47   23564 use JSV::Result;
  47         139  
  47         1532  
38 47     47   297 use Clone qw(clone);
  47         95  
  47         61010  
39              
40             sub validate {
41 1421     1421 0 2702 my ($self, $schema, $instance) = @_;
42              
43 1421 100       4132 local $self->{current_type} = !$self->loose_type ? detect_instance_type($instance)
44             : detect_instance_type_loose($instance);
45              
46 1421         3940 local $self->{cleanup_callbacks} = [];
47              
48 1421         2037 my $rv;
49 1421         2248 eval {
50 1421         1969 for my $keyword (@{ $self->keywords->{INSTANCE_TYPE_ANY()} }) {
  1421         4572  
51 11368 100       52288 next unless exists $schema->{$keyword->keyword};
52 1224         3127 $self->apply_keyword($keyword, $schema, $instance);
53             }
54              
55 1421 100       11022 if ($self->is_matched_types(qw/integer_or_string number_or_string/)) {
    100          
    100          
    100          
    100          
56 212         308 for my $keyword (
57 212         628 @{ $self->keywords->{INSTANCE_TYPE_NUMERIC()} },
58 212         1298 @{ $self->keywords->{INSTANCE_TYPE_STRING()} }
59             ) {
60 1484 100       7180 next unless exists $schema->{$keyword->keyword};
61 85         199 $self->apply_keyword($keyword, $schema, $instance);
62             }
63             }
64             elsif ($self->is_matched_types(qw/integer number/)) {
65 237         389 for my $keyword (@{ $self->keywords->{INSTANCE_TYPE_NUMERIC()} }) {
  237         761  
66 711 100       5289 next unless exists $schema->{$keyword->keyword};
67 62         198 $self->apply_keyword($keyword, $schema, $instance);
68             }
69             }
70             elsif ($self->is_matched_types(qw/string/)) {
71 439         697 for my $keyword (@{ $self->keywords->{INSTANCE_TYPE_STRING()} }) {
  439         1320  
72 1756 100       10481 next unless exists $schema->{$keyword->keyword};
73 108         307 $self->apply_keyword($keyword, $schema, $instance);
74             }
75             }
76             elsif ($self->current_type eq "array") {
77 157         1077 for my $keyword (@{ $self->keywords->{INSTANCE_TYPE_ARRAY()} }) {
  157         590  
78 628 100       4343 next unless exists $schema->{$keyword->keyword};
79 139         359 $self->apply_keyword($keyword, $schema, $instance);
80             }
81             }
82             elsif ($self->current_type eq "object") {
83 328         4502 for my $keyword (@{ $self->keywords->{INSTANCE_TYPE_OBJECT()} }) {
  328         959  
84             ### for addtionalProperties, patternProperties keyword without properties keyword
85 1640 50       8555 next unless ( ( grep { defined $_ && exists $schema->{$_} } ($keyword->keyword, @{$keyword->additional_keywords}) ) > 0 );
  2296 100       11794  
  1640         4985  
86             # next unless exists $schema->{$_->keyword};
87 418         1236 $self->apply_keyword($keyword, $schema, $instance);
88             }
89             }
90              
91 1421 50       7236 $rv = JSV::Result->new(
92             ($self->enable_history ? (history => $self->history) : ()),
93             );
94             };
95 1421 50       3962 if ( my $e = $@ ) {
96 0         0 $self->log_error(sprintf("Unexpected error: %s", $e));
97             }
98              
99 1421         1957 while (my $cb = pop @{ $self->{cleanup_callbacks} }) {
  1542         5404  
100 121         337 $cb->();
101             }
102              
103 1421 100       2072 if ( scalar @{ $self->errors } ) {
  1421         4172  
104 592 50       4933 $rv = JSV::Result->new(
105             errors => $self->errors,
106             ($self->enable_history ? (history => $self->history) : ()),
107             );
108 592 50       2675 if ( $self->throw_error ) {
109 0         0 croak $rv;
110             }
111             }
112              
113 1421         24808 return $rv;
114             }
115              
116             sub apply_keyword {
117 2036     2036 0 4063 my ($self, $keyword, $schema, $instance) = @_;
118              
119 2036         6447 local $self->{current_keyword} = $keyword->keyword;
120 2036         4078 local $self->{current_schema} = $schema;
121 2036         4123 local $self->{current_instance} = $instance;
122              
123 2036         7420 $keyword->validate($self, $schema, $instance);
124              
125 2036 50 33     12784 if ( $ENV{JSV_DEBUG} || $self->enable_history ) {
126 0         0 push @{ $self->history }, +{
  0         0  
127             keyword => $self->current_keyword,
128             pointer => $self->current_pointer,
129             schema => $self->current_schema,
130             instance => $self->resolve_current_instance,
131             };
132             }
133             }
134              
135             sub log_error {
136 422     422 0 827 my ($self, $message) = @_;
137              
138 422         1388 my $error = +{
139             keyword => $self->current_keyword,
140             pointer => $self->current_pointer,
141             schema => $self->current_schema,
142             instance => $self->resolve_current_instance,
143             schema_pointer => $self->current_schema_pointer,
144             schema_pointer_history => clone($self->schema_pointer_history),
145             message => $message,
146             };
147              
148 422 50       8880 if ( $ENV{JSV_DEBUG} ) {
149 0         0 require Data::Dump;
150 0         0 warn "history = " . Data::Dump::dump($self->history);
151 0         0 warn "error = " . Data::Dump::dump($error);
152             }
153              
154 422 50       1408 if ( $self->throw_immediate ) {
155 0 0       0 croak JSV::Result->new(
156             error => $error,
157             ($self->enable_history ? (history => $self->history) : ()),
158             );
159             }
160             else {
161 422         2287 push @{ $self->{errors} }, $error;
  422         1861  
162             }
163             }
164              
165             sub resolve_current_instance {
166 422     422 0 5982 my $self = shift;
167              
168 422         617 my $instance;
169 422 100       1290 if ( JSON::is_bool($self->current_instance) ) {
170 11 100       130 if ( $self->current_instance == JSON::true ) {
171 9         135 $instance = "true";
172             }
173             else {
174 2         31 $instance = "false";
175             }
176             }
177             else {
178 411         5477 $instance = $self->current_instance;
179             }
180              
181 422         3488 return $instance;
182             }
183              
184             sub is_matched_types {
185 3602     3602 0 8071 my ($self, @types) = @_;
186              
187 3602 100       5692 return (grep { $self->{current_type} eq $_ } @types) > 0 ? 1 : 0;
  6232         27288  
188             }
189              
190             sub register_cleanup_callback {
191 121     121 0 185 my ($self, $cb) = @_;
192              
193 121         146 push @{ $self->{cleanup_callbacks} }, $cb;
  121         352  
194             }
195              
196             1;