Integrate mainline (for ndbm fixes etc.)
[p5sagit/p5-mst-13.2.git] / lib / Attribute / Handlers.pm
index 5e8f861..d4cbfff 100644 (file)
@@ -2,7 +2,7 @@ package Attribute::Handlers;
 use 5.006;
 use Carp;
 use warnings;
-$VERSION = '0.75';
+$VERSION = '0.76';
 # $DB::single=1;
 
 my %symcache;
@@ -31,6 +31,14 @@ 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,...}";
@@ -44,8 +52,7 @@ sub import {
     while (@_) {
        my $cmd = shift;
         if ($cmd =~ /^autotie((?:ref)?)$/) {
-           my $tiedata = '($was_arrayref ? $data : @$data)';
-              $tiedata = ($1 ? '$ref, ' : '') . $tiedata;
+           my $tiedata = ($1 ? '$ref, ' : '') . '@$data';
             my $mapping = shift;
            _usage_AH_ $class unless ref($mapping) eq 'HASH';
            while (my($attr, $tieclass) = each %$mapping) {
@@ -121,6 +128,8 @@ sub _gen_handler_AH_() {
                        $phase{$ref}{CHECK} = 1
                                if $data =~ s/\s*,?\s*(CHECK)\s*,?\s*//
                                || ! keys %{$phase{$ref}};
+                       # Added for cleanup to not pollute next call.
+                       (%lastattr = ()),
                        croak "Can't have two ATTR specifiers on one subroutine"
                                if keys %lastattr;
                        croak "Bad attribute type: ATTR($data)"
@@ -132,8 +141,15 @@ sub _gen_handler_AH_() {
                        next unless $handler;
                        my $decl = [$pkg, $ref, $attr, $data,
                                    $raw{$handler}, $phase{$handler}];
-                       _apply_handler_AH_($decl,'BEGIN');
-                       push @declarations, $decl;
+                       foreach my $gphase (@global_phases) {
+                           _apply_handler_AH_($decl,$gphase)
+                               if $global_phases{$gphase} <= $global_phase;
+                       }
+                       # if _gen_handler_AH_ is being called after CHECK it's
+                       # for a lexical, so we don't want to keep a reference 
+                       # around
+                       push @declarations, $decl
+                               if $global_phase == 0;
                }
                $_ = undef;
            }
@@ -170,13 +186,14 @@ sub _apply_handler_AH_ {
 }
 
 CHECK {
+       $global_phase++;
        _resolve_lastattr;
        _apply_handler_AH_($_,'CHECK') foreach @declarations;
 }
 
-INIT { _apply_handler_AH_($_,'INIT') foreach @declarations }
+INIT { $global_phase++; _apply_handler_AH_($_,'INIT') foreach @declarations }
 
-END { _apply_handler_AH_($_,'END') foreach @declarations }
+END { $global_phase++; _apply_handler_AH_($_,'END') foreach @declarations }
 
 1;
 __END__
@@ -187,8 +204,8 @@ Attribute::Handlers - Simpler definition of attribute handlers
 
 =head1 VERSION
 
-This document describes version 0.75 of Attribute::Handlers,
-released September  3, 2001.
+This document describes version 0.76 of Attribute::Handlers,
+released November 15, 2001.
 
 =head1 SYNOPSIS
 
@@ -502,9 +519,28 @@ variables. For example:
                 print $next;
         }
 
-In fact, this pattern is so widely applicable that Attribute::Handlers
+Note that, because the C<Cycle> attribute receives its arguments in the
+C<$data> variable, if the attribute is given a list of arguments, C<$data>
+will consist of a single array reference; otherwise, it will consist of the
+single argument directly. Since Tie::Cycle requires its cycling values to
+be passed as an array reference, this means that we need to wrap
+non-array-reference arguments in an array constructor:
+
+        $data = [ $data ] unless ref $data eq 'ARRAY';
+
+Typically, however, things are the other way around: the tieable class expects
+its arguments as a flattened list, so the attribute looks like:
+
+        sub UNIVERSAL::Cycle : ATTR(SCALAR) {
+                my ($package, $symbol, $referent, $attr, $data, $phase) = @_;
+                my @data = ref $data eq 'ARRAY' ? @$data : $data;
+                tie $$referent, 'Tie::Whatever', @data;
+        }
+
+
+This software pattern is so widely applicable that Attribute::Handlers
 provides a way to automate it: specifying C<'autotie'> in the
-C<use Attribute::Handlers> statement. So, the previous example,
+C<use Attribute::Handlers> statement. So, the cycling example,
 could also be written:
 
         use Attribute::Handlers autotie => { Cycle => 'Tie::Cycle' };
@@ -513,11 +549,16 @@ could also be written:
 
         package main;
 
-        my $next : Cycle('A'..'Z');     # $next is now a tied variable
+        my $next : Cycle(['A'..'Z']);     # $next is now a tied variable
 
         while (<>) {
                 print $next;
 
+Note that we now have to pass the cycling values as an array reference,
+since the C<autotie> mechanism passes C<tie> a list of arguments as a list
+(as in the Tie::Whatever example), I<not> as an array reference (as in
+the original Tie::Cycle example at the start of this section).
+
 The argument after C<'autotie'> is a reference to a hash in which each key is
 the name of an attribute to be created, and each value is the class to which
 variables ascribed that attribute should be tied.