File Coverage

blib/lib/POE/Filter/Redis.pm
Criterion Covered Total %
statement 65 73 89.0
branch 29 36 80.5
condition 3 3 100.0
subroutine 11 12 91.6
pod 5 5 100.0
total 113 129 87.6


line stmt bran cond sub pod time code
1             package POE::Filter::Redis;
2             BEGIN {
3 1     1   599 $POE::Filter::Redis::VERSION = '0.02';
4             }
5              
6             #ABSTRACT: A POE Filter for the Redis protocol
7              
8 1     1   5 use strict;
  1         2  
  1         24  
9 1     1   6 use warnings;
  1         1  
  1         31  
10 1     1   5 use Carp qw[carp croak];
  1         1  
  1         72  
11 1     1   4 use base qw[POE::Filter];
  1         2  
  1         904  
12              
13             # Bit mask.
14             use constant {
15 1         115 PARSER_IDLE => 0x01,
16             PARSER_BETWEEN_BULK => 0x02,
17             PARSER_IN_BULK => 0x04,
18 1     1   589 };
  1         2  
19              
20             use constant {
21 1         1234 SELF_BUFFER => 0,
22             SELF_STATE => 1,
23             SELF_HAS => 2,
24             SELF_LENGTH => 3,
25             SELF_AWAITING => 4,
26             SELF_TYPE => 5,
27 1     1   6 };
  1         1  
28              
29             sub new {
30 1     1 1 13 my $package = shift;
31              
32 1         3 my %args = @_;
33              
34 1         7 return bless [
35             '', # SELF_BUFFER
36             PARSER_IDLE, # SELF_STATE
37             [ ], # SELF_HAS
38             0, # SELF_LENGTH
39             0, # SELF_AWAITING
40             undef, # SELF_TYPE
41             ], $package;
42             }
43              
44             sub get_one_start {
45 52     52 1 36265 my ($self, $stream) = @_;
46 52         93 $self->[SELF_BUFFER] .= join '', @{ $stream };
  52         138  
47             }
48              
49             sub get_one {
50 68     68 1 314 my $self = shift;
51              
52 68 100 100     431 return [ ] unless (
53             length $self->[SELF_BUFFER] and $self->[SELF_BUFFER] =~ /\x0D\x0A/
54             );
55              
56             # I expect it to be here mostly.
57 28 100       65 if ($self->[SELF_STATE] & PARSER_IDLE) {
58              
59             # Single-line responses. Remain in PARSER_IDLE state.
60 18 100       99 return [ [ $1, $2 ] ] if $self->[SELF_BUFFER] =~ s/^([-+:])(.*?)\x0D\x0A//s;
61              
62 12 100       63 if ($self->[SELF_BUFFER] =~ s/^\*(-?\d+)\x0D\x0A//) {
    50          
63              
64             # Zero-item multibulk is an empty list.
65             # Remain in the PARSER_IDLE state.
66 6 100       36 return [ [ '*', ] ] if $1 == 0;
67              
68             # Negative item multibulk is an undef list.
69 4 100       18 return [ [ '*', undef ] ] if $1 < 0;
70              
71 2         9 @$self[SELF_STATE, SELF_AWAITING, SELF_HAS, SELF_TYPE] = (
72             PARSER_BETWEEN_BULK, $1, [], '*'
73             );
74             }
75             elsif ($self->[SELF_BUFFER] =~ s/^\$(-?\d+)\x0D\x0A//) {
76              
77             # -1 length is undef.
78             # Remain in the PARSER_IDLE state.
79 6 100       22 return [ [ '$', undef ] ] if $1 < 0;
80              
81 4         18 @$self[SELF_STATE, SELF_AWAITING, SELF_LENGTH, SELF_HAS, SELF_TYPE] = (
82             PARSER_IN_BULK, 1, $1 + 2, [], '$'
83             );
84             }
85             else {
86             # TODO - Recover somehow.
87 0         0 croak "illegal redis response:\n$self->[SELF_BUFFER]";
88             }
89             }
90              
91 16         23 while (1) {
92 20 100       46 if ($self->[SELF_STATE] & PARSER_BETWEEN_BULK) {
93              
94             # Can't parse a bulk header?
95 12 100       57 return [ ] unless $self->[SELF_BUFFER] =~ s/^\$(-?\d+)\x0D\x0A//;
96              
97             # -1 length is undef.
98 6 100       22 if ($1 < 0) {
99 2 50       4 if (push(@{$self->[SELF_HAS]}, undef) == $self->[SELF_AWAITING]) {
  2         10  
100 0         0 $self->[SELF_STATE] = PARSER_IDLE;
101 0         0 return [ [ $self->[SELF_TYPE], @{$self->[SELF_HAS]} ] ];
  0         0  
102             }
103              
104             # Remain in PARSER_BETWEEN_BULK state.
105 2         4 next;
106             }
107              
108             # Got a bulk length.
109 4         14 @$self[SELF_STATE, SELF_LENGTH] = (PARSER_IN_BULK, $1 + 2);
110              
111             # Fall through.
112             }
113              
114             # TODO - Just for debugging..
115 12 50       29 croak "unexpected state $self->[SELF_STATE]" unless (
116             $self->[SELF_STATE] & PARSER_IN_BULK
117             );
118              
119             # Not enough data?
120 12 100       38 return [ ] if length $self->[SELF_BUFFER] < $self->[SELF_LENGTH];
121              
122             # Got a bulk value.
123 8 100       8 if (
124 8         42 push(
125             @{$self->[SELF_HAS]},
126             substr(
127             substr($self->[SELF_BUFFER], 0, $self->[SELF_LENGTH], ''),
128             0, $self->[SELF_LENGTH] - 2
129             )
130             ) == $self->[SELF_AWAITING]
131             ) {
132 6         8 $self->[SELF_STATE] = PARSER_IDLE;
133 6         10 return [ [ $self->[SELF_TYPE], @{$self->[SELF_HAS]} ] ];
  6         27  
134             }
135              
136             # But... not enough of them.
137 2         5 $self->[SELF_STATE] = PARSER_BETWEEN_BULK;
138             }
139              
140 0         0 croak "never gonna give you up, never gonna let you down";
141             }
142              
143             sub get_pending {
144 0     0 1 0 my $self = shift;
145 0 0       0 return [ $self->[SELF_BUFFER] ] if length $self->[SELF_BUFFER];
146 0         0 return undef;
147             }
148              
149             sub put {
150 4     4 1 2787 my ($self,$cmds) = @_;
151              
152 4         6 my @raw;
153 4         7 foreach my $line ( @{ $cmds } ) {
  4         8  
154 4 50       13 next unless ref $line eq 'ARRAY';
155 4 50       6 next unless scalar @{ $line };
  4         39  
156 4         5 my $cmd = shift @{ $line };
  4         9  
157 4         11 push @raw,
158             join( "\x0D\x0A",
159 8         38 '*' . ( 1 + @{ $line } ),
160 4         10 map { ('$' . length $_ => $_) }
161 4         8 ( uc($cmd), @{ $line } ) ) . "\x0D\x0A";
162             }
163 4         27 \@raw;
164             }
165              
166             qq[Redis Filter];
167              
168              
169             # vim: ts=2 sw=2 expandtab
170              
171             __END__