File Coverage

blib/lib/Atto.pm
Criterion Covered Total %
statement 64 67 95.5
branch 18 32 56.2
condition 2 2 100.0
subroutine 11 11 100.0
pod 0 1 0.0
total 95 113 84.0


line stmt bran cond sub pod time code
1             package Atto;
2             $Atto::VERSION = '0.002';
3             # ABSTRACT: A tiny microservice builder
4              
5 3     3   227870 use 5.008_001;
  3         14  
6 3     3   21 use warnings;
  3         6  
  3         117  
7 3     3   18 use strict;
  3         13  
  3         145  
8              
9 3     3   16 use Carp qw(croak);
  3         5  
  3         183  
10 3     3   723 use JSON::MaybeXS ();
  3         1124  
  3         77  
11 3     3   2837 use WWW::Form::UrlEncoded qw(parse_urlencoded);
  3         17846  
  3         431  
12              
13             my %methods_for_package;
14              
15             sub import {
16 2     2   17 my ($class, @methods) = @_;
17 2         5 my $package = caller;
18 2         4 $methods_for_package{$package} = { map { $_ => undef } @methods };
  2         3486  
19             }
20              
21             sub psgi {
22 2     2 0 18 my $package = caller;
23              
24 2         6 my $methods = $methods_for_package{$package};
25 2         9 for my $method (keys %$methods) {
26 3     3   17 my $coderef = do { no strict 'refs'; *{$package.'::'.$method}{CODE} };
  3         5  
  3         1872  
  2         4  
  2         3  
  2         11  
27 2 50       7 croak "method $method not found in $package" unless $coderef;
28 2         6 $methods->{$method} = $coderef;
29             }
30              
31 2         14 my $json = JSON::MaybeXS->new->allow_nonref;
32              
33             my $response = sub {
34 4     4   8 my ($code, $raw) = @_;
35 4         6 my $body = [ eval { $json->encode($raw) } ];
  4         53  
36 4 50       12 if ($@) {
37 0         0 $code = 500;
38 0         0 $body = [ $json->encode("couldn't encode response: $@") ];
39             }
40              
41 4         33 [ $code, [ 'Content-type' => 'application/json' ], $body ]
42 2         52 };
43              
44             sub {
45 4     4   82066 my ($env) = @_;
46              
47 4 50       20 return $response->(405, "request method must be POST (not $env->{REQUEST_METHOD})") unless $env->{REQUEST_METHOD} eq 'POST';
48              
49 4         19 my ($method) = $env->{REQUEST_URI} =~ m{^/([^/]+)};
50 4 50       15 return $response->(400, "method not found in request URL") unless defined $method;
51              
52 4 50       17 return $response->(404, "method not found") unless $methods->{$method};
53              
54 4   100     25 my $len = 0+($env->{CONTENT_LENGTH} || 0);
55              
56 4         9 my $args = {};
57 4 100       12 if ($len > 0) {
58 2 50       6 return $response->(400, "content type not provided") unless defined $env->{CONTENT_TYPE};
59              
60 2 100       8 if ($env->{CONTENT_TYPE} eq 'application/json') {
    50          
61 1         33 my $nread = $env->{'psgi.input'}->read(my $content, $len);
62 1 50       15 return $response->(400, sprintf("expected %d bytes (from content-length), got %d", $len, $nread)) if $nread != $len;
63              
64 1         2 $args = eval { $json->decode($content) };
  1         7  
65 1 50       5 return $response->(400, $@) if $@;
66             }
67             elsif ($env->{CONTENT_TYPE} eq 'application/x-www-form-urlencoded') {
68 1         4 my $nread = $env->{'psgi.input'}->read(my $content, $len);
69 1 50       9 return $response->(400, sprintf("expected %d bytes (from content-length), got %d", $len, $nread)) if $nread != $len;
70              
71 1         7 %$args = parse_urlencoded($content);
72 1 50       31 return $response->(400, $@) if $@;
73             }
74             else {
75 0         0 return $response->(400, "unknown content type");
76             }
77             }
78              
79             # XXX prototypes
80              
81 4 50       26 my @args =
    50          
82             ref $args eq 'ARRAY' ? @$args :
83             ref $args eq 'HASH' ? %$args :
84             ($args);
85              
86 4         7 my $ret = eval { $methods->{$method}->(@args) };
  4         15  
87 4 50       29 return $response->(500, "method call failed: $@") if $@;
88              
89 4         11 return $response->(200, $ret);
90             }
91 2         13 }
92              
93             1;
94             __END__