File Coverage

blib/lib/McBain/WithWebSocket.pm
Criterion Covered Total %
statement 18 38 47.3
branch 0 6 0.0
condition 0 2 0.0
subroutine 6 11 54.5
pod 4 4 100.0
total 28 61 45.9


line stmt bran cond sub pod time code
1             package McBain::WithWebSocket;
2              
3             # ABSTRACT: Load a McBain API as a WebSocket server
4              
5 1     1   26856 use warnings;
  1         2  
  1         29  
6 1     1   5 use strict;
  1         1  
  1         29  
7              
8 1     1   5 use Carp;
  1         5  
  1         69  
9 1     1   1143 use JSON;
  1         16424  
  1         5  
10 1     1   1091 use Net::WebSocket::Server;
  1         213396  
  1         118  
11              
12             our $VERSION = "2.000000";
13             $VERSION = eval $VERSION;
14              
15             my $json = JSON->new->convert_blessed;
16              
17             =head1 NAME
18            
19             McBain::WithWebSocket - Load a McBain API as a WebSocket server
20              
21             =head1 SYNOPSIS
22              
23             # write your API as you normally would, and create
24             # a simple server script:
25              
26             #!/usr/bin/perl -w
27              
28             use warnings;
29             use strict;
30             use MyAPI -withWebSocket;
31              
32             MyAPI->start(80); # default port is 8080
33              
34             # if your API is object oriented
35             MyAPI->new(%attrs)->start(80);
36              
37             =head1 DESCRIPTION
38              
39             C turns your L API into a L
40             server using L.
41              
42             The created server will be a JSON-in JSON-out service. When a client sends a message to
43             the server, it is expected to be a JSON string, which will be converted into a hash-ref
44             and serve as the payload for the API. The payload must have a C key, which holds
45             the complete path of the route/method to invoke (for example, C). The
46             results from the API will be formatted into JSON as well and sent back to the client.
47              
48             Note that if an API method does not return a hash-ref, this runner module will automatically
49             turn it into a hash-ref to ensure that conversion into JSON will be possible. The created
50             hash-ref will have one key - holding the method's name, with whatever was returned from the
51             method as its value. For example, if method C in topic C returns an
52             integer (say 7), then the client will get the JSON C<{ "GET:/math/divide": 7 }>. To avoid this
53             behavior, make sure your API's methods return hash-refs.
54              
55             =head2 EXAMPLE CLIENT
56              
57             I haven't succeeded in creating a Perl WebSocket client using any of the related modules on
58             CPAN. Documentation in this area seems to be extremely lacking. On the other hand, I created
59             a very simply client with NodeJS in about two minutes. If you need to test your C API
60             running as a WebSocket server, and have a similar problem, take a look at the C
61             file in the examples directory of this distribution.
62              
63             =head2 SUPPORTED HTTP METHODS
64              
65             This runner support all methods natively supported by L. That is: C, C,
66             C, C and C.
67              
68             The C method is special. It returns a hash-ref of methods supported by a specific
69             route, including description and parameter definitions, if any. See L
70             for more information.
71              
72             =head1 METHODS EXPORTED TO YOUR API
73              
74             =head2 start( [ $port ] )
75              
76             Starts the WebSocket server, listening on the supplied port. If C<$port> is not provided,
77             C<8080> is used by default.
78              
79             =head1 METHODS REQUIRED BY MCBAIN
80              
81             =head2 init( $target )
82              
83             Creates and exports the L method for your API's root package.
84              
85             =cut
86              
87             sub init {
88 0     0 1   my ($class, $target) = @_;
89              
90 0 0         if ($target->is_root) {
91 1     1   11 no strict 'refs';
  1         3  
  1         680  
92 0           *{"${target}::start"} = sub {
93 0     0     my ($pkg, $port) = @_;
94              
95             Net::WebSocket::Server->new(
96             listen => $port || 8080,
97             on_connect => sub {
98 0           my ($serv, $conn) = @_;
99              
100             $conn->on(
101             utf8 => sub {
102 0           my ($conn, $payload) = @_;
103              
104 0           $conn->send_utf8($pkg->call($payload));
105             }
106 0           );
107             }
108 0   0       )->start;
109 0           };
110             }
111             }
112              
113             =head2 generate_env( $req )
114              
115             Receives the request JSON and creates C's standard env
116             hash-ref from it.
117              
118             =cut
119              
120             sub generate_env {
121 0     0 1   my ($self, $payload) = @_;
122              
123 0           $payload = $json->decode($payload);
124              
125 0 0         confess { code => 400, error => "Namespace must match : where METHOD is one of GET, POST, PUT, DELETE or OPTIONS" }
126             unless $payload->{path} =~ m/^(GET|POST|PUT|DELETE|OPTIONS):[^:]+$/;
127              
128 0           my ($method, $route) = split(/:/, delete($payload->{path}));
129              
130             return {
131 0           METHOD => $method,
132             ROUTE => $route,
133             PAYLOAD => $payload
134             };
135             }
136              
137             =head2 generate_res( $env, $res )
138              
139             Converts the result from an API method in JSON. Read the discussion under
140             L for more info.
141              
142             =cut
143              
144             sub generate_res {
145 0     0 1   my ($self, $env, $res) = @_;
146              
147 0 0         $res = { $env->{METHOD}.':'.$env->{ROUTE} => $res }
148             unless ref $res eq 'HASH';
149              
150 0           return $json->encode($res);
151             }
152              
153             =head2 handle_exception( $err )
154              
155             Formats exceptions into JSON.
156              
157             =cut
158              
159             sub handle_exception {
160 0     0 1   my ($class, $err) = @_;
161              
162 0           return $json->encode($err);
163             }
164              
165             =head1 CONFIGURATION AND ENVIRONMENT
166            
167             No configuration files are required.
168            
169             =head1 DEPENDENCIES
170            
171             C depends on the following CPAN modules:
172            
173             =over
174              
175             =item * L
176              
177             =item * L
178            
179             =item * L
180            
181             =back
182              
183             =head1 INCOMPATIBILITIES WITH OTHER MODULES
184              
185             None reported.
186              
187             =head1 BUGS AND LIMITATIONS
188              
189             Please report any bugs or feature requests to
190             C, or through the web interface at
191             L.
192              
193             =head1 SUPPORT
194              
195             You can find documentation for this module with the perldoc command.
196              
197             perldoc McBain::WithWebSocket
198              
199             You can also look for information at:
200              
201             =over 4
202            
203             =item * RT: CPAN's request tracker
204            
205             L
206            
207             =item * AnnoCPAN: Annotated CPAN documentation
208            
209             L
210            
211             =item * CPAN Ratings
212            
213             L
214            
215             =item * Search CPAN
216            
217             L
218            
219             =back
220            
221             =head1 AUTHOR
222            
223             Ido Perlmuter
224            
225             =head1 LICENSE AND COPYRIGHT
226            
227             Copyright (c) 2014, Ido Perlmuter C<< ido@ido50.net >>.
228            
229             This module is free software; you can redistribute it and/or
230             modify it under the same terms as Perl itself, either version
231             5.8.1 or any later version. See L
232             and L.
233            
234             The full text of the license can be found in the
235             LICENSE file included with this module.
236            
237             =head1 DISCLAIMER OF WARRANTY
238            
239             BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
240             FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
241             OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
242             PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
243             EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
244             WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
245             ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
246             YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
247             NECESSARY SERVICING, REPAIR, OR CORRECTION.
248            
249             IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
250             WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
251             REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
252             LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
253             OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
254             THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
255             RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
256             FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
257             SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
258             SUCH DAMAGES.
259            
260             =cut
261              
262             1;
263             __END__