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 44     44   1101 use strict;
  44         73  
  44         3338  
4 44     44   224 use warnings;
  44         63  
  44         6095  
5              
6             use Class::Accessor::Lite (
7 44         497 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 44     44   203 );
  44         64  
32              
33 44     44   14699 use Carp qw(croak);
  44         79  
  44         2563  
34 44     44   186 use JSON;
  44         307  
  44         274  
35 44     44   7147 use JSV::Keyword qw(:constants);
  44         71  
  44         8244  
36 44     44   21081 use JSV::Util::Type qw(detect_instance_type detect_instance_type_loose);
  44         333  
  44         4699  
37 44     44   30263 use JSV::Result;
  44         122  
  44         1243  
38 44     44   221 use Clone qw(clone);
  44         72  
  44         59305  
39              
40             sub validate {
41 1381     1381 0 2494 my ($self, $schema, $instance) = @_;
42              
43 1381 100       3603 local $self->{current_type} = !$self->loose_type ? detect_instance_type($instance)
44             : detect_instance_type_loose($instance);
45              
46 1381         3999 local $self->{cleanup_callbacks} = [];
47              
48 1381         1820 my $rv;
49 1381         2113 eval {
50 1381         1788 for my $keyword (@{ $self->keywords->{INSTANCE_TYPE_ANY()} }) {
  1381         6965  
51 11048 100       47853 next unless exists $schema->{$keyword->keyword};
52 1222         2974 $self->apply_keyword($keyword, $schema, $instance);
53             }
54              
55 1381 100       9891 if ($self->is_matched_types(qw/integer_or_string number_or_string/)) {
    100          
    100          
    100          
    100          
56 210         272 for my $keyword (
57 210         781 @{ $self->keywords->{INSTANCE_TYPE_NUMERIC()} },
58 210         1320 @{ $self->keywords->{INSTANCE_TYPE_STRING()} }
59             ) {
60 1470 100       7217 next unless exists $schema->{$keyword->keyword};
61 83         672 $self->apply_keyword($keyword, $schema, $instance);
62             }
63             }
64             elsif ($self->is_matched_types(qw/integer number/)) {
65 237         353 for my $keyword (@{ $self->keywords->{INSTANCE_TYPE_NUMERIC()} }) {
  237         1166  
66 711 100       4897 next unless exists $schema->{$keyword->keyword};
67 62         180 $self->apply_keyword($keyword, $schema, $instance);
68             }
69             }
70             elsif ($self->is_matched_types(qw/string/)) {
71 403         545 for my $keyword (@{ $self->keywords->{INSTANCE_TYPE_STRING()} }) {
  403         1290  
72 1612 100       8223 next unless exists $schema->{$keyword->keyword};
73 72         174 $self->apply_keyword($keyword, $schema, $instance);
74             }
75             }
76             elsif ($self->current_type eq "array") {
77 155         1026 for my $keyword (@{ $self->keywords->{INSTANCE_TYPE_ARRAY()} }) {
  155         413  
78 620 100       4227 next unless exists $schema->{$keyword->keyword};
79 137         329 $self->apply_keyword($keyword, $schema, $instance);
80             }
81             }
82             elsif ($self->current_type eq "object") {
83 328         3845 for my $keyword (@{ $self->keywords->{INSTANCE_TYPE_OBJECT()} }) {
  328         891  
84             ### for addtionalProperties, patternProperties keyword without properties keyword
85 1640 50       7833 next unless ( ( grep { defined $_ && exists $schema->{$_} } ($keyword->keyword, @{$keyword->additional_keywords}) ) > 0 );
  2296 100       10157  
  1640         4641  
86             # next unless exists $schema->{$_->keyword};
87 418         1097 $self->apply_keyword($keyword, $schema, $instance);
88             }
89             }
90              
91 1381 50       6177 $rv = JSV::Result->new(
92             ($self->enable_history ? (history => $self->history) : ()),
93             );
94             };
95 1381 50       3711 if ( my $e = $@ ) {
96 0         0 $self->log_error(sprintf("Unexpected error: %s", $e));
97             }
98              
99 1381         1705 while (my $cb = pop @{ $self->{cleanup_callbacks} }) {
  1502         4676  
100 121         282 $cb->();
101             }
102              
103 1381 100       1721 if ( scalar @{ $self->errors } ) {
  1381         4260  
104 562 50       4312 $rv = JSV::Result->new(
105             errors => $self->errors,
106             ($self->enable_history ? (history => $self->history) : ()),
107             );
108 562 50       2591 if ( $self->throw_error ) {
109 0         0 croak $rv;
110             }
111             }
112              
113 1381         21944 return $rv;
114             }
115              
116             sub apply_keyword {
117 1994     1994 0 3925 my ($self, $keyword, $schema, $instance) = @_;
118              
119 1994         5838 local $self->{current_keyword} = $keyword->keyword;
120 1994         3623 local $self->{current_schema} = $schema;
121 1994         3861 local $self->{current_instance} = $instance;
122              
123 1994         6702 $keyword->validate($self, $schema, $instance);
124              
125 1994 50 33     8589 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 394     394 0 765 my ($self, $message) = @_;
137              
138 394         1145 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 394 50       10585 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 394 50       1178 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 394         2063 push @{ $self->{errors} }, $error;
  394         1821  
162             }
163             }
164              
165             sub resolve_current_instance {
166 394     394 0 5156 my $self = shift;
167              
168 394         504 my $instance;
169 394 100       1025 if ( JSON::is_bool($self->current_instance) ) {
170 11 100       125 if ( $self->current_instance == JSON::true ) {
171 9         164 $instance = "true";
172             }
173             else {
174 2         29 $instance = "false";
175             }
176             }
177             else {
178 383         4456 $instance = $self->current_instance;
179             }
180              
181 394         3111 return $instance;
182             }
183              
184             sub is_matched_types {
185 3486     3486 0 7094 my ($self, @types) = @_;
186              
187 3486 100       4879 return (grep { $self->{current_type} eq $_ } @types) > 0 ? 1 : 0;
  6038         23193  
188             }
189              
190             sub register_cleanup_callback {
191 121     121 0 209 my ($self, $cb) = @_;
192              
193 121         141 push @{ $self->{cleanup_callbacks} }, $cb;
  121         341  
194             }
195              
196             1;