File Coverage

blib/lib/Devel/PartialDump.pm
Criterion Covered Total %
statement 131 154 85.0
branch 54 68 79.4
condition 25 32 78.1
subroutine 31 36 86.1
pod 20 24 83.3
total 261 314 83.1


line stmt bran cond sub pod time code
1 3     3   56389 use strict;
  3         8  
  3         81  
2 3     3   16 use warnings;
  3         8  
  3         165  
3             package Devel::PartialDump; # git description: v0.19-3-ga398185
4             # vim: set ts=8 sts=4 sw=4 tw=115 et :
5             # ABSTRACT: Partial dumping of data structures, optimized for argument printing.
6             # KEYWORDS: development debugging dump dumper diagnostics deep data structures
7              
8             our $VERSION = '0.20';
9              
10 3     3   18 use Carp ();
  3         7  
  3         88  
11 3     3   18 use Scalar::Util qw(looks_like_number reftype blessed);
  3         8  
  3         370  
12              
13 3     3   1233 use namespace::clean 0.19;
  3         43126  
  3         18  
14              
15             use Class::Tiny {
16 3         33 max_length => undef,
17             max_elements => 6,
18             max_depth => 2,
19             stringify => 0,
20             pairs => 1,
21             objects => 1,
22             list_delim => ", ",
23             pair_delim => ": ",
24 3     3   1894 };
  3         9514  
25              
26             use Sub::Exporter -setup => {
27             exports => [qw(dump warn show show_scalar croak carp confess cluck $default_dumper)],
28             groups => {
29             easy => [qw(dump warn show show_scalar carp croak)],
30             carp => [qw(croak carp)],
31             },
32             collectors => {
33             override_carp => sub {
34 3     3   3335 no warnings 'redefine';
  3         7  
  3         261  
35 0         0 require Carp;
36 0         0 *Carp::caller_info = \&replacement_caller_info;
37             },
38             },
39 3     3   1637 };
  3         29926  
  3         58  
40              
41             # a replacement for Carp::caller_info
42             sub replacement_caller_info {
43 0     0 0 0 my $i = shift(@_) + 1;
44              
45             package DB; # git description: v0.19-3-ga398185
46 0         0 my %call_info;
47             @call_info{
48 0         0 qw(pack file line sub has_args wantarray evaltext is_require)
49             } = caller($i);
50              
51 0 0       0 return unless (defined $call_info{pack});
52              
53 0         0 my $sub_name = Carp::get_subname(\%call_info);
54              
55 0 0       0 if ($call_info{has_args}) {
56 0         0 $sub_name .= '(' . Devel::PartialDump::dump(@DB::args) . ')';
57             }
58              
59 0         0 $call_info{sub_name} = $sub_name;
60              
61 0 0       0 return wantarray() ? %call_info : \%call_info;
62             }
63              
64              
65             sub warn_str {
66 3     3 0 9 my ( @args ) = @_;
67 3         19 my $self;
68              
69 3 100 66     27 if ( blessed($args[0]) and $args[0]->isa(__PACKAGE__) ) {
70 1         2 $self = shift @args;
71             } else {
72 2         4 $self = our $default_dumper;
73             }
74             return $self->_join(
75             map {
76 3 50 33     8 !ref($_) && defined($_)
  3         15  
77             ? $_
78             : $self->dump($_)
79             } @args
80             );
81             }
82              
83             sub warn {
84 2     2 1 691 Carp::carp(warn_str(@_));
85             }
86              
87             foreach my $f ( qw(carp croak confess cluck) ) {
88 3     3   2830 no warnings 'redefine';
  3         8  
  3         4271  
89 1     1 1 740 eval "sub $f {
  1     0 1 4  
  0     0 1 0  
  0     0 1 0  
  0         0  
  0         0  
  0         0  
  0         0  
90             local \$Carp::CarpLevel = \$Carp::CarpLevel + 1;
91             Carp::$f(warn_str(\@_));
92             }";
93             }
94              
95             sub show {
96 1     1 1 1156 my ( @args ) = @_;
97 1         2 my $self;
98              
99 1 50 33     7 if ( blessed($args[0]) and $args[0]->isa(__PACKAGE__) ) {
100 0         0 $self = shift @args;
101             } else {
102 1         2 $self = our $default_dumper;
103             }
104              
105 1         4 $self->warn(@args);
106              
107 1 50       50 return ( @args == 1 ? $args[0] : @args );
108             }
109              
110 0     0 1 0 sub show_scalar ($) { goto \&show }
111              
112             sub _join {
113 3     3   7 my ( $self, @strings ) = @_;
114              
115 3         5 my $ret = "";
116              
117 3 50       9 if ( @strings ) {
118 3   50     14 my $sep = $, || $" || " ";
119 3         27 my $re = qr/(?: \s| \Q$sep\E )$/x;
120              
121 3         8 my $last = pop @strings;
122              
123 3         7 foreach my $string ( @strings ) {
124 0         0 $ret .= $string;
125 0 0       0 $ret .= $sep unless $string =~ $re;
126             }
127              
128 3         7 $ret .= $last;
129             }
130              
131 3         355 return $ret;
132             }
133              
134             sub dump {
135 38     38 1 8782 my ( @args ) = @_;
136 38         62 my $self;
137              
138 38 100 66     338 if ( blessed($args[0]) and $args[0]->isa(__PACKAGE__) ) {
139 37         81 $self = shift @args;
140             } else {
141 1         2 $self = our $default_dumper;
142             }
143              
144 38 100       103 my $method = "dump_as_" . ( $self->should_dump_as_pairs(@args) ? "pairs" : "list" );
145              
146 38         186 my $dump = $self->$method(1, @args);
147              
148 38 100 100     683 if ( defined $self->max_length and length($dump) > $self->max_length ) {
149 2         83 my $max_length = $self->max_length - 3;
150 2 100       18 $max_length = 0 if $max_length < 0;
151 2         7 substr( $dump, $max_length, length($dump) - $max_length ) = '...';
152             }
153              
154 38 50       303 if ( not defined wantarray ) {
155 0         0 CORE::warn "$dump\n";
156             } else {
157 38         210 return $dump;
158             }
159             }
160              
161             sub should_dump_as_pairs {
162 38     38 0 91 my ( $self, @what ) = @_;
163              
164 38 100       729 return unless $self->pairs;
165              
166 29 100       277 return if @what % 2 != 0; # must be an even list
167              
168 7         27 for ( my $i = 0; $i < @what; $i += 2 ) {
169 23 100       68 return if ref $what[$i]; # plain strings are keys
170             }
171              
172 6         24 return 1;
173             }
174              
175             sub dump_as_pairs {
176 11     11 1 34 my ( $self, $depth, @what ) = @_;
177              
178 11         20 my $truncated;
179 11 100 100     198 if ( defined $self->max_elements and ( @what / 2 ) > $self->max_elements ) {
180 2         67 $truncated = 1;
181 2         40 @what = splice(@what, 0, $self->max_elements * 2 );
182             }
183              
184 11 100       406 return join( $self->list_delim, $self->_dump_as_pairs($depth, @what), ($truncated ? "..." : ()) );
185             }
186              
187             sub _dump_as_pairs {
188 33     33   144 my ( $self, $depth, @what ) = @_;
189              
190 33 100       146 return unless @what;
191              
192 22         50 my ( $key, $value, @rest ) = @what;
193              
194             return (
195 22         50 ( $self->format_key($depth, $key) . $self->pair_delim . $self->format($depth, $value) ),
196             $self->_dump_as_pairs($depth, @rest),
197             );
198             }
199              
200             sub dump_as_list {
201 40     40 1 98 my ( $self, $depth, @what ) = @_;
202              
203 40         60 my $truncated;
204 40 100 100     627 if ( defined $self->max_elements and @what > $self->max_elements ) {
205 2         45 $truncated = 1;
206 2         28 @what = splice(@what, 0, $self->max_elements );
207             }
208              
209 40 100       1598 return join( $self->list_delim, ( map { $self->format($depth, $_) } @what ), ($truncated ? "..." : ()) );
  100         370  
210             }
211              
212             sub format {
213 127     127 1 384 my ( $self, $depth, $value ) = @_;
214              
215 127 100       538 defined($value)
    100          
    100          
    100          
216             ? ( ref($value)
217             ? ( blessed($value)
218             ? $self->format_object($depth, $value)
219             : $self->format_ref($depth, $value) )
220             : ( looks_like_number($value)
221             ? $self->format_number($depth, $value)
222             : $self->format_string($depth, $value) ) )
223             : $self->format_undef($depth, $value),
224             }
225              
226             sub format_key {
227 22     22 1 43 my ( $self, $depth, $key ) = @_;
228 22         339 return $key;
229             }
230              
231             sub format_ref {
232 20     20 1 47 my ( $self, $depth, $ref ) = @_;
233              
234 20 100       301 if ( $depth > $self->max_depth ) {
235 2         17 return overload::StrVal($ref);
236             } else {
237 18         132 my $reftype = reftype($ref);
238 18 100 100     91 $reftype = 'SCALAR'
239             if $reftype eq 'REF' || $reftype eq 'LVALUE';
240 18         52 my $method = "format_" . lc $reftype;
241              
242 18 50       76 if ( $self->can($method) ) {
243 18         54 return $self->$method( $depth, $ref );
244             } else {
245 0         0 return overload::StrVal($ref);
246             }
247             }
248             }
249              
250             sub format_array {
251 8     8 1 19 my ( $self, $depth, $array ) = @_;
252              
253 8   100     38 my $class = blessed($array) || '';
254 8 100       26 $class .= "=" if $class;
255              
256 8         65 return $class . "[ " . $self->dump_as_list($depth + 1, @$array) . " ]";
257             }
258              
259             sub format_hash {
260 5     5 1 12 my ( $self, $depth, $hash ) = @_;
261              
262 5   100     28 my $class = blessed($hash) || '';
263 5 100       15 $class .= "=" if $class;
264              
265 5         44 return $class . "{ " . $self->dump_as_pairs($depth + 1, map { $_ => $hash->{$_} } sort keys %$hash) . " }";
  7         25  
266             }
267              
268             sub format_scalar {
269 5     5 0 13 my ( $self, $depth, $scalar ) = @_;
270              
271 5   100     22 my $class = blessed($scalar) || '';
272 5 100       14 $class .= "=" if $class;
273              
274 5         36 return $class . "\\" . $self->format($depth + 1, $$scalar);
275             }
276              
277             sub format_object {
278 9     9 1 16 my ( $self, $depth, $object ) = @_;
279              
280 9 100       124 if ( $self->objects ) {
281 3         40 return $self->format_ref($depth, $object);
282             } else {
283 6 100       99 return $self->stringify ? "$object" : overload::StrVal($object);
284             }
285             }
286              
287             sub format_string {
288 21     21 1 46 my ( $self, $depth, $str ) =@_;
289             # FIXME use String::Escape ?
290              
291             # remove vertical whitespace
292 21         45 $str =~ s/\n/\\n/g;
293 21         35 $str =~ s/\r/\\r/g;
294              
295             # reformat nonprintables
296 3     3   1918 $str =~ s/(\P{IsPrint})/"\\x{" . sprintf("%x", ord($1)) . "}"/ge;
  3         38  
  3         45  
  21         51  
  1         11  
297              
298 21         56 $self->quote($str);
299             }
300              
301             sub quote {
302 21     21 1 46 my ( $self, $str ) = @_;
303              
304 21         152 qq{"$str"};
305             }
306              
307 1     1 1 14 sub format_undef { "undef" }
308              
309             sub format_number {
310 79     79 1 137 my ( $self, $depth, $value ) = @_;
311 79         219 return "$value";
312             }
313              
314             our $default_dumper = __PACKAGE__->new;
315              
316             1;
317              
318             __END__