Data-Transformer
view release on metacpan or search on metacpan
NAME
Data::Transformer - Traverse a data structure, altering it in place
SYNOPSIS
use Data::Transformer;
# A: SIMPLE USAGE:
# trim extra whitespace from normal strings inside %data.
my $trim = sub { local($_)=shift; $$_ =~ s/^\s*//; $$_ =~ s/\s*$//; };
my $t = Data::Transformer->new(normal=>$trim);
$t->traverse(\%data);
# B: MORE COMPLEX USAGE:
# (a) uppercase all keys in all hashes contained in $data
# and (b) convert any arrays to hashes:
my $uc_hash = sub {
my $h = shift;
my @keys = keys %h;
foreach (@keys) {
my $uc = uc($_);
if ($uc ne $_ && !exists($h->{$uc})) {
$h->{$uc} = $h->{$_};
delete $h->{$_};
} elsif ($uc ne $_) {
die "Bad key $_: '$uc' exists already";
}
}
};
my $ar_conv = sub {
my %h = @{$_[0]};
return sub { \%h };
};
my $t = Data::Transformer->new(
hash => $uc_hash,
array => $ar_conv,
node_limit => 500_000 );
eval { $t->traverse($data) };
warn "Could not complete transformation: $@" if $@;
# C: NON-DESTRUCTIVE TRAVERSAL
# You don't actually have to change anything...
my $size = 0;
my $t = Data::Transformer->new(
normal => sub { $size+=length(${ $_[0] }) },
hash => sub { $size+=length($_) for keys %{ $_[0] } },
);
my $nodes = $t->tranverse(\%data);
print "Number of nodes: $nodes\n";
print "Size of keys + values: $size\n";
# D: OBJECTS INSIDE A DATA STRUCTURE
# Affect objects by using the class name as a key:
my $t = Data::Transformer->new(
'My::Class' => sub { shift->set_foo('bar') }
);
DESCRIPTION
Data type callbacks
The basic idea is that you provide a callback subroutine for each type
of data that you wish to affect or collect information from.
The constructor, "new()", expects a hash with at least one of the
following keys:
* normal : used for normal, non-reference data
* array : used for array references
* hash : used for hash references
* code : used for anonymous subroutines (coderefs)
* scalar : used for scalar references
* glob : used for globs (such as filehandle holders)
The value in each case is a coderef representing the callback for the
data type in question.
The array and hash types are special in that they are traversed into.
It is possible to affect objects inside the data structure by specifying
a callback keyed to the name of the class they belong to. They are not
automatically recursed into, however, even if they happen to be blessed
hash or array references.
Similarly, a scalar reference is not automatically traversed into, even
if it may contain a reference to an arrayref or a hashref. To make the
module traverse into scalar references, you need to return a coderef
encapsulating a different data type in the scalar handler, thus changing
them (and prompting a reiteration over that data point). This applies to
objects as well.
Additional option for the constructor
node_limit:
If an integer value for this is specified, it overrides the default
node processing limit of 2**16. This cannot be set higher than
2**20-1.
traverse()
The traverse() method returns the number of nodes processed. This may be
different from both the number of nodes in the actual data structure and
the number of nodes after the transformation, for the following reasons:
* Reiteration into a particular node may have occurred, which
increments the node count.
* Blessed references (objects) will not normally be iterated into,
but are merely treated as leaves in the data structure.
* The processing code passed to the constructor may well affect the
number of nodes.
Note on data type changes
If you want to change a data type (for instance replace an array by a
hash as in example B, above) you have to return a coderef from the
callback for the original data type. This coderef encapsulates the
replacement data for the node in question.
After the node has thus been replaced, it is re-evaluated to apply any
transformations you may have defined for the new data type.
Be careful of potential infinite loops when doing this with more than
( run in 0.626 second using v1.01-cache-2.11-cpan-39bf76dae61 )