File Coverage

blib/lib/Hash/MultiValue.pm
Criterion Covered Total %
statement 166 178 93.2
branch 24 26 92.3
condition 4 6 66.6
subroutine 29 33 87.8
pod 19 25 76.0
total 242 268 90.3


line stmt bran cond sub pod time code
1             package Hash::MultiValue;
2              
3 8     8   237558 use strict;
  8         22  
  8         374  
4 8     8   49 no warnings 'void';
  8         16  
  8         326  
5 8     8   229 use 5.006_002;
  8         38  
  8         660  
6             our $VERSION = '0.15';
7              
8 8     8   47 use Carp ();
  8         140  
  8         241  
9 8     8   47 use Scalar::Util qw(refaddr);
  8         13  
  8         1620  
10              
11             # there does not seem to be a relevant RT or perldelta entry for this
12 8     8   59 use constant _SPLICE_SAME_ARRAY_SEGFAULT => $] < '5.008007';
  8         24  
  8         2367  
13              
14             my %keys;
15             my %values;
16             my %registry;
17              
18             BEGIN {
19 8     8   50 require Config;
20 8   33     392 my $needs_registry = ($^O eq 'Win32' || $Config::Config{useithreads});
21 8 50       47 if ($needs_registry) {
22             *CLONE = sub {
23 0         0 foreach my $oldaddr (keys %registry) {
24 0         0 my $this = refaddr $registry{$oldaddr};
25 0         0 $keys{$this} = delete $keys{$oldaddr};
26 0         0 $values{$this} = delete $values{$oldaddr};
27 0         0 Scalar::Util::weaken($registry{$this} = delete $registry{$oldaddr});
28             }
29 0         0 };
30             }
31 8         19174 *NEEDS_REGISTRY = sub () { $needs_registry };
  0         0  
32             }
33              
34             if (defined &UNIVERSAL::ref::import) {
35             UNIVERSAL::ref->import;
36             }
37              
38 0     0 0 0 sub ref { 'HASH' }
39              
40             sub create {
41 7     7 0 14 my $class = shift;
42 7         25 my $self = bless {}, $class;
43 7         38 my $this = refaddr $self;
44 7         29 $keys{$this} = [];
45 7         16 $values{$this} = [];
46 7         13 Scalar::Util::weaken($registry{$this} = $self) if NEEDS_REGISTRY;
47 7         18 $self;
48             }
49              
50             sub new {
51 6     6 1 75 my $class = shift;
52 6         33 my $self = $class->create;
53 6         17 unshift @_, $self;
54 6         13 goto &{ $self->can('merge_flat') };
  6         69  
55             }
56              
57             sub from_mixed {
58 1     1 1 20 my $class = shift;
59 1         5 my $self = $class->create;
60 1         4 unshift @_, $self;
61 1         2 goto &{ $self->can('merge_mixed') };
  1         15  
62             }
63              
64             sub DESTROY {
65 9     9   6127 my $this = refaddr shift;
66 9         776 delete $keys{$this};
67 9         27 delete $values{$this};
68 9         997 delete $registry{$this} if NEEDS_REGISTRY;
69             }
70              
71             sub get {
72 0     0 1 0 my($self, $key) = @_;
73 0         0 $self->{$key};
74             }
75              
76             sub get_all {
77 4     4 1 3181 my($self, $key) = @_;
78 4         15 my $this = refaddr $self;
79 4         12 my $k = $keys{$this};
80 4         15 (@{$values{$this}}[grep { $key eq $k->[$_] } 0 .. $#$k]);
  4         20  
  16         40  
81             }
82              
83             sub get_one {
84 2     2 1 26 my ($self, $key) = @_;
85 2         8 my @v = $self->get_all($key);
86 2 100       14 return $v[0] if @v == 1;
87 1 50       5 Carp::croak "Key not found: $key" if not @v;
88 1         221 Carp::croak "Multiple values match: $key";
89             }
90              
91             sub set {
92 6     6 1 17 my $self = shift;
93 6         10 my $key = shift;
94              
95 6         17 my $this = refaddr $self;
96 6         12 my $k = $keys{$this};
97 6         13 my $v = $values{$this};
98              
99 6         20 my @idx = grep { $key eq $k->[$_] } 0 .. $#$k;
  29         64  
100              
101 6         15 my $added = @_ - @idx;
102 6 100       32 if ($added > 0) {
    100          
103 1         3 my $start = $#$k + 1;
104 1         5 push @$k, ($key) x $added;
105 1         38 push @idx, $start .. $#$k;
106             }
107             elsif ($added < 0) {
108 4         14 my ($start, @drop, @keep) = splice @idx, $added;
109 4         14 for my $i ($start+1 .. $#$k) {
110 10 100 100     108 if (@drop and $i == $drop[0]) {
111 4         6 shift @drop;
112 4         9 next;
113             }
114 6         13 push @keep, $i;
115             }
116              
117             splice @$_, $start, 0+@$_, ( _SPLICE_SAME_ARRAY_SEGFAULT
118             ? @{[ @$_[@keep] ]} # force different source array
119             : @$_[@keep]
120 4         36 ) for $k, $v;
121             }
122              
123 6 100       20 if (@_) {
124 3         11 @$v[@idx] = @_;
125 3         9 $self->{$key} = $_[-1];
126             }
127             else {
128 3         8 delete $self->{$key};
129             }
130              
131 6         16 $self;
132             }
133              
134             sub add {
135 4     4 1 2804 my $self = shift;
136 4         7 my $key = shift;
137 4         17 $self->merge_mixed( $key => \@_ );
138 4         10 $self;
139             }
140              
141             sub merge_flat {
142 6     6 0 15 my $self = shift;
143 6         18 my $this = refaddr $self;
144 6         149 my $k = $keys{$this};
145 6         14 my $v = $values{$this};
146 6 100       24 push @{ $_ & 1 ? $v : $k }, $_[$_] for 0 .. $#_;
  46         287  
147 6         15 @{$self}{@$k} = @$v;
  6         50  
148 6         27 $self;
149             }
150              
151             sub merge_mixed {
152 5     5 0 10 my $self = shift;
153 5         91 my $this = refaddr $self;
154 5         13 my $k = $keys{$this};
155 5         13 my $v = $values{$this};
156              
157 5         7 my $hash;
158 5 100       20 $hash = shift if @_ == 1;
159              
160 5 100       54 while ( my ($key, $value) = @_ ? splice @_, 0, 2 : each %$hash ) {
161 7 100       53 my @value = CORE::ref($value) eq 'ARRAY' ? @$value : $value;
162 7 100       46 next if not @value;
163 6         20 $self->{$key} = $value[-1];
164 6         19 push @$k, ($key) x @value;
165 6         38 push @$v, @value;
166             }
167              
168 5         547 $self;
169             }
170              
171             sub remove {
172 2     2 1 8 my ($self, $key) = @_;
173 2         8 $self->set($key);
174 2         4 $self;
175             }
176              
177             sub clear {
178 1     1 1 3 my $self = shift;
179 1         2 %$self = ();
180 1         3 my $this = refaddr $self;
181 1         4 $keys{$this} = [];
182 1         2 $values{$this} = [];
183 1         2 $self;
184             }
185              
186             sub clone {
187 1     1 1 465 my $self = shift;
188 1         6 CORE::ref($self)->new($self->flatten);
189             }
190              
191             sub keys {
192 3     3 1 1704 my $self = shift;
193 3         7 return @{$keys{refaddr $self}};
  3         32  
194             }
195              
196             sub values {
197 1     1 1 3 my $self = shift;
198 1         1 return @{$values{refaddr $self}};
  1         15  
199             }
200              
201             sub flatten {
202 11     11 1 39 my $self = shift;
203 11         31 my $this = refaddr $self;
204 11         22 my $k = $keys{$this};
205 11         19 my $v = $values{$this};
206 11         29 map { $k->[$_], $v->[$_] } 0 .. $#$k;
  44         172  
207             }
208              
209             sub each {
210 2     2 1 4259 my ($self, $code) = @_;
211 2         9 my $this = refaddr $self;
212 2         4 my $k = $keys{$this};
213 2         4 my $v = $values{$this};
214 2         569 for (0 .. $#$k) {
215 8         35 $code->($k->[$_], $v->[$_]);
216             }
217 2         11 return $self;
218             }
219              
220             sub as_hashref {
221 1     1 1 7 my $self = shift;
222 1         6 my %hash = %$self;
223 1         5 \%hash;
224             }
225              
226             sub as_hashref_mixed {
227 1     1 1 3497 my $self = shift;
228 1         5 my $this = refaddr $self;
229 1         3 my $k = $keys{$this};
230 1         3 my $v = $values{$this};
231              
232 1         2 my %hash;
233 1         5 push @{$hash{$k->[$_]}}, $v->[$_] for 0 .. $#$k;
  4         13  
234 1         4 for (CORE::values %hash) {
235 3 100       30 $_ = $_->[0] if 1 == @$_;
236             }
237              
238 1         4 \%hash;
239             }
240              
241 0     0 1 0 sub mixed { $_[0]->as_hashref_mixed }
242              
243             sub as_hashref_multi {
244 1     1 1 2681 my $self = shift;
245 1         4 my $this = refaddr $self;
246 1         3 my $k = $keys{$this};
247 1         2 my $v = $values{$this};
248              
249 1         3 my %hash;
250 1         5 push @{$hash{$k->[$_]}}, $v->[$_] for 0 .. $#$k;
  4         18  
251              
252 1         4 \%hash;
253             }
254              
255 0     0 1 0 sub multi { $_[0]->as_hashref_multi }
256              
257             sub STORABLE_freeze {
258 2     2 0 63 my $self = shift;
259 2         6 my $this = refaddr $self;
260 2         154 return '', $keys{$this}, $values{$this};
261             }
262              
263             sub STORABLE_thaw {
264 2     2 0 29 my $self = shift;
265 2         4 my ($is_cloning, $serialised, $k, $v) = @_;
266 2         5 my $this = refaddr $self;
267 2         4 $keys {$this} = $k;
268 2         3 $values{$this} = $v;
269 2         2 @{$self}{@$k} = @$v;
  2         7  
270 2         13 return $self;
271             }
272              
273             1;
274             __END__
275              
276             =encoding utf-8
277              
278             =for stopwords
279              
280             =head1 NAME
281              
282             Hash::MultiValue - Store multiple values per key
283              
284             =head1 SYNOPSIS
285              
286             use Hash::MultiValue;
287              
288             my $hash = Hash::MultiValue->new(
289             foo => 'a',
290             foo => 'b',
291             bar => 'baz',
292             );
293              
294             # $hash is an object, but can be used as a hashref and DWIMs!
295             my $foo = $hash->{foo}; # 'b' (the last entry)
296             my $foo = $hash->get('foo'); # 'b' (always, regardless of context)
297             my @foo = $hash->get_all('foo'); # ('a', 'b')
298              
299             keys %$hash; # ('foo', 'bar') not guaranteed to be ordered
300             $hash->keys; # ('foo', 'foo', 'bar') guaranteed to be ordered
301              
302             =head1 DESCRIPTION
303              
304             Hash::MultiValue is an object (and a plain hash reference) that may
305             contain multiple values per key, inspired by MultiDict of WebOb.
306              
307             =head1 RATIONALE
308              
309             In a typical web application, the request parameters (a.k.a CGI
310             parameters) can be single value or multi values. Using CGI.pm style
311             C<param> is one way to deal with this problem (and it is good, as long
312             as you're aware of its list context gotcha), but there's another
313             approach to convert parameters into a hash reference, like Catalyst's
314             C<< $c->req->parameters >> does, and it B<sucks>.
315              
316             Why? Because the value could be just a scalar if there is one value
317             and an array ref if there are multiple, depending on I<user input>
318             rather than I<how you code it>. So your code should always be like
319             this to be defensive:
320              
321             my $p = $c->req->parameters;
322             my @maybe_multi = ref $p->{m} eq 'ARRAY' ? @{$p->{m}} : ($p->{m});
323             my $must_single = ref $p->{m} eq 'ARRAY' ? $p->{m}->[0] : $p->{m};
324              
325             Otherwise you'll get a random runtime exception of I<Can't use string
326             as an ARRAY ref> or get stringified array I<ARRAY(0xXXXXXXXXX)> as a
327             string, I<depending on user input> and that is miserable and
328             insecure.
329              
330             This module provides a solution to this by making it behave like a
331             single value hash reference, but also has an API to get multiple
332             values on demand, explicitly.
333              
334             =head1 HOW THIS WORKS
335              
336             The object returned by C<new> is a blessed hash reference that
337             contains the last entry of the same key if there are multiple values,
338             but it also keeps the original pair state in the object tracker (a.k.a
339             inside out objects) and allows you to access the original pairs and
340             multiple values via the method calls, such as C<get_all> or C<flatten>.
341              
342             This module does not use C<tie> or L<overload> and is quite fast.
343              
344             Yes, there is L<Tie::Hash::MultiValue> and this module tries to solve
345             exactly the same problem, but using a different implementation.
346              
347             =head1 UPDATING CONTENTS
348              
349             When you update the content of the hash, B<DO NOT UPDATE> using the
350             hash reference interface: this won't write through to the tracking
351             object.
352              
353             my $hash = Hash::MultiValue->new(...);
354              
355             # WRONG
356             $hash->{foo} = 'bar';
357             delete $hash->{foo};
358              
359             # Correct
360             $hash->add(foo => 'bar');
361             $hash->remove('foo');
362              
363             See below for the list of updating methods.
364              
365             =head1 METHODS
366              
367             =over 4
368              
369             =item new
370              
371             $hash = Hash::MultiValue->new(@pairs);
372              
373             Creates a new object that can be treated as a plain hash reference as well.
374              
375             =item get
376              
377             $value = $hash->get($key);
378             $value = $hash->{$key};
379              
380             Returns a single value for the given C<$key>. If there are multiple
381             values, the last one (not first one) is returned. See below for why.
382              
383             Note that this B<always> returns the single element as a scalar,
384             regardless of its context, unlike CGI.pm's C<param> method etc.
385              
386             =item get_one
387              
388             $value = $hash->get_one($key);
389              
390             Returns a single value for the given C<$key>. This method B<croaks> if
391             there is no value or multiple values associated with the key, so you
392             should wrap it with eval or modules like L<Try::Tiny>.
393              
394             =item get_all
395              
396             @values = $hash->get_all($key);
397              
398             Returns a list of values for the given C<$key>. This method B<always>
399             returns a list regardless of its context. If there is no value
400             attached, the result will be an empty list.
401              
402             =item keys
403              
404             @keys = $hash->keys;
405              
406             Returns a list of all keys, including duplicates (see the example in the
407             L</SYNOPSIS>).
408              
409             If you want only unique keys, use C<< keys %$hash >>, as normal.
410              
411             =item values
412              
413             @values = $hash->values;
414              
415             Returns a list of all values, in the same order as C<< $hash->keys >>.
416              
417             =item set
418              
419             $hash->set($key [, $value ... ]);
420              
421             Changes the stored value(s) of the given C<$key>. This removes or adds
422             pairs as necessary to store the new list but otherwise preserves order
423             of existing pairs. C<< $hash->{$key} >> is updated to point to the last
424             value.
425              
426             =item add
427              
428             $hash->add($key, $value [, $value ... ]);
429              
430             Appends a new value to the given C<$key>. This updates the value of
431             C<< $hash->{$key} >> as well so it always points to the last value.
432              
433             =item remove
434              
435             $hash->remove($key);
436              
437             Removes a key and associated values for the given C<$key>.
438              
439             =item clear
440              
441             $hash->clear;
442              
443             Clears the hash to be an empty hash reference.
444              
445             =item flatten
446              
447             @pairs = $hash->flatten;
448              
449             Gets pairs of keys and values. This should be exactly the same pairs
450             which are given to C<new> method unless you updated the data.
451              
452             =item each
453              
454             $hash->each($code);
455              
456             # e.g.
457             $hash->each(sub { print "$_[0] = $_[1]\n" });
458              
459             Calls C<$code> once for each C<($key, $value)> pair. This is a more convenient
460             alternative to calling C<flatten> and then iterating over it two items at a
461             time.
462              
463             Inside C<$code>, C<$_> contains the current iteration through the loop,
464             starting at 0. For example:
465              
466             $hash = Hash::MultiValue->new(a => 1, b => 2, c => 3, a => 4);
467              
468             $hash->each(sub { print "$_: $_[0] = $_[1]\n" });
469             # 0: a = 1
470             # 1: b = 2
471             # 2: c = 3
472             # 3: a = 4
473              
474             Be careful B<not> to change C<@_> inside your coderef! It will update
475             the tracking object but not the plain hash. In the future, this
476             limitation I<may> be removed.
477              
478             =item clone
479              
480             $new = $hash->clone;
481              
482             Creates a new Hash::MultiValue object that represents the same data,
483             but obviously not sharing the reference. It's identical to:
484              
485             $new = Hash::MultiValue->new($hash->flatten);
486              
487             =item as_hashref
488              
489             $copy = $hash->as_hashref;
490              
491             Creates a new plain (unblessed) hash reference where a value is a
492             single scalar. It's identical to:
493              
494             $copy = +{%$hash};
495              
496             =item as_hashref_mixed, mixed
497              
498             $mixed = $hash->as_hashref_mixed;
499             $mixed = $hash->mixed;
500              
501             Creates a new plain (unblessed) hash reference where the value is a
502             single scalar, or an array ref when there are multiple values for a
503             same key. Handy to create a hash reference that is often used in web
504             application frameworks request objects such as L<Catalyst>. Ths method
505             does exactly the opposite of C<from_mixed>.
506              
507             =item as_hashref_multi, multi
508              
509             $multi = $hash->as_hashref_multi;
510             $multi = $hash->multi;
511              
512             Creates a new plain (unblessed) hash reference where values are all
513             array references, regardless of there are single or multiple values
514             for a same key.
515              
516             =item from_mixed
517              
518             $hash = Hash::MultiValue->from_mixed({
519             foo => [ 'a', 'b' ],
520             bar => 'c',
521             });
522              
523             Creates a new object out of a hash reference where the value is single
524             or an array ref depending on the number of elements. Handy to convert
525             from those request objects used in web frameworks such as L<Catalyst>.
526             This method does exactly the opposite of C<as_hashref_mixed>.
527              
528             =back
529              
530             =head1 WHY LAST NOT FIRST?
531              
532             You might wonder why this module uses the I<last> value of the same
533             key instead of I<first>. There's no strong reasoning on this decision
534             since one is as arbitrary as the other, but this is more consistent to
535             what Perl does:
536              
537             sub x {
538             return ('a', 'b', 'c');
539             }
540              
541             my $x = x(); # $x = 'c'
542              
543             my %a = ( a => 1 );
544             my %b = ( a => 2 );
545              
546             my %m = (%a, %b); # $m{a} = 2
547              
548             When perl gets a list in a scalar context it gets the last entry. Also
549             if you merge hashes having a same key, the last one wins.
550              
551             =head1 NOTES ON ref
552              
553             If you pass this MultiValue hash object to some upstream functions
554             that you can't control and does things like:
555              
556             if (ref $args eq 'HASH') {
557             ...
558             }
559              
560             because this is a blessed hash reference it doesn't match and would
561             fail. To avoid that you should call C<as_hashref> to get a
562             I<finalized> (= non-blessed) hash reference.
563              
564             You can also use UNIVERSAL::ref to make it work magically:
565              
566             use UNIVERSAL::ref; # before loading Hash::MultiValue
567             use Hash::MultiValue;
568              
569             and then all C<ref> calls to Hash::MultiValue objects will return I<HASH>.
570              
571             =head1 THREAD SAFETY
572              
573             Prior to version 0.09, this module wasn't safe in a threaded
574             environment, including win32 fork() emulation. Versions newer than
575             0.09 is considered thread safe.
576              
577             =head1 AUTHOR
578              
579             Tatsuhiko Miyagawa E<lt>miyagawa@bulknews.netE<gt>
580              
581             Aristotle Pagaltzis
582              
583             Hans Dieter Pearcey
584              
585             Thanks to Michael Peters for the suggestion to use inside-out objects
586             instead of tie.
587              
588             =head1 LICENSE
589              
590             This library is free software; you can redistribute it and/or modify
591             it under the same terms as Perl itself.
592              
593             =head1 SEE ALSO
594              
595             =over 4
596              
597             =item * L<http://pythonpaste.org/webob/#multidict>
598              
599             =item * L<Tie::Hash::MultiValue>
600              
601             =back
602              
603             =cut