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_01';
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   173214 use strict;
  11         15  
  11         255  
16 11     11   37 use Carp;
  11         11  
  11         429  
17 11     11   4908 use Hash::AutoHash;
  11         111588  
  11         50  
18 11     11   1639 use base qw(Hash::AutoHash);
  11         12  
  11         1622  
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   49 use strict;
  11         14  
  11         182  
42 11     11   32 use Carp;
  11         13  
  11         584  
43             BEGIN {
44 11     11   242 our @ISA=qw(Hash::AutoHash::helper);
45             }
46 11     11   57 use Hash::AutoHash qw(autohash_tie);
  11         12  
  11         33  
47              
48             sub _new {
49 175     175   139669 my($helper_class,$class,@args)=@_;
50 175         549 my $self=autohash_tie Hash::AutoHash::MultiValued::tie,@args;
51 175         1118 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   1389 use strict;
  11         12  
  11         195  
60 11     11   38 use Tie::Hash;
  11         10  
  11         262  
61 11     11   32 use List::MoreUtils qw(uniq);
  11         10  
  11         55  
62             our @ISA=qw(Tie::ExtraHash);
63 11     11   3339 use constant STORAGE=>0;
  11         12  
  11         828  
64 11     11   43 use constant UNIQUE=>1;
  11         18  
  11         459  
65 11     11   38 use constant FILTER=>2;
  11         13  
  11         6303  
66              
67             sub TIEHASH {
68 175     175   1445 my($class,@hash)=@_;
69 175         379 my $self=bless [{}],$class;
70 175 100       521 if (@hash==1) { # flatten if ARRAY or HASH
71 4         7 my $hash=shift @hash;
72 4 50       26 @hash=('ARRAY' eq ref $hash)? @$hash: ('HASH' eq ref $hash)? %$hash: ();
    100          
73             }
74 175         465 while (@hash>1) { # store initial values
75 304         440 my($key,$value)=splice @hash,0,2; # shift 1st two elements
76 304         539 $self->STORE($key,$value);
77             }
78 175         425 $self;
79             }
80             sub FETCH {
81 2464     2464   1295706 my($self,$key)=@_;
82 2464         2509 my $storage=$self->[STORAGE];
83 2464         2260 my $values=$storage->{$key};
84 2464 100       6783 wantarray? (defined $values? @$values: ()): $values;
    100          
85             }
86             sub STORE {
87 600     600   153578 my($self,$key,@new_values)=@_;
88 600         670 my $storage=$self->[STORAGE];
89 600         612 my $values=$storage->{$key};
90 600 50       1027 if (@new_values) {
91 600 100 100     2360 @new_values=@{$new_values[0]} if @new_values==1 && 'ARRAY' eq ref $new_values[0];
  492         750  
92 600 100       926 if (my $unique=$self->[UNIQUE]) {
93 86         73 my($a,$b);
94 86         110 for $a (@new_values) {
95 124 100       216 push(@$values,$a) unless grep {$b=$_; &$unique($a,$b)} @$values;
  156         186  
  156         176  
96             }}
97             else {
98 514         656 push(@$values,@new_values);
99             }
100 600         836 $storage->{$key}=$values;
101             }
102 600 0       1594 wantarray? (defined $values? @$values: ()): $values;
    50          
103             }
104             sub unique {
105 75     75   10376 my $self=shift;
106 75 100       158 return $self->[UNIQUE] unless @_;
107 72         148 my $unique=$self->[UNIQUE]=shift;
108 72 100 100 116   360 $unique=$self->[UNIQUE]=sub {$_[0] eq $_[1]} if $unique && 'CODE' ne ref $unique;
  116         256  
109 72 100       138 if ($unique) { # apply to existing values
110 51         79 my $storage=$self->[STORAGE];
111 51         126 my @values=grep {defined $_} values %$storage;
  82         158  
112 51         81 for my $values (@values) {
113 82 100       170 next unless @$values;
114             # leave 1st value in @$values. put rest in @new_values
115 58         79 my @new_values=splice(@$values,1);
116 58         52 my($a,$b);
117 58         76 for $a (@new_values) {
118 51 100       109 push(@$values,$a) unless grep {$b=$_; &$unique($a,$b)} @$values;
  68         66  
  68         91  
119             }}
120             }
121 72         131 $unique;
122             }
123             sub filter {
124 74     74   8391 my $self=shift;
125 74 100       240 my $filter=@_? $self->[FILTER]=shift: $self->[FILTER];
126 74 100       162 if ($filter) { # appy filter to existing values
127 50 100       133 $filter=$self->[FILTER]=\&uniq unless 'CODE' eq ref $filter;
128 50         62 my $storage=$self->[STORAGE];
129 50         114 my @values=grep {defined $_} values %$storage;
  80         169  
130 50         78 map {@$_=&$filter(@$_)} @values; # updates each list in-place
  80         379  
131             }
132 74         217 $filter;
133             }
134              
135             1;
136             __END__