File Coverage

blib/lib/Dancer2/Plugin/REST.pm
Criterion Covered Total %
statement 56 56 100.0
branch 12 14 85.7
condition 9 14 64.2
subroutine 18 18 100.0
pod 2 3 66.6
total 97 105 92.3


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.01';
5 6     6   1825349 use 5.12.0; # for the sub attributes
  6         16  
6              
7 6     6   24 use strict;
  6         8  
  6         111  
8 6     6   23 use warnings;
  6         7  
  6         113  
9              
10 6     6   20 use Carp;
  6         8  
  6         261  
11              
12 6     6   2692 use Dancer2::Plugin;
  6         66003  
  6         28  
13              
14 6     6   11785 use Dancer2::Core::HTTP 0.203000;
  6         108  
  6         129  
15 6     6   21 use List::Util qw/ pairmap pairgrep /;
  6         18  
  6         1421  
16              
17             # [todo] - add XML support
18             my $content_types = {
19             json => 'application/json',
20             yml => 'text/x-yaml',
21             };
22              
23             has '+app' => (
24             handles => [qw/
25             add_hook
26             add_route
27             setting
28             response
29             request
30             send_error
31             set_response
32             /],
33             );
34              
35             sub prepare_serializer_for_format :PluginKeyword {
36 4     4 1 54298 my $self = shift;
37              
38 4         41 my $conf = $self->config;
39             my $serializers = (
40             ($conf && exists $conf->{serializers})
41             ? $conf->{serializers}
42 4 50 33     1576 : { 'json' => 'JSON',
43             'yml' => 'YAML',
44             'dump' => 'Dumper',
45             }
46             );
47              
48             $self->add_hook( Dancer2::Core::Hook->new(
49             name => 'before',
50             code => sub {
51 15     15   385227 my $format = $self->request->params->{'format'};
52 15 50 100     1659 $format ||= $self->request->captures->{'format'} if $self->request->captures;
53              
54             return delete $self->response->{serializer}
55 15 100       730 unless defined $format;
56              
57 9 100       44 my $serializer = $serializers->{$format}
58             or return $self->send_error("unsupported format requested: " . $format, 404);
59              
60 8         72 $self->setting(serializer => $serializer);
61              
62             $self->set_response( Dancer2::Core::Response->new(
63 8         74392 %{ $self->response },
  8         110  
64             serializer => $self->setting('serializer'),
65             ) );
66              
67             $self->response->content_type(
68 8   66     5964 $content_types->{$format} || $self->setting('content_type')
69             );
70             }
71 4         123 ) );
72 6     6   29 };
  6         7  
  6         28  
73              
74             sub resource :PluginKeyword {
75 3     3 1 38788 my ($self, $resource, %triggers) = @_;
76              
77             my %actions = (
78             update => 'put',
79             create => 'post',
80 3         7 map { $_ => $_ } qw/ get delete /
  6         16  
81             );
82              
83             croak "resource should be given with triggers"
84             unless defined $resource
85 3 100 66     15 and grep { $triggers{$_} } keys %actions;
  12         196  
86              
87 2         15 while( my( $action, $code ) = each %triggers ) {
88             $self->add_route(
89             method => $actions{$action},
90             regexp => $_,
91             code => $code,
92 8         9725 ) for map { sprintf $_, '/:id' x ($action ne 'create') }
  16         140  
93             "/${resource}%s.:format", "/${resource}%s";
94             }
95 6     6   2716 };
  6         8  
  6         19  
96              
97             sub send_entity :PluginKeyword {
98 10     10 0 18 my ($self, $entity, $http_code) = @_;
99              
100 10   50     132 $self->response->status($http_code || 200);
101 10         1948 $entity;
102 6     6   1951 };
  6         7  
  6         19  
103              
104             sub _status_helpers {
105             return
106             # see https://github.com/PerlDancer/Dancer2/pull/1235
107 846 100   846   1207 pairmap { ( $a =~ /\d.*_/ ? (split '_', $a, 2 )[1] : $a ), $b }
108 846 100   846   1338 pairmap { $a =~ /^\d+$/ ? ( $a => $a ) : ( $a => $b ) }
109 1266     1266   1790 pairgrep { $a !~ /[A-Z]/ }
110 6     6   43 Dancer2::Core::HTTP->all_mappings;
111             }
112              
113             plugin_keywords pairmap {
114             { # inner scope because of the pairmap closure bug <:-P
115             my( $helper_name, $code ) = ( $a, $b );
116             $helper_name = "status_${helper_name}";
117              
118             $helper_name => sub {
119             $_[0]->send_entity(
120             ( ( $code >= 400 && ! ref $_[1] ) ? {error => $_[1]} : $_[1] ),
121             $code
122             );
123             };
124             }
125             } _status_helpers();
126              
127             1;
128              
129             __END__