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   228 use strict;
  35         69  
  35         1004  
2 35     35   170 use warnings;
  35         70  
  35         1230  
3             package YAML::PP::Schema;
4 35     35   226 use B;
  35         65  
  35         1531  
5 35     35   16829 use Module::Load qw//;
  35         39516  
  35         1310  
6              
7             our $VERSION = '0.036_001'; # TRIAL VERSION
8              
9 35     35   10658 use YAML::PP::Common qw/ YAML_PLAIN_SCALAR_STYLE /;
  35         84  
  35         2222  
10              
11 35     35   273 use Scalar::Util qw/ blessed /;
  35         76  
  35         88829  
12              
13             sub new {
14 752     752 0 2351 my ($class, %args) = @_;
15              
16 752         1434 my $yaml_version = delete $args{yaml_version};
17 752         1330 my $bool = delete $args{boolean};
18 752 50       1838 $bool = 'perl' unless defined $bool;
19 752 50       1685 if (keys %args) {
20 0         0 die "Unexpected arguments: " . join ', ', sort keys %args;
21             }
22 752         1823 my $true;
23             my $false;
24 752         0 my @bool_class;
25 752         2317 my @bools = split m/,/, $bool;
26 752         1523 for my $b (@bools) {
27 752 50       2287 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         3523 require JSON::PP;
33 593   50     2728 $true ||= \&_bool_jsonpp_true;
34 593   50     2226 $false ||= \&_bool_jsonpp_false;
35 593         1450 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     775 $true ||= \&_bool_perl_true;
45 159   50     606 $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         6327 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         3792 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         2875 return $self;
80             }
81              
82 31122     31122 0 49226 sub resolvers { return $_[0]->{resolvers} }
83 24411     24411 0 38543 sub representers { return $_[0]->{representers} }
84              
85 2239     2239 0 5504 sub true { return $_[0]->{true} }
86 2239     2239 0 5412 sub false { return $_[0]->{false} }
87 1419 100   1419 0 2047 sub bool_class { return @{ $_[0]->{bool_class} } ? $_[0]->{bool_class} : undef }
  1419         5107  
88 770     770 0 1825 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 1751 my ($self, @schemas) = @_;
100 752         1649 my $yaml_version = $self->yaml_version;
101 752         1274 my $i = 0;
102 752         1989 while ($i < @schemas) {
103 782         1419 my $item = $schemas[ $i ];
104 782 100       1824 if ($item eq '+') {
105 440         812 $item = $DEFAULT_SCHEMA{ $yaml_version };
106             }
107 782         1206 $i++;
108 782 100       2096 if (blessed($item)) {
109 10         49 $item->register(
110             schema => $self,
111             );
112 10         49 next;
113             }
114 772         1082 my @options;
115 772   100     2116 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         31 push @options, $schemas[ $i ];
123 14         34 $i++;
124             }
125              
126 772         1183 my $class;
127 772 100       2142 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         5 Module::Load::load $class;
133             }
134             else {
135 771         1871 $class = "YAML::PP::Schema::$item";
136 771 50       3687 unless ($class =~ m/\A[A-Za-z0-9_:]+\z/) {
137 0         0 die "Module name '$class' is invalid";
138             }
139 771   66     3581 $LOADED_SCHEMA{ $item } ||= Module::Load::load $class;
140             }
141 772         53109 $class->register(
142             schema => $self,
143             options => \@options,
144             );
145              
146             }
147             }
148              
149             sub add_resolver {
150 20688     20688 0 43796 my ($self, %args) = @_;
151 20688         28916 my $tag = $args{tag};
152 20688         26934 my $rule = $args{match};
153 20688         30940 my $resolvers = $self->resolvers;
154 20688         36946 my ($type, @rule) = @$rule;
155 20688         27971 my $implicit = $args{implicit};
156 20688 100       36123 $implicit = 1 unless defined $implicit;
157 20688         27209 my $resolver_list = [];
158 20688 50       34924 if ($tag) {
159 20688 100       34147 if (ref $tag eq 'Regexp') {
160 56   100     204 my $res = $resolvers->{tags} ||= [];
161 56         146 push @$res, [ $tag, {} ];
162 56         119 push @$resolver_list, $res->[-1]->[1];
163             }
164             else {
165 20632   100     46662 my $res = $resolvers->{tag}->{ $tag } ||= {};
166 20632         34569 push @$resolver_list, $res;
167             }
168             }
169 20688 100       33446 if ($implicit) {
170 20566   100     37560 push @$resolver_list, $resolvers->{value} ||= {};
171             }
172 20688         29976 for my $res (@$resolver_list) {
173 41254 100       64809 if ($type eq 'equals') {
    100          
    50          
174 33726         50908 my ($match, $value) = @rule;
175 33726 50       60111 unless (exists $res->{equals}->{ $match }) {
176 33726         57395 $res->{equals}->{ $match } = $value;
177             }
178 33726         93726 next;
179             }
180             elsif ($type eq 'regex') {
181 5922         8844 my ($match, $value) = @rule;
182 5922         6986 push @{ $res->{regex} }, [ $match => $value ];
  5922         17731  
183             }
184             elsif ($type eq 'all') {
185 1606         2373 my ($value) = @rule;
186 1606         4281 $res->{all} = $value;
187             }
188             }
189             }
190              
191             sub add_sequence_resolver {
192 57     57 0 150 my ($self, %args) = @_;
193 57         168 return $self->add_collection_resolver(sequence => %args);
194             }
195              
196             sub add_mapping_resolver {
197 222     222 0 651 my ($self, %args) = @_;
198 222         549 return $self->add_collection_resolver(mapping => %args);
199             }
200              
201             sub add_collection_resolver {
202 279     279 0 646 my ($self, $type, %args) = @_;
203 279         445 my $tag = $args{tag};
204 279         377 my $implicit = $args{implicit};
205 279         509 my $resolvers = $self->resolvers;
206              
207 279 100 66     1001 if ($tag and ref $tag eq 'Regexp') {
    50          
208 136   100     401 my $res = $resolvers->{ $type }->{tags} ||= [];
209             push @$res, [ $tag, {
210             on_create => $args{on_create},
211             on_data => $args{on_data},
212 136         753 } ];
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     1160 };
219             }
220             }
221              
222             sub add_representer {
223 20573     20573 0 43497 my ($self, %args) = @_;
224              
225 20573         30842 my $representers = $self->representers;
226 20573 100       37306 if (my $flags = $args{flags}) {
227 1494         2181 my $rep = $representers->{flags};
228 1494         2467 push @$rep, \%args;
229 1494         3306 return;
230             }
231 19079 100       31734 if (my $regex = $args{regex}) {
232 748         1224 my $rep = $representers->{regex};
233 748         1642 push @$rep, \%args;
234 748         1788 return;
235             }
236 18331 100       29293 if (my $regex = $args{class_matches}) {
237 26         58 my $rep = $representers->{class_matches};
238 26         80 push @$rep, [ $args{class_matches}, $args{code} ];
239 26         64 return;
240             }
241 18305 100       29132 if (my $class_equals = $args{class_equals}) {
242 593 50 33     1804 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         1036 my $rep = $representers->{class_equals};
249             $rep->{ $class_equals } = {
250             code => $args{code},
251 593         1440 };
252 593         1779 return;
253             }
254 17712 100       29103 if (my $class_isa = $args{class_isa}) {
255 2         4 my $rep = $representers->{class_isa};
256 2         5 push @$rep, [ $args{class_isa}, $args{code} ];
257 2         5 return;
258             }
259 17710 50       28173 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       31428 if (defined(my $equals = $args{equals})) {
267 16867         22101 my $rep = $representers->{equals};
268             $rep->{ $equals } = {
269             code => $args{code},
270 16867         33412 };
271 16867         50845 return;
272             }
273 843 100       1829 if (defined(my $scalarref = $args{scalarref})) {
274             $representers->{scalarref} = {
275             code => $args{code},
276 24         63 };
277 24         69 return;
278             }
279 819 100       1691 if (defined(my $refref = $args{refref})) {
280             $representers->{refref} = {
281             code => $args{code},
282 24         57 };
283 24         56 return;
284             }
285 795 100       1748 if (defined(my $coderef = $args{coderef})) {
286             $representers->{coderef} = {
287             code => $args{code},
288 24         52 };
289 24         56 return;
290             }
291 771 100       1609 if (defined(my $glob = $args{glob})) {
292             $representers->{glob} = {
293             code => $args{code},
294 24         60 };
295 24         66 return;
296             }
297 747 50       1742 if (my $undef = $args{undefined}) {
298 747         1216 $representers->{undef} = $undef;
299 747         2002 return;
300             }
301             }
302              
303             sub load_scalar {
304 7639     7639 0 12835 my ($self, $constructor, $event) = @_;
305 7639         12415 my $tag = $event->{tag};
306 7639         11825 my $value = $event->{value};
307              
308 7639         14621 my $resolvers = $self->resolvers;
309 7639         10851 my $res;
310 7639 100       13518 if ($tag) {
311 697         1653 $res = $resolvers->{tag}->{ $tag };
312 697 100 100     2159 if (not $res and my $matches = $resolvers->{tags}) {
313 18         44 for my $match (@$matches) {
314 30         66 my ($re, $rule) = @$match;
315 30 100       187 if ($tag =~ $re) {
316 15         30 $res = $rule;
317 15         35 last;
318             }
319             }
320             }
321             }
322             else {
323 6942         10163 $res = $resolvers->{value};
324 6942 100       14533 if ($event->{style} ne YAML_PLAIN_SCALAR_STYLE) {
325 2619         7278 return $value;
326             }
327             }
328              
329 5020 100       11056 if (my $equals = $res->{equals}) {
330 4393 100       11357 if (exists $equals->{ $value }) {
331 424         807 my $res = $equals->{ $value };
332 424 100       1117 if (ref $res eq 'CODE') {
333 80         211 return $res->($constructor, $event);
334             }
335 344         971 return $res;
336             }
337             }
338 4596 100       9213 if (my $regex = $res->{regex}) {
339 3956         7790 for my $item (@$regex) {
340 14035         23154 my ($re, $sub) = @$item;
341 14035         65250 my @matches = $value =~ $re;
342 14035 100       35560 if (@matches) {
343 554         2051 return $sub->($constructor, $event, \@matches);
344             }
345             }
346             }
347 4042 100       8927 if (my $catch_all = $res->{all}) {
348 3791 50       9205 if (ref $catch_all eq 'CODE') {
349 3791         10455 return $catch_all->($constructor, $event);
350             }
351 0         0 return $catch_all;
352             }
353 251         778 return $value;
354             }
355              
356             sub create_sequence {
357 1213     1213 0 2211 my ($self, $constructor, $event) = @_;
358 1213         1999 my $tag = $event->{tag};
359 1213         1915 my $data = [];
360 1213         1622 my $on_data;
361              
362 1213         2430 my $resolvers = $self->resolvers->{sequence};
363 1213 100       2426 if ($tag) {
364 35 100       134 if (my $equals = $resolvers->{tag}->{ $tag }) {
365 6         17 my $on_create = $equals->{on_create};
366 6         12 $on_data = $equals->{on_data};
367 6 50       27 $on_create and $data = $on_create->($constructor, $event);
368 6         23 return ($data, $on_data);
369             }
370 29 100       110 if (my $matches = $resolvers->{tags}) {
371 15         37 for my $match (@$matches) {
372 16         43 my ($re, $actions) = @$match;
373 16         33 my $on_create = $actions->{on_create};
374 16 100       125 if ($tag =~ $re) {
375 14         27 $on_data = $actions->{on_data};
376 14 50       70 $on_create and $data = $on_create->($constructor, $event);
377 14         51 return ($data, $on_data);
378             }
379             }
380             }
381             }
382              
383 1193         3161 return ($data, $on_data);
384             }
385              
386             sub create_mapping {
387 1303     1303 0 2340 my ($self, $constructor, $event) = @_;
388 1303         2181 my $tag = $event->{tag};
389 1303         2344 my $data = {};
390 1303         1873 my $on_data;
391              
392 1303         2645 my $resolvers = $self->resolvers->{mapping};
393 1303 100       2536 if ($tag) {
394 83 100       408 if (my $equals = $resolvers->{tag}->{ $tag }) {
395 24         60 my $on_create = $equals->{on_create};
396 24         46 $on_data = $equals->{on_data};
397 24 100       126 $on_create and $data = $on_create->($constructor, $event);
398 24         102 return ($data, $on_data);
399             }
400 59 100       227 if (my $matches = $resolvers->{tags}) {
401 31         76 for my $match (@$matches) {
402 94         187 my ($re, $actions) = @$match;
403 94         166 my $on_create = $actions->{on_create};
404 94 100       451 if ($tag =~ $re) {
405 28         64 $on_data = $actions->{on_data};
406 28 50       156 $on_create and $data = $on_create->($constructor, $event);
407 28         111 return ($data, $on_data);
408             }
409             }
410             }
411             }
412              
413 1251         3493 return ($data, $on_data);
414             }
415              
416 31     31   96 sub _bool_jsonpp_true { JSON::PP::true() }
417              
418 0     0   0 sub _bool_booleanpm_true { boolean::true() }
419              
420 15     15   53 sub _bool_perl_true { !!1 }
421              
422 33     33   104 sub _bool_jsonpp_false { JSON::PP::false() }
423              
424 0     0   0 sub _bool_booleanpm_false { boolean::false() }
425              
426 1     1   3 sub _bool_perl_false { !!0 }
427              
428             1;
429              
430             __END__