I have a hash, e.g. $hash->{'foo'}{'bar'}.
I want to call Carp::cluck in any place where value of bar key changed.
How to do that ? Is there any ready module on CPAN that can do that trick ?
On
Tie::Trace almost gets you there.
use Tie::Trace 'watch';
my $hash = { foo => { bar => "original value" } };
watch $hash->{foo}{bar};
sub f1 { f2() }
sub f2 { f3() }
sub f3 { $hash->{foo}{bar} = "new value" }
f1();
Output:
'new value' at watch.pl line 6
You can make the output produce a full stack trace by importing Carp::Always or by monkey patching the Tie::Trace::_carpit function or with a $SIG{__WARN__} handler like
$SIG{__WARN__} = sub {
if (caller(0) eq 'Tie::Trace') {
# warning is from Tie::Trace
Carp::cluck(@_);
} else {
CORE::warn(@_);
}
};
...
On
I have done a "simple" function to do this, it doesn't work with hashes which contains arrays:
use v5.28;
use Storable qw(dclone);
my $ori = {
'hola' => {
'hola' => 'adios',
'adios' => 'hola'
},
'hey' => 'you'
};
my $copy = dclone($ori);
$ori->{'hola'}{'adios'} = {'good', 'bye', 'hello', 'year'};
compare($ori, $copy, sub { say $_[0]; });
sub compare {
my $original = shift;
my $copy = shift;
my $func = shift;
for my $el (keys %{$original}) {
if (ref $original->{$el} eq ref {}) {
compare($original->{$el}, ref $copy->{$el}
eq ref {} ? $copy->{$el}:{} , $func);
} else {
unless ($copy->{$el} eq $original->{$el}) {
&$func($original->{$el}, {}, $func);
}
}
}
}
~
produces
But that's a fatal error, and it doesn't include a trace (unless Carp::Always is used).
I would recommend adding set magic to the scalar.
produces
The same can be accomplished with
tie, but it would be more expensive. (Tied variables are built uponmagic.)