File Coverage

blib/lib/Eve/HttpDispatcher.pm
Criterion Covered Total %
statement 15 76 19.7
branch 0 20 0.0
condition n/a
subroutine 5 9 55.5
pod 4 4 100.0
total 24 109 22.0


line stmt bran cond sub pod time code
1             package Eve::HttpDispatcher;
2              
3 8     8   50 use parent qw(Eve::Class);
  8         18  
  8         82  
4              
5 8     8   537 use strict;
  8         23  
  8         535  
6 8     8   46 use warnings;
  8         14  
  8         231  
7              
8 8     8   4128 use Eve::Event::HttpResponseReady;
  8         19  
  8         205  
9 8     8   43 use Eve::Exception;
  8         14  
  8         8557  
10              
11             =head1 NAME
12              
13             B - an event handler for HTTP request events.
14              
15             =head1 SYNOPSIS
16              
17             use Eve::HttpDispatcher;
18              
19             my $dispatcher = Eve::HttpDispatcher->new(
20             request_constructor => $request_constructor,
21             response => $response,
22             base_uri => $base_uri,
23             alias_base_uri_list => [$alias_base_uri, $another_alias_base_uri],
24             event_map => $event_map);
25              
26             $dispatcher->bind(
27             name => $name
28             pattern => $pattern,
29             resource_constructor => $resource_constructor);
30              
31             $dispatcher->bind(
32             name => $name_404
33             pattern => $pattern_404,
34             resource_constructor => $resource_constructor_404,
35             exception => 'Eve::Exception::Http::404NotFound');
36              
37             $dispatcher->handle(event => $event);
38              
39             =head1 DESCRIPTION
40              
41             B class is a central component to a web
42             service application.
43              
44             =head3 Constructor arguments
45              
46             =over 4
47              
48             =item C
49              
50             a code reference that returns an HTTP request object when passed an
51             environment hash
52              
53             =item C
54              
55             an HTTP response object
56              
57             =item C
58              
59             an event map object.
60              
61             =item C
62              
63             a base URI object used for resource binding.
64              
65             =item C
66              
67             (optional) a reference to a list of additional base URI objects that
68             will be used for resource matching.
69              
70             =back
71              
72             =head1 METHODS
73              
74             =head2 B
75              
76             =cut
77              
78             sub init {
79 0     0 1   my ($self, %arg_hash) = @_;
80 0           Eve::Support::arguments(
81             \%arg_hash,
82             my ($request_constructor, $response, $event_map, $base_uri),
83             my $alias_base_uri_list = []);
84              
85 0           $self->{'_request_constructor'} = $request_constructor;
86 0           $self->{'_response'} = $response;
87 0           $self->{'_event_map'} = $event_map;
88 0           $self->{'_base_uri'} = $base_uri;
89              
90 0           $self->{'_base_uri_list'} = [$base_uri, @{$alias_base_uri_list}];
  0            
91              
92 0           $self->{'_uri_map'} = {};
93 0           $self->{'_name_map'} = {};
94 0           $self->{'_exception_name_hash'} = {};
95              
96 0           return;
97             }
98              
99             =head2 B
100              
101             Binds an HTTP resource.
102              
103             =head3 Arguments
104              
105             =over 4
106              
107             =item C
108              
109             a name identifying the binding
110              
111             =item C
112              
113             an URI pattern string that can contain placeholders
114              
115             =item C
116              
117             a URI object that represents a base URL for the binding resource
118              
119             =item C
120              
121             a code reference that returns an HTTP resource object
122              
123             =item C
124              
125             (optional) an HTTP exception class name that the bound resource should
126             be used to handle. B: there can be only one resource bound to
127             handle a certain exception.
128              
129             =back
130              
131             =head3 Throws
132              
133             =over 3
134              
135             =item C
136              
137             if either name or compound URI or exception class name is not unique.
138              
139             =back
140              
141             =cut
142              
143             sub bind {
144 0     0 1   my ($self, %arg_hash) = @_;
145 0           Eve::Support::arguments(
146             \%arg_hash, my ($name, $pattern, $resource_constructor),
147             my $exception = \undef);
148              
149 0 0         if (exists $self->_uri_map->{$pattern}) {
150 0           Eve::Error::HttpDispatcher->throw(
151             message => 'Binding URI must be unique: '.$pattern);
152             }
153              
154 0 0         if (exists $self->_name_map->{$name}) {
155 0           Eve::Error::HttpDispatcher->throw(
156             message => 'Binding name must be unique: '.$name);
157             }
158              
159             # Constructing all possible URIs that this resource is supposed to match
160             # using the base URI as well as possible aliases
161 0           my $uri_list = [];
162 0           for my $uri (@{$self->_base_uri_list}) {
  0            
163 0           my $map_uri = $uri->clone();
164 0           $map_uri->path_concat(string => $pattern);
165              
166 0           push(@{$uri_list}, $map_uri);
  0            
167             }
168              
169 0           $self->_uri_map->{$pattern} = {
170             'resource' => $resource_constructor->(),
171             'uri_list' => $uri_list};
172              
173 0           $self->_name_map->{$name} = {
174             'pattern' => $pattern, 'uri_list' => $uri_list};
175              
176 0 0         if (defined $exception) {
177 0 0         if (exists $self->_exception_name_hash->{$exception}) {
178 0           Eve::Error::HttpDispatcher->throw(
179             message => 'Exception name must be unique: ' . $exception);
180             }
181 0           $self->_exception_name_hash->{$exception} = $pattern;
182             }
183              
184 0           return;
185             }
186              
187             =head2 B
188              
189             =head3 Arguments
190              
191             =over 4
192              
193             =item C
194              
195             a name identifying the binding
196              
197             =back
198              
199             =head3 Returns
200              
201             A URI bound to the resource name.
202              
203             =head3 Throws
204              
205             =over 3
206              
207             =item C
208              
209             When there is no resource with the requested name.
210              
211             =back
212              
213             =cut
214              
215             sub get_uri {
216 0     0 1   my ($self, %arg_hash) = @_;
217 0           Eve::Support::arguments(\%arg_hash, my $name);
218              
219 0 0         if (not exists $self->_name_map->{$name}) {
220 0           Eve::Error::HttpDispatcher->throw(
221             message => 'There is no resource with such name: '.$name);
222             }
223              
224             # We are relying on the fact that the page URI generated by the
225             # base URI is always the first in the uri list for the specified
226             # name.
227 0           return $self->_name_map->{$name}->{'uri_list'}->[0];
228             }
229              
230             =head2 B
231              
232             Chooses a resource using the request URI and delegates control to the
233             resource's C method. It also passes placeholder matches
234             into this method.
235              
236             =head3 Arguments
237              
238             =over 4
239              
240             =item C
241              
242             a C object.
243              
244             =back
245              
246             =head3 Throws
247              
248             =over 3
249              
250             =item C
251              
252             if no resources match the request and no resources that handle the
253             C are bound.
254              
255             =back
256              
257             =cut
258              
259             sub handle {
260 0     0 1   my ($self, %arg_hash) = @_;
261 0           Eve::Support::arguments(\%arg_hash, my $event);
262              
263 0           $self->{'_request'} =
264             $self->_request_constructor->(env_hash => $event->env_hash);
265              
266 0           my $response;
267              
268 0           eval {
269 0           for my $key (keys %{$self->_uri_map}) {
  0            
270 0           my $request_uri = $self->_request->get_uri();
271              
272 0           for my $uri (@{$self->_uri_map->{$key}->{'uri_list'}}) {
  0            
273 0           my $match_hash = $uri->match(uri => $request_uri);
274 0 0         if ($match_hash) {
275 0           $response = $self->_uri_map->{$key}->{'resource'}->process(
276             matches_hash => $match_hash,
277             request => $self->_request);
278 0           return;
279             }
280             }
281             }
282              
283 0           Eve::Exception::Http::404NotFound->throw();
284             };
285              
286 0           my $e;
287 0 0         if ($e = Eve::Exception::Base->caught()) {
    0          
288 0 0         if (defined $self->_exception_name_hash->{ref $e}){
289             my $resource =
290             $self->_uri_map->{$self->_exception_name_hash->{ref $e}}->{
291 0           'resource'};
292              
293 0           $response = $resource->process(
294             matches_hash => {'exception' => $e},
295             request => $self->_request);
296             } else {
297 0           $e->throw();
298             }
299             } elsif ($e = Exception::Class->caught()) {
300 0 0         ref $e ? $e->rethrow() : die $e;
301             }
302              
303 0           $event->response = $response;
304              
305 0           return;
306             }
307              
308             =head1 SEE ALSO
309              
310             =over 4
311              
312             =item L
313              
314             =item L
315              
316             =item L
317              
318             =item L
319              
320             =back
321              
322             =head1 LICENSE AND COPYRIGHT
323              
324             Copyright 2012 Igor Zinovyev.
325              
326             This program is free software; you can redistribute it and/or modify it
327             under the terms of either: the GNU General Public License as published
328             by the Free Software Foundation; or the Artistic License.
329              
330             See http://dev.perl.org/licenses/ for more information.
331              
332              
333             =head1 AUTHOR
334              
335             =over 4
336              
337             =item L
338              
339             =item L
340              
341             =back
342              
343             =cut
344              
345             1;