File Coverage

blib/lib/Specio/PartialDump.pm
Criterion Covered Total %
statement 76 83 91.5
branch 31 40 77.5
condition 9 12 75.0
subroutine 21 21 100.0
pod 0 1 0.0
total 137 157 87.2


line stmt bran cond sub pod time code
1             package Specio::PartialDump;
2              
3 29     29   205 use strict;
  29         56  
  29         838  
4 29     29   160 use warnings;
  29         52  
  29         1266  
5              
6             our $VERSION = '0.47';
7              
8 29     29   182 use Scalar::Util qw( looks_like_number reftype blessed );
  29         89  
  29         1834  
9              
10 29     29   186 use Exporter qw( import );
  29         74  
  29         34478  
11              
12             our @EXPORT_OK = qw( partial_dump );
13              
14             my $MaxLength = 100;
15             my $MaxElements = 6;
16             my $MaxDepth = 2;
17              
18             sub partial_dump {
19 2485     2485 0 6528 my (@args) = @_;
20              
21 2485 50       5474 my $dump
22             = _should_dump_as_pairs(@args)
23             ? _dump_as_pairs( 1, @args )
24             : _dump_as_list( 1, @args );
25              
26 2485 100       11215 if ( length($dump) > $MaxLength ) {
27 2         6 my $max_length = $MaxLength - 3;
28 2 50       11 $max_length = 0 if $max_length < 0;
29 2         8 substr( $dump, $max_length, length($dump) - $max_length ) = '...';
30             }
31              
32 2485         13928 return $dump;
33             }
34              
35             sub _should_dump_as_pairs {
36 2485     2485   4412 my (@what) = @_;
37              
38 2485 50       12571 return if @what % 2 != 0; # must be an even list
39              
40 0         0 for ( my $i = 0; $i < @what; $i += 2 ) {
41 0 0       0 return if ref $what[$i]; # plain strings are keys
42             }
43              
44 0         0 return 1;
45             }
46              
47             sub _dump_as_pairs {
48 316     316   867 my ( $depth, @what ) = @_;
49              
50 316         541 my $truncated;
51 316 50 33     1973 if ( defined $MaxElements and ( @what / 2 ) > $MaxElements ) {
52 0         0 $truncated = 1;
53 0         0 @what = splice( @what, 0, $MaxElements * 2 );
54             }
55              
56 316 50       1003 return join(
57             ', ', _dump_as_pairs_recursive( $depth, @what ),
58             ( $truncated ? "..." : () )
59             );
60             }
61              
62             sub _dump_as_pairs_recursive {
63 508     508   1244 my ( $depth, @what ) = @_;
64              
65 508 100       3716 return unless @what;
66              
67 192         506 my ( $key, $value, @rest ) = @what;
68              
69             return (
70 192         458 ( _format_key( $depth, $key ) . ': ' . _format( $depth, $value ) ),
71             _dump_as_pairs_recursive( $depth, @rest ),
72             );
73             }
74              
75             sub _dump_as_list {
76 2751     2751   5934 my ( $depth, @what ) = @_;
77              
78 2751         3841 my $truncated;
79 2751 50       6182 if ( @what > $MaxElements ) {
80 0         0 $truncated = 1;
81 0         0 @what = splice( @what, 0, $MaxElements );
82             }
83              
84             return join(
85 2751 50       5961 ', ', ( map { _format( $depth, $_ ) } @what ),
  2789         5670  
86             ( $truncated ? "..." : () )
87             );
88             }
89              
90             sub _format {
91 3887     3887   6956 my ( $depth, $value ) = @_;
92              
93 3887 100       19278 defined($value)
    100          
    100          
    100          
94             ? (
95             ref($value)
96             ? (
97             blessed($value)
98             ? _format_object( $depth, $value )
99             : _format_ref( $depth, $value )
100             )
101             : (
102             looks_like_number($value)
103             ? _format_number( $depth, $value )
104             : _format_string( $depth, $value )
105             )
106             )
107             : _format_undef( $depth, $value ),;
108             }
109              
110             sub _format_key {
111 192     192   435 my ( undef, $key ) = @_;
112 192         629 return $key;
113             }
114              
115             sub _format_ref {
116 2076     2076   3849 my ( $depth, $ref ) = @_;
117              
118 2076 100       4171 if ( $depth > $MaxDepth ) {
119 22         93 return overload::StrVal($ref);
120             }
121             else {
122 2054         4775 my $reftype = reftype($ref);
123 2054 100 66     8873 $reftype = 'SCALAR'
124             if $reftype eq 'REF' || $reftype eq 'LVALUE';
125 2054         5183 my $method = "_format_" . lc $reftype;
126              
127 2054 100       11304 if ( my $sub = __PACKAGE__->can($method) ) {
128 1488         3895 return $sub->( $depth, $ref );
129             }
130             else {
131 566         1921 return overload::StrVal($ref);
132             }
133             }
134             }
135              
136             sub _format_array {
137 266     266   643 my ( $depth, $array ) = @_;
138              
139 266   100     1305 my $class = blessed($array) || '';
140 266 100       735 $class .= "=" if $class;
141              
142 266         1604 return $class . "[ " . _dump_as_list( $depth + 1, @$array ) . " ]";
143             }
144              
145             sub _format_hash {
146 316     316   859 my ( $depth, $hash ) = @_;
147              
148 316   100     1447 my $class = blessed($hash) || '';
149 316 100       999 $class .= "=" if $class;
150              
151             return $class . "{ " . _dump_as_pairs(
152             $depth + 1,
153 316         2138 map { $_ => $hash->{$_} } sort keys %$hash
  192         772  
154             ) . " }";
155             }
156              
157             sub _format_scalar {
158 906     906   1721 my ( $depth, $scalar ) = @_;
159              
160 906   100     3319 my $class = blessed($scalar) || '';
161 906 100       2416 $class .= "=" if $class;
162              
163 906         3573 return $class . "\\" . _format( $depth + 1, $$scalar );
164             }
165              
166             sub _format_object {
167 1159     1159   2418 my ( $depth, $object ) = @_;
168              
169 1159         2611 return _format_ref( $depth, $object );
170             }
171              
172             sub _format_string {
173 505     505   1324 my ( undef, $str ) = @_;
174              
175             # FIXME use String::Escape ?
176              
177             # remove vertical whitespace
178 505         1352 $str =~ s/\n/\\n/g;
179 505         1026 $str =~ s/\r/\\r/g;
180              
181             # reformat nonprintables
182 29     29   17617 $str =~ s/(\P{IsPrint})/"\\x{" . sprintf("%x", ord($1)) . "}"/ge;
  29         405  
  29         521  
  505         1305  
  4         32  
183              
184 505         1404 _quote($str);
185             }
186              
187             sub _quote {
188 505     505   1135 my ($str) = @_;
189              
190 505         3742 qq{"$str"};
191             }
192              
193 188     188   1485 sub _format_undef {"undef"}
194              
195             sub _format_number {
196 1118     1118   2251 my ( undef, $value ) = @_;
197 1118         7550 return "$value";
198             }
199              
200             # ABSTRACT: A partially rear-ended copy of Devel::PartialDump without prereqs
201              
202             1;
203              
204             __END__
205              
206             =pod
207              
208             =encoding UTF-8
209              
210             =head1 NAME
211              
212             Specio::PartialDump - A partially rear-ended copy of Devel::PartialDump without prereqs
213              
214             =head1 VERSION
215              
216             version 0.47
217              
218             =head1 SYNOPSIS
219              
220             use Specio::PartialDump qw( partial_dump );
221              
222             partial_dump( { foo => 42 } );
223             partial_dump(qw( a b c d e f g ));
224             partial_dump( foo => 42, bar => [ 1, 2, 3 ], );
225              
226             =head1 DESCRIPTION
227              
228             This is a copy of Devel::PartialDump with all the OO bits and prereqs
229             removed. You may want to use this module in your own code to generate nicely
230             formatted messages when a type constraint fails.
231              
232             This module optionally exports one sub, C<partial_dump>. This sub accepts any
233             number of arguments. If given more than one, it will assume that it's either
234             been given a list of key/value pairs (to build a hash) or a list of values (to
235             build an array) and dump them appropriately. Objects and references are
236             stringified in a sane way.
237              
238             =for Pod::Coverage partial_dump
239              
240             =head1 SUPPORT
241              
242             Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
243              
244             I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
245              
246             =head1 SOURCE
247              
248             The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
249              
250             =head1 AUTHOR
251              
252             Dave Rolsky <autarch@urth.org>
253              
254             =head1 COPYRIGHT AND LICENSE
255              
256             This software is copyright (c) 2008 by יובל קוג'מן (Yuval Kogman).
257              
258             This is free software; you can redistribute it and/or modify it under
259             the same terms as the Perl 5 programming language system itself.
260              
261             =cut