File Coverage

blib/lib/Nephia/Plugin/SocketIO.pm
Criterion Covered Total %
statement 27 60 45.0
branch 0 6 0.0
condition n/a
subroutine 9 17 52.9
pod 2 3 66.6
total 38 86 44.1


line stmt bran cond sub pod time code
1             package Nephia::Plugin::SocketIO;
2 1     1   656 use 5.008005;
  1         2  
  1         32  
3 1     1   5 use strict;
  1         1  
  1         26  
4 1     1   4 use warnings;
  1         9  
  1         28  
5 1     1   758 use parent 'Nephia::Plugin';
  1         245  
  1         4  
6 1     1   3502 use PocketIO;
  1         103155  
  1         30  
7 1     1   1354 use Plack::Builder;
  1         23792  
  1         80  
8 1     1   812 use HTML::Escape ();
  1         796  
  1         25  
9 1     1   1725 use Sub::Recursive;
  1         477  
  1         125  
10 1     1   1049 use Nephia::Plugin::SocketIO::Assets;
  1         3  
  1         502  
11              
12             our $VERSION = "0.01";
13              
14             sub new {
15 0     0 0   my ($class, %opts) = @_;
16 0           my $self = $class->SUPER::new(%opts);
17 0           $self->app->builder_chain->append('SocketIO' => $class->can('_wrap_app'));
18 0           return $self;
19             }
20              
21 0     0 1   sub exports { qw/socketio/ }
22              
23             sub socketio {
24 0     0 1   my ($self, $context) = @_;
25             return sub ($&) {
26 0     0     my ($event, $code) = @_;
27             $self->app->{events}{$event} = sub {
28 0           my ($socket, $var) = @_;
29 0           $code->($socket, _escape_html_recursive($var)); ### avoid XSS by _escape_html_recursive
30 0           };
31 0           };
32             }
33              
34             sub _wrap_app {
35 0     0     my ($app, $coderef) = @_;
36             builder {
37             mount '/socket.io.js' => sub {
38 0           [200, ['Content-Type' => 'text/javascript'], [Nephia::Plugin::SocketIO::Assets->get('socket.io.js')]];
39 0     0     };
40             mount '/socket.io' => PocketIO->new(handler => sub {
41 0           my $socket = shift;
42 0           for my $event (keys %{$app->{events}}) {
  0            
43 0           $socket->on($event => $app->{events}{$event});
44             }
45 0           $socket->send({buffer => []});
46 0           });
47             mount '/' => builder {
48             enable 'SimpleContentFilter', filter => sub{
49 0           s|()|$1\n|i;
50 0           };
51 0           $coderef;
52 0           };
53 0           };
54             }
55              
56             sub _escape_html_recursive {
57 0     0     my $v = shift;
58 0 0         return unless $v;
59             my $work = recursive {
60 0     0     my $val = shift;
61 0           ref($val) eq 'ARRAY' ? [map {$REC->($_)} @$val] :
  0            
62 0 0         ref($val) eq 'HASH' ? +{map {($_, $REC->($val->{$_}))} keys %$val} :
    0          
63             HTML::Escape::escape_html($val) ;
64 0           };
65 0           $work->($v);
66             }
67              
68             1;
69             __END__