File Coverage

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


line stmt bran cond sub pod time code
1 35     35   245 use strict;
  35         75  
  35         1068  
2 35     35   198 use warnings;
  35         69  
  35         1319  
3             package YAML::PP::Schema;
4 35     35   207 use B;
  35         76  
  35         1634  
5 35     35   17389 use Module::Load qw//;
  35         40139  
  35         1363  
6              
7             our $VERSION = '0.036_002'; # TRIAL VERSION
8              
9 35     35   11062 use YAML::PP::Common qw/ YAML_PLAIN_SCALAR_STYLE /;
  35         85  
  35         2256  
10              
11 35     35   303 use Scalar::Util qw/ blessed /;
  35         66  
  35         90892  
12              
13             sub new {
14 752     752 0 2354 my ($class, %args) = @_;
15              
16 752         1487 my $yaml_version = delete $args{yaml_version};
17 752         1318 my $bool = delete $args{boolean};
18 752 50       1829 $bool = 'perl' unless defined $bool;
19 752 50       1674 if (keys %args) {
20 0         0 die "Unexpected arguments: " . join ', ', sort keys %args;
21             }
22 752         1757 my $true;
23             my $false;
24 752         0 my @bool_class;
25 752         2444 my @bools = split m/,/, $bool;
26 752         1414 for my $b (@bools) {
27 752 50       2301 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 593         3845 require JSON::PP;
33 593   50     2613 $true ||= \&_bool_jsonpp_true;
34 593   50     2066 $false ||= \&_bool_jsonpp_false;
35 593         1430 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     760 $true ||= \&_bool_perl_true;
45 159   50     656 $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 752         5931 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 752         3401 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 752         2948 return $self;
80             }
81              
82 31122     31122 0 47959 sub resolvers { return $_[0]->{resolvers} }
83 24411     24411 0 38462 sub representers { return $_[0]->{representers} }
84              
85 2239     2239 0 5410 sub true { return $_[0]->{true} }
86 2239     2239 0 5299 sub false { return $_[0]->{false} }
87 1419 100   1419 0 1965 sub bool_class { return @{ $_[0]->{bool_class} } ? $_[0]->{bool_class} : undef }
  1419         4866  
88 770     770 0 1746 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 752     752 0 1798 my ($self, @schemas) = @_;
100 752         1758 my $yaml_version = $self->yaml_version;
101 752         1208 my $i = 0;
102 752         2070 while ($i < @schemas) {
103 782         1334 my $item = $schemas[ $i ];
104 782 100       1757 if ($item eq '+') {
105 440         883 $item = $DEFAULT_SCHEMA{ $yaml_version };
106             }
107 782         1145 $i++;
108 782 100       1970 if (blessed($item)) {
109 10         53 $item->register(
110             schema => $self,
111             );
112 10         51 next;
113             }
114 772         1092 my @options;
115 772   100     2122 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         33 push @options, $schemas[ $i ];
123 14         32 $i++;
124             }
125              
126 772         1223 my $class;
127 772 100       2236 if ($item =~ m/^\:(.*)/) {
128 1         3 $class = "$1";
129 1 50       5 unless ($class =~ m/\A[A-Za-z0-9_:]+\z/) {
130 0         0 die "Module name '$class' is invalid";
131             }
132 1         4 Module::Load::load $class;
133             }
134             else {
135 771         1968 $class = "YAML::PP::Schema::$item";
136 771 50       4050 unless ($class =~ m/\A[A-Za-z0-9_:]+\z/) {
137 0         0 die "Module name '$class' is invalid";
138             }
139 771   66     3560 $LOADED_SCHEMA{ $item } ||= Module::Load::load $class;
140             }
141 772         53950 $class->register(
142             schema => $self,
143             options => \@options,
144             );
145              
146             }
147             }
148              
149             sub add_resolver {
150 20688     20688 0 43331 my ($self, %args) = @_;
151 20688         28457 my $tag = $args{tag};
152 20688         26156 my $rule = $args{match};
153 20688         31016 my $resolvers = $self->resolvers;
154 20688         36435 my ($type, @rule) = @$rule;
155 20688         27306 my $implicit = $args{implicit};
156 20688 100       37085 $implicit = 1 unless defined $implicit;
157 20688         27715 my $resolver_list = [];
158 20688 50       34862 if ($tag) {
159 20688 100       34087 if (ref $tag eq 'Regexp') {
160 56   100     191 my $res = $resolvers->{tags} ||= [];
161 56         134 push @$res, [ $tag, {} ];
162 56         118 push @$resolver_list, $res->[-1]->[1];
163             }
164             else {
165 20632   100     47293 my $res = $resolvers->{tag}->{ $tag } ||= {};
166 20632         33780 push @$resolver_list, $res;
167             }
168             }
169 20688 100       33315 if ($implicit) {
170 20566   100     39490 push @$resolver_list, $resolvers->{value} ||= {};
171             }
172 20688         30801 for my $res (@$resolver_list) {
173 41254 100       65388 if ($type eq 'equals') {
    100          
    50          
174 33726         50806 my ($match, $value) = @rule;
175 33726 50       60111 unless (exists $res->{equals}->{ $match }) {
176 33726         57662 $res->{equals}->{ $match } = $value;
177             }
178 33726         93328 next;
179             }
180             elsif ($type eq 'regex') {
181 5922         8613 my ($match, $value) = @rule;
182 5922         7018 push @{ $res->{regex} }, [ $match => $value ];
  5922         17653  
183             }
184             elsif ($type eq 'all') {
185 1606         2358 my ($value) = @rule;
186 1606         4792 $res->{all} = $value;
187             }
188             }
189             }
190              
191             sub add_sequence_resolver {
192 57     57 0 167 my ($self, %args) = @_;
193 57         160 return $self->add_collection_resolver(sequence => %args);
194             }
195              
196             sub add_mapping_resolver {
197 222     222 0 673 my ($self, %args) = @_;
198 222         611 return $self->add_collection_resolver(mapping => %args);
199             }
200              
201             sub add_collection_resolver {
202 279     279 0 624 my ($self, $type, %args) = @_;
203 279         432 my $tag = $args{tag};
204 279         389 my $implicit = $args{implicit};
205 279         465 my $resolvers = $self->resolvers;
206              
207 279 100 66     1035 if ($tag and ref $tag eq 'Regexp') {
    50          
208 136   100     391 my $res = $resolvers->{ $type }->{tags} ||= [];
209             push @$res, [ $tag, {
210             on_create => $args{on_create},
211             on_data => $args{on_data},
212 136         747 } ];
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     1163 };
219             }
220             }
221              
222             sub add_representer {
223 20573     20573 0 42211 my ($self, %args) = @_;
224              
225 20573         30830 my $representers = $self->representers;
226 20573 100       37993 if (my $flags = $args{flags}) {
227 1494         2180 my $rep = $representers->{flags};
228 1494         2516 push @$rep, \%args;
229 1494         3285 return;
230             }
231 19079 100       33647 if (my $regex = $args{regex}) {
232 748         1223 my $rep = $representers->{regex};
233 748         1593 push @$rep, \%args;
234 748         1842 return;
235             }
236 18331 100       30278 if (my $regex = $args{class_matches}) {
237 26         55 my $rep = $representers->{class_matches};
238 26         94 push @$rep, [ $args{class_matches}, $args{code} ];
239 26         65 return;
240             }
241 18305 100       30054 if (my $class_equals = $args{class_equals}) {
242 593 50 33     1934 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 593         944 my $rep = $representers->{class_equals};
249             $rep->{ $class_equals } = {
250             code => $args{code},
251 593         1431 };
252 593         1823 return;
253             }
254 17712 100       28781 if (my $class_isa = $args{class_isa}) {
255 2         3 my $rep = $representers->{class_isa};
256 2         5 push @$rep, [ $args{class_isa}, $args{code} ];
257 2         5 return;
258             }
259 17710 50       29005 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 17710 100       31319 if (defined(my $equals = $args{equals})) {
267 16867         21442 my $rep = $representers->{equals};
268             $rep->{ $equals } = {
269             code => $args{code},
270 16867         32798 };
271 16867         49633 return;
272             }
273 843 100       1805 if (defined(my $scalarref = $args{scalarref})) {
274             $representers->{scalarref} = {
275             code => $args{code},
276 24         57 };
277 24         70 return;
278             }
279 819 100       1596 if (defined(my $refref = $args{refref})) {
280             $representers->{refref} = {
281             code => $args{code},
282 24         85 };
283 24         59 return;
284             }
285 795 100       1633 if (defined(my $coderef = $args{coderef})) {
286             $representers->{coderef} = {
287             code => $args{code},
288 24         66 };
289 24         57 return;
290             }
291 771 100       1674 if (defined(my $glob = $args{glob})) {
292             $representers->{glob} = {
293             code => $args{code},
294 24         57 };
295 24         68 return;
296             }
297 747 50       1652 if (my $undef = $args{undefined}) {
298 747         1173 $representers->{undef} = $undef;
299 747         1977 return;
300             }
301             }
302              
303             sub load_scalar {
304 7639     7639 0 12794 my ($self, $constructor, $event) = @_;
305 7639         11866 my $tag = $event->{tag};
306 7639         11947 my $value = $event->{value};
307              
308 7639         14358 my $resolvers = $self->resolvers;
309 7639         10346 my $res;
310 7639 100       13505 if ($tag) {
311 697         1805 $res = $resolvers->{tag}->{ $tag };
312 697 100 100     2234 if (not $res and my $matches = $resolvers->{tags}) {
313 18         49 for my $match (@$matches) {
314 30         66 my ($re, $rule) = @$match;
315 30 100       198 if ($tag =~ $re) {
316 15         31 $res = $rule;
317 15         31 last;
318             }
319             }
320             }
321             }
322             else {
323 6942         10300 $res = $resolvers->{value};
324 6942 100       14511 if ($event->{style} ne YAML_PLAIN_SCALAR_STYLE) {
325 2619         7319 return $value;
326             }
327             }
328              
329 5020 100       10917 if (my $equals = $res->{equals}) {
330 4393 100       11267 if (exists $equals->{ $value }) {
331 424         875 my $res = $equals->{ $value };
332 424 100       1002 if (ref $res eq 'CODE') {
333 80         212 return $res->($constructor, $event);
334             }
335 344         971 return $res;
336             }
337             }
338 4596 100       9348 if (my $regex = $res->{regex}) {
339 3956         7666 for my $item (@$regex) {
340 14035         23072 my ($re, $sub) = @$item;
341 14035         66566 my @matches = $value =~ $re;
342 14035 100       37322 if (@matches) {
343 554         2080 return $sub->($constructor, $event, \@matches);
344             }
345             }
346             }
347 4042 100       9278 if (my $catch_all = $res->{all}) {
348 3791 50       9794 if (ref $catch_all eq 'CODE') {
349 3791         10567 return $catch_all->($constructor, $event);
350             }
351 0         0 return $catch_all;
352             }
353 251         815 return $value;
354             }
355              
356             sub create_sequence {
357 1213     1213 0 2117 my ($self, $constructor, $event) = @_;
358 1213         1880 my $tag = $event->{tag};
359 1213         1930 my $data = [];
360 1213         1698 my $on_data;
361              
362 1213         2354 my $resolvers = $self->resolvers->{sequence};
363 1213 100       2447 if ($tag) {
364 35 100       134 if (my $equals = $resolvers->{tag}->{ $tag }) {
365 6         13 my $on_create = $equals->{on_create};
366 6         13 $on_data = $equals->{on_data};
367 6 50       31 $on_create and $data = $on_create->($constructor, $event);
368 6         28 return ($data, $on_data);
369             }
370 29 100       93 if (my $matches = $resolvers->{tags}) {
371 15         30 for my $match (@$matches) {
372 16         37 my ($re, $actions) = @$match;
373 16         33 my $on_create = $actions->{on_create};
374 16 100       125 if ($tag =~ $re) {
375 14         31 $on_data = $actions->{on_data};
376 14 50       67 $on_create and $data = $on_create->($constructor, $event);
377 14         51 return ($data, $on_data);
378             }
379             }
380             }
381             }
382              
383 1193         3115 return ($data, $on_data);
384             }
385              
386             sub create_mapping {
387 1303     1303 0 2469 my ($self, $constructor, $event) = @_;
388 1303         2078 my $tag = $event->{tag};
389 1303         2150 my $data = {};
390 1303         1811 my $on_data;
391              
392 1303         2622 my $resolvers = $self->resolvers->{mapping};
393 1303 100       2586 if ($tag) {
394 83 100       272 if (my $equals = $resolvers->{tag}->{ $tag }) {
395 24         62 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         95 return ($data, $on_data);
399             }
400 59 100       173 if (my $matches = $resolvers->{tags}) {
401 31         66 for my $match (@$matches) {
402 94         175 my ($re, $actions) = @$match;
403 94         147 my $on_create = $actions->{on_create};
404 94 100       439 if ($tag =~ $re) {
405 28         63 $on_data = $actions->{on_data};
406 28 50       137 $on_create and $data = $on_create->($constructor, $event);
407 28         124 return ($data, $on_data);
408             }
409             }
410             }
411             }
412              
413 1251         3277 return ($data, $on_data);
414             }
415              
416 31     31   95 sub _bool_jsonpp_true { JSON::PP::true() }
417              
418 0     0   0 sub _bool_booleanpm_true { boolean::true() }
419              
420 15     15   48 sub _bool_perl_true { !!1 }
421              
422 33     33   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__