File Coverage

blib/lib/Web/Dispatcher/Simple.pm
Criterion Covered Total %
statement 73 112 65.1
branch 4 12 33.3
condition 0 5 0.0
subroutine 23 38 60.5
pod 0 14 0.0
total 100 181 55.2


line stmt bran cond sub pod time code
1             package Web::Dispatcher::Simple;
2 3     3   1195 use strict;
  3         3  
  3         103  
3 3     3   13 use warnings;
  3         3  
  3         116  
4             our $VERSION = '0.11';
5              
6 3     3   12 use Carp ();
  3         5  
  3         48  
7 3     3   2358 use Router::Simple;
  3         17386  
  3         87  
8 3     3   2504 use Try::Tiny;
  3         4332  
  3         168  
9 3     3   1592 use Web::Dispatcher::Simple::Request;
  3         10  
  3         98  
10 3     3   18 use Web::Dispatcher::Simple::Response;
  3         6  
  3         64  
11 3     3   16 use Scalar::Util qw(blessed);
  3         5  
  3         262  
12              
13             my $_ROUTER = Router::Simple->new;
14              
15             sub import {
16 3     3   33 my $caller = caller;
17              
18 3     3   18 no strict 'refs';
  3         5  
  3         93  
19 3     3   15 no warnings 'redefine';
  3         6  
  3         878  
20              
21 3         8 *{"${caller}::router"} = \&router;
  3         20  
22              
23 3         13 my @http_methods = qw/get post put del any/;
24 3         8 for my $http_method (@http_methods) {
25 15     7   45 *{"${caller}\::$http_method"} = sub { goto \&$http_method };
  15         78  
  7         585  
26             }
27              
28 3         102 strict->import;
29 3         14384 warnings->import;
30             }
31              
32             sub _stub {
33 15     15   22 my $name = shift;
34 15     0   81 return sub { Carp::croak("Can't call $name() outside router block") };
  0         0  
35             }
36              
37             {
38             my @declarations = qw(get post put del any);
39             for my $keyword (@declarations) {
40 3     3   18 no strict 'refs';
  3         5  
  3         224  
41             *$keyword = _stub $keyword;
42             }
43             }
44              
45             sub router (&) { ## no critic
46 2     2 0 32 my $block = shift;
47              
48 2 50       13 if ($block) {
49 3     3   15 no warnings 'redefine';
  3         4  
  3         4672  
50 2     3   20 local *get = sub { do_get(@_) };
  3         17  
51 2     1   10 local *post = sub { do_post(@_) };
  1         5  
52 2     0   11 local *put = sub { do_put(@_) };
  0         0  
53 2     0   10 local *del = sub { do_del(@_) };
  0         0  
54 2     3   11 local *any = sub { do_any(@_) };
  3         10  
55 2         9 $block->();
56              
57 0     0   0 return sub { dispatch(shift) }
58 2         254 }
59             }
60              
61             # HTTP Methods
62             sub route {
63 7     7 0 15 my ( $pattern, $code, $methods ) = @_;
64 7 50       25 unless ( ref $code eq 'CODE' ) {
65 0         0 Carp::croak("The logic for $pattern must be CodeRef");
66             }
67              
68             $_ROUTER->connect(
69 15         78 $pattern,
70             { action => $code },
71 7         25 { method => [ map { uc $_ } @$methods ] }
72             );
73             }
74              
75             sub do_any {
76 3 100   3 0 9 if ( scalar @_ == 4 ) {
77 2         5 my ( $methods, $pattern, $code ) = @_;
78 2         5 route( $pattern, $code, $methods );
79             }
80             else {
81 1         3 my ( $pattern, $code ) = @_;
82 1         5 route( $pattern, $code, [ 'GET', 'POST', 'DELETE', 'PUT', 'HEAD' ] );
83             }
84             }
85              
86             sub do_get {
87 3     3 0 10 my ( $pattern, $code ) = @_;
88 3         16 route( $pattern, $code, [ 'GET', 'HEAD' ] );
89             }
90              
91             sub do_post {
92 1     1 0 3 my ( $pattern, $code ) = @_;
93 1         5 route( $pattern, $code, ['POST'] );
94             }
95              
96             sub do_put {
97 0     0 0   my ( $pattern, $code ) = @_;
98 0           route( $pattern, $code, ['PUT'] );
99             }
100              
101             sub do_del {
102 0     0 0   my ( $pattern, $code ) = @_;
103 0           route( $pattern, $code, ['DELETE'] );
104             }
105              
106             # dispatch
107             sub dispatch {
108 0     0 0   my $env = shift;
109 0 0         if ( my $match = $_ROUTER->match($env) ) {
110 0           my $req = Web::Dispatcher::Simple::Request->new($env);
111              
112             # enable configuring
113 0           $req->decode_params();
114 0           return handle_request( $req, $match );
115             }
116             else {
117 0           return handle_not_found();
118             }
119             }
120              
121             sub handle_request {
122 0     0 0   my ( $req, $match ) = @_;
123 0           my $code = delete $match->{action};
124             my $res = try {
125 0     0     $code->( $req, $match );
126             }
127             catch {
128 0     0     handle_exception($_);
129 0           };
130 0           return psgify_response($res);
131             }
132              
133             sub psgify_response {
134 0     0 0   my $res = shift;
135 0           my $psgi_res;
136 0   0       my $res_type = ref($res) || '';
137 0 0 0       if ( blessed $res && $res->isa('Plack::Response') ) {
    0          
138 0           $res->encode_body;
139 0           $psgi_res = $res->finalize;
140             }
141             elsif ( $res_type eq 'ARRAY' ) {
142 0           my $response = Web::Dispatcher::Simple::Response->new(@$res);
143 0           $response->encode_body;
144 0           $psgi_res = $response->finalize;
145             }
146             else {
147 0           Carp::croak("unknown response type: $res_type. The response is $res");
148             }
149 0           $psgi_res;
150             }
151              
152             sub handle_exception {
153 0     0 0   my $e = shift;
154 0           warn "An internal error occured during processing request: $e";
155 0           return internal_server_error($e);
156             }
157              
158             sub handle_not_found {
159 0     0 0   return not_found();
160             }
161              
162             sub not_found {
163 0     0 0   [ 404, [], [ 'Not Found' ] ];
164             }
165              
166             sub internal_server_error {
167 0     0 0   my $e = shift;
168 0           [ 500, [], [ 'Internal Server Error'] ];
169             }
170              
171             1;
172              
173             __END__