File Coverage

blib/lib/YAML/PP/Schema.pm
Criterion Covered Total %
statement 235 251 93.6
branch 101 118 85.5
condition 31 43 72.0
subroutine 27 29 93.1
pod 0 16 0.0
total 394 457 86.2


line stmt bran cond sub pod time code
1 50     50   151325 use strict;
  50         70  
  50         1326  
2 50     50   162 use warnings;
  50         80  
  50         2220  
3             package YAML::PP::Schema;
4 50     50   1679 use B;
  50         66  
  50         1050  
5 50     50   18378 use Module::Load qw//;
  50         52629  
  50         3349  
6              
7             our $VERSION = 'v0.40.1'; # TRIAL VERSION
8              
9 50     50   15325 use YAML::PP::Common qw/ YAML_PLAIN_SCALAR_STYLE /;
  50         121  
  50         3286  
10              
11 50     50   266 use Carp qw/ croak /;
  50         169  
  50         2060  
12 50     50   219 use Scalar::Util qw/ blessed /;
  50         71  
  50         106352  
13              
14             sub new {
15 770     770 0 2335 my ($class, %args) = @_;
16              
17 770         1441 my $yaml_version = delete $args{yaml_version};
18 770         1171 my $bool = delete $args{boolean};
19 770 50       1563 $bool = 'perl' unless defined $bool;
20 770 50       1678 if (keys %args) {
21 0         0 die "Unexpected arguments: " . join ', ', sort keys %args;
22             }
23 770         1750 my $true;
24             my $false;
25 770         0 my @bool_class;
26 770         2271 my @bools = split m/,/, $bool;
27 770         1298 for my $b (@bools) {
28 763 50 66     2954 if ($b eq '*') {
    100          
    50          
    50          
29 0         0 push @bool_class, ('boolean', 'JSON::PP::Boolean');
30 0         0 last;
31             }
32             elsif ($b eq 'JSON::PP') {
33 595         4517 require JSON::PP;
34 595   50     2778 $true ||= \&_bool_jsonpp_true;
35 595   50     2173 $false ||= \&_bool_jsonpp_false;
36 595         1412 push @bool_class, 'JSON::PP::Boolean';
37             }
38             elsif ($b eq 'boolean') {
39 0         0 require boolean;
40 0   0     0 $true ||= \&_bool_booleanpm_true;
41 0   0     0 $false ||= \&_bool_booleanpm_false;
42 0         0 push @bool_class, 'boolean';
43             }
44             elsif ($b eq 'perl' or $b eq 'perl_experimental') {
45 168         358 push @bool_class, 'perl';
46             }
47             else {
48 0         0 die "Invalid value for 'boolean': '$b'. Allowed: ('perl', 'boolean', 'JSON::PP')";
49             }
50             }
51             # Ensure booleans are resolved
52 770   100     2151 $true ||= \&_bool_perl_true;
53 770   100     1825 $false ||= \&_bool_perl_false;
54              
55 770         6902 my %representers = (
56             'undef' => undef,
57             flags => [],
58             equals => {},
59             regex => [],
60             class_equals => {},
61             class_matches => [],
62             class_isa => [],
63             scalarref => undef,
64             refref => undef,
65             coderef => undef,
66             glob => undef,
67             tied_equals => {},
68             bool => undef,
69             );
70 770         4216 my $self = bless {
71             yaml_version => $yaml_version,
72             resolvers => {},
73             representers => \%representers,
74             true => $true,
75             false => $false,
76             bool_class => \@bool_class,
77             }, $class;
78 770         2821 return $self;
79             }
80              
81 52812     52812 0 66113 sub resolvers { return $_[0]->{resolvers} }
82 25077     25077 0 29135 sub representers { return $_[0]->{representers} }
83              
84 2293     2293 0 4449 sub true { return $_[0]->{true} }
85 2293     2293 0 4070 sub false { return $_[0]->{false} }
86 1604 100   1604 0 1583 sub bool_class { return @{ $_[0]->{bool_class} } ? $_[0]->{bool_class} : undef }
  1604         4389  
87 788     788 0 1640 sub yaml_version { return $_[0]->{yaml_version} }
88              
89             my %LOADED_SCHEMA = (
90             JSON => 1,
91             );
92             my %DEFAULT_SCHEMA = (
93             '1.2' => 'Core',
94             '1.1' => 'YAML1_1',
95             );
96              
97             sub load_subschemas {
98 770     770 0 1782 my ($self, @schemas) = @_;
99 770         1707 my $yaml_version = $self->yaml_version;
100 770         1200 my $i = 0;
101 770         1876 while ($i < @schemas) {
102 1389         2133 my $item = $schemas[ $i ];
103 1389 100       2560 if ($item eq '+') {
104 457         1053 $item = $DEFAULT_SCHEMA{ $yaml_version };
105             }
106 1389         1630 $i++;
107 1389 100       2373 if (blessed($item)) {
108 10         35 $item->register(
109             schema => $self,
110             );
111 10         39 next;
112             }
113 1379         1921 my @options;
114 1379   100     6716 while ($i < @schemas
      100        
115             and (
116             $schemas[ $i ] =~ m/^[^A-Za-z]/
117             or
118             $schemas[ $i ] =~ m/^[a-zA-Z0-9]+=/
119             )
120             ) {
121 14         18 push @options, $schemas[ $i ];
122 14         25 $i++;
123             }
124              
125 1379         1805 my $class;
126 1379 100       2830 if ($item =~ m/^\:(.*)/) {
127 1         3 $class = "$1";
128 1 50       3 unless ($class =~ m/\A[A-Za-z0-9_:]+\z/) {
129 0         0 die "Module name '$class' is invalid";
130             }
131 1         4 Module::Load::load $class;
132             }
133             else {
134 1378         2177 $class = "YAML::PP::Schema::$item";
135 1378 50       5559 unless ($class =~ m/\A[A-Za-z0-9_:]+\z/) {
136 0         0 die "Module name '$class' is invalid";
137             }
138 1378   66     6106 $LOADED_SCHEMA{ $item } ||= Module::Load::load $class;
139             }
140 1379         82628 $class->register(
141             schema => $self,
142             options => \@options,
143             );
144              
145             }
146             }
147              
148             sub add_resolver {
149 21820     21820 0 32517 my ($self, %args) = @_;
150 21820         22288 my $tag = $args{tag};
151 21820         21966 my $rule = $args{match};
152 21820         25027 my $resolvers = $self->resolvers;
153 21820         27160 my ($type, @rule) = @$rule;
154 21820         20775 my $implicit = $args{implicit};
155 21820 100       27736 $implicit = 1 unless defined $implicit;
156 21820         20847 my $resolver_list = [];
157 21820 50       26596 if ($tag) {
158 21820 100       25308 if (ref $tag eq 'Regexp') {
159 681   100     1953 my $res = $resolvers->{tags} ||= [];
160 681         1299 push @$res, [ $tag, {} ];
161 681         1487 push @$resolver_list, $res->[-1]->[1];
162             }
163             else {
164 21139   100     35699 my $res = $resolvers->{tag}->{ $tag } ||= {};
165 21139         24874 push @$resolver_list, $res;
166             }
167             }
168 21820 100       25914 if ($implicit) {
169 21073   100     29323 push @$resolver_list, $resolvers->{value} ||= {};
170             }
171 21820         22934 for my $res (@$resolver_list) {
172 42893 100       49519 if ($type eq 'equals') {
    100          
    50          
173 34554         37121 my ($match, $value) = @rule;
174 34554 50       45706 unless (exists $res->{equals}->{ $match }) {
175 34554         45728 $res->{equals}->{ $match } = $value;
176             }
177 34554         64632 next;
178             }
179             elsif ($type eq 'regex') {
180 6066         6483 my ($match, $value) = @rule;
181 6066         5386 push @{ $res->{regex} }, [ $match => $value ];
  6066         13816  
182             }
183             elsif ($type eq 'all') {
184 2273         2661 my ($value) = @rule;
185 2273         5172 $res->{all} = $value;
186             }
187             }
188             }
189              
190             sub add_sequence_resolver {
191 669     669 0 1322 my ($self, %args) = @_;
192 669         1709 return $self->add_collection_resolver(sequence => %args);
193             }
194              
195             sub add_mapping_resolver {
196 874     874 0 1600 my ($self, %args) = @_;
197 874         1597 return $self->add_collection_resolver(mapping => %args);
198             }
199              
200             sub add_collection_resolver {
201 1543     1543 0 2582 my ($self, $type, %args) = @_;
202 1543         1877 my $tag = $args{tag};
203 1543         1748 my $implicit = $args{implicit};
204 1543         1845 my $resolvers = $self->resolvers;
205              
206 1543 100 66     4105 if ($tag and ref $tag eq 'Regexp') {
    50          
207 1394   100     3884 my $res = $resolvers->{ $type }->{tags} ||= [];
208             push @$res, [ $tag, {
209             on_create => $args{on_create},
210             on_data => $args{on_data},
211 1394         4771 } ];
212             }
213             elsif ($tag) {
214             my $res = $resolvers->{ $type }->{tag}->{ $tag } ||= {
215             on_create => $args{on_create},
216             on_data => $args{on_data},
217 149   50     847 };
218             }
219             }
220              
221             sub add_representer {
222 21226     21226 0 29682 my ($self, %args) = @_;
223              
224 21226         23596 my $representers = $self->representers;
225 21226 100       27684 if (my $flags = $args{flags}) {
226 1530         1714 my $rep = $representers->{flags};
227 1530         1879 push @$rep, \%args;
228 1530         2247 return;
229             }
230 19696 100       24577 if (my $regex = $args{regex}) {
231 766         976 my $rep = $representers->{regex};
232 766         1202 push @$rep, \%args;
233 766         1292 return;
234             }
235 18930 100       24038 if (my $regex = $args{class_matches}) {
236 26         30 my $rep = $representers->{class_matches};
237 26         46 push @$rep, [ $args{class_matches}, $args{code} ];
238 26         42 return;
239             }
240 18904 100 66     25172 if (my $bool = $args{bool} and $] >= 5.036000) {
241             $representers->{bool} = {
242             code => $args{code},
243 165         357 };
244 165         323 return;
245             }
246 18739 100       21965 if (my $class_equals = $args{class_equals}) {
247 595         750 my $rep = $representers->{class_equals};
248             $rep->{ $class_equals } = {
249             code => $args{code},
250 595         1227 };
251 595         1394 return;
252             }
253 18144 100       21102 if (my $class_isa = $args{class_isa}) {
254 2         2 my $rep = $representers->{class_isa};
255 2         6 push @$rep, [ $args{class_isa}, $args{code} ];
256 2         3 return;
257             }
258 18142 50       21229 if (my $tied_equals = $args{tied_equals}) {
259 0         0 my $rep = $representers->{tied_equals};
260             $rep->{ $tied_equals } = {
261             code => $args{code},
262 0         0 };
263 0         0 return;
264             }
265 18142 100       23200 if (defined(my $equals = $args{equals})) {
266 17281         16845 my $rep = $representers->{equals};
267             $rep->{ $equals } = {
268             code => $args{code},
269 17281         26574 };
270 17281         34010 return;
271             }
272 861 100       1480 if (defined(my $scalarref = $args{scalarref})) {
273             $representers->{scalarref} = {
274             code => $args{code},
275 24         56 };
276 24         157 return;
277             }
278 837 100       1503 if (defined(my $refref = $args{refref})) {
279             $representers->{refref} = {
280             code => $args{code},
281 24         37 };
282 24         55 return;
283             }
284 813 100       1407 if (defined(my $coderef = $args{coderef})) {
285             $representers->{coderef} = {
286             code => $args{code},
287 24         51 };
288 24         38 return;
289             }
290 789 100       1284 if (defined(my $glob = $args{glob})) {
291             $representers->{glob} = {
292             code => $args{code},
293 24         40 };
294 24         41 return;
295             }
296 765 50       1425 if (my $undef = $args{undefined}) {
297 765         1147 $representers->{undef} = $undef;
298 765         1480 return;
299             }
300             }
301              
302             sub load_scalar {
303 26588     26588 0 34519 my ($self, $constructor, $event) = @_;
304 26588         32367 my $tag = $event->{tag};
305 26588         32508 my $value = $event->{value};
306              
307 26588         43025 my $resolvers = $self->resolvers;
308 26588         27198 my $res;
309 26588 100       33463 if ($tag) {
310 697 100       1305 if ($tag eq '!') {
311 7         17 return $value;
312             }
313 690         1406 $res = $resolvers->{tag}->{ $tag };
314 690 100 66     1660 if (not $res and my $matches = $resolvers->{tags}) {
315 56         104 for my $match (@$matches) {
316 76         139 my ($re, $rule) = @$match;
317 76 100       521 if ($tag =~ $re) {
318 56         78 $res = $rule;
319 56         109 last;
320             }
321             }
322             }
323             #unless ($res) {
324             # croak "Unknown tag '$tag'. Use schema 'Catchall' to ignore unknown tags";
325             #}
326             }
327             else {
328 25891         29099 $res = $resolvers->{value};
329 25891 100       44216 if ($event->{style} ne YAML_PLAIN_SCALAR_STYLE) {
330 2658         6287 return $value;
331             }
332             }
333              
334 23923 100       39107 if (my $equals = $res->{equals}) {
335 23303 100       40833 if (exists $equals->{ $value }) {
336 426         567 my $res = $equals->{ $value };
337 426 100       813 if (ref $res eq 'CODE') {
338 82         155 return $res->($constructor, $event);
339             }
340 344         659 return $res;
341             }
342             }
343 23497 100       33710 if (my $regex = $res->{regex}) {
344 22864         32779 for my $item (@$regex) {
345 61704         73458 my ($re, $sub) = @$item;
346 61704         257314 my @matches = $value =~ $re;
347 61704 100       100166 if (@matches) {
348 9875         21113 return $sub->($constructor, $event, \@matches);
349             }
350             }
351             }
352 13622 100       21812 if (my $catch_all = $res->{all}) {
353 13595 50       24379 if (ref $catch_all eq 'CODE') {
354 13595         27345 return $catch_all->($constructor, $event);
355             }
356 0         0 return $catch_all;
357             }
358 27         70 return $value;
359             }
360              
361             sub create_sequence {
362 1230     1230 0 1696 my ($self, $constructor, $event) = @_;
363 1230         1697 my $tag = $event->{tag};
364 1230         1373 my $data = [];
365 1230         1327 my $on_data;
366              
367 1230         2071 my $resolvers = $self->resolvers->{sequence};
368 1230 100       2031 if ($tag) {
369 35 100       93 if (my $equals = $resolvers->{tag}->{ $tag }) {
370 6         15 my $on_create = $equals->{on_create};
371 6         10 $on_data = $equals->{on_data};
372 6 50       27 $on_create and $data = $on_create->($constructor, $event);
373 6         19 return ($data, $on_data);
374             }
375 29 50       81 if (my $matches = $resolvers->{tags}) {
376 29         54 for my $match (@$matches) {
377 31         70 my ($re, $actions) = @$match;
378 31         46 my $on_create = $actions->{on_create};
379 31 100       233 if ($tag =~ $re) {
380 29         51 $on_data = $actions->{on_data};
381 29 100       88 $on_create and $data = $on_create->($constructor, $event);
382 29         96 return ($data, $on_data);
383             }
384             }
385             }
386             #croak "Unknown tag '$tag'. Use schema 'Catchall' to ignore unknown tags";
387             }
388              
389 1195         2437 return ($data, $on_data);
390             }
391              
392             sub create_mapping {
393 1631     1631 0 2490 my ($self, $constructor, $event) = @_;
394 1631         2451 my $tag = $event->{tag};
395 1631         1891 my $data = {};
396 1631         1786 my $on_data;
397              
398 1631         2981 my $resolvers = $self->resolvers->{mapping};
399 1631 100       2733 if ($tag) {
400 83 100       251 if (my $equals = $resolvers->{tag}->{ $tag }) {
401 24         41 my $on_create = $equals->{on_create};
402 24         38 $on_data = $equals->{on_data};
403 24 100       124 $on_create and $data = $on_create->($constructor, $event);
404 24         247 return ($data, $on_data);
405             }
406 59 50       174 if (my $matches = $resolvers->{tags}) {
407 59         107 for my $match (@$matches) {
408 146         220 my ($re, $actions) = @$match;
409 146         191 my $on_create = $actions->{on_create};
410 146 100       723 if ($tag =~ $re) {
411 59         99 $on_data = $actions->{on_data};
412 59 100       194 $on_create and $data = $on_create->($constructor, $event);
413 59         195 return ($data, $on_data);
414             }
415             }
416             }
417             #croak "Unknown tag '$tag'. Use schema 'Catchall' to ignore unknown tags";
418             }
419              
420 1548         3486 return ($data, $on_data);
421             }
422              
423 31     31   96 sub _bool_jsonpp_true { JSON::PP::true() }
424              
425 0     0   0 sub _bool_booleanpm_true { boolean::true() }
426              
427 16     16   36 sub _bool_perl_true { !!1 }
428              
429 33     33   116 sub _bool_jsonpp_false { JSON::PP::false() }
430              
431 0     0   0 sub _bool_booleanpm_false { boolean::false() }
432              
433 2     2   5 sub _bool_perl_false { !!0 }
434              
435             1;
436              
437             __END__