File Coverage

blib/lib/Tie/Hash/Regex.pm
Criterion Covered Total %
statement 36 36 100.0
branch 16 16 100.0
condition 13 13 100.0
subroutine 7 7 100.0
pod n/a
total 72 72 100.0


line stmt bran cond sub pod time code
1              
2             =head1 NAME
3              
4             Tie::Hash::Regex - Match hash keys using Regular Expressions
5              
6             =head1 SYNOPSIS
7              
8             use Tie::Hash::Regex;
9             my %h;
10              
11             tie %h, 'Tie::Hash::Regex';
12              
13             $h{key} = 'value';
14             $h{key2} = 'another value';
15             $h{stuff} = 'something else';
16              
17             print $h{key}; # prints 'value'
18             print $h{2}; # prints 'another value'
19             print $h{'^s'}; # prints 'something else'
20              
21             print tied(%h)->FETCH('k'); # prints 'value' and 'another value'
22              
23             delete $h{k}; # deletes $h{key} and $h{key2};
24              
25             or (new! improved!)
26              
27             my $h : Regex;
28              
29             =head1 DESCRIPTION
30              
31             Someone asked on Perlmonks if a hash could do fuzzy matches on keys - this
32             is the result.
33              
34             If there's no exact match on the key that you pass to the hash, then the
35             key is treated as a regex and the first matching key is returned. You can
36             force it to leap straight into the regex checking by passing a qr'ed
37             regex into the hash like this:
38              
39             my $val = $h{qr/key/};
40              
41             C and C also do regex matching. In the case of C
42             I values matching your regex key will be deleted from the hash.
43              
44             One slightly strange thing. Obviously if you give a hash a regex key, then
45             it's possible that more than one key will match (consider C<$h{qw/./}>).
46             It might be nice to be able to do stuff like:
47              
48             my @vals = $h{$pat};
49              
50             to get I matching values back. Unfortuately, Perl knows that a given
51             hash key can only ever return one value and so forces scalar context on
52             the C call when using the tied interface. You can get round this
53             using the slightly less readable:
54              
55             my @vals = tied(%h)->FETCH($pat);
56              
57             =head2 ATTRIBUTE INTERFACE
58              
59             From version 0.06, you can use attributes to define your hash as being tied
60             to Tie::Hash::Regex. You'll need to install the module Attribute::Handlers.
61              
62             =cut
63              
64             package Tie::Hash::Regex;
65              
66 1     1   73091 use 5.006;
  1         15  
67 1     1   5 use strict;
  1         2  
  1         17  
68 1     1   4 use warnings;
  1         2  
  1         79  
69             our ($VERSION, @ISA, @EXPORT, @EXPORT_OK);
70              
71             require Exporter;
72             require Tie::Hash;
73 1     1   543 use Attribute::Handlers autotie => { "__CALLER__::Regex" => __PACKAGE__ };
  1         4478  
  1         5  
74              
75             @ISA = qw(Exporter Tie::StdHash);
76             @EXPORT = qw();
77             @EXPORT_OK =();
78              
79             $VERSION = 1.12;
80              
81             =head1 METHODS
82              
83             =head2 FETCH
84              
85             Get a value from the hash. If there isn't an exact match try a regex
86             match.
87              
88             =cut
89              
90             sub FETCH {
91 6     6   4466 my $self = shift;
92 6         9 my $key = shift;
93              
94 6         12 my $is_re = (ref $key eq 'Regexp');
95              
96 6 100 100     28 return $self->{$key} if !$is_re && exists $self->{$key};
97              
98 5 100       56 $key = qr/$key/ unless $is_re;
99              
100             # NOTE: wantarray will _never_ be true when FETCH is called
101             # using the standard hash semantics. I've put that piece
102             # of code in for people who are happy using syntax like:
103             # tied(%h)->FETCH(qr/$pat/);
104 5 100       14 if (wantarray) {
105 1         9 return @{$self}{ grep /$key/, keys %$self };
  1         5  
106             } else {
107 4   100     43 /$key/ and return $self->{$_} for keys %$self;
108             }
109              
110 1         10 return;
111             }
112              
113             =head2 EXISTS
114              
115             See if a key exists in the hash. If there isn't an exact match try a regex
116             match.
117              
118             =cut
119              
120             sub EXISTS {
121 4     4   655 my $self = shift;
122 4         12 my $key = shift;
123              
124 4         8 my $is_re = (ref $key eq 'Regexp');
125              
126 4 100 100     21 return 1 if !$is_re && exists $self->{$key};
127              
128 3 100       27 $key = qr/$key/ unless $is_re;
129              
130 3   100     34 /$key/ && return 1 for keys %$self;
131              
132 1         5 return;
133             }
134              
135             =head2 DELETE
136              
137             Delete a key from the hash. If there isn't an exact match try a regex
138             match.
139              
140             =cut
141              
142             sub DELETE {
143 3     3   1265 my $self = shift;
144 3         6 my $key = shift;
145              
146 3         18 my $is_re = (ref $key eq 'Regexp');
147              
148 3 100 100     20 return delete $self->{$key} if !$is_re && exists $self->{$key};
149              
150 2 100       14 $key = qr/$key/ unless $is_re;
151              
152 2         8 for (keys %$self) {
153 3 100       26 if (/$key/) {
154 2         13 delete $self->{$_};
155             }
156             }
157             }
158              
159             1;
160             __END__