File Coverage

blib/lib/Data/Schema.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package Data::Schema;
2             our $VERSION = '0.136';
3              
4              
5             # ABSTRACT: (DEPRECATED) Validate nested data structures with nested structure
6              
7              
8 25     25   813175 use Moose;
  0            
  0            
9             use Data::Schema::Config;
10             use Data::ModeMerge;
11             use Data::Schema::Type::Schema;
12             use Digest::MD5 qw/md5_hex/;
13             use Storable qw/freeze/;
14             #use Data::Dumper; # debugging
15              
16             # for loading plugins/types on startup. see import()
17             my %Default_Plugins = (); # package name => object
18             my %Default_Types = (
19             # XXX aliases should not create different handler object
20             str => 'Str',
21             string => 'Str',
22             cistr => 'CIStr',
23             cistring => 'CIStr',
24             bool => 'Bool',
25             boolean => 'Bool',
26             hash => 'Hash',
27             array => 'Array',
28             object => 'Object',
29             obj => 'Object',
30             int => 'Int',
31             integer => 'Int',
32             float => 'Float',
33             either => 'Either',
34             or => 'Either',
35             any => 'Either',
36             all => 'All',
37             and => 'All',
38              
39             typename => 'TypeName',
40             );
41             for (keys %Default_Types) { $Default_Types{$_} = "Data::Schema::Type::" . $Default_Types{$_} }
42              
43             my %Package_Default_Types; # importing package => ...
44             my %Package_Default_Plugins; # importing package => ...
45             my $Current_Call_Pkg;
46              
47              
48             sub ds_validate {
49             my ($data, $schema) = @_;
50             my $ds = __PACKAGE__->new(schema => $schema);
51             $ds->validate($data);
52             }
53              
54             our $Merger = new Data::ModeMerge;
55             $Merger->config->recurse_array(1);
56              
57              
58              
59             has plugins => (is => 'rw');
60             has type_handlers => (is => 'rw');
61              
62             # we keep this hash for lexical visibility. although the sub might
63             # already be defined, but at times should not be visible to the
64             # schema.
65             has compiled_subnames => (is => 'rw');
66              
67              
68             has config => (is => 'rw');
69              
70             has validation_state_stack => (is => 'rw');
71              
72             # BEGIN validation state
73             has schema => (is => 'rw');
74             # has data_copy?
75             has too_many_errors => (is => 'rw');
76             has too_many_warnings => (is => 'rw');
77             has errors => (is => 'rw');
78             has warnings => (is => 'rw');
79             has data_pos => (is => 'rw');
80             has schema_pos => (is => 'rw');
81             has stash => (is => 'rw'); # for storing stuffs during validation
82             # END validation state
83              
84             has logs => (is => 'rw'); # for debugging
85              
86             has compilation_state_stack => (is => 'rw');
87              
88             # BEGIN compilation state
89             # -- stash
90              
91             # -- same as stash, but won't be reset during inner calls to _emit_perl
92             has outer_stash => (is => 'rw');
93             # END compilation state
94              
95              
96              
97             sub BUILD {
98             #print "DEBUG: Creating new DS object\n";
99             my ($self, $args) = @_;
100              
101             # config
102             if ($self->config) {
103             # some sanity checks
104             my $is_hashref = ref($self->config) eq 'HASH';
105             die "config must be a hashref or a Data::Schema::Config" unless
106             $is_hashref || UNIVERSAL::isa($self->config, "Data::Schema::Config");
107             $self->config(Data::Schema::Config->new(%{ $self->config })) if $is_hashref;
108             die "config->schema_search_path must be an arrayref" unless ref($self->config->schema_search_path) eq 'ARRAY';
109             } else {
110             $self->config(Data::Schema::Config->new);
111             }
112              
113             # add default type handlers
114             if ($args->{type_handlers}) {
115             # some sanity checks
116             die "type_handlers must be a hashref" unless ref($args->{type_handlers}) eq 'HASH';
117             } else {
118             $self->type_handlers({});
119             my $deftypes = $Current_Call_Pkg && $Package_Default_Types{$Current_Call_Pkg} ? $Package_Default_Types{$Current_Call_Pkg} : \%Default_Types;
120             $self->register_type($_, $deftypes->{$_}) for keys %$deftypes;
121             }
122              
123             # add default plugins
124             if ($self->plugins) {
125             # some sanity checks
126             die "plugins must be an arrayref" unless ref($self->plugins) eq 'ARRAY';
127             } else {
128             $self->plugins([]);
129             my $defpl = $Current_Call_Pkg && $Package_Default_Plugins{$Current_Call_Pkg} ? $Package_Default_Plugins{$Current_Call_Pkg} : \%Default_Plugins;
130             #print Dumper $defpl;
131             $self->register_plugin($_) for keys %$defpl;
132             }
133              
134             $self->validation_state_stack([]) unless $self->validation_state_stack;
135             $self->compilation_state_stack([]) unless $self->compilation_state_stack;
136             $self->compiled_subnames({}) unless $self->compiled_subnames;
137             };
138              
139              
140             sub merge_attr_hashes {
141             my ($self, $attr_hashes) = @_;
142             my @merged;
143             #my $did_merging;
144             my $res = {error=>''};
145              
146             my $i = -1;
147             while (++$i < @$attr_hashes) {
148             if (!$i) { push @merged, $attr_hashes->[$i]; next }
149             my $has_merge_prefix = grep {/^[*+.!^-]/} keys %{ $attr_hashes->[$i] };
150             if (!$has_merge_prefix) { push @merged, $attr_hashes->[$i]; next }
151             my $mres = $Merger->merge($merged[-1], $attr_hashes->[$i]);
152             #$did_merging++;
153             #print "DEBUG: prefix_merge $i (".Data::Schema::Type::Base::_dump({}, $merged[-1]).", ".
154             # Data::Schema::Type::Base::_dump({}, $attr_hashes->[$i])." = ".($mres->{success} ? Data::Schema::Type::Base::_dump({}, $mres->{result}) : "FAIL")."\n";
155             if (!$mres->{success}) {
156             $res->{error} = $mres->{error};
157             last;
158             }
159             $merged[-1] = $mres->{result};
160             }
161             $res->{result} = \@merged unless $res->{error};
162             $res->{success} = !$res->{error};
163              
164             #print "DEBUG: merge_attr_hashes($self, ".Data::Schema::Type::Base::_dump({}, $attr_hashes).
165             # ") = ".($res->{success} ? Data::Schema::Type::Base::_dump({}, $res->{result}) : "FAIL")."\n";
166             $res;
167             }
168              
169              
170             sub init_validation_state {
171             my ($self) = @_;
172             $self->schema(undef);
173             $self->errors([]);
174             $self->warnings([]);
175             $self->too_many_errors(0);
176             $self->too_many_warnings(0);
177             $self->data_pos([]);
178             $self->schema_pos([]);
179             $self->stash({});
180             }
181              
182              
183             sub save_validation_state {
184             my ($self) = @_;
185             my $state = {
186             schema => $self->schema,
187             errors => $self->errors,
188             warnings => $self->warnings,
189             too_many_errors => $self->too_many_errors,
190             too_many_warnings => $self->too_many_warnings,
191             data_pos => $self->data_pos,
192             schema_pos => $self->schema_pos,
193             stash => $self->stash,
194             };
195             push @{ $self->validation_state_stack }, $state;
196             }
197              
198              
199             sub restore_validation_state {
200             my ($self) = @_;
201             my $state = pop @{ $self->validation_state_stack };
202             die "Can't restore validation state, stack is empty!" unless $state;
203             $self->schema($state->{schema});
204             $self->errors($state->{errors});
205             $self->warnings($state->{warnings});
206             $self->too_many_errors($state->{too_many_errors});
207             $self->too_many_warnings($state->{too_many_warnings});
208             $self->data_pos($state->{data_pos});
209             $self->schema_pos($state->{schema_pos});
210             $self->stash($state->{stash});
211             }
212              
213              
214             sub init_compilation_state {
215             my ($self, $inner) = @_;
216             $self->stash({});
217             $self->schema_pos([]) unless $self->schema_pos;
218             $self->outer_stash({compiling=>1}) unless $inner;
219             }
220              
221              
222             sub save_compilation_state {
223             my ($self) = @_;
224             my $state = {
225             stash => $self->stash,
226             };
227             push @{ $self->compilation_state_stack }, $state;
228             }
229              
230              
231             sub restore_compilation_state {
232             my ($self) = @_;
233             my $state = pop @{ $self->compilation_state_stack };
234             die "Can't restore validation state, stack is empty!" unless $state;
235             $self->stash($state->{stash});
236             }
237              
238             sub emitpl_my {
239             my ($self, @varnames) = @_;
240             join("", map { !$self->stash->{"C_var_$_"}++ ? "my $_;\n" : "" } @varnames);
241             }
242              
243             sub emitpl_require {
244             my ($self, @modnames) = @_;
245             join("", map { !$self->outer_stash->{"C_req_$_"}++ ? "require $_;\n" : "" } @modnames);
246             }
247              
248              
249             sub data_error {
250             my ($self, $message) = @_;
251             return if $self->too_many_errors;
252             do { $self->too_many_errors(1); $self->debug("Too many errors", 3); return } if
253             defined($self->config->max_errors) && $self->config->max_errors > 0 &&
254             @{ $self->errors } >= $self->config->max_errors;
255             push @{ $self->errors }, [[@{$self->data_pos}], [@{$self->schema_pos}], $message];
256             }
257              
258             sub emitpl_data_error {
259             my ($self, $msg, $is_literal) = @_;
260             my $perl;
261              
262             my $lit;
263             if ($is_literal) {
264             $lit = $msg;
265             } else {
266             $msg =~ s/(['\\])/\\$1/g;
267             $lit = "'$msg'";
268             }
269             $perl = 'push @errors, [[@$datapos],[@$schemapos],'.$lit.']; last L1 if @errors >= '.$self->config->max_errors.";";
270             if (defined($self->config->max_errors) && $self->config->max_errors > 0) {
271             $perl = 'if (@errors < '.$self->config->max_errors.') { '.$perl.' }';
272             }
273             $perl;
274             }
275              
276              
277             sub data_warn {
278             my ($self, $message) = @_;
279             return if $self->too_many_warnings;
280             do { $self->too_many_warnings(1); return } if
281             defined($self->config->max_warnings) && $self->config->max_warnings > 0 &&
282             @{ $self->warnings } >= $self->config->max_warnings;
283             push @{ $self->warnings }, [[@{$self->data_pos}], [@{$self->schema_pos}], $message];
284             }
285              
286             sub emitpl_data_warn {
287             my ($self, $msg, $is_literal) = @_;
288             my $perl;
289              
290             my $lit;
291             if ($is_literal) {
292             $lit = $msg;
293             } else {
294             $msg =~ s/(['\\])/\\$1/g;
295             $lit = "'$msg'";
296             }
297             $perl = 'push @warnings, [[@$datapos],[@$schemapos],'.$lit.']; ';
298             if (defined($self->config->max_warnings) && $self->config->max_warnings > 0) {
299             $perl = 'if (@warnings < '.$self->config->max_warnings.') { '.$perl.'} ';
300             }
301             $perl;
302             }
303              
304              
305             sub debug {
306             my ($self, $message, $level) = @_;
307             $level //= 1; # XXX should've been: 1=FATAL, 2=ERROR, 3=WARN, 4=INFO, 5=DEBUG as usual
308             return unless $level <= $self->config->debug;
309             $message = $message->() if ref($message) eq 'CODE';
310             push @{ $self->logs }, [[@{$self->data_pos}], [@{$self->schema_pos}], $message];
311             }
312              
313             sub emitpl_push_errwarn {
314             my ($self, $errorsvarname, $warningsvarname) = @_;
315             $errorsvarname //= 'suberrors';
316             $warningsvarname //= 'subwarnings';
317             my $perl1 = 'push @warnings, @$'.$warningsvarname.'; ';
318             if (defined($self->config->max_warnings) && $self->config->max_warnings > 0) {
319             $perl1 = 'if (@warnings < '.$self->config->max_warnings.') { '.$perl1.'} ';
320             }
321             my $perl2 .= 'push @errors, @$'.$errorsvarname.'; last L1 if @errors >= '.$self->config->max_errors."; ";
322             if (defined($self->config->max_errors) && $self->config->max_errors > 0) {
323             $perl2 = 'if (@errors < '.$self->config->max_errors.') { '.$perl2.'} ';
324             }
325             $perl1 . $perl2;
326             }
327              
328              
329             sub schema_error {
330             my ($self, $message) = @_;
331             die "Schema error: $message";
332             }
333              
334             sub _pos_as_str {
335             my ($self, $pos_elems) = @_;
336             my $res = join "/", @$pos_elems;
337             $res =~ s/\s+/_/sg;
338             $res;
339             }
340              
341              
342             sub check_type_name {
343             my ($self, $name) = @_;
344             $name =~ /\A[a-z_][a-z0-9_]{0,63}\z/;
345             # XXX synchronize with DST::TypeName
346             }
347              
348             sub _load_type_handler {
349             my ($self, $name) = @_;
350             my $obj_or_class = $self->type_handlers->{$name};
351             die "BUG: unknown type: $name" unless $obj_or_class;
352             return $obj_or_class if ref($obj_or_class);
353             eval "require $obj_or_class";
354             die "Can't load class $obj_or_class: $@" if $@;
355             my $obj = $obj_or_class->new();
356             $obj->validator($self);
357             $self->type_handlers->{$name} = $obj;
358             $obj;
359             }
360              
361              
362             sub register_type {
363             my ($self, $name, $obj_or_class) = @_;
364              
365             $self->check_type_name($name) or die "Invalid type name syntax: $name";
366              
367             if (exists $self->type_handlers->{$name}) {
368             die "Type already registered: $name";
369             }
370              
371             $self->type_handlers->{$name} = $obj_or_class;
372              
373             if (ref($obj_or_class)) {
374             $obj_or_class->validator($self);
375             } elsif (!$self->config->defer_loading) {
376             $self->_load_type_handler($name);
377             }
378             }
379              
380              
381             sub register_plugin {
382             my ($self, $obj_or_class) = @_;
383              
384             my $obj;
385             if (ref($obj_or_class)) {
386             $obj = $obj_or_class;
387             } else {
388             eval "use $obj_or_class";
389             die "Can't load class $obj_or_class: $@" if $@;
390             $obj = $obj_or_class->new();
391             }
392             $obj->validator($self);
393             push @{ $self->plugins }, $obj;
394             }
395              
396              
397             sub call_handler {
398             my ($self, $name, @args) = @_;
399             $name = "handle_$name" unless $name =~ /^handle_/;
400             for my $p (@{ $self->plugins }) {
401             if ($p->can($name)) {
402             #print "DEBUG: calling plugin $p, handler $name ...\n";
403             my $res = $p->$name(@args);
404             #print "DEBUG: res = $res ...\n";
405             return $res if $res != -1;
406             }
407             }
408             -1;
409             }
410              
411              
412             sub get_type_handler {
413             my ($self, $name) = @_;
414             my $th;
415             #print "DEBUG: Getting type handler for type $name ...\n";
416             #print "DEBUG: Current type handlers: ", Data::Dumper->new([$self->type_handlers])->Indent(1)->Dump;
417             if (!($th = $self->type_handlers->{$name})) {
418             # let's give plugin a chance to do something about it and then try again
419             if ($self->call_handler("unknown_type", $name) > 0) {
420             $th = $self->type_handlers->{$name};
421             }
422             } else {
423             unless (ref($th)) {
424             $th = $self->_load_type_handler($name);
425             }
426             }
427             #print "DEBUG: Type handler got: ".Dumper($th)."\n";
428             $th;
429             }
430              
431              
432             sub normalize_schema {
433             my ($self, $schema) = @_;
434              
435             if (!defined($schema)) {
436              
437             return "schema is missing";
438              
439             } elsif (!ref($schema)) {
440              
441             return { type=>$schema, attr_hashes=>[], def=>undef };
442              
443             } elsif (ref($schema) eq 'ARRAY') {
444              
445             my $type = $schema->[0];
446             if (!defined($type)) {
447             return "array form needs at least 1 element for type";
448             }
449             my @attr_hashes;
450             for (1..@$schema-1) {
451             if (ref($schema->[$_]) ne 'HASH') {
452             return "array form element [$_] (attrhash) must be a hashref";
453             }
454             push @attr_hashes, $schema->[$_];
455             }
456             return { type=>$type, attr_hashes=>\@attr_hashes, def=>undef };
457              
458             } elsif (ref($schema) eq 'HASH') {
459              
460             my $type = $schema->{type};
461             if (!defined($type)) {
462             return "hash form must have 'type' key";
463             }
464             my @attr_hashes;
465             my $a = $schema->{attrs};
466             if (defined($a)) {
467             if (ref($a) ne 'HASH') {
468             return "hash form 'attrs' key must be a hashref";
469             }
470             push @attr_hashes, $a;
471             }
472             $a = $schema->{attr_hashes};
473             if (defined($a)) {
474             if (ref($a) ne 'ARRAY') {
475             return "hash form 'attr_hashes' key must be an arrayref";
476             }
477             for (0..@$a-1) {
478             if (ref($a->[$_]) ne 'HASH') {
479             return "hash form 'attr_hashes'[$_] must be a hashref";
480             }
481             push @attr_hashes, $a->[$_];
482             }
483             }
484             my $def = {};
485             $a = $schema->{def};
486             if (defined($a)) {
487             if (ref($a) ne 'HASH') {
488             return "hash form 'def' key must be a hashref";
489             }
490             }
491             $def = $a;
492             for (keys %$schema) {
493             return "hash form has unknown key `$_'" unless /^(type|attrs|attr_hashes|def)$/;
494             }
495             return { type=>$type, attr_hashes=>\@attr_hashes, def=>$def };
496              
497             }
498              
499             return "schema must be a str, arrayref, or hashref";
500             }
501              
502              
503             sub register_schema_as_type {
504             my ($self, $nschema, $name, $path) = @_;
505             $path ||= "";
506             my $res = {};
507              
508             while (1) {
509             if ($self->type_handlers->{$name}) {
510             $res->{error} = "type `$name' already registered (path `$path')";
511             last;
512             }
513             if (ref($nschema) ne 'HASH') {
514             $res->{error} = "schema must be in 3rd form (hash): (path `$path')";
515             last;
516             }
517             if ($nschema->{def}) {
518             for (keys %{ $nschema->{def} }) {
519             my $r = $self->register_schema_as_type($nschema->{def}{$_}, $_, "$path/$_");
520             if (!$r->{success}) {
521             $res->{error} = $r->{error};
522             last;
523             }
524             }
525             }
526             my $th = Data::Schema::Type::Schema->new(nschema=>$nschema, name=>$name);
527             $self->register_type($name => $th);
528             last;
529             }
530             $res->{success} = !$res->{error};
531             $res;
532             }
533              
534              
535             sub validate {
536             my ($self, $data, $schema) = @_;
537             my $saved_schema = $self->schema;
538             $schema ||= $self->schema;
539              
540             $self->init_validation_state();
541             $self->init_compilation_state() if $self->config->compile;
542             $self->logs([]);
543             $self->_validate($data, $schema);
544             $self->schema($saved_schema);
545              
546             {success => !@{$self->errors},
547             errors => [$self->errors_as_array],
548             warnings => [$self->warnings_as_array],
549             logs => [$self->logs_as_array],
550             };
551             }
552              
553              
554             sub errors_as_array {
555             my ($self) = @_;
556             map { sprintf "%s (data\@%s schema\@%s)", $_->[2], $self->_pos_as_str($_->[0]), $self->_pos_as_str($_->[1]) } @{ $self->errors };
557             }
558              
559              
560             sub warnings_as_array {
561             my ($self) = @_;
562             map { sprintf "%s (data\@%s schema\@%s)", $_->[2], $self->_pos_as_str($_->[0]), $self->_pos_as_str($_->[1]) } @{ $self->warnings };
563             }
564              
565              
566             sub logs_as_array {
567             my ($self) = @_;
568             map { sprintf "%s (data\@%s schema\@%s)", $_->[2], $self->_pos_as_str($_->[0]), $self->_pos_as_str($_->[1]) } @{ $self->logs };
569             }
570              
571             sub _schema2csubname {
572             my ($self, $schema) = @_;
573              
574             # deal with perl hash randomization
575             local $Storable::canonical = 1;
576              
577             # avoid warning from Storable when trying to freeze coderef
578             local $self->config->{gettext_function} =
579             ($self->config->{gettext_function} // "")."";
580              
581             my $n1 = defined($schema) ? (ref($schema) ? md5_hex(freeze($schema)) : $schema) : "";
582             my $n2 = md5_hex(freeze($self->config));
583             "__cs_${n1}_$n2";
584             }
585              
586              
587             sub emit_perl {
588             my ($self, $schema, $inner) = @_;
589             $self->init_compilation_state($inner);
590             $self->_emit_perl(undef, $schema);
591             }
592              
593             sub _emit_perl {
594             _validate_or_emit_perl(@_, 'EMIT_PERL');
595             }
596              
597             # the difference between validate() and _validate(): _validate() is not for the
598             # end-user, it doesn't initialize validation state and so can be used in the
599             # middle of another validation (e.g. for validating schema types). _validate()
600             # also doesn't format and returns the list of errors, you need to get them
601             # yourself from the validator.
602              
603             sub _validate {
604             my ($self, $data, $schema) = @_;
605             _validate_or_emit_perl(@_, 'VALIDATE');
606             }
607              
608             sub _validate_or_emit_perl {
609             my ($self, $data, $schema, $action) = @_;
610              
611             die "Schema must be specified" unless defined($schema);
612              
613             my $compile = $self->config->compile;
614             my $csubname = $self->_schema2csubname($schema);
615             if ($compile && $action eq 'VALIDATE' && $self->compiled_subnames->{$csubname}) {
616             #print "HIT!\n";
617             goto LV1;
618             }
619              
620             # since schema may define types inside it, we save the original types list
621             # so we can register new types and then restore back to original state
622             # later.
623             my $orig_type_handlers;
624             my $orig_compiled_subnames;
625              
626             {
627             my $s = $self->normalize_schema($schema);
628             if (!ref($s)) {
629             $self->schema_error($s);
630             last;
631             }
632              
633             if ($s->{def}) {
634             #print "DEBUG: Saving type handlers\n";
635             $orig_type_handlers = { %{$self->type_handlers} };
636             $orig_compiled_subnames = { %{$self->compiled_subnames} };
637             push @{ $self->schema_pos }, 'def', '';
638             my $has_err;
639             for (keys %{ $s->{def} }) {
640             $self->schema_pos->[-1] = $_;
641             my $subs = $self->normalize_schema($s->{def}{$_});
642             if (!ref($subs)) {
643             $has_err++;
644             $self->data_error("normalize schema type error: $s");
645             last;
646             }
647             my $res = $self->register_schema_as_type($subs, $_);
648             if (!$res->{success}) {
649             $has_err++;
650             $self->data_error("register schema type error: $res->{error}");
651             last;
652             }
653             }
654             pop @{ $self->schema_pos };
655             pop @{ $self->schema_pos };
656             last if $has_err;
657             }
658              
659             my $th = $self->get_type_handler($s->{type});
660             if (!$th) {
661             $self->schema_error("unknown type `$s->{type}'");
662             last;
663             }
664             if ($compile || $action eq 'EMIT_PERL') {
665             $self->outer_stash->{"C_def_$csubname"}++;
666             my $code = $th->emit_perl($s->{attr_hashes}, $csubname);
667             return $code if $action eq 'EMIT_PERL';
668             if (!$code) {
669             $self->schema_error("no Perl code generated");
670             last;
671             }
672             unless ($Data::Schema::__compiled::{$csubname}) {
673             eval "package Data::Schema::__compiled; $code; package Data::Schema;";
674             my $eval_error = $@;
675             if ($eval_error) {
676             my $i=1; my @c; for (split /\n/, $code) { push @c, sprintf "%4d|%s\n", $i++, $_ } $code = join "", @c;
677             print STDERR $code;
678             print STDERR $eval_error;
679             die "Can't compile code: $eval_error";
680             }
681             }
682             #print "DEBUG: Compiled $csubname\n";
683             $self->compiled_subnames->{$csubname} = 1;
684             } else {
685             $th->handle_type($data, $s->{attr_hashes});
686             }
687             }
688              
689             if ($orig_type_handlers) {
690             #print "DEBUG: Restoring original type handlers\n";
691             $self->type_handlers($orig_type_handlers);
692             $self->compiled_subnames($orig_compiled_subnames);
693             }
694              
695             LV1:
696             # execute compiled code
697             if ($compile) {
698             no strict 'refs';
699             my ($errors, $warnings) = "Data::Schema::__compiled::$csubname"->($data);
700             push @{ $self->errors }, @$errors;
701             push @{ $self->warnings }, @$warnings;
702             }
703             }
704              
705              
706             sub compile {
707             my ($self, $schema) = @_;
708             my $csubname = $self->_schema2csubname($schema);
709             unless ($Data::Schema::__compiled::{$csubname}) {
710             $self->save_compilation_state;
711             my $code = $self->emit_perl($schema);
712             $self->restore_compilation_state;
713             die "Can't generate Perl code for schema" unless $code;
714             eval "package Data::Schema::__compiled; $code; package Data::Schema;";
715             my $eval_error = $@;
716             if ($eval_error) {
717             my $i=1; my @c; for (split /\n/, $code) { push @c, sprintf "%4d|%s\n", $i++, $_ } $code = join "", @c;
718             print STDERR $code;
719             print STDERR $eval_error;
720             die "Can't compile code: $eval_error";
721             }
722             }
723             my $cfullsubname = "Data::Schema::__compiled::$csubname";
724             (\&$cfullsubname, $csubname);
725             }
726              
727             sub emitpls_sub {
728             my ($self, $schema) = @_;
729              
730             my $csubname = $self->_schema2csubname($schema);
731             #print "DEBUG: emitting $csubname\n";
732             my $perl = '';
733              
734             if ($Data::Schema::__compiled::{$csubname} ||
735             $self->outer_stash->{"C_def_$csubname"}++) {
736             #print "DEBUG: skipped emitting $csubname (already done)\n";
737             } else {
738             #print "DEBUG: marking $csubname in outer stash\n";
739             $self->outer_stash->{"C_def_$csubname"}++;
740             $self->save_compilation_state;
741             $perl = $self->emit_perl($schema, 1);
742             $self->restore_compilation_state;
743             die "Can't generate Perl code for schema" unless $perl;
744             }
745             ($perl, $csubname);
746             }
747              
748             sub import {
749             my $pkg = shift;
750             $Current_Call_Pkg = caller(0);
751              
752             no strict 'refs';
753              
754             # default export
755             my @export = qw(ds_validate);
756             *{$Current_Call_Pkg."::$_"} = \&{$pkg."::$_"} for @export;
757              
758             return if $Package_Default_Types{$Current_Call_Pkg};
759             my $dt = { %Default_Types };
760             my $dp = { %Default_Plugins };
761             for (@_) {
762             my $e = $_;
763             if (grep {$e eq $_} @export) {
764             } elsif ($e =~ /^Plugin::/) {
765             $e = "Data::Schema::" . $e;
766             unless (grep {$_ eq $e} keys %$dp) {
767             eval "require $e"; die $@ if $@;
768             $dp->{$e} = $e->new();
769             }
770             } elsif ($e =~ /^Type::/) {
771             $e = "Data::Schema::" . $e;
772             eval "require $e"; die $@ if $@;
773             my $th = $e->new();
774             my $names = ${$e."::DS_TYPE"};
775             die "$e doesn't have \$DS_TYPE" unless $names;
776             $names = [$names] unless ref($names) eq 'ARRAY';
777             for (@$names) {
778             if (!check_type_name(undef, $_)) {
779             die "$e tries to define invalid type name: `$_`";
780             } elsif (exists $dt->{$_}) {
781             die "$e tries to redefine existing type '$_' (handler: $dt->{$_})";
782             }
783             $dt->{$_} = $e;
784             }
785             } elsif ($e =~ /^Schema::/) {
786             $e = "Data::Schema::" . $e;
787             eval "require $e"; die $@ if $@;
788             my $schemas = ${$e."::DS_SCHEMAS"};
789             die "$e doesn't have \$DS_SCHEMAS" unless $schemas;
790             for (keys %$schemas) {
791             if (!check_type_name(undef, $_)) {
792             die "$e tries to define invalid type name: `$_`";
793             } elsif (exists $dt->{$_}) {
794             die "$e tries to redefine existing type '$_' (handler: $dt->{$_})";
795             }
796             my $nschema = normalize_schema(undef, $schemas->{$_});
797             if (ref($nschema) ne 'HASH') {
798             die "Can't normalize schema in $e: $nschema";
799             }
800             require Data::Schema::Type::Schema;
801             $dt->{$_} = Data::Schema::Type::Schema->new(nschema=>$nschema, name=>$_);
802             }
803             } else {
804             die "Can't export $_! Can only export: ".join(@export, '/^{Plugin,Type,Schema}::.*/');
805             }
806             }
807             $Package_Default_Types{$Current_Call_Pkg} = $dt;
808             $Package_Default_Plugins{$Current_Call_Pkg} = $dp;
809             #print Dumper(\%Package_Default_Plugins);
810             }
811              
812              
813             __PACKAGE__->meta->make_immutable;
814             no Moose;
815             1;
816              
817             __END__
818             =pod
819              
820             =head1 NAME
821              
822             Data::Schema - (DEPRECATED) Validate nested data structures with nested structure
823              
824             =head1 VERSION
825              
826             version 0.136
827              
828             =head1 SYNOPSIS
829              
830             # OO interface
831             use Data::Schema;
832             my $ds = Data::Schema->new();
833             my $schema = [array => {min_len=>2, max_len=>4}];
834             my $data = [1, 2, 3];
835             my $res = $ds->validate($data, $schema);
836             print "valid!" if $res->{success}; # prints 'valid!'
837              
838             # procedural interface
839             use Data::Schema;
840             my $sch = ["hash",
841             {keys =>
842             {name => "str",
843             age => ["int", {required=>1, min=>18}]
844             }
845             }
846             ];
847             my $r;
848             $r = ds_validate({name=>"Lucy", age=>18}, $sch); # success
849             $r = ds_validate({name=>"Lucy" }, $sch); # fail: missing age
850             $r = ds_validate({name=>"Lucy", age=>16}, $sch); # fail: underage
851              
852             # some schema examples
853              
854             # -- array
855             "array"
856              
857             # -- array of ints
858             [array => {of=>"int"}]
859              
860             # -- array of positive, even ints
861             [array => {of=>[int => {min=>0, divisible_by=>2}]}]
862              
863             # -- 3x3x3 "multi-dim" arrays
864             [array => {len=>3, of=>
865             [array => {len=>3, of=>
866             [array => {len=>3}]}]}]
867              
868             # -- HTTP headers, each header can be a string or array of strings
869             [hash => {
870             required => 1,
871             keys_match => '^\w+(-w+)*$',
872             values_of => [either => {of=>[
873             "str",
874             [array=>{of=>"str", minlen=>1}],
875             ]}],
876             }]
877              
878             # -- records (demonstrates subschema and attribute merging). Note:
879             # I am not sexist or anything, just that for the love of g*d I
880             # can't think of a better example atm. it's late...
881             {def => {
882             person => [hash => {
883             keys => {
884             name => "str",
885             race => "str",
886             age => [int => {min=>0, max=>100}],
887             },
888             }],
889              
890             # women are like people, but they have additional keys
891             # 'husband' and 'cup_size' (additive) and different age
892             # restriction (replace).
893              
894             woman => [person => {
895             '*keys' => {
896             husband => "str",
897             cup_size => [str => {one_of=>[qw/AA A B C D DD/]}],
898             '*age' => [int => {min=>0, max=>120}],
899             },
900             }],
901              
902             # girls are like women, but they do not have husbands yet
903             # (remove keys)
904              
905             girl => [woman => {
906             '*keys' => {
907             '!husband' => undef,
908             }
909             }],
910              
911             girls => [array => {of=>"girl"}],
912             },
913             type => "girls",
914             };
915              
916             =head1 DESCRIPTION
917              
918             B<NOTE: THIS MODULE IS DEPRECATED AND WILL NOT BE DEVELOPED FURTHER. PLEASE
919             SEE Data::Sah INSTEAD.>
920              
921             Data::Schema (DS) is a schema system for data validation. It lets you
922             write schemas as data structures, ranging from very simple (a scalar)
923             to fairly complex (nested hashes/arrays with various criteria).
924              
925             Writing schemas as data structures themselves has several advantages. First, it
926             is more portable across languages (e.g. using YAML to share schemas between
927             Perl, Python, PHP, Ruby). Second, you can validate the schema using the schema
928             system itself. Third, it is easy to generate code, help message (e.g. so-called
929             "usage" for function/command line script), etc. from the schema.
930              
931             Potential application of DS: validating configuration, function
932             parameters, command line arguments, etc.
933              
934             To get started, see L<Data::Schema::Manual::Tutorial>.
935              
936             =head1 IMPORTING
937              
938             When importing this module, you can pass a list of module names.
939              
940             use Data::Schema qw(Plugin::Foo Type::Bar Schema::Baz ...);
941             my $ds = Data::Schema->new; # foo, bar, baz will be loaded by default
942              
943             This is a shortcut to the more verbose form:
944              
945             use Data::Schema;
946             my $ds = Data::Schema->new;
947              
948             $ds->register_plugin('Data::Schema::Plugin::Foo');
949              
950             $ds->register_type('bar', 'Data::Schema::Type::Bar');
951              
952             use Data::Schema::Schema::Baz;
953             $ds->register_schema_as_type($_, $Data::Schema::Schema::Baz::DS_SCHEMAS->{$_})
954             for keys %$Data::Schema::Schema::Baz::DS_SCHEMAS;
955              
956             =head1 FUNCTIONS
957              
958             =head2 ds_validate($data, $schema)
959              
960             Non-OO wrapper for validate(). Exported by default. See C<validate()> method.
961              
962             =head1 ATTRIBUTES
963              
964             =head2 config
965              
966             Configuration object. See L<Data::Schema::Config>.
967              
968             =head1 METHODS
969              
970             =head2 merge_attr_hashes($attr_hashes)
971              
972             Merge several attribute hashes if there are hashes that can be merged (i.e.
973             contains merge prefix in its keys). Used by DST::Base and DST::Schema. As DS
974             user, normally you wouldn't need this.
975              
976             =head2 init_validation_state()
977              
978             Initialize validation state. Used internally by validate(). As DS user, normally
979             you wouldn't need this.
980              
981             =head2 save_validation_state()
982              
983             Save validation state (position in data, position in schema, number of errors,
984             etc) into a stack, so that you can start using the validator to validate a new
985             data with a new schema, even in the middle of validating another data/schema.
986             Used internally by validate() and DST::Schema. As DS user, normally you wouldn't
987             need this.
988              
989             See also: B<restore_validation_state()>.
990              
991             =head2 restore_validation_state()
992              
993             Restore the last validation state from the stack. Used internally by
994             validate() and DST::Schema. As DS user, normally you wouldn't need
995             this.
996              
997             See also: B<save_validation_state()>.
998              
999             =head2 init_compilation_state()
1000              
1001             Initialize compilation state. Used internally by emit_perl(). As DS
1002             user, normally you wouldn't need this.
1003              
1004             =head2 save_compilation_state()
1005              
1006             Save compilation state. Used internally by emit_perl() and
1007             DST::Schema. As DS user, normally you wouldn't need this.
1008              
1009             See also: B<restore_compilation_state()>.
1010              
1011             =head2 restore_compilation_state()
1012              
1013             Restore the last compilation state from the stack. Used internally by
1014             emit_perl() and DST::Schema. As DS user, normally you wouldn't need
1015             this.
1016              
1017             See also: B<save_compilation_state()>.
1018              
1019             =head2 data_error($message)
1020              
1021             Add a data error when in validation process. Will not add if there are
1022             already too many errors (C<too_many_errors> attribute is true). Used
1023             by type handlers. As DS user, normally you wouldn't need this.
1024              
1025             =head2 data_warn($message)
1026              
1027             Add a data warning when in validation process. Will not add if there
1028             are already too many warnings (C<too_many_warnings> attribute is
1029             true). Used by type handlers. As DS user, normally you wouldn't need
1030             this.
1031              
1032             =head2 debug($message[, $level])
1033              
1034             Log debug messages. Used by type handlers when validating. As DS user,
1035             normally you wouldn't need this.
1036              
1037             =head2 schema_error($message)
1038              
1039             Method to call when encountering schema error during
1040             validation/compilation. Used by type handlers. As DS user, normally
1041             you wouldn't need this.
1042              
1043             =head2 check_type_name($name)
1044              
1045             Checks whether C<$name> is a valid type name. Returns true if valid,
1046             false if invalid. By default it requires that type name starts with a
1047             lowercase letter and contains only lowercase letters, numbers, and
1048             underscores. Maximum length is 64.
1049              
1050             You can override this method if you want stricter/looser type name
1051             criteria.
1052              
1053             =head2 register_type($name, $class|$obj)
1054              
1055             Register a new type, along with a class name (C<$class>) or the actual object
1056             (C<$obj>) to handle the type. If C<$class> is given, the class will be require'd
1057             and instantiated to become object later when needed via get_type_handler.
1058              
1059             Any object can become a type handler, as long as it has:
1060              
1061             =over
1062              
1063             =item *
1064              
1065             a C<validator()> rw property to store/set validator object;
1066              
1067             =item *
1068              
1069             C<handle_type()> method to handle type checking;
1070              
1071             =item *
1072              
1073             zero or more C<handle_attr_*()> methods to handle attribute checking.
1074              
1075             =back
1076              
1077             See L<Data::Schema::Manual::TypeHandler> for more details on writing a type
1078             handler.
1079              
1080             =head2 register_plugin($class|$obj)
1081              
1082             Register a new plugin. Accept a plugin object or class. If C<$class> is given,
1083             the class will be require'd (if not already require'd) and instantiated to
1084             become object.
1085              
1086             Any object can become a plugin, you don't need to subclass from anything, as
1087             long as it has:
1088              
1089             =over 4
1090              
1091             =item *
1092              
1093             a C<validator()> rw property to store/set validator object;
1094              
1095             =item *
1096              
1097             zero or more C<handle_*()> methods to handle some events/hooks.
1098              
1099             =back
1100              
1101             See L<Data::Schema::Manual::Plugin> for more details on writing a plugin.
1102              
1103             =head2 call_handler($name, [@args])
1104              
1105             Try handle_*() method from each registered plugin until one returns 0 or 1. If
1106             a plugin return -1 (decline) then we continue to the next plugin. Returns the
1107             status of the last plugin. Returns -1 if there's no handler to invoke.
1108              
1109             =head2 get_type_handler($name)
1110              
1111             Try to get type handler for a certain type. If type handler is not an object (a
1112             class name), instantiate it first. If type is not found, invoke
1113             handle_unknown_type() in plugins to give plugins a chance to load the type. If
1114             type is still not found, return undef.
1115              
1116             =head2 normalize_schema($schema)
1117              
1118             Normalize a schema into the third form (hash form) ({type=>...,
1119             attr_hashes=>..., def=>...) as well as do some sanity checks on it. Returns an
1120             error message string if fails.
1121              
1122             =head2 register_schema_as_type($schema, $name)
1123              
1124             Register schema as new type. $schema is a normalized schema. Return {success=>(0
1125             or 1), error=>...}. Fails if type with name B<$name> is already defined, or if
1126             $schema cannot be parsed. Might actually register more than one type actually,
1127             if the schema contains other types in it (hash form of schema can define types).
1128              
1129             =head2 validate($data[, $schema])
1130              
1131             Validate a data structure. $schema must be given unless you already give the
1132             schema via the B<schema> attribute.
1133              
1134             Returns {success=>0 or 1, errors=>[...], warnings=>[...]}. The
1135             'success' key will be set to 1 if the data validates, otherwise
1136             'errors' will be filled with the details.
1137              
1138             =head2 errors_as_array
1139              
1140             Return formatted errors in an array of strings.
1141              
1142             =head2 warnings_as_array
1143              
1144             Return formatted warnings in an array of strings.
1145              
1146             =head2 logs_as_array
1147              
1148             Return formatted logs in an array of strings.
1149              
1150             =head2 emit_perl([$schema])
1151              
1152             Return Perl code equivalent to schema C<$schema>.
1153              
1154             If you want to get the compiled code (as a coderef) directly, use
1155             C<compile>.
1156              
1157             =head2 compile($schema)
1158              
1159             Compile the schema into Perl code and return a 2-element list:
1160             ($coderef, $subname). $coderef is the resulting subroutine and
1161             $subname is the subroutine name in the compilation namespace
1162             (Data::Schema::__compiled).
1163              
1164             If the same schema is already compiled, the existing compiled
1165             subroutine is returned instead.
1166              
1167             Dies if code can't be generated, or an error occured when compiling
1168             the code.
1169              
1170             If you just want to get the Perl code in a string, use C<emit_perl>.
1171              
1172             =head1 COMPARISON WITH OTHER DATA VALIDATION MODULES
1173              
1174             There are already a lot of data validation modules on CPAN. However, most of
1175             them do not validate nested data structures. Many seem to focus only on "form"
1176             (which is usually presented as shallow hash in Perl).
1177              
1178             And of the rest which do nested data validation, either I am not really fond of
1179             the syntax, or the validator/schema system is not simple/flexible/etc enough for
1180             my taste. For example, other data validation modules might require you to always
1181             write:
1182              
1183             { type => "int" }
1184              
1185             even when all you want is just validating an int with no other extra
1186             requirements. With DS you can just write:
1187              
1188             "int"
1189              
1190             Another design consideration for DS is, I want to maximize reusability of my
1191             schemas. And thus DS allows you to define schemas in terms of other schemas.
1192             External schemas can be "require"-d from Perl variables or loaded from YAML
1193             files. Of course, you can also extend with Perl as usual (e.g. writing new
1194             types and new attributes).
1195              
1196             =head1 SEE ALSO
1197              
1198             L<Data::Schema::Manual::Tutorial>
1199              
1200             L<Data::Schema::Manual::Schema>
1201              
1202             L<Data::Schema::Manual::TypeHandler>
1203              
1204             L<Data::Schema::Manual::Plugin>
1205              
1206             Some other data validation modules on CPAN: L<Data::FormValidator>, L<Data::Rx>,
1207             L<Kwalify>.
1208              
1209             L<Config::Tree> uses Data::Schema to check command-line options and
1210             makes it easy to generate --help/usage information.
1211              
1212             L<LUGS::Events::Parser> by Steven Schubiger is apparently one of the
1213             first modules (outside my own of course) which use Data::Schema.
1214              
1215             L<Data::Schema::Schema::> namespace is reserved for modules that
1216             contain DS schemas. For example, L<Data::Schema::Schema::CPANMeta>
1217             validates CPAN META.yml. L<Data::Schema::Schema::Schema> contains the
1218             schema for DS schema itself.
1219              
1220             =head1 BUGS
1221              
1222             Please report any bugs or feature requests to C<bug-data-schema at
1223             rt.cpan.org>, or through the web interface at
1224             L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Data-Schema>. I
1225             will be notified, and then you'll automatically be notified of
1226             progress on your bug as I make changes.
1227              
1228             =head1 SUPPORT
1229              
1230             You can find documentation for this module with the perldoc command.
1231              
1232             perldoc Data::Schema
1233              
1234             You can also look for information at:
1235              
1236             =over 4
1237              
1238             =item * RT: CPAN's request tracker
1239              
1240             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Data-Schema>
1241              
1242             =item * AnnoCPAN: Annotated CPAN documentation
1243              
1244             L<http://annocpan.org/dist/Data-Schema>
1245              
1246             =item * CPAN Ratings
1247              
1248             L<http://cpanratings.perl.org/d/Data-Schema>
1249              
1250             =item * Search CPAN
1251              
1252             L<http://search.cpan.org/dist/Data-Schema/>
1253              
1254             =back
1255              
1256             =head1 ACKNOWLEDGEMENTS
1257              
1258             =head1 AUTHOR
1259              
1260             Steven Haryanto <stevenharyanto@gmail.com>
1261              
1262             =head1 COPYRIGHT AND LICENSE
1263              
1264             This software is copyright (c) 2009 by Steven Haryanto.
1265              
1266             This is free software; you can redistribute it and/or modify it under
1267             the same terms as the Perl 5 programming language system itself.
1268              
1269             =cut
1270