File Coverage

blib/lib/Mojo/IOLoop/Stream/Role/LineBuffer.pm
Criterion Covered Total %
statement 29 29 100.0
branch 8 10 80.0
condition 1 2 50.0
subroutine 5 5 100.0
pod 2 2 100.0
total 45 48 93.7


line stmt bran cond sub pod time code
1             package Mojo::IOLoop::Stream::Role::LineBuffer;
2              
3 1     1   912 use Mojo::Base -role;
  1         3  
  1         8  
4              
5             our $VERSION = '0.006';
6              
7             has 'read_line_separator' => sub { qr/\x0D?\x0A/ };
8             has 'write_line_separator' => "\x0D\x0A";
9              
10             requires qw(on emit write);
11              
12             sub watch_lines {
13 3     3 1 3152 my $self = shift;
14 3 50       10 return $self if $self->{_read_line_read_cb};
15             $self->{_read_line_read_cb} = $self->on(read => sub {
16 7     7   6030 my ($self, $bytes) = @_;
17 7         21 $self->{_read_line_buffer} .= $bytes;
18 7         23 my $sep = $self->read_line_separator;
19 7         27 my $pos;
20 7         130 while ($self->{_read_line_buffer} =~ m/\G(.*?)($sep)/gs) {
21 6         30 $pos = pos $self->{_read_line_buffer};
22 6         31 $self->emit(read_line => "$1", "$2");
23             } continue {
24 6         94 $sep = $self->read_line_separator;
25             }
26 7 100       54 $self->{_read_line_buffer} = substr($self->{_read_line_buffer}, $pos)
27             if $pos;
28 3         25 });
29             $self->{_read_line_close_cb} = $self->on(close => sub {
30 3     3   227 my $self = shift;
31 3 50 50     17 if (length(my $buffer = delete $self->{_read_line_buffer} // '')) {
32 3         17 my $sep = $self->read_line_separator;
33 3         14 my $pos = 0;
34 3         52 while ($buffer =~ m/\G(.*?)($sep)/gs) {
35 2         5 $pos = pos $buffer;
36 2         26 $self->emit(read_line => "$1", "$2");
37             } continue {
38 2         37 $sep = $self->read_line_separator;
39             }
40 3 100       42 $self->emit(read_line => $pos ? substr($buffer, $pos) : $buffer)
    100          
41             if $pos < length $buffer;
42             }
43 3         34 });
44 3         32 return $self;
45             }
46              
47             sub write_line {
48 2     2 1 1576 my ($self, $line) = (shift, shift);
49 2         18 my $sep = $self->write_line_separator;
50 2         21 $self->write("$line$sep", @_);
51             }
52              
53             1;
54              
55             =head1 NAME
56              
57             Mojo::IOLoop::Stream::Role::LineBuffer - Read and write streams by lines
58              
59             =head1 SYNOPSIS
60              
61             use Mojo::IOLoop;
62             use Mojo::IOLoop::Stream;
63             my $output_stream = Mojo::IOLoop::Stream->with_roles('+LineBuffer')->new($handle);
64             Mojo::IOLoop->client({port => 3000} => sub {
65             my ($loop, $err, $stream) = @_;
66             $stream->with_roles('+LineBuffer')->watch_lines->on(read_line => sub {
67             my ($stream, $line) = @_;
68             say "Received line: $line";
69             $output_stream->write_line('Got it!');
70             });
71             });
72              
73             =head1 DESCRIPTION
74              
75             L composes the method
76             L which causes a L object to emit the
77             L event for each line received. The L method is
78             also provided to add a line separator to the passed data before writing.
79              
80             =head1 EVENTS
81              
82             L can emit the following events.
83              
84             =head2 read_line
85              
86             $stream->on(read_line => sub {
87             my ($stream, $line, $separator) = @_;
88             ...
89             });
90              
91             Emitted when a line ending in L arrives on the stream,
92             and when the stream closes if data is still buffered. The separator is passed
93             as a separate argument if present.
94              
95             =head1 ATTRIBUTES
96              
97             L composes the following attributes.
98              
99             =head2 read_line_separator
100              
101             my $separator = $stream->read_line_separator;
102             $stream = $stream->read_line_separator(qr/\x0D\x0A/);
103              
104             Regular expression to indicate new lines in received bytes. Defaults to a
105             newline (LF) character optionally preceded by a CR character (C<\x0D?\x0A>).
106             Note that if you set this to L or
107             C<\v> (vertical whitespace), this may match the CR character of a CR/LF
108             sequence and consider the LF as a separate line if they are read separately.
109              
110             =head2 write_line_separator
111              
112             my $separator = $stream->write_line_separator;
113             $stream = $stream->write_line_separator("\x0A");
114              
115             Byte sequence to indicate new lines in data written with L.
116             Defaults to the network newline CR/LF (C<\x0D\x0A>).
117              
118             =head1 METHODS
119              
120             L composes the following methods.
121              
122             =head2 watch_lines
123              
124             $stream = $stream->watch_lines;
125              
126             Subscribe to the L and
127             L events, to buffer received bytes and emit
128             L when L is encountered or the stream is
129             closed with buffered data.
130              
131             =head2 write_line
132              
133             $stream = $stream->write_line($bytes);
134             $stream = $stream->write_line($bytes => sub {...});
135              
136             Write a line to the stream by appending L to the data.
137             The optional drain callback will be executed once all data has been written.
138              
139             =head1 BUGS
140              
141             Report any issues on the public bugtracker.
142              
143             =head1 AUTHOR
144              
145             Dan Book
146              
147             =head1 COPYRIGHT AND LICENSE
148              
149             This software is Copyright (c) 2018 by Dan Book.
150              
151             This is free software, licensed under:
152              
153             The Artistic License 2.0 (GPL Compatible)
154              
155             =head1 SEE ALSO
156              
157             L, L, L,
158             L