File Coverage

blib/lib/Hash/Dispatch.pm
Criterion Covered Total %
statement 53 55 96.3
branch 17 20 85.0
condition 3 6 50.0
subroutine 13 13 100.0
pod 1 1 100.0
total 87 95 91.5


line stmt bran cond sub pod time code
1             package Hash::Dispatch;
2             BEGIN {
3 1     1   90875 $Hash::Dispatch::VERSION = '0.0010';
4             }
5             # ABSTRACT: Find CODE in a hash (hashlike)
6              
7 1     1   10 use strict;
  1         3  
  1         32  
8 1     1   5 use warnings;
  1         2  
  1         31  
9              
10 1     1   814 use Any::Moose;
  1         37532  
  1         7  
11              
12 1     1   1575 use List::MoreUtils qw/ natatime /;
  1         1250  
  1         525  
13              
14             has map => qw/ is ro required 1 isa ArrayRef /;
15              
16             sub dispatch {
17 12     12 1 3157 my $self = shift;
18 12 100 66     118 if ( blessed $self && $self->isa( 'Hash::Dispatch' ) ) {
19 11         35 return $self->_dispatch_object( @_ );
20             }
21             else {
22 1         6 return $self->_dispatch_class( @_ );
23             }
24             }
25              
26             sub _dispatch_class {
27 1     1   2 my $self = shift;
28 1 50 33     7 return $self->new( map => [ %{ $_[0] } ] ) if 1 == @_ && ref $_[0] eq 'HASH';
  0         0  
29 1         29 return $self->new( map => [ @_ ] );
30             }
31              
32             sub _dispatch_object {
33 11     11   14 my $self = shift;
34 11         19 my $query = shift;
35              
36 11         15 my $original_query = $query;
37 11         12 my ( $value, $captured, %seen );
38 11         13 while ( 1 ) {
39 16         31 ( $value, $captured ) = $self->_lookup( $query );
40 16 100       45 return unless defined $value;
41 14 100       34 last if ref $value eq 'CODE';
42 7 100       20 if ( $seen{ $value } ) {
43 2         23 die "*** Dispatch loop detected on query ($original_query => $query)";
44             }
45 5         13 $seen{ $query } = 1;
46 5         7 $query = $value;
47             }
48              
49 7         18 return $self->_result( $value, $captured );
50             }
51              
52             sub _lookup {
53 16     16   23 my $self = shift;
54 16         17 my $query = shift;
55              
56 16         20 my $each = natatime 2, @{ $self->map };
  16         127  
57 16         75 while ( my ( $key, $value ) = $each->() ) {
58 94 100       150 if ( ref $key eq '' ) {
    50          
59 83 100       313 if ( $key eq $query ) {
60 12         69 return ( $value );
61             }
62             }
63             elsif ( ref $key eq 'Regexp' ) {
64 11 100       80 if ( my @captured = ( $query =~ $key ) ) {
65 3         18 return ( $value, \@captured );
66             }
67             }
68             else {
69 0         0 die "*** Invalid dispatch key ($key)";
70             }
71             }
72              
73 1         5 return;
74             }
75              
76             sub _result {
77 7     7   17 my $self = shift;
78            
79 7         86 return Hash::Dispatch::Result->new( value => $_[0], captured => $_[1] );
80             }
81              
82             package Hash::Dispatch::Result;
83             BEGIN {
84 1     1   14 $Hash::Dispatch::Result::VERSION = '0.0010';
85             }
86              
87 1     1   6 use Any::Moose;
  1         1  
  1         6  
88              
89             has value => qw/ is ro required 1 isa CodeRef /;
90             has captured => qw/ reader _captured /;
91              
92             sub captured {
93 3     3   1248 my $self = shift;
94 3 50       4 return @{ $self->_captured || [] };
  3         32  
95             }
96              
97             1;
98              
99              
100              
101             =pod
102              
103             =head1 NAME
104              
105             Hash::Dispatch - Find CODE in a hash (hashlike)
106              
107             =head1 VERSION
108              
109             version 0.0010
110              
111             =head1 SYNOPSIS
112              
113             $dispatch = Hash::Dispatch->dispatch(
114              
115             'xyzzy' => sub {
116             return 'xyzzy';
117             },
118              
119             qr/.../ => 'xyzzy',
120              
121             ...
122              
123             );
124              
125             $result = $dispatch->dispatch( 'xyzzy' );
126              
127             $result->value->( ... );
128              
129             =head1 DESCRIPTION
130              
131             Hash::Dispatch is a tool for creating a hash-like lookup for returning a CODE reference
132              
133             It is hash-like because a query against the dispatcher will only return once a CODE reference a found. If a key (a string or regular expression) maps to a string, then that will cause the lookup to begin again with the new value, recursing until a CODE reference is found or a deadend is reached:
134              
135             a => CODE0
136             b => CODE1
137             c => CODE2
138             d => a
139             qr/z/ => c
140              
141             query( a ) => CODE0
142             query( b ) => CODE1
143             query( d ) => CODE0
144             query( xyzzy ) => CODE2
145             query( j ) => undef
146              
147             Hash::Dispatch will throw an exception if it is cycling:
148              
149             a => b
150             b => a
151              
152             query( a ) => {{{ Exception! }}}
153              
154             =head1 USAGE
155              
156             =head2 $dispatcher = Hash::Dispatch->dispatch( ... )
157              
158             Returns a new C<$dispatcher> with the given mapping
159              
160             =head2 $result = $dispatcher->dispatch( )
161              
162             Search C<$dispatcher> with C<< >>
163              
164             Returns an object with a C<< ->value >> method that contains the CODE reference
165              
166             Returns undef is nothing is found
167              
168             =head1 AUTHOR
169              
170             Robert Krimen
171              
172             =head1 COPYRIGHT AND LICENSE
173              
174             This software is copyright (c) 2011 by Robert Krimen.
175              
176             This is free software; you can redistribute it and/or modify it under
177             the same terms as the Perl 5 programming language system itself.
178              
179             =cut
180              
181              
182             __END__