X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FAttribute%2FHandlers.pm;h=d4cbfffc25d5de5672591c28c1c390b68b669f3f;hb=235bddc8d16c512a7d89f327f65cee68b1f5848c;hp=5e8f86162c4f947ef6e51928b63afef057810265;hpb=d1be9408a3c14848d30728674452e191ba5fffaa;p=p5sagit%2Fp5-mst-13.2.git diff --git a/lib/Attribute/Handlers.pm b/lib/Attribute/Handlers.pm index 5e8f861..d4cbfff 100644 --- a/lib/Attribute/Handlers.pm +++ b/lib/Attribute/Handlers.pm @@ -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 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 statement. So, the previous example, +C 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 mechanism passes C a list of arguments as a list +(as in the Tie::Whatever example), I 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.