File Coverage

blib/lib/Hash/AutoHash/MultiValued.pm
Criterion Covered Total %
statement 99 99 100.0
branch 33 38 86.8
condition 6 6 100.0
subroutine 21 21 100.0
pod n/a
total 159 164 96.9


line stmt bran cond sub pod time code
1             package Hash::AutoHash::MultiValued;
2             our $VERSION='1.17';
3             $VERSION=eval $VERSION; # I think this is the accepted idiom..
4              
5             #################################################################################
6             #
7             # Author: Nat Goodman
8             # Created: 09-03-05
9             # $Id:
10             #
11             # AutoHash with multivalued elements
12             # Inspired by Tie::Hash::Multivalue
13             #
14             #################################################################################
15 11     11   246117 use strict;
  11         27  
  11         412  
16 11     11   53 use Carp;
  11         20  
  11         621  
17 11     11   10508 use Hash::AutoHash;
  11         99703  
  11         78  
18 11     11   3347 use base qw(Hash::AutoHash);
  11         27  
  11         2610  
19              
20             our @NORMAL_EXPORT_OK=@Hash::AutoHash::EXPORT_OK;
21             my $helper_class=__PACKAGE__.'::helper';
22             our @EXPORT_OK=$helper_class->EXPORT_OK;
23             our @SUBCLASS_EXPORT_OK=$helper_class->SUBCLASS_EXPORT_OK;
24              
25             # sub new {
26             # my $class_or_self=@_>0 && shift;
27             # # send to parent if called as object method. will access hash slot via AUTOLOAD
28             # return $class_or_self->SUPER::new(@_) if ref $class_or_self;
29             # # do regular 'new' via helper class if called as class method.
30             # my $helper_class=$class_or_self.'::helper';
31             # $helper_class->_new($class_or_self,@_);
32             # }
33              
34             #################################################################################
35             # helper package exists to avoid polluting Hash::AutoHash::Args namespace with
36             # subs that would mask accessor/mutator AUTOLOADs
37             # functions herein (except _new) are exportable by Hash::AutoHash::Args
38             #################################################################################
39             package Hash::AutoHash::MultiValued::helper;
40             our $VERSION=$Hash::AutoHash::MultiValued::VERSION;
41 11     11   62 use strict;
  11         19  
  11         361  
42 11     11   66 use Carp;
  11         19  
  11         810  
43             BEGIN {
44 11     11   435 our @ISA=qw(Hash::AutoHash::helper);
45             }
46 11     11   63 use Hash::AutoHash qw(autohash_tie);
  11         32  
  11         53  
47              
48             sub _new {
49 175     175   249995 my($helper_class,$class,@args)=@_;
50 175         859 my $self=autohash_tie Hash::AutoHash::MultiValued::tie,@args;
51 175         1390 bless $self,$class;
52             }
53              
54             #################################################################################
55             # Tied hash which implements Hash::AutoHash::MultiValued
56             #################################################################################
57             package Hash::AutoHash::MultiValued::tie;
58             our $VERSION=$Hash::AutoHash::MultiValued::VERSION;
59 11     11   2279 use strict;
  11         22  
  11         323  
60 11     11   51 use Tie::Hash;
  11         19  
  11         464  
61 11     11   55 use List::MoreUtils qw(uniq);
  11         21  
  11         708  
62             our @ISA=qw(Tie::ExtraHash);
63 11     11   74 use constant STORAGE=>0;
  11         28  
  11         6098  
64 11     11   231 use constant UNIQUE=>1;
  11         28  
  11         901  
65 11     11   56 use constant FILTER=>2;
  11         16  
  11         19554  
66              
67             sub TIEHASH {
68 175     175   2261 my($class,@hash)=@_;
69 175         673 my $self=bless [{}],$class;
70 175 100       837 if (@hash==1) { # flatten if ARRAY or HASH
71 4         10 my $hash=shift @hash;
72 4 50       32 @hash=('ARRAY' eq ref $hash)? @$hash: ('HASH' eq ref $hash)? %$hash: ();
    100          
73             }
74 175         605 while (@hash>1) { # store initial values
75 304         713 my($key,$value)=splice @hash,0,2; # shift 1st two elements
76 304         900 $self->STORE($key,$value);
77             }
78 175         664 $self;
79             }
80             sub FETCH {
81 2464     2464   2305109 my($self,$key)=@_;
82 2464         3499 my $storage=$self->[STORAGE];
83 2464         3625 my $values=$storage->{$key};
84 2464 100       10930 wantarray? (defined $values? @$values: ()): $values;
    100          
85             }
86             sub STORE {
87 600     600   279318 my($self,$key,@new_values)=@_;
88 600         1011 my $storage=$self->[STORAGE];
89 600         925 my $values=$storage->{$key};
90 600 50       1531 if (@new_values) {
91 600 100 100     3547 @new_values=@{$new_values[0]} if @new_values==1 && 'ARRAY' eq ref $new_values[0];
  492         1373  
92 600 100       1578 if (my $unique=$self->[UNIQUE]) {
93 86         109 my($a,$b);
94 86         153 for $a (@new_values) {
95 124 100       391 push(@$values,$a) unless grep {$b=$_; &$unique($a,$b)} @$values;
  156         286  
  156         306  
96             }}
97             else {
98 514         1091 push(@$values,@new_values);
99             }
100 600         1432 $storage->{$key}=$values;
101             }
102 600 0       2616 wantarray? (defined $values? @$values: ()): $values;
    50          
103             }
104             sub unique {
105 75     75   17512 my $self=shift;
106 75 100       291 return $self->[UNIQUE] unless @_;
107 72         190 my $unique=$self->[UNIQUE]=shift;
108 72 100 100 116   514 $unique=$self->[UNIQUE]=sub {$_[0] eq $_[1]} if $unique && 'CODE' ne ref $unique;
  116         532  
109 72 100       197 if ($unique) { # apply to existing values
110 51         88 my $storage=$self->[STORAGE];
111 51         172 my @values=grep {defined $_} values %$storage;
  82         244  
112 51         115 for my $values (@values) {
113 82 100       261 next unless @$values;
114             # leave 1st value in @$values. put rest in @new_values
115 58         132 my @new_values=splice(@$values,1);
116 58         91 my($a,$b);
117 58         114 for $a (@new_values) {
118 51 100       141 push(@$values,$a) unless grep {$b=$_; &$unique($a,$b)} @$values;
  68         119  
  68         139  
119             }}
120             }
121 72         246 $unique;
122             }
123             sub filter {
124 74     74   14799 my $self=shift;
125 74 100       525 my $filter=@_? $self->[FILTER]=shift: $self->[FILTER];
126 74 100       210 if ($filter) { # appy filter to existing values
127 50 100       175 $filter=$self->[FILTER]=\&uniq unless 'CODE' eq ref $filter;
128 50         102 my $storage=$self->[STORAGE];
129 50         141 my @values=grep {defined $_} values %$storage;
  80         221  
130 50         98 map {@$_=&$filter(@$_)} @values; # updates each list in-place
  80         460  
131             }
132 74         346 $filter;
133             }
134              
135             1;
136             __END__