Attribute-Handlers

 view release on metacpan or  search on metacpan

lib/Attribute/Handlers.pm  view on Meta::CPAN


my $qual_id = qr/^[_a-z]\w*(::[_a-z]\w*)*$/i;

sub import {
    my $class = shift @_;
    return unless $class eq "Attribute::Handlers";
    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);
	        }
                my $code = qq{
                    : 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"
                    }
                };

                if ($attr =~ /\A__CALLER__::/) {
                    no strict 'refs';
                    my $add_import = caller;
                    my $next = defined &{ $add_import . '::import' } && \&{ $add_import . '::import' };
                    *{ $add_import . '::import' } = sub {
                        my $caller = caller;
                        my $full_attr = $attr;
                        $full_attr =~ s/__CALLER__/$caller/;
                        eval qq{ sub $full_attr $code 1; }
                            or die "Internal error: $@";

                        goto &$next
                            if $next;
                        my $uni = defined &UNIVERSAL::import && \&UNIVERSAL::import;
                        for my $isa (@{ $add_import . '::ISA' }) {
                            if (my $import = $isa->can('import')) {
                                goto &$import
                                    if $import != $uni;
                            }
                        }
                        goto &$uni
                            if $uni;
                    };
                }
                else {
                    $attr = caller()."::".$attr unless $attr =~ /::/;
                    eval qq{ sub $attr $code 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::_TEST_::MODIFY_CODE_ATTRIBUTES {
		$delayed = \&Attribute::Handlers::_TEST_::t != $_[1];
		return ();
	}
	sub Attribute::Handlers::_TEST_::t :T { }
	*_delayed_name_resolution = sub() { $delayed };
	undef &Attribute::Handlers::_TEST_::MODIFY_CODE_ATTRIBUTES;
	undef &Attribute::Handlers::_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'";
	croak "Attribute handler '$2' doesn't handle $1 attributes";
}

my $builtin = $] ge '5.027000'
    ? qr/lvalue|method|shared/
    : qr/lvalue|method|locked|shared|unique/;

sub _gen_handler_AH_() {



( run in 2.639 seconds using v1.01-cache-2.11-cpan-2e0ccfb7a10 )