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 ?
my $hash = { foo => { bar => 1 } };
Internals::SvREADONLY( $hash->{foo}{bar}, 1 );
$hash->{foo}{bar} = 2;
produces
Modification of a read-only value attempted at -e line 4.
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.
use Carp qw( cluck );
use Variable::Magic qw( wizard cast );
my $wizard = wizard(
set => sub {
cluck("Warning: Modification of a read-only value attempted");
},
);
my $hash = { foo => { bar => 1 } };
cast( $hash->{foo}{bar}, $wizard );
$hash->{foo}{bar} = 2;
produces
Warning: Modification of a read-only value attempted at -e line 6.
main::__ANON__(SCALAR(0x4200c90), undef) called at -e line 12
eval {...} called at -e line 12
The same can be accomplished with tie
, but it would be more expensive. (Tied variables are built upon magic
.)
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(@_);
}
};
...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With