Attribute-Handlers-Clean
view release on metacpan or search on metacpan
lib/Attribute/Handlers/Clean.pm view on Meta::CPAN
}
my %validtype = (
VAR => [qw[SCALAR ARRAY HASH]],
ANY => [qw[SCALAR ARRAY HASH CODE]],
"" => [qw[SCALAR ARRAY HASH CODE]],
SCALAR => [qw[SCALAR]],
ARRAY => [qw[ARRAY]],
HASH => [qw[HASH]],
CODE => [qw[CODE]],
);
my %lastattr;
my @declarations;
my %raw;
my %phase;
my %sigil = (SCALAR=>'$', ARRAY=>'@', HASH=>'%');
my $global_phase = 0;
my %global_phases = (
BEGIN => 0,
CHECK => 1,
INIT => 2,
END => 3,
);
my @global_phases = qw(BEGIN CHECK INIT END);
sub _usage_AH_ {
croak "Usage: use $_[0] autotie => {AttrName => TieClassName,...}";
}
my $qual_id = qr/^[_a-z]\w*(::[_a-z]\w*)*$/i;
sub import {
my $class = shift @_;
{
my @caller_0 = caller 0;
my @caller_1 = caller 1;
# Allow just calling "package->import" inside of subclses own import method, to mean their caller should get the same import effect from this module - in the same style of warnings->unimport
my $caller = (@caller_1 and $caller_1[3] eq "$caller_0[0]::import") ? $caller_1[0] : $caller_0[0];
no strict 'refs';
warnings->unimport('reserved');
push (@{"${caller}::ISA"}, $class) unless $caller eq $class or grep {$_ eq $class} @{"${caller}::ISA"};
for (@{$validtype{ANY}}) {
*{"${caller}::MODIFY_${_}_ATTRIBUTES"} = _gen_handler_AH_() unless defined *{"${caller}::MODIFY_${_}_ATTRIBUTES"};
}
}
return unless $class eq __PACKAGE__;
while (@_) {
my $cmd = shift;
if ($cmd =~ /^autotie((?:ref)?)$/) {
my $tiedata = ($1 ? '$ref, ' : '') . '@$data';
my $mapping = shift;
_usage_AH_ $class unless ref($mapping) eq 'HASH';
while (my($attr, $tieclass) = each %$mapping) {
$tieclass =~ s/^([_a-z]\w*(::[_a-z]\w*)*)(.*)/$1/is;
my $args = $3||'()';
_usage_AH_ $class unless $attr =~ $qual_id && $tieclass =~ $qual_id && eval "use base q\0$tieclass\0; 1";
if ($tieclass->isa('Exporter')) {
local $Exporter::ExportLevel = 2;
$tieclass->import(eval $args);
}
$attr =~ s/__CALLER__/caller(1)/e;
$attr = caller()."::".$attr unless $attr =~ /::/;
eval '# line '.__LINE__.' "'.__FILE__.qq{"
sub $attr : ATTR(VAR) {
my (\$ref, \$data) = \@_[2,4];
my \$was_arrayref = ref \$data eq 'ARRAY';
\$data = [ \$data ] unless \$was_arrayref;
my \$type = ref(\$ref)||"value (".(\$ref||"<undef>").")";
(\$type eq 'SCALAR') ?
tie \$\$ref,'$tieclass',$tiedata :
(\$type eq 'ARRAY') ?
tie \@\$ref,'$tieclass',$tiedata :
(\$type eq 'HASH') ?
tie \%\$ref,'$tieclass',$tiedata :
die "Can't autotie a \$type\n";
} 1
} or die "Internal error: $@";
}
} else {
croak "Can't understand $_";
}
}
}
# On older perls, code attribute handlers run before the sub gets placed
# in its package. Since the :ATTR handlers need to know the name of the
# sub they're applied to, the name lookup (via findsym) needs to be
# delayed: we do it immediately before we might need to find attribute
# handlers from their name. However, on newer perls (which fix some
# problems relating to attribute application), a sub gets placed in its
# package before its attributes are processed. In this case, the
# delayed name lookup might be too late, because the sub we're looking
# for might have already been replaced. So we need to detect which way
# round this perl does things, and time the name lookup accordingly.
BEGIN {
my $delayed;
sub Attribute::Handlers::Clean::_TEST_::MODIFY_CODE_ATTRIBUTES {
$delayed = \&Attribute::Handlers::Clean::_TEST_::t != $_[1];
return ();
}
sub Attribute::Handlers::Clean::_TEST_::t :T { }
*_delayed_name_resolution = sub() { $delayed };
undef &Attribute::Handlers::Clean::_TEST_::MODIFY_CODE_ATTRIBUTES;
undef &Attribute::Handlers::Clean::_TEST_::t;
}
sub _resolve_lastattr {
return unless $lastattr{ref};
my $sym = findsym @lastattr{'pkg','ref'} or die "Internal error: $lastattr{pkg} symbol went missing";
my $name = *{$sym}{NAME};
warn "Declaration of $name attribute in package $lastattr{pkg} may clash with future reserved word\n" if $^W and $name !~ /[A-Z]/;
foreach ( @{$validtype{$lastattr{type}}} ) {
no strict 'refs';
*{"$lastattr{pkg}::_ATTR_${_}_${name}"} = $lastattr{ref};
}
%lastattr = ();
}
sub AUTOLOAD {
return if $AUTOLOAD =~ /::DESTROY$/;
my ($class) = $AUTOLOAD =~ m/(.*)::/g;
$AUTOLOAD =~ m/_ATTR_(.*?)_(.*)/ or croak "Can't locate class method '$AUTOLOAD' via package '$class'";
( run in 3.317 seconds using v1.01-cache-2.11-cpan-63c85eba8c4 )