File Coverage

blib/lib/Dancer2/Plugin/REST.pm
Criterion Covered Total %
statement 55 56 98.2
branch 7 10 70.0
condition 8 10 80.0
subroutine 19 19 100.0
pod 2 3 66.6
total 91 98 92.8


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.02';
5 7     7   6764049 use 5.12.0; # for the sub attributes
  7         31  
6              
7 7     7   42 use strict;
  7         16  
  7         173  
8 7     7   39 use warnings;
  7         17  
  7         220  
9              
10 7     7   38 use Carp;
  7         20  
  7         550  
11              
12 7     7   3935 use Dancer2::Plugin;
  7         92567  
  7         79  
13              
14 7     7   23762 use Dancer2::Core::HTTP 0.203000;
  7         213  
  7         275  
15 7     7   45 use List::Util qw/ pairmap pairgrep /;
  7         15  
  7         1759  
16              
17             my %content_types = (
18             yaml => 'text/x-yaml',
19             yml => 'text/x-yaml',
20             json => 'application/json',
21             dump => 'text/x-data-dumper',
22             '' => 'text/html',
23             );
24              
25             # TODO check if we use the handles
26             has '+app' => (
27             handles => [qw/
28             add_hook
29             add_route
30             response
31             request
32             /],
33             );
34              
35             sub prepare_serializer_for_format :PluginKeyword {
36 5     5 1 75031 my $self = shift;
37              
38 5         130 my $conf = $self->app->config;
39 5 50       165 if( my $serializer = $conf->{serializer} ) {
40 0         0 warn "serializer '$serializer' specified in config file, overrode by Dancer2::Plugin::REST\n";
41             }
42              
43 5         19 $conf->{serializer} = 'Mutable::REST';
44              
45             $self->add_hook( Dancer2::Core::Hook->new(
46             name => 'before',
47             code => sub {
48             my $format = $self->request->params->{'format'}
49 16   100 16   1215816 || eval { $self->request->captures->{'format'} };
50              
51 16 50 100     912 my $content_type = lc $content_types{$format||''} or return;
52              
53 16         309 $self->request->headers->header( 'Content-Type' => $content_type );
54              
55             }
56 5         158 ) );
57 7     7   47 }
  7         15  
  7         56  
58              
59             sub resource :PluginKeyword {
60 3     3 1 72483 my ($self, $resource, %triggers) = @_;
61              
62             my %actions = (
63             update => 'put',
64             create => 'post',
65 3         27 map { $_ => $_ } qw/ get delete /
  6         96  
66             );
67              
68             croak "resource should be given with triggers"
69             unless defined $resource
70 3 100 66     33 and grep { $triggers{$_} } keys %actions;
  12         306  
71              
72 2         16 while( my( $action, $code ) = each %triggers ) {
73             $self->add_route(
74             method => $actions{$action},
75             regexp => $_,
76             code => $code,
77 8         14396 ) for map { sprintf $_, '/:id' x ($action ne 'create') }
  16         274  
78             "/${resource}%s.:format", "/${resource}%s";
79             }
80 7     7   4456 };
  7         20  
  7         30  
81              
82             sub send_entity :PluginKeyword {
83 10     10 0 123 my ($self, $entity, $http_code) = @_;
84              
85 10   50     260 $self->response->status($http_code || 200);
86 10         2210 $entity;
87 7     7   3141 };
  7         17  
  7         31  
88              
89             sub _status_helpers {
90             return
91             # see https://github.com/PerlDancer/Dancer2/pull/1235
92 987 50   987   2894 pairmap { ( $a =~ /\d.*_/ ? (split '_', $a, 2 )[1] : $a ), $b }
93 987 100   987   2926 pairmap { $a =~ /^\d+$/ ? ( $a => $a ) : ( $a => $b ) }
94 1477     1477   3953 pairgrep { $a !~ /[A-Z]/ }
95 7     7   102 Dancer2::Core::HTTP->all_mappings;
96             }
97              
98             plugin_keywords pairmap {
99             { # inner scope because of the pairmap closure bug <:-P
100             my( $helper_name, $code ) = ( $a, $b );
101             $helper_name = "status_${helper_name}";
102              
103             $helper_name => sub {
104             $_[0]->send_entity(
105             ( ( $code >= 400 && ! ref $_[1] ) ? {error => $_[1]} : $_[1] ),
106             $code
107             );
108             };
109             }
110             } _status_helpers();
111              
112             1;
113              
114             package
115             Dancer2::Serializer::Mutable::REST;
116              
117             # TODO write patch for D2:S:M to provide our own mapping
118             # and then we'll be able to preserce the 'text/html'
119              
120 7     7   5197 use Moo;
  7         14  
  7         45  
121              
122             extends 'Dancer2::Serializer::Mutable';
123              
124             around _get_content_type => sub {
125             my( $orig, $self, $entity ) = @_;
126              
127             $self->has_request or return;
128              
129             my $ct = $self->request->header( 'content_type' );
130              
131             if( $ct eq 'text/html' or $ct eq '' ) {
132             $self->set_content_type( 'text/html' );
133             return;
134             }
135              
136             $orig->($self,$entity);
137             };
138              
139             1;
140              
141             __END__