File Coverage

blib/lib/Mojo/Redis2/Cursor.pm
Criterion Covered Total %
statement 3 62 4.8
branch 0 30 0.0
condition 0 7 0.0
subroutine 1 16 6.2
pod 9 9 100.0
total 13 124 10.4


line stmt bran cond sub pod time code
1             package Mojo::Redis2::Cursor;
2 34     34   191 use Mojo::Base '-base';
  34         59  
  34         162  
3              
4             has 'redis';
5             has command => sub { ['SCAN', 0] };
6             has _cursor_pos => 1;
7              
8             sub again {
9 0     0 1   my $self = shift;
10 0           $self->command->[$self->_cursor_pos] = 0;
11 0           delete $self->{_finished};
12 0           return $self;
13             }
14              
15             sub all {
16 0 0   0 1   my $cb = ref $_[-1] eq 'CODE' ? pop : undef;
17 0           my $self = shift;
18              
19 0           my $list = [];
20              
21             # non-blocking
22 0 0         if ($cb) {
23              
24             # __SUB__ available only from 5.16
25 0           my $wrapper;
26             $wrapper = sub {
27 0   0 0     push @$list, @{$_[2] // []};
  0            
28 0 0         return $self->$cb($_[1], $list) if $_[0]->{_finished};
29 0           $self->next($wrapper);
30 0           };
31 0           return $self->next(@_ => $wrapper);
32             }
33              
34             # blocking
35             else {
36 0           while (my $r = $self->next(@_)) { push @$list, @$r }
  0            
37 0           return $list;
38             }
39             }
40              
41 0     0 1   sub finished { !!shift->{_finished} }
42              
43             sub hgetall {
44 0     0 1   my $cur = shift->_clone('HSCAN', shift, 0);
45 0           return $cur->all(@_);
46             }
47              
48             sub hkeys {
49 0 0   0 1   my $cb = ref $_[-1] eq 'CODE' ? pop : undef;
50 0           my $cur = shift->_clone('HSCAN' => shift, 0, @_);
51             my $wrapper = sub {
52 0 0   0     my $keys = [grep { $a = !$a } @{$_[2] || []}];
  0            
  0            
53 0           return $cur->$cb($_[1], $keys);
54 0           };
55 0 0         my $resp = $cur->all($cb ? ($wrapper) : ());
56 0 0         return $resp if $cb;
57 0     0     $cb = sub { $_[2] };
  0            
58 0           return $wrapper->(undef, '', $resp);
59             }
60              
61             sub keys {
62 0     0 1   my $cur = shift->_clone('SCAN', 0);
63 0 0 0       unshift @_, 'MATCH' if $_[0] && !ref $_[0];
64 0           return $cur->all(@_);
65             }
66              
67             sub new {
68 0     0 1   my $self = shift->SUPER::new(@_);
69 0 0         $self->_cursor_pos($self->command->[0] eq 'SCAN' ? 1 : 2);
70 0           return $self;
71             }
72              
73             sub next {
74 0 0   0 1   my $cb = ref $_[-1] eq 'CODE' ? pop : undef;
75 0           my $self = shift;
76              
77 0 0         return undef if $self->{_finished};
78              
79 0           my ($command, $pos) = ($self->command, $self->_cursor_pos);
80 0 0         if (@_) { splice @$command, $pos + 1, 4, @_ }
  0            
81             my $wrapper = sub {
82 0     0     my (undef, $err, $resp) = @_;
83 0   0       $command->[$pos] = (my $cur = $resp->[0] // 0);
84 0 0         $self->{_finished} = 1 if $cur == 0;
85 0           return $self->$cb($err, $resp->[1]);
86 0           };
87              
88 0 0         my $resp = $self->redis->_execute(basic => @$command, $cb ? ($wrapper) : ());
89 0 0         return $resp if $cb;
90 0     0     $cb = sub { $_[2] };
  0            
91 0           return $wrapper->(undef, '', $resp);
92             }
93              
94             sub smembers {
95 0     0 1   my $cur = shift->_clone('SSCAN', shift, 0);
96 0           return $cur->all(@_);
97             }
98              
99             sub _clone {
100 0     0     my $self = shift;
101 0           return $self->new(command => [@_])->redis($self->redis);
102             }
103              
104             1;
105              
106             =encoding utf8
107              
108             =head1 NAME
109              
110             Mojo::Redis2::Cursor - Cursor iterator for SCAN commands.
111              
112             =head1 SYNOPSIS
113              
114             use Mojo::Redis2;
115             use Mojo::Redis2::Cursor;
116              
117             my $cursor = Mojo::Redis2::Cursor->new(redis => Mojo::Redis2->new)
118             ->command(["SCAN", 0, MATCH => "namespace*"]);
119              
120             # blocking
121             while (my $r = $cursor->next) { say join "\n", @$r }
122              
123             # or non-blocking
124             use feature "current_sub";
125             $cursor->next(
126             sub {
127             my ($cur, $err, $r) = @_;
128             say join "\n", @$r;
129             return Mojo::IOLoop->stop unless $cur->next(__SUB__);
130             }
131             );
132             Mojo::IOLoop->start;
133              
134              
135             =head1 DESCRIPTION
136              
137             L is an iterator object for C family commands.
138              
139             =head1 ATTRIBUTES
140              
141             =head2 command
142              
143             $arrayref = $self->command;
144              
145             Holds the command that is issued to the redis server, but without updated index
146             information.
147              
148             =head2 redis
149              
150             my $redus = $cursor->redis;
151             $cursor->redis(Mojo::Redis2->new);
152              
153             Redis object to work with.
154              
155             =head1 METHODS
156              
157             L inherits all methods from L and implements
158             the following new ones.
159              
160             =head2 again
161              
162             $cursor->again;
163             my $res = $cursor->again->all;
164              
165             Reset cursor to start iterating from the beginning.
166              
167             =head2 all
168              
169             my $keys = $cursor->all(COUNT => 5);
170             $cursor->all(sub {
171             my ($cur, $err, $res) = @_;
172             });
173              
174             Repeatedly call L to fetch all matching elements. Optional
175             arguments will be passed along.
176              
177             In case of error will return all data fetched so far.
178              
179             =head2 finished
180              
181             my $is_finished = $cursor->finished;
182              
183             Indicate that full iteration had been made and no additional elements can be
184             fetched.
185              
186             =head2 hgetall
187              
188             my $hash = $redis2->scan->hgetall("redis.key");
189             $hash = $cursor->hgetall("redis.key");
190             $cursor->hgetall("redis.key" => sub {...});
191              
192             Implements standard C command using C.
193              
194             =head2 hkeys
195              
196             my $keys = $redis2->scan->hkeys("redis.key");
197             $keys = $cursor->hkeys("redis.key");
198             $cursor->hkeys("redis.key" => sub {...});
199              
200             Implements standard C command using C.
201              
202             =head2 keys
203              
204             my $keys = $redis2->scan->keys;
205             $keys = $cursor->keys("*");
206             $cursor->keys("*" => sub {
207             my ($cur, $err, $keys) = @_;
208             ...
209             });
210              
211             Implements standard C command using C.
212              
213             =head2 new
214              
215             my $cursor = Mojo::Redis2::Cursor->new(
216             command => ["SCAN", 0, MATCH => "namespace*"]);
217             $cursor = Mojo::Redis2::Cursor->new(
218             command => [ZSCAN => "redis.key", 0, COUNT => 15]);
219              
220             Object constructor. Follows same semantics as Redis command.
221              
222             =head2 next
223              
224             # blocking
225             my $res = $cursor->next;
226              
227             # non-blocking
228             $cursor->next(sub {
229             my ($cur, $err, $res) = @_;
230             ...
231             })
232              
233             Issue next C family command with cursor value from previous iteration. If
234             last argument is coderef, will made a non-blocking call. In blocking mode returns
235             arrayref with fetched elements. If no more items available, will return
236             C, for both blocking and non-blocking, without calling callback.
237              
238             my $res = $cursor->next(MATCH => "namespace*");
239             $cursor->next(COUNT => 100, sub { ... });
240              
241             Accepts the same optional arguments as original Redis command, which will replace
242             old values and will be used for this and next iterations.
243              
244             =head2 smembers
245              
246             my $list = $redis2->scan->smembers("redis.key");
247             $list = $cursor->smembers("redis.key");
248             $cursor->smembers("redis.key" => sub {...});
249              
250             Implements standard C command using C.
251              
252             =head1 LINKS
253              
254             L
255              
256             =cut