File Coverage

blib/lib/YAML/PP/Schema.pm
Criterion Covered Total %
statement 227 248 91.5
branch 96 118 81.3
condition 27 44 61.3
subroutine 26 28 92.8
pod 0 16 0.0
total 376 454 82.8


line stmt bran cond sub pod time code
1 35     35   227 use strict;
  35         69  
  35         997  
2 35     35   175 use warnings;
  35         58  
  35         1272  
3             package YAML::PP::Schema;
4 35     35   276 use B;
  35         72  
  35         1532  
5 35     35   17528 use Module::Load qw//;
  35         40788  
  35         1326  
6              
7             our $VERSION = '0.036'; # VERSION
8              
9 35     35   10634 use YAML::PP::Common qw/ YAML_PLAIN_SCALAR_STYLE /;
  35         86  
  35         2110  
10              
11 35     35   284 use Scalar::Util qw/ blessed /;
  35         98  
  35         92055  
12              
13             sub new {
14 190     190 0 682 my ($class, %args) = @_;
15              
16 190         423 my $yaml_version = delete $args{yaml_version};
17 190         373 my $bool = delete $args{boolean};
18 190 50       459 $bool = 'perl' unless defined $bool;
19 190 50       442 if (keys %args) {
20 0         0 die "Unexpected arguments: " . join ', ', sort keys %args;
21             }
22 190         471 my $true;
23             my $false;
24 190         0 my @bool_class;
25 190         612 my @bools = split m/,/, $bool;
26 190         397 for my $b (@bools) {
27 190 50       838 if ($b eq '*') {
    100          
    50          
    50          
    0          
28 0         0 push @bool_class, ('boolean', 'JSON::PP::Boolean');
29 0         0 last;
30             }
31             elsif ($b eq 'JSON::PP') {
32 31         198 require JSON::PP;
33 31   50     185 $true ||= \&_bool_jsonpp_true;
34 31   50     137 $false ||= \&_bool_jsonpp_false;
35 31         85 push @bool_class, 'JSON::PP::Boolean';
36             }
37             elsif ($b eq 'boolean') {
38 0         0 require boolean;
39 0   0     0 $true ||= \&_bool_booleanpm_true;
40 0   0     0 $false ||= \&_bool_booleanpm_false;
41 0         0 push @bool_class, 'boolean';
42             }
43             elsif ($b eq 'perl') {
44 159   50     713 $true ||= \&_bool_perl_true;
45 159   50     600 $false ||= \&_bool_perl_false;
46             }
47             elsif ($b eq 'perl_experimental') {
48 0   0     0 $true ||= \&_bool_perl_true;
49 0   0     0 $false ||= \&_bool_perl_false;
50 0         0 push @bool_class, 'perl_experimental';
51             }
52             else {
53 0         0 die "Invalid value for 'boolean': '$b'. Allowed: ('perl', 'boolean', 'JSON::PP')";
54             }
55             }
56              
57 190         1769 my %representers = (
58             'undef' => undef,
59             flags => [],
60             equals => {},
61             regex => [],
62             class_equals => {},
63             class_matches => [],
64             class_isa => [],
65             scalarref => undef,
66             refref => undef,
67             coderef => undef,
68             glob => undef,
69             tied_equals => {},
70             );
71 190         995 my $self = bless {
72             yaml_version => $yaml_version,
73             resolvers => {},
74             representers => \%representers,
75             true => $true,
76             false => $false,
77             bool_class => \@bool_class,
78             }, $class;
79 190         742 return $self;
80             }
81              
82 11038     11038 0 17806 sub resolvers { return $_[0]->{resolvers} }
83 7160     7160 0 12276 sub representers { return $_[0]->{representers} }
84              
85 553     553 0 1441 sub true { return $_[0]->{true} }
86 553     553 0 1432 sub false { return $_[0]->{false} }
87 294 100   294 0 455 sub bool_class { return @{ $_[0]->{bool_class} } ? $_[0]->{bool_class} : undef }
  294         1355  
88 208     208 0 575 sub yaml_version { return $_[0]->{yaml_version} }
89              
90             my %LOADED_SCHEMA = (
91             JSON => 1,
92             );
93             my %DEFAULT_SCHEMA = (
94             '1.2' => 'Core',
95             '1.1' => 'YAML1_1',
96             );
97              
98             sub load_subschemas {
99 190     190 0 515 my ($self, @schemas) = @_;
100 190         428 my $yaml_version = $self->yaml_version;
101 190         310 my $i = 0;
102 190         554 while ($i < @schemas) {
103 220         418 my $item = $schemas[ $i ];
104 220 100       583 if ($item eq '+') {
105 148         352 $item = $DEFAULT_SCHEMA{ $yaml_version };
106             }
107 220         317 $i++;
108 220 100       702 if (blessed($item)) {
109 10         44 $item->register(
110             schema => $self,
111             );
112 10         51 next;
113             }
114 210         297 my @options;
115 210   100     904 while ($i < @schemas
      100        
116             and (
117             $schemas[ $i ] =~ m/^[^A-Za-z]/
118             or
119             $schemas[ $i ] =~ m/^[a-zA-Z0-9]+=/
120             )
121             ) {
122 14         35 push @options, $schemas[ $i ];
123 14         30 $i++;
124             }
125              
126 210         333 my $class;
127 210 100       652 if ($item =~ m/^\:(.*)/) {
128 1         3 $class = "$1";
129 1 50       6 unless ($class =~ m/\A[A-Za-z0-9_:]+\z/) {
130 0         0 die "Module name '$class' is invalid";
131             }
132 1         7 Module::Load::load $class;
133             }
134             else {
135 209         527 $class = "YAML::PP::Schema::$item";
136 209 50       1082 unless ($class =~ m/\A[A-Za-z0-9_:]+\z/) {
137 0         0 die "Module name '$class' is invalid";
138             }
139 209   66     1063 $LOADED_SCHEMA{ $item } ||= Module::Load::load $class;
140             }
141 210         13276 $class->register(
142             schema => $self,
143             options => \@options,
144             );
145              
146             }
147             }
148              
149             sub add_resolver {
150 4952     4952 0 10541 my ($self, %args) = @_;
151 4952         6876 my $tag = $args{tag};
152 4952         6274 my $rule = $args{match};
153 4952         7417 my $resolvers = $self->resolvers;
154 4952         8953 my ($type, @rule) = @$rule;
155 4952         6572 my $implicit = $args{implicit};
156 4952 100       9245 $implicit = 1 unless defined $implicit;
157 4952         6805 my $resolver_list = [];
158 4952 50       8140 if ($tag) {
159 4952 100       8195 if (ref $tag eq 'Regexp') {
160 56   100     222 my $res = $resolvers->{tags} ||= [];
161 56         177 push @$res, [ $tag, {} ];
162 56         116 push @$resolver_list, $res->[-1]->[1];
163             }
164             else {
165 4896   100     11405 my $res = $resolvers->{tag}->{ $tag } ||= {};
166 4896         8171 push @$resolver_list, $res;
167             }
168             }
169 4952 100       8401 if ($implicit) {
170 4830   100     8845 push @$resolver_list, $resolvers->{value} ||= {};
171             }
172 4952         7278 for my $res (@$resolver_list) {
173 9782 100       15643 if ($type eq 'equals') {
    100          
    50          
174 7874         11690 my ($match, $value) = @rule;
175 7874 50       13905 unless (exists $res->{equals}->{ $match }) {
176 7874         13704 $res->{equals}->{ $match } = $value;
177             }
178 7874         21622 next;
179             }
180             elsif ($type eq 'regex') {
181 1426         2189 my ($match, $value) = @rule;
182 1426         1694 push @{ $res->{regex} }, [ $match => $value ];
  1426         4488  
183             }
184             elsif ($type eq 'all') {
185 482         736 my ($value) = @rule;
186 482         1433 $res->{all} = $value;
187             }
188             }
189             }
190              
191             sub add_sequence_resolver {
192 57     57 0 177 my ($self, %args) = @_;
193 57         152 return $self->add_collection_resolver(sequence => %args);
194             }
195              
196             sub add_mapping_resolver {
197 222     222 0 704 my ($self, %args) = @_;
198 222         610 return $self->add_collection_resolver(mapping => %args);
199             }
200              
201             sub add_collection_resolver {
202 279     279 0 639 my ($self, $type, %args) = @_;
203 279         413 my $tag = $args{tag};
204 279         364 my $implicit = $args{implicit};
205 279         489 my $resolvers = $self->resolvers;
206              
207 279 100 66     1058 if ($tag and ref $tag eq 'Regexp') {
    50          
208 136   100     380 my $res = $resolvers->{ $type }->{tags} ||= [];
209             push @$res, [ $tag, {
210             on_create => $args{on_create},
211             on_data => $args{on_data},
212 136         683 } ];
213             }
214             elsif ($tag) {
215             my $res = $resolvers->{ $type }->{tag}->{ $tag } ||= {
216             on_create => $args{on_create},
217             on_data => $args{on_data},
218 143   50     1214 };
219             }
220             }
221              
222             sub add_representer {
223 4837     4837 0 10250 my ($self, %args) = @_;
224              
225 4837         8841 my $representers = $self->representers;
226 4837 100       8771 if (my $flags = $args{flags}) {
227 370         605 my $rep = $representers->{flags};
228 370         657 push @$rep, \%args;
229 370         841 return;
230             }
231 4467 100       7654 if (my $regex = $args{regex}) {
232 186         333 my $rep = $representers->{regex};
233 186         481 push @$rep, \%args;
234 186         435 return;
235             }
236 4281 100       6914 if (my $regex = $args{class_matches}) {
237 26         58 my $rep = $representers->{class_matches};
238 26         130 push @$rep, [ $args{class_matches}, $args{code} ];
239 26         65 return;
240             }
241 4255 100       6823 if (my $class_equals = $args{class_equals}) {
242 31 50 33     147 if ($] >= 5.036000 and $class_equals eq 'perl_experimental') {
243             $representers->{bool} = {
244             code => $args{code},
245 0         0 };
246 0         0 return;
247             }
248 31         62 my $rep = $representers->{class_equals};
249             $rep->{ $class_equals } = {
250             code => $args{code},
251 31         98 };
252 31         101 return;
253             }
254 4224 100       6741 if (my $class_isa = $args{class_isa}) {
255 2         4 my $rep = $representers->{class_isa};
256 2         6 push @$rep, [ $args{class_isa}, $args{code} ];
257 2         6 return;
258             }
259 4222 50       6656 if (my $tied_equals = $args{tied_equals}) {
260 0         0 my $rep = $representers->{tied_equals};
261             $rep->{ $tied_equals } = {
262             code => $args{code},
263 0         0 };
264 0         0 return;
265             }
266 4222 100       7191 if (defined(my $equals = $args{equals})) {
267 3941         5221 my $rep = $representers->{equals};
268             $rep->{ $equals } = {
269             code => $args{code},
270 3941         8195 };
271 3941         12036 return;
272             }
273 281 100       644 if (defined(my $scalarref = $args{scalarref})) {
274             $representers->{scalarref} = {
275             code => $args{code},
276 24         70 };
277 24         65 return;
278             }
279 257 100       558 if (defined(my $refref = $args{refref})) {
280             $representers->{refref} = {
281             code => $args{code},
282 24         100 };
283 24         73 return;
284             }
285 233 100       507 if (defined(my $coderef = $args{coderef})) {
286             $representers->{coderef} = {
287             code => $args{code},
288 24         66 };
289 24         102 return;
290             }
291 209 100       475 if (defined(my $glob = $args{glob})) {
292             $representers->{glob} = {
293             code => $args{code},
294 24         84 };
295 24         61 return;
296             }
297 185 50       452 if (my $undef = $args{undefined}) {
298 185         308 $representers->{undef} = $undef;
299 185         476 return;
300             }
301             }
302              
303             sub load_scalar {
304 4415     4415 0 7376 my ($self, $constructor, $event) = @_;
305 4415         6584 my $tag = $event->{tag};
306 4415         6979 my $value = $event->{value};
307              
308 4415         8087 my $resolvers = $self->resolvers;
309 4415         5700 my $res;
310 4415 100       7890 if ($tag) {
311 577         1434 $res = $resolvers->{tag}->{ $tag };
312 577 100 100     1809 if (not $res and my $matches = $resolvers->{tags}) {
313 18         53 for my $match (@$matches) {
314 30         77 my ($re, $rule) = @$match;
315 30 100       208 if ($tag =~ $re) {
316 15         27 $res = $rule;
317 15         28 last;
318             }
319             }
320             }
321             }
322             else {
323 3838         6148 $res = $resolvers->{value};
324 3838 100       8512 if ($event->{style} ne YAML_PLAIN_SCALAR_STYLE) {
325 2096         5582 return $value;
326             }
327             }
328              
329 2319 100       5316 if (my $equals = $res->{equals}) {
330 1808 100       4482 if (exists $equals->{ $value }) {
331 273         563 my $res = $equals->{ $value };
332 273 100       621 if (ref $res eq 'CODE') {
333 77         192 return $res->($constructor, $event);
334             }
335 196         503 return $res;
336             }
337             }
338 2046 100       4140 if (my $regex = $res->{regex}) {
339 1506         2932 for my $item (@$regex) {
340 4879         7977 my ($re, $sub) = @$item;
341 4879         22507 my @matches = $value =~ $re;
342 4879 100       13101 if (@matches) {
343 299         1214 return $sub->($constructor, $event, \@matches);
344             }
345             }
346             }
347 1747 100       3871 if (my $catch_all = $res->{all}) {
348 1537 50       3652 if (ref $catch_all eq 'CODE') {
349 1537         4627 return $catch_all->($constructor, $event);
350             }
351 0         0 return $catch_all;
352             }
353 210         625 return $value;
354             }
355              
356             sub create_sequence {
357 804     804 0 1323 my ($self, $constructor, $event) = @_;
358 804         1179 my $tag = $event->{tag};
359 804         1279 my $data = [];
360 804         1081 my $on_data;
361              
362 804         1403 my $resolvers = $self->resolvers->{sequence};
363 804 100       1524 if ($tag) {
364 21 100       74 if (my $equals = $resolvers->{tag}->{ $tag }) {
365 6         18 my $on_create = $equals->{on_create};
366 6         19 $on_data = $equals->{on_data};
367 6 50       42 $on_create and $data = $on_create->($constructor, $event);
368 6         25 return ($data, $on_data);
369             }
370 15 50       51 if (my $matches = $resolvers->{tags}) {
371 15         44 for my $match (@$matches) {
372 16         40 my ($re, $actions) = @$match;
373 16         27 my $on_create = $actions->{on_create};
374 16 100       123 if ($tag =~ $re) {
375 14         27 $on_data = $actions->{on_data};
376 14 50       74 $on_create and $data = $on_create->($constructor, $event);
377 14         58 return ($data, $on_data);
378             }
379             }
380             }
381             }
382              
383 784         1946 return ($data, $on_data);
384             }
385              
386             sub create_mapping {
387 588     588 0 993 my ($self, $constructor, $event) = @_;
388 588         906 my $tag = $event->{tag};
389 588         924 my $data = {};
390 588         851 my $on_data;
391              
392 588         1159 my $resolvers = $self->resolvers->{mapping};
393 588 100       1145 if ($tag) {
394 55 100       199 if (my $equals = $resolvers->{tag}->{ $tag }) {
395 24         54 my $on_create = $equals->{on_create};
396 24         39 $on_data = $equals->{on_data};
397 24 100       116 $on_create and $data = $on_create->($constructor, $event);
398 24         88 return ($data, $on_data);
399             }
400 31 50       84 if (my $matches = $resolvers->{tags}) {
401 31         75 for my $match (@$matches) {
402 94         199 my ($re, $actions) = @$match;
403 94         142 my $on_create = $actions->{on_create};
404 94 100       487 if ($tag =~ $re) {
405 28         55 $on_data = $actions->{on_data};
406 28 50       135 $on_create and $data = $on_create->($constructor, $event);
407 28         108 return ($data, $on_data);
408             }
409             }
410             }
411             }
412              
413 536         1417 return ($data, $on_data);
414             }
415              
416 31     31   91 sub _bool_jsonpp_true { JSON::PP::true() }
417              
418 0     0   0 sub _bool_booleanpm_true { boolean::true() }
419              
420 15     15   57 sub _bool_perl_true { !!1 }
421              
422 30     30   119 sub _bool_jsonpp_false { JSON::PP::false() }
423              
424 0     0   0 sub _bool_booleanpm_false { boolean::false() }
425              
426 1     1   4 sub _bool_perl_false { !!0 }
427              
428             1;
429              
430             __END__