package ACME::AutoRedact;
use strict;
use warnings;
# VERSION
use Scope::Upper qw(localize :words);
use Scalar::Util qw(blessed);
# ABSTRACT: string-like object that can redact its value out of derived strings
=head1 SYNOPSIS
my $password_redacted = ACME::AutoRedact->new($password);
my $complicated_string = "some text with $password_redacted";
# this gets the password
do_thing_with($complicated_string);
# this gets asterisks
{ ACME::AutoRedact->redact; log($complicated_string) }
=cut
use overload
q{""} => \&stringify,
q{.} => \&concat,
fallback => 1,
;
sub _build_redacted {
my ($self) = @_;
return q{*} x length($self->{value});
}
=attr C
The "revealed" string value of this object.
=attr C
The "redacted" string value of this object. Defaults to a sequence of
C<*> as long as the L.
=attr C
Either C or C (the default). What the object should do
when stringifying, absent any specific directive (see L<< /C
>> and L<< /C >>).
=method C
ACME::AutoRedact->new($string);
ACME::AutoRedact->new({ value => $string });
ACME::AutoRedact->new({
value => $string,
redacted => $other_string,
default_behaviour => 'redact',
});
Constructs a new object; the L version defaults to a
sequence of C<*> as long as the L, the L is "reveal".
=cut
sub new {
my ($class,@args) = @_;
my $self;
if (@args==1 and not ref $args[0]) {
$self = { value => $args[0] };
}
elsif (@args==1) {
$self = $args[0];
}
else {
$self = { @args };
}
bless $self,$class;
$self->{redacted} //= $self->_build_redacted;
return $self;
}
our $requested_behaviour; ## no critic(ProhibitPackageVars)
=method C
ACME::AutoRedact->redact;
This sets (locally to the scope in which it's called) the
L to be "redact".
=cut
sub redact {
localize *requested_behaviour, \'redact', UP;
}
=method C
ACME::AutoRedact->reveal;
This sets (locally to the scope in which it's called) the
L to be "reveal".
=cut
sub reveal {
localize *requested_behaviour, \'reveal', UP;
}
=method C
Overloaded stringification method. Returns either the normal
L, or the L one, depending on the current
behaviour.
=cut
sub stringify {
my ($self) = @_;
my $behaviour = $requested_behaviour // $self->{default_behaviour} // 'reveal';
if ($behaviour eq 'reveal') {
return $self->{value};
}
else {
return $self->{redacted};
}
}
=method C
Overloaded concatenation method. Returns a new object with both normal
L and L value being the (appropriate) concatenation
of the arguments.
=cut
sub concat {
my ($self,$other,$swap) = @_;
my %new;
if ((blessed($other)//'') eq __PACKAGE__) {
($self,$other) = ($other,$self) if $swap;
$new{value} = $self->{value} . $other->{value};
$new{redacted} = $self->{redacted} . $other->{redacted};
$new{default_behaviour} = $self->{default_behaviour};
}
else {
for my $k (qw(value redacted)) {
$new{$k} = $swap
? "$other" . $self->{$k}
: $self->{$k} . "$other";
}
$new{default_behaviour} = $self->{default_behaviour};
}
return ref($self)->new(\%new);
}
1;