The Perl and Raku Conference 2025: Greenville, South Carolina - June 27-29 Learn more

use strictures 2;
use base qw(Exporter);
our $VERSION = '0.006000';
$VERSION =~ tr/_//d;
our @EXPORT = map +($_, $_.'T'), qw(Df Dto Dwarn Derr);
our $ddc = Data::Dumper::Compact->new;
sub import {
my ($class, @args) = @_;
my $opts;
if (@args and ref($args[0]) eq 'HASH') {
$opts = shift @args;
} else {
while (@args and $args[0] =~ /^-(.*)$/) {
my $k = $1;
my $v = (shift(@args), shift(@args));
$opts->{$k} = $v;
}
}
$ddc = Data::Dumper::Compact->new($opts) if $opts;
return if @args == 1 and $args[0] eq ':none';
$class->export_to_level(1, @args);
}
sub _ef {
map +(@_ > 1 ? [ list => $_ ] : $_->[0]),
[ map $ddc->expand($_), @_ ];
}
sub Df { $ddc->format(_ef(@_)) }
sub DfT {
my ($tag, @args) = @_;
my @fmt = (ref($tag) eq 'ARRAY'
? do { ($tag, my $tweak) = @$tag; _ef($tweak->(@args)) }
: _ef(@args)
);
$ddc->format([ list => [ [ key => $tag ], @fmt ] ]);
}
sub _dto {
my ($fmt, $noret, $to, @args) = @_;
return unless @args > $noret;
$to->($fmt->(@args));
return wantarray ? @args[$noret..$#args] : $args[$noret];
}
sub Dto { _dto(\&Df, 0, @_) }
sub DtoT { _dto(\&DfT, 1, @_) }
my $W = sub { warn $_[0] };
sub Dwarn { Dto($W, @_) }
sub DwarnT { DtoT($W, @_) }
sub Dwarn1 {
return () unless @_;
my $one = shift;
wantarray ? (Dwarn($one), @_) : Dwarn($one)
}
my $E = sub { print STDERR $_[0] };
sub Derr { Dto($E, @_) }
sub DerrT { DtoT($E, @_) }
sub Derr1 {
return () unless @_;
my $one = shift;
wantarray ? (Derr($one), @_) : Derr($one)
}
1;
=head1 NAME
Devel::DDCWarn - Easy printf-style debugging with L<Data::Dumper::Compact>
=head1 SYNOPSIS
use Devel::DDCWarn;
my $x = Dwarn some_sub_call(); # warns and returns value
my @y = Derr other_sub_call(); # prints to STDERR and returns value
my $x = DwarnT X => some_sub_call(); # warns with tag 'X' and returns value
my @y = DerrT X => other_sub_call(); # similar
=head1 DESCRIPTION
L<Devel::DDCWarn> is a L<Devel::Dwarn> equivalent for L<Data::Dumper::Compact>.
The idea, basically, is that it's incredibly annoying to start off with code
like this:
return some_sub_call();
and then realise you need the value, so you have to write:
my @ret = some_sub_call();
warn Dumper [ THE_THING => @ret ];
return @ret;
With L<Devel::DDCWarn>, one can instead write:
return DwarnT THE_THING => some_sub_call();
and expect it to Just Work.
To integrate with your logging, you can do:
our $L = sub { $log->debug("DDC debugging: ".$_[0] };
...
return DtoT $L, THE_THING => some_sub_call();
When applying printf debugging style approaches, it's also very useful to
be able to do:
perl -MDevel::DDCwarn ...
and then within the code being debugged, abusing the fact that a prefix of ::
is short for main:: so we can add:
return ::DwarnT THE_THING => some_sub_call();
and if we forget to remove them, the lack of command-line L<Devel::DDCWarn>
exported into main:: will produce a compile time failure. This is exceedingly
useful for noticing you forgot to remove a debug statement I<before> you
commit it along with the test and fix.
=head1 EXPORTS
All of these subroutines are exported by default.
L<Data::Dumper::Compact> is referred to herein as DDC.
=head2 Dwarn
my $x = Dwarn make_x();
my @y = Dwarn make_y_array();
C<warn()>s the L</Df> DDC dump of its input, then returns the first element
in scalar context or all arguments in list context.
=head2 Derr
my $x = Derr make_x();
my @y = Derr make_y_array();
prints the L</Df> DDC dump of its input to STDERR, then returns the first
element in scalar context or all arguments in list context.
=head2 DwarnT
my $x = Dwarn TAG => make_x();
my @y = Dwarn TAG => make_y_array();
Like L</Dwarn>, but passes its first argument, the tag, through to L</DfT>
but skips it for the return value.
=head2 DerrT
my $x = Derr TAG => make_x();
my @y = Derr TAG => make_y_array();
Like L</Derr>, but accepts a tag argument that is included in the output
but is skipped for the return value.
=head2 Dto
Dto(sub { warn $_[0] }, @args);
Like L</Dwarn>, but instead of warning, calls the subroutine passed as the
first argument - this function is low level but still returns the C<@args>.
=head2 DtoT
DtoT(sub { err $_[0] }, $tag, @args);
The tagged version of L<Dto>.
=head2 Df
my $x = Df($thing);
my $y = Df(@other_things);
A single value is returned formatted by DDC. Multiple values are transformed
to a DDC list.
=head2 DfT
my $x = Df($tag => $thing);
my $y = Df($tag => @other_things);
A tag plus a single value is formatted as a two element list. A tag plus
multiple values is formatted as a list containing the tag and a list of the
values.
If the tag is an arrayref, is assumed to be:
my $x = Df([ $tag, $tweak ] => @things);
and what's dumped is C<<$tweak->(@things)>> instead of C<@things>. This
means that e.g. one can write:
return Dwarn([ foo => sub { +{ @_ } } ], %things);
to output the things as a hashref while still returning a flattened hash.
=head1 CONFIGURATION
use Devel::DDCWarn \%options, ...;
perl -MDevel::DDCWarn=-optname,value,-other,value ...;
$Devel::DDCWarn::ddc = Data::Dumper::Compact->new(\%options);
Options passed as a hashref on a C<use> line or using - prefixing on the
command line are used to initialise the L<Data::Dumper::Compact> object.
Note that this primarily being a debugging and/or scripting oriented tool, if
something initialises us again later, this will reset the (single) global
C<$ddc> used by this code and change all output throught the process.
However, if you need a localised change of formatting style, C<$ddc> is a full
fledged global so you are absolutely allowed to C<local> it:
my $ddc = Data::Dumper::Compact->new(\%my_local_options);
local $Devel::DDCWarn::ddc = $ddc;
If you have a convincing reason for using this functionality in a way where
the globality is a bug rather than a feature, please start a conversation
with the authors so we can figure out what to do about it.
=head1 COPYRIGHT
Copyright (c) 2019 the L<Data::Dumper::Compact/AUTHOR> and
L<Data::Dumper::Compact/CONTRIBUTORS> as listed in L<Data::Dumper::Compact>.
=head1 LICENSE
This library is free software and may be distributed under the same terms
as perl itself. See L<https://dev.perl.org/licenses/>.
=cut