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