File Coverage

blib/lib/Test/Mocha/PartialDump.pm
Criterion Covered Total %
statement 98 99 100.0
branch 61 62 100.0
condition 18 18 100.0
subroutine 21 21 100.0
pod 0 15 100.0
total 198 215 100.0


line stmt bran cond sub pod time code
1             package Test::Mocha::PartialDump;
2             # ABSTRACT: Partial dumping of data structures, optimized for argument printing
3             $Test::Mocha::PartialDump::VERSION = '0.61';
4             # ===================================================================
5             # This code was copied and adapted from Devel::PartialDump 0.15.
6             #
7             # Copyright (c) 2008, 2009 Yuval Kogman. All rights reserved
8             # This program is free software; you can redistribute
9             # it and/or modify it under the same terms as Perl itself.
10             #
11             # ===================================================================
12              
13 22     22   14062 use strict;
  22         25  
  22         552  
14 22     22   81 use warnings;
  22         20  
  22         466  
15              
16 22     22   89 use Scalar::Util qw( looks_like_number reftype blessed );
  22         22  
  22         1204  
17              
18             use constant {
19 22         20465 ELLIPSIS => '...',
20             ELLIPSIS_LEN => 3,
21 22     22   98 };
  22         45  
22              
23             sub new {
24             # uncoverable pod
25 31     31 0 4698 my ( $class, %args ) = @_;
26              
27             # attribute defaults
28             ## no critic (ProhibitMagicNumbers)
29 31 100       143 $args{max_length} = undef unless exists $args{max_length};
30 31 100       104 $args{max_elements} = 6 unless exists $args{max_elements};
31 31 100       120 $args{max_depth} = 2 unless exists $args{max_depth};
32 31 100       85 $args{stringify} = 0 unless exists $args{stringify};
33 31 100       98 $args{pairs} = 1 unless exists $args{pairs};
34 31 100       81 $args{objects} = 1 unless exists $args{objects};
35 31 100       102 $args{list_delim} = ', ' unless exists $args{list_delim};
36 31 100       98 $args{pair_delim} = ': ' unless exists $args{pair_delim};
37             ## use critic
38              
39 31         111 return bless \%args, $class;
40             }
41              
42             sub dump { ## no critic (ProhibitBuiltinHomonyms)
43             # uncoverable pod
44 316     316 0 753 my ( $self, @args ) = @_;
45              
46 316 100       524 my $method =
47             'dump_as_' . ( $self->should_dump_as_pairs(@args) ? 'pairs' : 'list' );
48              
49 316         666 my $dump = $self->$method( 1, @args );
50              
51 316 100 100     981 if ( defined $self->{max_length}
52             and length($dump) > $self->{max_length} )
53             {
54 2         4 my $max_length = $self->{max_length} - ELLIPSIS_LEN;
55 2 100       4 $max_length = 0 if $max_length < 0;
56 2         5 substr $dump, $max_length, length($dump) - $max_length, ELLIPSIS;
57             }
58              
59 316         1178 return $dump;
60             }
61              
62             sub should_dump_as_pairs {
63             # uncoverable pod
64 316     316 0 323 my ( $self, @what ) = @_;
65              
66 316 100       646 return unless $self->{pairs};
67              
68 307 100       938 return if @what % 2 != 0; # must be an even list
69              
70 138         222 for my $i ( grep { $_ % 2 == 0 } 0 .. @what ) {
  204         329  
71 164 100       439 return if ref $what[$i]; # plain strings are keys
72             }
73              
74 131         318 return 1;
75             }
76              
77             sub dump_as_pairs {
78             # uncoverable pod
79 171     171 0 208 my ( $self, $depth, @what ) = @_;
80              
81 171         132 my $truncated;
82 171 100 100     790 if ( defined $self->{max_elements}
83             and ( @what / 2 ) > $self->{max_elements} )
84             {
85 2         3 $truncated = 1;
86 2         5 @what = splice @what, 0, $self->{max_elements} * 2;
87             }
88              
89 171 100       456 return join
90             $self->{list_delim},
91             $self->_dump_as_pairs( $depth, @what ),
92             ( $truncated ? ELLIPSIS : () );
93             }
94              
95             sub _dump_as_pairs {
96 230     230   620 my ( $self, $depth, @what ) = @_;
97              
98 230 100       913 return unless @what;
99              
100 59         79 my ( $key, $value, @rest ) = @what;
101              
102             return (
103             (
104 59         109 $self->format_key( $depth, $key )
105             . $self->{pair_delim}
106             . $self->format( $depth, $value )
107             ),
108             $self->_dump_as_pairs( $depth, @rest ),
109             );
110             }
111              
112             sub dump_as_list {
113             # uncoverable pod
114 190     190 0 259 my ( $self, $depth, @what ) = @_;
115              
116 190         161 my $truncated;
117 190 100 100     810 if ( defined $self->{max_elements} and @what > $self->{max_elements} ) {
118 2         3 $truncated = 1;
119 2         5 @what = splice @what, 0, $self->{max_elements};
120             }
121              
122 257         423 return join
123             $self->{list_delim},
124 190 100       294 ( map { $self->format( $depth, $_ ) } @what ),
125             ( $truncated ? ELLIPSIS : () );
126             }
127              
128             sub format { ## no critic (ProhibitBuiltinHomonyms)
129             # uncoverable pod
130 321     321 0 316 my ( $self, $depth, $value ) = @_;
131              
132 321 100       1314 return defined($value)
    100          
    100          
    100          
133             ? (
134             ref($value)
135             ? (
136             blessed($value)
137             ? $self->format_object( $depth, $value )
138             : $self->format_ref( $depth, $value )
139             )
140             : (
141             looks_like_number($value)
142             ? $self->format_number( $depth, $value )
143             : $self->format_string( $depth, $value )
144             )
145             )
146             : $self->format_undef( $depth, $value );
147             }
148              
149             sub format_key {
150             # uncoverable pod
151 59     59 0 64 my ( $self, $depth, $key ) = @_;
152 59         164 return $key;
153             }
154              
155             sub format_ref {
156             # uncoverable pod
157 52     52 0 59 my ( $self, $depth, $ref ) = @_;
158              
159 52 100       101 if ( $depth > $self->{max_depth} ) {
160 2         7 return overload::StrVal($ref);
161             }
162             else {
163 50         97 my $reftype = reftype($ref);
164 50 100 100     183 $reftype = 'SCALAR'
165             if $reftype eq 'REF' || $reftype eq 'LVALUE';
166 50         86 my $method = 'format_' . lc $reftype;
167              
168             # uncoverable branch false
169 50 50       168 if ( $self->can($method) ) {
170 50         109 return $self->$method( $depth, $ref );
171             }
172             else {
173 0         0 return overload::StrVal($ref); # uncoverable statement
174             }
175             }
176             }
177              
178             sub format_array {
179             # uncoverable pod
180 5     5 0 6 my ( $self, $depth, $array ) = @_;
181              
182 5   100     17 my $class = blessed($array) || q{};
183 5 100       10 $class .= q{=} if $class;
184              
185 5         9 return $class . '[ ' . $self->dump_as_list( $depth + 1, @{$array} ) . ' ]';
  5         33  
186             }
187              
188             sub format_hash {
189             # uncoverable pod
190 40     40 0 45 my ( $self, $depth, $hash ) = @_;
191              
192 40   100     139 my $class = blessed($hash) || q{};
193 40 100       180 $class .= q{=} if $class;
194              
195             return
196 40         133 $class . '{ '
197             . $self->dump_as_pairs( $depth + 1,
198 40         65 map { $_ => $hash->{$_} } sort keys %{$hash} )
  40         158  
199             . ' }';
200             }
201              
202             sub format_scalar {
203             # uncoverable pod
204 5     5 0 5 my ( $self, $depth, $scalar ) = @_;
205              
206 5   100     21 my $class = blessed($scalar) || q{};
207 5 100       12 $class .= q{=} if $class;
208              
209 5         10 return $class . q{\\} . $self->format( $depth + 1, ${$scalar} );
  5         40  
210             }
211              
212             sub format_object {
213             # uncoverable pod
214 67     67 0 71 my ( $self, $depth, $object ) = @_;
215              
216 67 100       120 if ( $self->{objects} ) {
217 3         5 return $self->format_ref( $depth, $object );
218             }
219             else {
220 64 100       230 return $self->{stringify} ? "$object" : overload::StrVal($object);
221             }
222             }
223              
224             sub format_number {
225             # uncoverable pod
226 171     171 0 219 my ( $self, $depth, $value ) = @_;
227 171         475 return "$value";
228             }
229              
230             sub format_string {
231             # uncoverable pod
232 33     33 0 38 my ( $self, $depth, $str ) = @_;
233             # FIXME use String::Escape ?
234              
235             # remove vertical whitespace
236 33         52 $str =~ s/\n/\\n/smg;
237 33         35 $str =~ s/\r/\\r/smg;
238              
239             # reformat nonprintables
240 22     22   10611 $str =~ s/ (\P{IsPrint}) /"\\x{" . sprintf("%x", ord($1)) . "}"/xsmge;
  22         164  
  22         208  
  33         103  
  1         9  
241              
242 33         177 return qq{"$str"};
243             }
244              
245             sub format_undef {
246             # uncoverable pod
247 1     1 0 5 return 'undef';
248             }
249              
250             1;