File Coverage

blib/lib/Net/Snarl.pm
Criterion Covered Total %
statement 18 58 31.0
branch 0 28 0.0
condition 0 12 0.0
subroutine 6 12 50.0
pod 3 3 100.0
total 27 113 23.8


line stmt bran cond sub pod time code
1             package Net::Snarl;
2              
3 1     1   44245 use strict;
  1         3  
  1         40  
4 1     1   5 use warnings;
  1         2  
  1         28  
5 1     1   33 use 5.008;
  1         10  
  1         63  
6             our $VERSION = 1.10;
7              
8 1     1   7 use Carp;
  1         1  
  1         110  
9 1     1   1901 use IO::Socket;
  1         28829  
  1         5  
10 1     1   1753 use Readonly;
  1         3017  
  1         1122  
11              
12             =head1 NAME
13              
14             Net::Snarl - Snarl network protocol
15              
16             =cut
17              
18             Readonly my $SNARL_PORT => 9887;
19             Readonly my $SNARL_PROTO_VERSION => '1.1';
20              
21             =head1 SYNOPSIS
22              
23             use Net::Snarl;
24              
25             # connect to localhost and register Net::Snarl application
26             my $snarl = Net::Snarl->register('Net::Snarl');
27             $snarl->add_class('Test'); # add Test notification class
28             $snarl->notify('Test', 'Hello', 'World', 5); # show hello world for 5 seconds
29              
30             =head1 DESCRIPTION
31              
32             A simple interface to send Snarl notifications across the network. Snarl must
33             be running on the target machine.
34              
35             =cut
36              
37             sub _send {
38 0     0     my ($self, %param) = @_;
39              
40 0           my $data = 'type=SNP#?version=' . $SNARL_PROTO_VERSION . '#?' .
41 0           join('#?', map { "$_=$param{$_}" } keys %param);
42              
43 0           $self->{socket}->print("$data\x0d\x0a");
44 0           return $self->_recv;
45             }
46              
47             sub _recv {
48 0     0     my ($self) = @_;
49              
50 0           my $data = $self->{socket}->getline();
51 0           chomp $data;
52              
53 0           my ($header, $version, $code, $desc, @rest) = split '/', $data;
54              
55 0 0         die "Unexpected response: $data" unless $header eq 'SNP';
56              
57             # hackishly disregard responses above 300
58 0 0         if ($code >= 300) {
59 0           push @{$self->{queue}}, [$code, $desc, @rest];
  0            
60 0           return $self->_recv;
61             }
62              
63 0           return $code, $desc, @rest;
64             }
65              
66             =head1 INTERFACE
67              
68             =head2 register($application, $host, $port)
69              
70             Connects to Snarl and register an application. Host defaults to localhost and
71             port defaults to C<$Net::Snarl::SNARL_PORT>.
72              
73             =cut
74              
75             sub register {
76 0     0 1   my ($class, $application, $host, $port) = @_;
77              
78 0 0         croak 'Cannot call register as an instance method' if ref $class;
79 0 0         croak 'Application name required' unless $application;
80              
81 0 0 0       my $socket = IO::Socket::INET->new(
      0        
82             PeerAddr => $host || 'localhost',
83             PeerPort => $port || $SNARL_PORT,
84             Proto => 'tcp',
85             ) or die "Unable to create socket: $!";
86              
87 0           my $self = bless { socket => $socket, application => $application }, $class;
88              
89 0           my ($result, $text) = $self->_send(
90             action => 'register',
91             app => $application,
92             );
93              
94 0 0         die "Unable to register: $text" if $result;
95              
96 0           return $self;
97             }
98              
99             =head2 add_class($class, $title)
100              
101             Registers a notification class with your application. Title is the optional
102             friendly name for the class.
103              
104             =cut
105              
106             sub add_class {
107 0     0 1   my ($self, $class, $title) = @_;
108              
109 0 0         croak 'Cannot call add_class as a class method' unless ref $self;
110 0 0         croak 'Class name required' unless $class;
111              
112 0   0       my ($result, $text) = $self->_send(
113             action => 'add_class',
114             app => $self->{application},
115             class => $class,
116             title => $title || $class,
117             );
118              
119 0 0         die "Unable to add class: $text" if $result;
120              
121 0           return 1;
122             }
123              
124             =head2 notify($class, $title, $text, $timeout, $icon)
125              
126             Displays a notification of the specified class. Timeout defaults to 0 (sticky)
127             and icon defaults to nothing.
128              
129             =cut
130              
131             sub notify {
132 0     0 1   my ($self, $class, $title, $text, $timeout, $icon) = @_;
133              
134 0 0         croak 'Cannot call notify as a class method' unless ref $self;
135 0 0         croak 'Class name required' unless $class;
136 0 0         croak 'Title required' unless $title;
137 0 0         croak 'Text required' unless $text;
138              
139 0   0       my ($result, $rtext) = $self->_send(
      0        
140             action => 'notification',
141             app => $self->{application},
142             class => $class,
143             title => $title,
144             text => $text,
145             timeout => $timeout || 0,
146             icon => $icon || '',
147             );
148              
149 0 0         die "Unable to send notification: $rtext" if $result;
150              
151 0           return 1;
152             }
153              
154             sub DESTROY {
155 0     0     my ($self) = @_;
156              
157 0           $self->_send(
158             action => 'unregister',
159             app => $self->{application},
160             );
161              
162 0           return;
163             }
164              
165             =head1 BUGS
166              
167             Please report and bugs or feature requests on GitHub
168             L
169              
170             =head1 TODO
171              
172             Later versions of Snarl report interactions with the notifications back to the
173             socket. Currently these are stored in a private queue. Eventually, I will
174             expose an interface for triggering callbacks on these events but that will most
175             likely require threading so I'm a little reluctant to implement it.
176              
177             =head1 AUTHOR
178              
179             Alan Berndt, C<< >>
180              
181             =head1 LICENSE AND COPYRIGHT
182              
183             Copyright 2013 Alan Berndt.
184              
185             Permission is hereby granted, free of charge, to any person obtaining a copy of
186             this software and associated documentation files (the "Software"), to deal in
187             the Software without restriction, including without limitation the rights to
188             use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
189             of the Software, and to permit persons to whom the Software is furnished to do
190             so, subject to the following conditions:
191              
192             The above copyright notice and this permission notice shall be included in all
193             copies or substantial portions of the Software.
194              
195             THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
196             IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
197             FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
198             AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
199             LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
200             OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
201             SOFTWARE.
202              
203             =cut
204              
205             1;