Data::Processor - Transform Perl Data Structures, Validate Data against a Schema, Produce Data from a Schema, or produce documentation directly from information in the Schema.
use Data::Processor;
my $schema = {
section => {
description => 'a section with a few members',
error_msg => 'cannot find "section" in config',
members => {
foo => {
# value restriction either with a regex..
value => qr{f.*},
description => 'a string beginning with "f"'
},
bar => {
# ..or with a validator callback.
validator => sub {
my $self = shift;
my $parent = shift;
# undef is "no-error" -> success.
no strict 'refs';
return undef
if $self->{value} == 42;
}
},
wuu => {
optional => 1
}
}
}
};
my $p = Data::Processor->new($schema);
my $data = {
section => {
foo => 'frobnicate',
bar => 42,
# "wuu" being optional, can be omitted..
}
};
my $error_collection = $p->validate($data, verbose=>0);
# no errors :-)
# in case of errors:
# ------------------
# print each error on one line.
say $error_collection;
# same
for my $e ($error_collection->as_array){
say $e;
# do more..
}
Data::Processor is a tool for transforming, verifying, and producing Perl data structures from / against a schema, defined as a Perl data structure.
my $processor = Data::Processor->new($schema);
optional parameters: - indent: count of spaces to insert when printing in verbose mode. Default 4 - depth: level at which to start. Default is 0. - verbose: Set to a true value to print messages during processing.
Validate the data against a schema. The schema either needs to be present already or be passed as an argument.
my $error_collection = $processor->validate($data, verbose=>0);
check that the schema is valid. This method gets called upon creation of a new Data::Processor object.
my $error_collection = $processor->validate_schema();
merges another schema into the schema (optionally at a specific node)
my $error_collection = $processor->merge_schema($schema_2);
merging rules:
- merging transformers will result in an error
- merge checks if all merged elements match existing elements
- non existing elements will be added from merging schema
- validators from existing and merging schema get combined
Returns the schema. Useful after schema merging.
Transform one key in the data according to rules specified as callbacks that themodule calls for you. Transforms the data in-place.
my $validator = Data::Processor::Validator->new($schema, data => $data)
my $error_string = $processor->transform($key, $schema_key, $value);
This is not tremendously useful at the moment, especially because validate() transforms during validation.
Writes a data template using the information found in the schema.
my $data = $processor->make_data(data=>$data);
Write descriptive pod from the schema.
my $pod_string = $processor->make_pod();
The schema is described by a nested hash. At the top level, and within a members definition, the keys are the same as the structure you are describing. So for example:
my $schema = {
coordinates => {
members => {
x => {
description => "the x coordinate",
},
y => {
description => "the y coordinate",
},
}
}
};
This schema describes a structure which might look like this:
{ coordinates => { x => 1, y => 2} }
Obviously this can be nested all the way down:
my $schema = {
house => {
members => {
bungalow => {
members => {
rooms => {
#...
}
}
}
}
}
};
To have a key point to an array of things, simply use the array key. So:
my $schema = {
houses => {
array => 1,
}
};
Would describe a structure like:
{ houses => [] }
And of course you can nest within here so:
my $schema = {
houses => {
array => 1,
members => {
name => {},
windows => {
array => 1,
}
},
},
};
Might describe:
{
houses => [
{ name => 'bob',
windows => []},
{ name => 'harry',
windows => []},
]
}
The description key within a definition describes that value:
my $schema = {
x => { description => 'The x coordinate' },
};
The error_msg key can be set to provide extra context for when a value is not found or fails the value test.
Most values are required by default. To reverse this use the "optional" key:
my $schema = {
x => {
optional => 1,
},
y => {
# required
},
};
Treating regular expressions as keys
If you set "regex" within a definition then it's key will be treated as a regular expression.
my $schema = {
'color_.+' => {
regex => 1
},
};
my $data = { color_red => 'red', color_blue => 'blue'};
Data::Processor->new($schema)->validate($data);
transform the data for further processing
Transformer maps to a sub ref which will be passed the value and the containing structure. Your return value provides the new value.
my $schema = {
x => {
transformer => sub{
my( $value, $section ) = @_;
$value = $value + 1;
return $value;
}
}
};
my $data = { x => 1 };
my $p = Data::Processor->new($schema);
my $val = Data::Processor::Validator->new( $schema, data => $data);
$p->transform_data('x', 'x', $val);
say $data->{x}; #will print 2
If you wish to provide an error from the transformer you should die with a hash reference with a key of "msg" mapping to your error:
my $schema = {
x => {
transformer => sub{
die { msg => "SOMETHING IS WRONG" };
}
},
};
my $p = Data::Processor->new($schema);
my $data = { x => 1 };
my $val = Data::Processor::Validator->new( $schema, data => $data);
my $error = $p->transform_data('x', 'x', $val);
say $error; # will print: error transforming 'x': SOMETHING IS WRONG
The transformer is called before any validator, so:
my $schema = {
x => {
transformer => sub{
my( $value, $section ) = @_;
return $value + 1;
},
validator => sub{
my( $value ) = @_;
if( $value < 2 ){
return "too low"
}
},
},
};
my $p = Data::Processor->new( $schema );
my $data = { x => 1 };
my $errors = $p->validate();
say $errors->count; # will print 0
say $data->{x}; # will print 2
checking against regular expression
To check a value against a regular expression you can use the value key within a definition, mapped to a quoted regex:
my $schema = {
x => {
value => qr{\d+}
}
};
checking more complex values using a callback
To conduct extensive checks you can use validator and provide a callback. Your sub will be passed the value and it's container. If you return anything it will be regarded as an error message, so to indicate a valid value you return nothing:
my $schema = {
bob => {
validator => sub{
my( $value, $section ) = @_;
if( $value ne 'bob' ){
return "Bob must equal bob!";
}
return;
},
},
};
my $p = Data::Processor->new($schema);
# would validate:
$p->validate({ bob => "bob" });
# would fail:
$p->validate({ bob => "harry"});
See also Data::Processor::ValidatorFactory
Validator may also be an object, in this case the object must implement a "validate" method.
The "validate" method should return undef if valid, or an error message string if there is a problem.
package FiveChecker;
sub new {
bless {}, shift();
}
sub validate{
my( $self, $val ) = @_;
$val == 5 or return "I wanted five!";
return;
}
package main;
my $checker = FiveChecker->new;
my $schema = (
five => (
validator => $checker,
),
);
my $dp = Data::Processor->new($schema);
$dp->validate({five => 6}); # fails
$dp->validate({five => 5}); # passes
You can for example use MooseX::Types and Type::Tiny type constraints that are objects which offer validate methods which work this way.
use Types::Standard -all;
# ... in schema ...
foo => {
validator => ArrayRef[Int],
description => 'an arrayref of integers'
},
Matthias Bloch matthias.bloch@puffin.ch
Copyright 2015- Matthias Bloch
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.