Data-Transformer

 view release on metacpan or  search on metacpan

lib/Data/Transformer.pm  view on Meta::CPAN

=head1 NAME

Data::Transformer - Traverse a data structure, altering it in place

=head1 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') }
 );

=head1 DESCRIPTION

=head2 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, C<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.

=head2 Additional option for the constructor

=over

=item 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.

=back

=head2 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.




( run in 0.948 second using v1.01-cache-2.11-cpan-39bf76dae61 )