File Coverage

blib/lib/Data/Type/Digger.pm
Criterion Covered Total %
statement 46 46 100.0
branch 19 22 86.3
condition 6 6 100.0
subroutine 7 7 100.0
pod 1 1 100.0
total 79 82 96.3


line stmt bran cond sub pod time code
1             package Data::Type::Digger;
2              
3             =head1 NAME
4              
5             Data::Type::Digger - digging types from data structures
6              
7             =head1 SYNOPSIS
8              
9             use Data::Type::Digger;
10              
11             my $in_data = { ... };
12              
13             my $out_data = dig( $in_data, do_scalar => { uc @_ } );
14              
15              
16             =head1 DESCRIPTION
17              
18             B helps you to deal with deep data structores.
19              
20              
21             =head1 METHODS
22              
23             =head2 C
24              
25             Perform recursive digging required types from data structure
26              
27             my $out_data = dig( $in_data, %params );
28              
29             in_data = source structure, required
30              
31             Params: # all param keys are optional
32             do_all => coderef, function called for all nodes
33             do_hash => coderef, function called for all hashref nodes
34             do_array => coderef, function called for all arrayref nodes
35             do_scalar => coderef, function called for all scalar nodes
36             do_type => coderef, function called for all nodes with ref = 'type'
37             unbless => 0 || 1, turn all blessed objects into simple hashrefs
38             clone => 1, make all actions on cloned structure and save the source data
39             max_deep => -1, int -1, undef (not limited) || int (limited to INT) depth of work
40             max_deep_cut => 0 || 1, save or cut the data deeper then max_deep
41              
42             coderef
43             assumes two params:
44             node - value of current node
45             key - name or index of parent_node (if parent node ref is hash or array)
46              
47             returns:
48             new value of node
49              
50              
51             =head1 SUPPORT AND DOCUMENTATION
52              
53             After installing, you can find documentation for this module with the
54             perldoc command.
55              
56             perldoc Data::Type::Digger
57              
58             You can also look for information at:
59              
60             RT, CPAN's request tracker (report bugs here)
61             http://rt.cpan.org/NoAuth/Bugs.html?Dist=Data-Type-Digger
62              
63             AnnoCPAN, Annotated CPAN documentation
64             http://annocpan.org/dist/Data-Type-Digger
65              
66             CPAN Ratings
67             http://cpanratings.perl.org/d/Data-Type-Digger
68              
69             Search CPAN
70             http://search.cpan.org/dist/Data-Type-Digger/
71              
72              
73             =head1 AUTHOR
74              
75            
76              
77              
78             =head1 LICENSE
79              
80             This program is free software; you can redistribute it and/or modify it
81             under the terms of the the Artistic License (2.0). You may obtain a
82             copy of the full license at:
83              
84             L
85              
86             =cut
87              
88 2     2   84137 use v5.18;
  2         6  
89 2     2   405 use Modern::Perl;
  2         8052  
  2         10  
90 2     2   1009 use Clone;
  2         3749  
  2         78  
91 2     2   10 use Scalar::Util;
  2         1  
  2         62  
92 2     2   6 use Exporter 'import';
  2         2  
  2         886  
93              
94             our $VERSION = '0.04';
95              
96             our @EXPORT_OK = qw/ dig /;
97              
98             our %_param;
99             our $_levels_to_stop;
100              
101             =head1 FUNCTIONS
102              
103             =head2 dig($data, %param)
104              
105             perform digging data and do some actions on values
106              
107             =cut
108              
109             sub dig {
110 14     14 1 7425 my ( $data, %param ) = @_;
111              
112 14 50       27 die 'no data given' unless $data;
113              
114 14         81 %_param = %param;
115              
116             # cloning data, if required
117 14 100       27 if ( $param{clone} ) {
118 1         14 $data = Clone::clone $data;
119             }
120              
121             # Countdown levels deep
122 14   100     41 $_levels_to_stop = $param{max_deep} || -1;
123              
124 14         18 _dig( $data );
125             };
126              
127              
128             # Working with current node and make recursive call for all subnodes
129             sub _dig {
130 78     78   67 my ( $data, $up_key ) = @_;
131              
132             # stop, if max deep reached
133 78 100       87 unless ( $_levels_to_stop-- ) {
134 6         5 $_levels_to_stop++;
135 6 100       12 return $_param{max_deep_cut} ? undef : $data;
136             };
137              
138             # Get a type of value, regardless of any blessing
139 72   100     157 my $ref = Scalar::Util::reftype( $data ) // '';
140              
141 72 100       93 if ( $ref eq 'ARRAY' ) {
142 9         17 for ( 0 .. @$data-1 ) {
143 25         29 $data->[$_] = _dig( $data->[$_], $_ );
144             };
145             }
146              
147 72 100       76 if ( $ref eq 'HASH' ) {
148 27         45 for ( keys %$data ) {
149 39         42 $data->{$_} = _dig( $data->{$_}, $_ );
150             };
151             }
152              
153             # Trying to apply something useful for this node
154 186         134 my @do = map { s/\:+/_/g; 'do_'.lc }
  186         221  
155 72   100     52 keys %{{ map { $_ => 1 } $ref, ( ref $data || 'scalar' ), 'all' }};
  72         121  
  216         283  
156              
157 72         112 for ( @do ) {
158 186 100       277 next unless $_param{$_};
159 36         64 $data = $_param{$_}->( $data, $up_key );
160             }
161              
162 72         82 $_levels_to_stop++;
163              
164             # If unblessing not required then do nothing
165 72 100       147 return $data unless $_param{unbless};
166              
167             # Unbless reference if we can
168 9 100       32 return { %$data } if $ref eq 'HASH';
169 3 50       4 return [ @$data ] if $ref eq 'ARRAY';
170              
171             # Don't know how to unbless this object
172 3 50       5 return undef if ref $data;
173              
174             # Just a simple scalar
175 3         6 return $data;
176             };
177              
178             1;