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   47280 use strict;
  3         9  
  3         74  
2 3     3   16 use warnings;
  3         5  
  3         127  
3             package Devel::PartialDump; # git description: v0.18-5-g7d2520e
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.19';
9              
10 3     3   16 use Carp ();
  3         6  
  3         52  
11 3     3   12 use Scalar::Util qw(looks_like_number reftype blessed);
  3         10  
  3         277  
12              
13 3     3   1081 use namespace::clean 0.19;
  3         36905  
  3         17  
14              
15             use Class::Tiny {
16 3         29 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   2040 };
  3         8018  
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   2686 no warnings 'redefine';
  3         6  
  3         188  
35 0         0 require Carp;
36 0         0 *Carp::caller_info = \&replacement_caller_info;
37             },
38             },
39 3     3   1402 };
  3         25196  
  3         40  
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.18-5-g7d2520e
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 7 my ( @args ) = @_;
67 3         6 my $self;
68              
69 3 100 66     22 if ( blessed($args[0]) and $args[0]->isa(__PACKAGE__) ) {
70 1         3 $self = shift @args;
71             } else {
72 2         5 $self = our $default_dumper;
73             }
74             return $self->_join(
75             map {
76 3 50 33     6 !ref($_) && defined($_)
  3         15  
77             ? $_
78             : $self->dump($_)
79             } @args
80             );
81             }
82              
83             sub warn {
84 2     2 1 687 Carp::carp(warn_str(@_));
85             }
86              
87             foreach my $f ( qw(carp croak confess cluck) ) {
88 3     3   2497 no warnings 'redefine';
  3         6  
  3         3174  
89 1     1 1 704 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 1041 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       32 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   8 my ( $self, @strings ) = @_;
114              
115 3         4 my $ret = "";
116              
117 3 50       9 if ( @strings ) {
118 3   50     14 my $sep = $, || $" || " ";
119 3         30 my $re = qr/(?: \s| \Q$sep\E )$/x;
120              
121 3         7 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         8 $ret .= $last;
129             }
130              
131 3         316 return $ret;
132             }
133              
134             sub dump {
135 38     38 1 7363 my ( @args ) = @_;
136 38         66 my $self;
137              
138 38 100 66     306 if ( blessed($args[0]) and $args[0]->isa(__PACKAGE__) ) {
139 37         75 $self = shift @args;
140             } else {
141 1         2 $self = our $default_dumper;
142             }
143              
144 38 100       124 my $method = "dump_as_" . ( $self->should_dump_as_pairs(@args) ? "pairs" : "list" );
145              
146 38         172 my $dump = $self->$method(1, @args);
147              
148 38 100 100     662 if ( defined $self->max_length and length($dump) > $self->max_length ) {
149 2         62 my $max_length = $self->max_length - 3;
150 2 100       12 $max_length = 0 if $max_length < 0;
151 2         7 substr( $dump, $max_length, length($dump) - $max_length ) = '...';
152             }
153              
154 38 50       315 if ( not defined wantarray ) {
155 0         0 CORE::warn "$dump\n";
156             } else {
157 38         184 return $dump;
158             }
159             }
160              
161             sub should_dump_as_pairs {
162 38     38 0 82 my ( $self, @what ) = @_;
163              
164 38 100       681 return unless $self->pairs;
165              
166 29 100       259 return if @what % 2 != 0; # must be an even list
167              
168 7         24 for ( my $i = 0; $i < @what; $i += 2 ) {
169 23 100       61 return if ref $what[$i]; # plain strings are keys
170             }
171              
172 6         19 return 1;
173             }
174              
175             sub dump_as_pairs {
176 11     11 1 28 my ( $self, $depth, @what ) = @_;
177              
178 11         19 my $truncated;
179 11 100 100     157 if ( defined $self->max_elements and ( @what / 2 ) > $self->max_elements ) {
180 2         45 $truncated = 1;
181 2         28 @what = splice(@what, 0, $self->max_elements * 2 );
182             }
183              
184 11 100       329 return join( $self->list_delim, $self->_dump_as_pairs($depth, @what), ($truncated ? "..." : ()) );
185             }
186              
187             sub _dump_as_pairs {
188 33     33   124 my ( $self, $depth, @what ) = @_;
189              
190 33 100       128 return unless @what;
191              
192 22         42 my ( $key, $value, @rest ) = @what;
193              
194             return (
195 22         49 ( $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 84 my ( $self, $depth, @what ) = @_;
202              
203 40         63 my $truncated;
204 40 100 100     557 if ( defined $self->max_elements and @what > $self->max_elements ) {
205 2         42 $truncated = 1;
206 2         27 @what = splice(@what, 0, $self->max_elements );
207             }
208              
209 40 100       1204 return join( $self->list_delim, ( map { $self->format($depth, $_) } @what ), ($truncated ? "..." : ()) );
  100         346  
210             }
211              
212             sub format {
213 127     127 1 344 my ( $self, $depth, $value ) = @_;
214              
215 127 100       496 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 35 my ( $self, $depth, $key ) = @_;
228 22         300 return $key;
229             }
230              
231             sub format_ref {
232 20     20 1 41 my ( $self, $depth, $ref ) = @_;
233              
234 20 100       275 if ( $depth > $self->max_depth ) {
235 2         16 return overload::StrVal($ref);
236             } else {
237 18         123 my $reftype = reftype($ref);
238 18 100 100     85 $reftype = 'SCALAR'
239             if $reftype eq 'REF' || $reftype eq 'LVALUE';
240 18         51 my $method = "format_" . lc $reftype;
241              
242 18 50       74 if ( $self->can($method) ) {
243 18         52 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     37 my $class = blessed($array) || '';
254 8 100       24 $class .= "=" if $class;
255              
256 8         51 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       14 $class .= "=" if $class;
264              
265 5         42 return $class . "{ " . $self->dump_as_pairs($depth + 1, map { $_ => $hash->{$_} } sort keys %$hash) . " }";
  7         22  
266             }
267              
268             sub format_scalar {
269 5     5 0 11 my ( $self, $depth, $scalar ) = @_;
270              
271 5   100     20 my $class = blessed($scalar) || '';
272 5 100       14 $class .= "=" if $class;
273              
274 5         35 return $class . "\\" . $self->format($depth + 1, $$scalar);
275             }
276              
277             sub format_object {
278 9     9 1 14 my ( $self, $depth, $object ) = @_;
279              
280 9 100       120 if ( $self->objects ) {
281 3         20 return $self->format_ref($depth, $object);
282             } else {
283 6 100       119 return $self->stringify ? "$object" : overload::StrVal($object);
284             }
285             }
286              
287             sub format_string {
288 21     21 1 40 my ( $self, $depth, $str ) =@_;
289             # FIXME use String::Escape ?
290              
291             # remove vertical whitespace
292 21         43 $str =~ s/\n/\\n/g;
293 21         34 $str =~ s/\r/\\r/g;
294              
295             # reformat nonprintables
296 3     3   1430 $str =~ s/(\P{IsPrint})/"\\x{" . sprintf("%x", ord($1)) . "}"/ge;
  3         38  
  3         38  
  21         48  
  1         6  
297              
298 21         48 $self->quote($str);
299             }
300              
301             sub quote {
302 21     21 1 44 my ( $self, $str ) = @_;
303              
304 21         134 qq{"$str"};
305             }
306              
307 1     1 1 12 sub format_undef { "undef" }
308              
309             sub format_number {
310 79     79 1 123 my ( $self, $depth, $value ) = @_;
311 79         187 return "$value";
312             }
313              
314             our $default_dumper = __PACKAGE__->new;
315              
316             1;
317              
318             __END__