File Coverage

blib/lib/Message/Router.pm
Criterion Covered Total %
statement 104 104 100.0
branch 59 76 77.6
condition 16 32 50.0
subroutine 10 10 100.0
pod 2 2 100.0
total 191 224 85.2


line stmt bran cond sub pod time code
1             package Message::Router;
2             $Message::Router::VERSION = '1.143160';
3 4     4   2817 use strict;use warnings;
  4     4   6  
  4         141  
  4         14  
  4         5  
  4         112  
4 4     4   2628 use Storable;
  4         11687  
  4         246  
5 4     4   1897 use Message::Match qw(mmatch);
  4         2031  
  4         285  
6 4     4   1745 use Message::Transform qw(mtransform);
  4         1598  
  4         258  
7             require Exporter;
8 4     4   20 use vars qw(@ISA @EXPORT_OK $config);
  4         7  
  4         2036  
9             @ISA = qw(Exporter);
10             @EXPORT_OK = qw(mroute mroute_config);
11              
12             sub mroute_config {
13 17     17 1 8129 my $new_config;
14 17         18 eval {
15 17 100       55 $new_config = shift
16             or die 'single argument must be a HASH reference';
17 16 100       37 die 'single argument must be a HASH reference'
18             if shift;
19 15 100 33     67 die 'single argument must be a HASH reference'
20             if not $new_config or not ref $new_config eq 'HASH';
21 14 100       34 die "passed config must have an ARRAY or HASH 'routes' key"
22             if not $new_config->{routes};
23 13 100 100     44 if( ref $new_config->{routes} ne 'ARRAY' and
24             ref $new_config->{routes} ne 'HASH') {
25 1         5 die "passed config must have an ARRAY or HASH 'routes' key"
26             }
27 12 100       24 if(ref $new_config->{routes} eq 'ARRAY') {
28 10         8 foreach my $route (@{$new_config->{routes}}) {
  10         19  
29 10 100       19 die "each route must be a HASH reference"
30             if not $route;
31 9 100       443 die "each route must be a HASH reference"
32             if not ref $route eq 'HASH';
33 8 100       22 die "each route has to have a HASH reference 'match' key"
34             if not $route->{match};
35 7 50       15 die "each route has to have a HASH reference 'match' key"
36             if not ref $route->{match} eq 'HASH';
37 7 100       11 if($route->{transform}) {
38 2 100       12 die "the optional 'transform' key must be a HASH reference"
39             if ref $route->{transform} ne 'HASH';
40             }
41 6 50       10 if($route->{forwards}) {
42 6 100       16 die "the optional 'forwards' key must be an ARRAY reference"
43             if ref $route->{forwards} ne 'ARRAY';
44 5         4 foreach my $forward (@{$route->{forwards}}) {
  5         7  
45 5 50       8 die 'each forward must be a HASH reference'
46             if not $forward;
47 5 100       24 die 'each forward must be a HASH reference'
48             if ref $forward ne 'HASH';
49 4 100       12 die "each forward must have a scalar 'handler' key"
50             if not $forward->{handler};
51 3 100       14 die "each forward must have a scalar 'handler' key"
52             if ref $forward->{handler};
53             }
54             }
55             }
56             }
57             };
58 17 100       37 if($@) {
59 13         37 die "Message::Router::mroute_config: $@\n";
60             }
61 4         5 $config = $new_config;
62 4         15 return $config;
63             }
64              
65             sub mroute {
66 6     6 1 784 eval {
67 6 100       23 my $message = shift or die 'single argument must be a HASH reference';
68 5 100 66     32 die 'single argument must be a HASH reference'
69             unless ref $message and ref $message eq 'HASH';
70 4 50       9 die 'single argument must be a HASH reference'
71             if shift;
72 4 50 66     19 if( $message->{static_forwards} and
  1   66     3  
      50        
73             ref $message->{static_forwards} and
74             ref $message->{static_forwards} eq 'ARRAY' and
75             scalar @{$message->{static_forwards}}) {
76 1         1 my $forward_recs = shift @{$message->{static_forwards}};
  1         1  
77 1 50       2 delete $message->{static_forwards} unless scalar @{$message->{static_forwards}};
  1         3  
78 1 50 33     7 die 'static_forwards: defined forward must be an ARRAY reference'
79             if not ref $forward_recs or ref $forward_recs ne 'ARRAY';
80 1         1 foreach my $forward_rec (@{$forward_recs}) {
  1         2  
81 1 50 33     6 die 'static_forwards: defined forward must contain a forward that is a HASH reference'
      33        
82             if not $forward_rec->{forward} or
83             not ref $forward_rec->{forward} or
84             ref $forward_rec->{forward} ne 'HASH';
85 1         79 my $message = Storable::dclone $message;
86 1 50       5 if($forward_rec->{log_history}) {
87 1         36 $forward_rec = Storable::dclone $forward_rec;
88 1 50       23 $message->{'.static_forwards_log'} = {
89             forward_history => []
90             } unless $message->{'.static_forwards_log'};
91 1         2 push @{$message->{'.static_forwards_log'}->{forward_history}}, $forward_rec;
  1         3  
92             }
93 1 50 33     11 if( $forward_rec->{transform} and
      33        
94             ref $forward_rec->{transform} and
95             ref $forward_rec->{transform} eq 'HASH') {
96 1         5 mtransform($message, $forward_rec->{transform});
97             }
98 1         23 eval {
99 4     4   19 no strict 'refs';
  4         6  
  4         732  
100 1 50       4 $forward_rec->{forward}->{handler} = 'IPC::Transit::Router::handler'
101             unless $forward_rec->{forward}->{handler};
102 1         1 &{$forward_rec->{forward}->{handler}}(
  1         6  
103             message => $message,
104             forward => $forward_rec->{forward}
105             );
106             };
107 1 50       10 die "static_forwards: handler failed: $@" if $@;
108             }
109 1         2 return 1;
110             }
111 3         3 my @routes;
112 3 100       9 if(ref $config->{routes} eq 'ARRAY') {
    50          
113 2         3 @routes = @{$config->{routes}};
  2         5  
114             } elsif(ref $config->{routes} eq 'HASH') {
115 1         1 foreach my $order (sort { $a <=> $b } keys %{$config->{routes}}) {
  1         5  
  1         6  
116 2         5 push @routes, $config->{routes}->{$order};
117             }
118             }
119 3         4 foreach my $route (@routes) {
120 4         5 eval {
121 4 50       13 if(mmatch($message, $route->{match})) {
122 3 50       78 if($route->{transform}) {
123 3         14 mtransform($message, $route->{transform});
124             }
125 3 50       45 if($route->{forwards}) {
126 3         13 foreach my $forward (@{$route->{forwards}}) {
  3         4  
127 4     4   19 no strict 'refs';
  4         7  
  4         636  
128 3         3 &{$forward->{handler}}(
  3         12  
129             message => $message,
130             route => $route,
131             routes => $config->{routes},
132             forward => $forward
133             );
134             }
135             }
136             }
137             };
138 4 100       65 if($@) {
139 1         4 die "Message::Router::mroute: $@\n";
140             }
141             }
142             };
143 6 100       14 if($@) {
144 3         9 die "Message::Router::mmatch: $@\n";
145             }
146 3         8 return 1;
147             }
148             1;
149              
150             __END__
151              
152             =head1 NAME
153              
154             Message::Router - Fast, simple message routing
155              
156             =head1 SYNOPSIS
157              
158             use Message::Router qw(mroute mroute_config);
159              
160             sub main::handler1 {
161             my %args = @_;
162             #gets:
163             # $args{message}
164             # $args{route}
165             # $args{routes}
166             # $args{forward}
167             print "$args{message}->{this}\n"; #from the transform
168             print "$args{forward}->{x}\n"; #from the specific forward
169             }
170              
171             mroute_config({
172             routes => [
173             { match => {
174             a => 'b',
175             },
176             forwards => [
177             { handler => 'main::handler1',
178             x => 'y',
179             },
180             ],
181             transform => {
182             this => 'that',
183             },
184             }
185             ],
186             });
187             mroute({a => 'b'}); #prints 'that', and then 'y', per the handler1 sub
188              
189             mroute_config({
190             routes => {
191             10 => {
192             match => {
193             a => 'b',
194             },
195             forwards => [
196             { handler => 'main::handler1',
197             x => 'y',
198             },
199             ],
200             transform => {
201             this => 'that',
202             },
203             }
204             ],
205             });
206             mroute({a => 'b'}); #prints 'that', and then 'y', per the handler1 sub
207             #same as the ARRAY based, but it uses the HASH keys in numerical order
208              
209             =head1 DESCRIPTION
210              
211             This library allows fast, flexible and general message routing.
212              
213             =head1 FUNCTIONS
214              
215             =head2 mroute_config($config);
216              
217             The config used by all mroute calls
218              
219             =head2 mroute($message);
220              
221             Pass $message through the config; this will emit zero or more callbacks.
222              
223             =head1 TODO
224              
225             A config validator.
226              
227             Short-circuiting
228              
229             More flexible match and transform configuration forms
230              
231             =head1 BUGS
232              
233             None known.
234              
235             =head1 COPYRIGHT
236              
237             Copyright (c) 2012, 2013 Dana M. Diederich. All Rights Reserved.
238              
239             =head1 AUTHOR
240              
241             Dana M. Diederich <dana@realms.org>
242              
243             =cut
244