File Coverage

blib/lib/Data/Transformer.pm
Criterion Covered Total %
statement 53 54 98.1
branch 37 40 92.5
condition 6 10 60.0
subroutine 5 5 100.0
pod 1 2 50.0
total 102 111 91.8


line stmt bran cond sub pod time code
1             package Data::Transformer;
2 1     1   23242 use strict;
  1         3  
  1         744  
3              
4             our $VERSION = 0.04;
5              
6             ################ CONSTRUCTOR ################
7              
8             sub new {
9 12     12 0 3532 my ($pk,%opt) = @_;
10 12   33     52 my $class = ref($pk) || $pk;
11 12         15 my $self = \%opt;
12 12         6248 bless($self,$class);
13 12         27 $self->_selfcheck;
14 9         27 return $self;
15             }
16              
17             ################ PUBLIC METHODS ################
18              
19             sub traverse {
20 9     9 1 415 my ($self,$data) = @_;
21 9 100       28 die "Data needs to be a reference" unless ref $data;
22 8         16 $self->{_seen} = {};
23 8         19 $self->_node($data);
24 7         18 return $self->{_node_calls};
25             }
26              
27             ################ PRIVATE METHODS ###############
28              
29             my %plainref = map { ($_=>1) } qw(ARRAY HASH CODE SCALAR GLOB);
30              
31             sub _node {
32 111     111   141 my ($self,$data) = @_;
33 111 100       266 die "Maximum node calls ($self->{node_limit}) reached"
34             if $self->{_node_calls}++ > $self->{node_limit};
35              
36 110   50     236 my $ref = ref $data || '';
37 110         101 my ($cb_ret,$node_ret);
38              
39 110 100 66     679 return if $ref && $self->{_seen}->{"$data"}++; # circular data structure!
40              
41             # Filter data
42 96 100       171 if ($plainref{$ref}) { # normal reference
    50          
43 88 100       232 $cb_ret = $self->{lc($ref)}->($data) if $self->{lc($ref)};
44             }
45             elsif ($ref) { # blessed reference
46 8 100       20 $cb_ret = $self->{$ref}->($data) if $self->{$ref};
47             }
48             else { # non-reference
49 0 0       0 $cb_ret = $self->{normal}->(\$data) if $self->{normal};
50             }
51              
52             # Recurse into $data (if appropriate):
53 96 100       450 if (ref $data eq 'HASH') {
    100          
54 29         52 foreach my $val (values %$data) {
55 108         121 while (1) {
56 112 100       177 if (ref $val) {
57 81         164 $node_ret = $self->_node($val);
58             } else {
59 31 100       79 $node_ret = $self->{normal}->(\$val) if $self->{normal};
60             }
61 111 100       268 if (ref $node_ret eq 'CODE') {
62 4         9 $val = $node_ret->();
63 4         24 next;
64             }
65 107         166 last;
66             }
67             }
68             }
69             elsif (ref $data eq 'ARRAY') {
70 21         27 foreach my $elm (@$data) {
71 207         194 while (1) {
72 208 100       271 if (ref $elm) {
73 22         43 $node_ret = $self->_node($elm);
74             } else {
75 186 100       399 $node_ret = $self->{normal}->(\$elm) if $self->{normal};
76             }
77 208 100       563 if (ref $node_ret eq 'CODE') {
78 1         3 $elm = $node_ret->();
79 1         6 next;
80             }
81 207         266 last;
82             }
83             }
84             }
85              
86 95         177 return $cb_ret;
87             }
88              
89              
90             sub _selfcheck {
91 12     12   14 my $self = shift;
92 12         15 my $found = 0;
93 12         39 for my $k (keys %$self) {
94 46 100       84 next if $k eq 'node_limit';
95 41         38 $found++;
96 41 100       109 die "The value for the '$k' option needs to be a coderef"
97             unless ref $self->{$k} eq 'CODE';
98             }
99 11 100       29 unless ($found) {
100 1         9 die "You need to specify an action for some node type";
101             }
102 10         27 $self->{_node_calls} = 0;
103 10   100     40 $self->{node_limit} ||= 2**16;
104 10 100       38 die "Cannot set node_limit higher than 2**20-1"
105             if $self->{node_limit} > 2**20-1;
106             }
107              
108             1;
109             __END__