File Coverage

blib/lib/McBain/WithPSGI.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package McBain::WithPSGI;
2              
3             # ABSTRACT: Load a McBain API as a RESTful PSGI web service
4              
5 1     1   25632 use warnings;
  1         3  
  1         71  
6 1     1   7 use strict;
  1         2  
  1         39  
7              
8 1     1   6 use Carp;
  1         7  
  1         77  
9 1     1   1232 use JSON;
  1         32232  
  1         7  
10 1     1   838 use Plack::Request;
  0            
  0            
11             use Plack::Component;
12              
13             our $VERSION = "2.000000";
14             $VERSION = eval $VERSION;
15              
16             my $json = JSON->new->utf8->convert_blessed;
17              
18             =head1 NAME
19            
20             McBain::WithPSGI - Load a McBain API as a RESTful PSGI web service
21              
22             =head1 SYNOPSIS
23              
24             # write your API as you normally would, and create
25             # a simple psgi file:
26              
27             #!/usr/bin/perl -w
28              
29             use warnings;
30             use strict;
31             use MyAPI -withPSGI;
32              
33             my $app = MyAPI->to_app;
34              
35             =head1 DESCRIPTION
36              
37             C turns your L API into a RESTful L web service based on
38             L, thus making C a web application framework.
39              
40             The created web service will be a JSON-in JSON-out service. Requests to your application
41             are expected to have a C of C. The JSON body
42             of a request will be the payload. The results of the API will be formatted into JSON as
43             well.
44              
45             Note that if an API method does not return a hash-ref, this runner module will automatically
46             turn it into a hash-ref to ensure that conversion into JSON will be possible. The created
47             hash-ref will have one key - holding the method's name, with whatever was returned from the
48             method as its value. For example, if method C in topic C returns an
49             integer (say 7), then the client will get the JSON C<{ "GET:/math/divide": 7 }>.
50              
51             =head2 SUPPORTED HTTP METHODS
52              
53             This runner support all methods natively supported by L. That is: C, C,
54             C, C and C. To add support for C requests, use L.
55              
56             The C method is special. It returns a list of all HTTP methods allowed by a specific
57             route (in the C header). The response body will be the same hash-ref returned by
58             C for C requests, JSON encoded.
59              
60             =head1 METHODS EXPORTED TO YOUR API
61              
62             None.
63              
64             =head1 METHODS REQUIRED BY MCBAIN
65              
66             =head2 init( $target )
67              
68             Makes the root topic of your API inherit L, so that it
69             officially becomes a Plack app. This will provide your API with the C
70             method.
71              
72             =cut
73              
74             sub init {
75             my ($class, $target) = @_;
76              
77             if ($target->is_root) {
78             no strict 'refs';
79             push(@{"${target}::ISA"}, 'Plack::Component');
80             }
81             }
82              
83             =head2 generate_env( $psgi_env )
84              
85             Receives the PSGI env hash-ref and creates McBain's standard env hash-ref
86             from it.
87              
88             =cut
89              
90             sub generate_env {
91             my ($self, $psgi_env) = @_;
92              
93             my $req = Plack::Request->new($psgi_env);
94              
95             return {
96             METHOD => $req->method,
97             ROUTE => $req->path,
98             PAYLOAD => $req->content ? $json->decode($req->content) : {}
99             };
100             }
101              
102             =head2 generate_res( $env, $res )
103              
104             Converts the result of an API method to JSON, and returns a standard PSGI
105             response array-ref.
106              
107             =cut
108              
109             sub generate_res {
110             my ($self, $env, $res) = @_;
111              
112             my @headers = ('Content-Type' => 'application/json; charset=UTF-8');
113              
114             if ($env->{METHOD} eq 'OPTIONS') {
115             push(@headers, 'Allow' => join(',', keys %$res));
116             }
117              
118             $res = { $env->{METHOD}.':'.$env->{ROUTE} => $res }
119             unless ref $res eq 'HASH';
120              
121             return [200, \@headers, [$json->encode($res)]];
122             }
123              
124             =head2 handle_exception( $err )
125              
126             Formats exceptions into JSON and returns a standard PSGI array-ref.
127              
128             =cut
129              
130             sub handle_exception {
131             my ($class, $err) = @_;
132              
133             return [delete($err->{code}), ['Content-Type' => 'application/json; charset=UTF-8'], [$json->encode($err)]];
134             }
135              
136             =head1 CONFIGURATION AND ENVIRONMENT
137              
138             No configuration files are required.
139              
140             =head1 DEPENDENCIES
141            
142             C depends on the following CPAN modules:
143            
144             =over
145              
146             =item * L
147              
148             =item * L
149            
150             =item * L
151            
152             =back
153              
154             =head1 INCOMPATIBILITIES WITH OTHER MODULES
155              
156             None reported.
157              
158             =head1 BUGS AND LIMITATIONS
159              
160             Please report any bugs or feature requests to
161             C, or through the web interface at
162             L.
163              
164             =head1 SUPPORT
165              
166             You can find documentation for this module with the perldoc command.
167              
168             perldoc McBain::WithPSGI
169              
170             You can also look for information at:
171              
172             =over 4
173            
174             =item * RT: CPAN's request tracker
175            
176             L
177            
178             =item * AnnoCPAN: Annotated CPAN documentation
179            
180             L
181            
182             =item * CPAN Ratings
183            
184             L
185            
186             =item * Search CPAN
187            
188             L
189            
190             =back
191            
192             =head1 AUTHOR
193            
194             Ido Perlmuter
195            
196             =head1 LICENSE AND COPYRIGHT
197            
198             Copyright (c) 2013, Ido Perlmuter C<< ido@ido50.net >>.
199            
200             This module is free software; you can redistribute it and/or
201             modify it under the same terms as Perl itself, either version
202             5.8.1 or any later version. See L
203             and L.
204            
205             The full text of the license can be found in the
206             LICENSE file included with this module.
207            
208             =head1 DISCLAIMER OF WARRANTY
209            
210             BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
211             FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
212             OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
213             PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
214             EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
215             WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
216             ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
217             YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
218             NECESSARY SERVICING, REPAIR, OR CORRECTION.
219            
220             IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
221             WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
222             REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
223             LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
224             OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
225             THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
226             RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
227             FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
228             SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
229             SUCH DAMAGES.
230            
231             =cut
232              
233             1;
234             __END__