File Coverage

blib/lib/Router/Resource.pm
Criterion Covered Total %
statement 53 53 100.0
branch 11 12 91.6
condition 5 8 62.5
subroutine 20 20 100.0
pod 8 14 57.1
total 97 107 90.6


line stmt bran cond sub pod time code
1             package Router::Resource;
2              
3 1     1   477 use strict;
  1         1  
  1         24  
4 1     1   12 use 5.8.1;
  1         3  
5 1     1   552 use Router::Simple::Route;
  1         2400  
  1         43  
6 1         18 use Sub::Exporter -setup => {
7             exports => [ qw(router resource missing GET POST PUT DELETE HEAD OPTIONS TRACE CONNECT PATCH)],
8             groups => { default => [ qw(resource router missing GET POST PUT DELETE HEAD OPTIONS TRACE CONNECT PATCH) ] }
9 1     1   655 };
  1         11155  
10              
11             our $VERSION = '0.21';
12              
13             sub new {
14 3     3 0 4 my $class = shift;
15 3         12 bless { @_, routes => [] };
16             }
17              
18             our (%METHS, $ROUTER);
19              
20             sub router(&;@) {
21 3     3 0 3158 my ($block, @settings) = @_;
22 3         12 local $ROUTER = __PACKAGE__->new(@settings);
23 3         8 $block->();
24 3         75 return $ROUTER;
25             }
26              
27             sub resource ($&) {
28 5     5 0 143 my ($path, $code) = @_;
29 5         9 local %METHS = ();
30 5         10 $code->();
31              
32             # Let HEAD use GET if not specified.
33 5   66     23 $METHS{HEAD} ||= $METHS{GET};
34              
35             # Add OPTIONS if requested.
36 5 100 66     21 if ($ROUTER->{auto_options} && !$METHS{OPTIONS}) {
37 1         4 my $methods = join(', ' => 'OPTIONS', keys %METHS);
38 1     1   5 $METHS{OPTIONS} = sub { [200, ['Allow', $methods], []] };
  1         7  
39             }
40              
41             # Add the route.
42 5         7 push @{ $ROUTER->{routes} }, Router::Simple::Route->new(
  5         37  
43             $path, { meths => { %METHS } }
44             );
45             }
46              
47 1     1 0 65 sub missing(&) { $ROUTER->{missing} = shift }
48 6     6 1 32 sub GET(&) { $METHS{GET} = shift }
49 1     1 1 6 sub HEAD(&) { $METHS{HEAD} = shift }
50 2     2 1 18 sub POST(&) { $METHS{POST} = shift }
51 2     2 1 10 sub PUT(&) { $METHS{PUT} = shift }
52 1     1   8 sub DELETE(&) { $METHS{DELETE} = shift }
53 1     1 1 6 sub OPTIONS(&) { $METHS{OPTIONS} = shift }
54 1     1 1 7 sub TRACE(&) { $METHS{TRACE} = shift }
55 1     1 1 9 sub CONNECT(&) { $METHS{CONNECT} = shift }
56 1     1 1 6 sub PATCH(&) { $METHS{PATCH} = shift }
57              
58             sub dispatch {
59 20     20 0 11765 my ($self, $env) = @_;
60 20         33 my $match = $self->match($env);
61 20 100       42 if (my $meth = $match->{meth}) {
62 15         39 return $meth->($env, $match->{data});
63             }
64             my $missing = $self->{missing} or return [
65 5 100       27 $match->{code}, $match->{headers}, [$match->{message}]
66             ];
67 2         5 return $missing->($env, $match);
68             }
69              
70             sub match {
71 23     23 0 1320 my ($self, $env) = @_;
72 23 50 50     67 my $meth = uc($env->{REQUEST_METHOD} || '') or return;
73              
74 23         18 for my $route (@{ $self->{routes} }) {
  23         46  
75 48 100       243 my $match = $route->match($env) or next;
76 20         446 my $meths = delete $match->{meths};
77             my $code = $meths->{$meth} or return {
78             code => 405,
79             message => 'not allowed',
80 20 100       44 headers => [Allow => join ', ', sort keys %{ $meths } ],
  4         35  
81             };
82 16         44 return { meth => $code, code => 200, data => $match };
83             }
84 3         31 return { code => 404, message => 'not found', headers => [] };
85             }
86              
87             1;
88             __END__