File Coverage

blib/lib/Mojolicious/Plugin/Surveil.pm
Criterion Covered Total %
statement 25 29 86.2
branch 3 4 75.0
condition 8 10 80.0
subroutine 6 7 85.7
pod 1 1 100.0
total 43 51 84.3


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::Surveil;
2 2     2   1422 use Mojo::Base 'Mojolicious::Plugin';
  2         5  
  2         14  
3              
4 2     2   420 use Mojo::JSON qw(decode_json encode_json);
  2         4  
  2         1209  
5              
6             our $VERSION = '0.03';
7              
8             sub register {
9 2     2 1 90 my ($self, $app, $config) = @_;
10              
11 2   100     15 $config->{enable_param} ||= '_surveil';
12 2   50     14 $config->{events} ||= [qw(blur click focus touchstart touchcancel touchend)];
13 2   100     10 $config->{handler} ||= \&_default_message_handler;
14 2   100     8 $config->{path} ||= '/mojolicious/plugin/surveil';
15              
16 2         3 push @{$app->renderer->classes}, __PACKAGE__;
  2         14  
17 2     3   47 $app->hook(after_render => sub { _hook_after_render($config, @_) });
  3         45762  
18              
19             $app->routes->websocket($config->{path})->to(
20             cb => sub {
21 1     1   9808 my $c = shift->inactivity_timeout(60);
22 1         126 $c->on(json => $config->{handler});
23             }
24 2         42 );
25             }
26              
27             sub _default_message_handler {
28 0     0   0 my ($c, $e) = @_;
29 0         0 my ($type, $target) = delete @$e{qw(type target)};
30 0         0 $c->app->log->debug(qq(Event "$type" on "$target" @{[encode_json $e]}));
  0         0  
31             }
32              
33             sub _hook_after_render {
34 3     3   10 my ($config, $c, $output, $format) = @_;
35 3 50       13 return if $format ne 'html';
36 3 100       15 return if !$c->param($config->{enable_param});
37              
38 2   50     626 my $scheme = $c->req->url->to_abs->scheme || 'http';
39 2         440 $scheme =~ s!^http!ws!;
40              
41             my $js = $c->render_to_string(
42             template => 'mojolicious/plugin/surveil',
43             events => encode_json($config->{events}),
44 2         15 surveil_url => $c->url_for($config->{path})->to_abs->scheme($scheme),
45             );
46              
47 2         8758 $$output =~ s!!$js!;
48             }
49              
50             1;
51              
52             =encoding utf8
53              
54             =head1 NAME
55              
56             Mojolicious::Plugin::Surveil - Surveil user actions
57              
58             =head1 VERSION
59              
60             0.03
61              
62             =head1 DESCRIPTION
63              
64             L is a plugin which allow you to see every
65             event a user trigger on your web page. It is meant as a debug tool for
66             seeing events, even if the browser does not have a JavaScript console.
67              
68             CAVEAT: The JavaScript that is injected require WebSocket in the browser to
69             run. The surveil events are attached to the "body" element, so any other event
70             that prevent events from bubbling will not emit this to the WebSocket
71             resource.
72              
73             =head1 SYNOPSIS
74              
75             =head2 Application
76              
77             use Mojolicious::Lite;
78             plugin "surveil";
79              
80             =head2 In your browser
81              
82             Visit L to enable the logging. Try clicking
83             around on your page and look in the console for log messages.
84              
85             =head2 Custom event handler
86              
87             use Mojo::Redis;
88             use Mojo::JSON "encode_json";
89              
90             plugin "surveil", {
91             handler => sub {
92             my ($c, $event) = @_;
93             my $ip = $c->tx->remote_address;
94             $c->redis->pubsub->notify("surveil:$ip" => encode_json $event);
95             }
96             };
97              
98             The above example is useful if you want to publish the events to
99             L instead of a log file. A developer can then run commands
100             below to see what a given user is doing:
101              
102             $ redis-cli psubscribe "surveil:*"
103             $ redis-cli subscribe "surveil:192.168.0.100"
104              
105             =head1 METHODS
106              
107             =head2 register
108              
109             $self->register($app, \%config);
110             $app->plugin("surveil" => \%config);
111              
112             Used to add an "after_render" hook into the application which adds a
113             JavaScript to every HTML document when the L is set.
114              
115             C<%config> can have the following settings:
116              
117             =over 2
118              
119             =item * enable_param
120              
121             Used to specify a query parameter to be part of the URL to enable surveil.
122              
123             Default is "_surveil".
124              
125             =item * events
126              
127             The events that should be reported back over the WebSocket.
128              
129             Defaults to blur, click, focus, touchstart, touchcancel and touchend.
130              
131             Note that the default list might change in the future.
132              
133             =item * handler
134              
135             A code ref that handles the events from the web page. This is useful if you
136             want to post them to an event bus instead of in the log file.
137              
138             =item * path
139              
140             The path to the WebSocket route.
141              
142             Defaults to C.
143              
144             =back
145              
146             =head1 COPYRIGHT AND LICENSE
147              
148             Copyright (C) 2014-2018, Jan Henning Thorsen
149              
150             This program is free software, you can redistribute it and/or modify it under
151             the terms of the Artistic License version 2.0.
152              
153             =head1 AUTHOR
154              
155             Jan Henning Thorsen - C
156              
157             =cut
158              
159             __DATA__