File Coverage

blib/lib/IRC/Message/Object.pm
Criterion Covered Total %
statement 68 69 98.5
branch 22 32 68.7
condition 1 2 50.0
subroutine 16 16 100.0
pod 6 8 75.0
total 113 127 88.9


line stmt bran cond sub pod time code
1             package IRC::Message::Object;
2             $IRC::Message::Object::VERSION = '0.091001';
3 5     5   2773 use strictures 2;
  5         1254  
  5         187  
4 5     5   838 use Carp;
  5         7  
  5         660  
5              
6 5     5   1995 use List::Objects::WithUtils;
  5         2830  
  5         29  
7 5     5   252467 use List::Objects::Types -all;
  5         406039  
  5         73  
8 5     5   21941 use Types::Standard -all;
  5         11  
  5         46  
9              
10 5     5   150270 use POE::Filter::IRCv3;
  5         10982  
  5         367  
11              
12 14     14 1 6855 sub ircmsg { __PACKAGE__->new(@_) }
13             our @EXPORT = our @EXPORT_OK = 'ircmsg';
14              
15              
16 5     5   2834 use Moo;
  5         37140  
  5         30  
17             extends 'Exporter::Tiny';
18              
19             has colonify => (
20             lazy => 1,
21             is => 'ro',
22             predicate => 1,
23             default => sub { 0 },
24             );
25              
26             has command => (
27             required => 1,
28             is => 'ro',
29             );
30              
31              
32             has filter => (
33             is => 'rw',
34             isa => HasMethods[qw/get put/],
35             lazy => 1,
36             predicate => 1,
37             builder => '__build_filter',
38             );
39              
40             sub __build_filter {
41 12 50   12   83 POE::Filter::IRCv3->new( colonify => (defined $_[1] ? $_[1] : 0) )
42             }
43              
44              
45             has prefix => (
46             is => 'ro',
47             lazy => 1,
48             predicate => 1,
49             default => sub { '' },
50             );
51              
52             has params => (
53             is => 'ro',
54             lazy => 1,
55             isa => ArrayObj,
56             coerce => 1,
57             predicate => 1,
58             default => sub { array },
59             );
60              
61             has raw_line => (
62             is => 'ro',
63             lazy => 1,
64             predicate => 1,
65             default => sub {
66             my ($self) = @_;
67             my %opts;
68             for (qw/prefix command params tags/) {
69             my $pred = "has_".$_;
70             $opts{$_} = $self->$_ if $self->$pred;
71             }
72             $self->filter->put([ \%opts ])->[0]
73             },
74             );
75              
76             has tags => (
77             lazy => 1,
78             is => 'ro',
79             isa => HashObj,
80             coerce => 1,
81             predicate => 'has_tags',
82             default => sub { hash },
83             );
84              
85             sub BUILDARGS {
86 19     19 0 12944 my $class = shift;
87 19 100       92 my %params = @_ > 1 ? @_ : (raw_line => $_[0]) ;
88              
89 19 100       90 if (! defined $params{command}) {
90 12 50       24 if (defined $params{raw_line}) {
91             ## Try to create self from raw_line instead:
92 12 50       56 my $filt = $params{filter} ?
93             $params{filter} : $class->__build_filter($params{colonify});
94 12         205 my $refs = $filt->get( [$params{raw_line}] );
95 12 50       939 %params = %{ $refs->[0] } if @$refs;
  12         88  
96             } else {
97 0         0 confess "Bad params; a command or a raw_line must be specified in new()"
98             }
99             }
100              
101 19         390 \%params
102             }
103              
104             sub get_tag {
105 1     1 1 3925 my ($self, $tag) = @_;
106 1 50       11 return unless $self->has_tags;
107             ## A tag might have an undef value ...
108             ## ... see has_tag
109 1         26 $self->tags->{$tag}
110             }
111              
112             sub has_tag {
113 2     2 1 615 my ($self, $tag) = @_;
114 2 50       9 return unless $self->has_tags;
115 2         44 exists $self->tags->{$tag}
116             }
117              
118             sub tags_as_array {
119 6     6 1 397 my ($self) = @_;
120 6 50       43 return array unless $self->has_tags;
121              
122 6         6 my @tag_array;
123 6         119 for ($self->tags->kv->all) {
124 18         140 my ($thistag, $thisval) = @$_;
125 18 100       40 push @tag_array,
126             defined $thisval ? join '=', $thistag, $thisval
127             : $thistag
128             };
129              
130 6         24 array @tag_array
131             }
132              
133             sub tags_as_string {
134 4     4 1 338 my ($self) = @_;
135 4 50       14 return unless $self->has_tags;
136              
137 4         5 my $str;
138 4         76 my $kv = $self->tags->kv;
139              
140 12   50     21 TAG: {
141 4         106 my $nxt = $kv->shift || last TAG;
142 12         46 my ($thistag, $thisval) = @$nxt;
143 12 100       22 $str .= $thistag . ( defined $thisval ? '='.$thisval : '' );
144 12 100       23 if ($kv->has_any) {
145 8         42 $str .= ';';
146             redo TAG
147 8         12 }
148             }
149              
150             $str
151 4         62 }
152              
153             sub truncate {
154 2     2 1 333 my ($self) = @_;
155              
156 2         2 my $new;
157 2         31 my $current = $self->raw_line;
158              
159             ## TODO check for CTCP first
160             ## if so, set flag, consider and readd trailing \001 ?
161              
162 2 100       19 if ($self->has_tags) {
163 1         4 my $tagstr = '@' . $self->tags_as_string;
164 1         4 my $trunc = substr $current, (length($tagstr) + 1), 510;
165 1         3 $new = join ' ', $tagstr, $trunc;
166             } else {
167             ## No tags, truncate to 510
168 1 50       3 $new = length $current <= 510 ? $current : substr $current, 0, 510 ;
169             }
170              
171 2         36 (ref $self)->new(raw_line => $new)
172             }
173              
174             sub TO_JSON {
175 1     1 0 4516 my ($self) = @_;
176             +{
177 1 50       28 command => $self->command,
178             prefix => $self->prefix,
179             params => $self->params,
180             ( $self->has_tags ? (tags => $self->tags) : () ),
181             }
182             }
183              
184             print
185             qq[ fine, be rude like that\n],
186             qq[ SORRY I WAS DISCUSSING THE ABILITY TO],
187             qq[ PUT AN IRCD ON A ROOMBA\n]
188             unless caller; 1;
189              
190             =pod
191              
192             =for Pod::Coverage BUILDARGS TO_JSON has_\w+
193              
194             =head1 NAME
195              
196             IRC::Message::Object - Incoming or outgoing IRC events
197              
198             =head1 SYNOPSIS
199              
200             ## Feed me some parameters:
201             my $event = IRC::Message::Object->new(
202             command => '001',
203             prefix => ':some.server.org',
204             params => [ 'user', 'Welcome to IRC' ],
205             );
206              
207             ## ... or import and use the 'ircmsg()' shortcut:
208             use IRC::Message::Object 'ircmsg';
209             my $event = ircmsg(
210             command => '001',
211             prefix => ':some.server.org',
212             params => [ 'user', 'Welcome to IRC' ],
213             );
214              
215             ## ... or take a raw IRC line (and parse it):
216             $event = ircmsg(
217             raw_line => ':some.server.org 001 user :Welcome to IRC'
218             );
219              
220             ## ... or feed from POE::Filter::IRCD or POE::Filter::IRCv3:
221             $event = ircmsg( %$ref_from_filter );
222              
223             ## ... retrieve useful bits later (see Methods):
224             my $cmd = $event->command;
225             my $line = $event->raw_line;
226             if ($event->has_tag('monkeys')) {
227             ...
228             }
229              
230             =head1 DESCRIPTION
231              
232             These objects represent incoming or outgoing IRC messages (events); they can
233             be created from either named parameters or a raw IRC line and provide
234             accessors with automatic parsing magic.
235              
236             =head2 Functions
237              
238             =head3 ircmsg
239              
240             Create a new B;
241             shortcut for C<< IRC::Message::Object->new >>.
242              
243             This module uses L, so you can rename the exported constructor
244             if you like:
245              
246             use IRC::Message::Object ircmsg => { -as => 'irc_ev' };
247              
248             =head2 Attributes and Methods
249              
250             =head3 raw_line
251              
252             The raw IRC line. The line is generated via the current
253             L if the message object wasn't constructed with one.
254              
255             predicate: C
256              
257             =head3 command
258              
259             The parsed command received.
260              
261             Note that if the C is set at construction time,
262             no case-folding takes place.
263             However, specifying a C at construction feeds
264             L, which will uppercase commands.
265              
266             =head3 params
267              
268             A L containing the parameters attached to the
269             message.
270              
271             predicate: C
272              
273             =head3 prefix
274              
275             The origin prefix.
276              
277             predicate: C
278              
279             =head3 colonify
280              
281             Passed through to L; see the
282             L documentation for details.
283              
284             Defaults to true.
285              
286             =head3 filter
287              
288             Can be used to change the L used to transform a raw line into a
289             HASH and vice-versa.
290              
291             Defaults to a L instance with C<< colonify => 0 >> set.
292              
293             =head3 get_tag
294              
295             Retrieve a specific IRCv3.2 message tag's value.
296              
297             This only works for tags with a defined value; see L to discover if
298             a tag exists.
299              
300             =head3 has_tag
301              
302             Takes a tag identifier; returns true if the tag exists.
303              
304             This is useful for finding out about tags that have no defined value.
305              
306             =head3 has_tags
307              
308             Returns true if there are tags present.
309              
310             =head3 tags
311              
312             IRCv3.2 message tags, as a L of key-value pairs.
313              
314             =head3 tags_as_array
315              
316             IRCv3.2 message tags, as a L of tags in the
317             form of 'key=value'
318              
319             =head3 tags_as_string
320              
321             IRCv3.2 message tags as a specification-compliant string.
322              
323             =head3 truncate
324              
325             Truncates the raw line to 510 characters, excluding message tags (per the
326             specification), and returns a new L.
327              
328             =head1 AUTHOR
329              
330             Jon Portnoy
331              
332             =cut