File Coverage

blib/lib/Dancer2/Plugin/REST.pm
Criterion Covered Total %
statement 44 44 100.0
branch 8 10 80.0
condition 9 14 64.2
subroutine 11 11 100.0
pod 2 3 66.6
total 74 82 90.2


line stmt bran cond sub pod time code
1             package Dancer2::Plugin::REST;
2             our $AUTHORITY = 'cpan:SUKRIA';
3             # ABSTRACT: A plugin for writing RESTful apps with Dancer2
4             $Dancer2::Plugin::REST::VERSION = '1.00';
5 6     6   2022686 use strict;
  6         9  
  6         141  
6 6     6   20 use warnings;
  6         7  
  6         109  
7              
8 6     6   17 use Carp;
  6         6  
  6         276  
9              
10 6     6   2670 use Dancer2::Plugin;
  6         67315  
  6         32  
11              
12             # [todo] - add XML support
13             my $content_types = {
14             json => 'application/json',
15             yml => 'text/x-yaml',
16             };
17              
18             has '+app' => (
19             handles => [qw/
20             add_hook
21             add_route
22             setting
23             response
24             request
25             send_error
26             set_response
27             /],
28             );
29              
30             sub prepare_serializer_for_format :PluginKeyword {
31 4     4 1 54359 my $self = shift;
32              
33 4         61 my $conf = $self->config;
34             my $serializers = (
35             ($conf && exists $conf->{serializers})
36             ? $conf->{serializers}
37 4 50 33     1611 : { 'json' => 'JSON',
38             'yml' => 'YAML',
39             'dump' => 'Dumper',
40             }
41             );
42              
43             $self->add_hook( Dancer2::Core::Hook->new(
44             name => 'before',
45             code => sub {
46 15     15   376163 my $format = $self->request->params->{'format'};
47 15 50 100     1665 $format ||= $self->request->captures->{'format'} if $self->request->captures;
48              
49             return delete $self->response->{serializer}
50 15 100       712 unless defined $format;
51              
52 9 100       42 my $serializer = $serializers->{$format}
53             or return $self->send_error("unsupported format requested: " . $format, 404);
54              
55 8         70 $self->setting(serializer => $serializer);
56              
57             $self->set_response( Dancer2::Core::Response->new(
58 8         72666 %{ $self->response },
  8         109  
59             serializer => $self->setting('serializer'),
60             ) );
61              
62             $self->response->content_type(
63 8   66     5890 $content_types->{$format} || $self->setting('content_type')
64             );
65             }
66 4         90 ) );
67 6     6   13306 };
  6         7  
  6         21  
68              
69             sub resource :PluginKeyword {
70 3     3 1 39299 my ($self, $resource, %triggers) = @_;
71              
72             my %actions = (
73             update => 'put',
74             create => 'post',
75 3         6 map { $_ => $_ } qw/ get delete /
  6         16  
76             );
77              
78             croak "resource should be given with triggers"
79             unless defined $resource
80 3 100 66     15 and grep { $triggers{$_} } keys %actions;
  12         201  
81              
82 2         12 while( my( $action, $code ) = each %triggers ) {
83             $self->add_route(
84             method => $actions{$action},
85             regexp => $_,
86             code => $code,
87 8         9497 ) for map { sprintf $_, '/:id' x ($action ne 'create') }
  16         136  
88             "/${resource}%s.:format", "/${resource}%s";
89             }
90 6     6   2553 };
  6         11  
  6         20  
91              
92             sub send_entity :PluginKeyword {
93 8     8 0 11 my ($self, $entity, $http_code) = @_;
94              
95 8   50     121 $self->response->status($http_code || 200);
96 8         1667 $entity;
97 6     6   1866 };
  6         7  
  6         16  
98              
99             # TODO refactor that if my patch goes for Dancer2::Core::HTTP
100             my %http_codes = (
101              
102             # 1xx
103             100 => 'Continue',
104             101 => 'Switching Protocols',
105             102 => 'Processing',
106              
107             # 2xx
108             200 => 'OK',
109             201 => 'Created',
110             202 => 'Accepted',
111             203 => 'Non-Authoritative Information',
112             204 => 'No Content',
113             205 => 'Reset Content',
114             206 => 'Partial Content',
115             207 => 'Multi-Status',
116             210 => 'Content Different',
117              
118             # 3xx
119             300 => 'Multiple Choices',
120             301 => 'Moved Permanently',
121             302 => 'Found',
122             303 => 'See Other',
123             304 => 'Not Modified',
124             305 => 'Use Proxy',
125             307 => 'Temporary Redirect',
126             310 => 'Too many Redirect',
127              
128             # 4xx
129             400 => 'Bad Request',
130             401 => 'Unauthorized',
131             402 => 'Payment Required',
132             403 => 'Forbidden',
133             404 => 'Not Found',
134             405 => 'Method Not Allowed',
135             406 => 'Not Acceptable',
136             407 => 'Proxy Authentication Required',
137             408 => 'Request Time-out',
138             409 => 'Conflict',
139             410 => 'Gone',
140             411 => 'Length Required',
141             412 => 'Precondition Failed',
142             413 => 'Request Entity Too Large',
143             414 => 'Request-URI Too Long',
144             415 => 'Unsupported Media Type',
145             416 => 'Requested range unsatisfiable',
146             417 => 'Expectation failed',
147             418 => 'Teapot',
148             422 => 'Unprocessable entity',
149             423 => 'Locked',
150             424 => 'Method failure',
151             425 => 'Unordered Collection',
152             426 => 'Upgrade Required',
153             449 => 'Retry With',
154             450 => 'Parental Controls',
155              
156             # 5xx
157             500 => 'Internal Server Error',
158             501 => 'Not Implemented',
159             502 => 'Bad Gateway',
160             503 => 'Service Unavailable',
161             504 => 'Gateway Time-out',
162             505 => 'HTTP Version not supported',
163             507 => 'Insufficient storage',
164             509 => 'Bandwidth Limit Exceeded',
165             );
166              
167             plugin_keywords map {
168             my $code = $_;
169             my $helper_name = lc($http_codes{$_});
170             $helper_name =~ s/[^\w]+/_/gms;
171             $helper_name = "status_${helper_name}";
172              
173             $helper_name => sub {
174             $_[0]->send_entity(
175             ( $code >= 400 ? {error => $_[1]} : $_[1] ),
176             $code
177             );
178             };
179             } keys %http_codes;
180              
181             1;
182              
183             __END__