File Coverage

blib/lib/DR/Tarantool/AsyncClient.pm
Criterion Covered Total %
statement 27 137 19.7
branch 0 32 0.0
condition 0 22 0.0
subroutine 9 30 30.0
pod 10 11 90.9
total 46 232 19.8


line stmt bran cond sub pod time code
1 4     4   5673 use utf8;
  4         10  
  4         21  
2 4     4   105 use strict;
  4         6  
  4         90  
3 4     4   13 use warnings;
  4         4  
  4         109  
4              
5             package DR::Tarantool::AsyncClient;
6 4     4   361 use DR::Tarantool::LLClient;
  4         5  
  4         79  
7 4     4   1304 use DR::Tarantool::Spaces;
  4         7  
  4         106  
8 4     4   1188 use DR::Tarantool::Tuple;
  4         42  
  4         109  
9 4     4   20 use Carp;
  4         4  
  4         211  
10             $Carp::Internal{ (__PACKAGE__) }++;
11 4     4   16 use Data::Dumper;
  4         3  
  4         137  
12 4     4   15 use Scalar::Util 'blessed';
  4         3  
  4         5073  
13              
14             =head1 NAME
15              
16             DR::Tarantool::AsyncClient - async client for L
17              
18             =head1 SYNOPSIS
19              
20             use DR::Tarantool::AsyncClient 'tarantool';
21              
22             DR::Tarantool::AsyncClient->connect(
23             host => '127.0.0.1',
24             port => 12345,
25             spaces => {
26             0 => {
27             name => 'users',
28             fields => [
29             qw(login password role),
30             {
31             name => 'counter',
32             type => 'NUM'
33             }
34             ],
35             indexes => {
36             0 => 'login',
37             1 => [ qw(login password) ],
38             }
39             },
40             2 => {
41             name => 'roles',
42             fields => [ qw(name title) ],
43             indexes => {
44             0 => 'name',
45             1 => {
46             name => 'myindex',
47             fields => [ 'name', 'title' ],
48             }
49             }
50             }
51             },
52             sub {
53             my ($client) = @_;
54             ...
55             }
56             );
57              
58             $client->ping(sub { ... });
59              
60             $client->insert('space', [ 'user', 10, 'password' ], sub { ... });
61              
62             $client->call_lua(foo => ['arg1', 'arg2'], sub { });
63              
64             $client->select('space', 1, sub { ... });
65              
66             $client->delete('space', 1, sub { ... });
67              
68             $client->update('space', 1, [ passwd => set => 'abc' ], sub { .. });
69              
70              
71             =head1 Class methods
72              
73              
74             =cut
75              
76             sub _split_args {
77              
78 0 0   0     if (@_ % 2) {
79 0           my ($self, %opts) = @_;
80 0           my $cb = delete $opts{cb};
81 0           return ($self, $cb, %opts);
82             }
83              
84 0           my $cb = pop;
85 0           splice @_, 1, 0, $cb;
86 0           return @_;
87             }
88              
89              
90             =head2 connect
91              
92             Connects to L, returns (by callback)
93             an object which can be used to make requests.
94              
95             DR::Tarantool::AsyncClient->connect(
96             host => $host,
97             port => $port,
98             spaces => $spaces,
99             reconnect_period => 0.5,
100             reconnect_always => 1,
101             sub {
102             my ($obj) = @_;
103             if (ref $obj) {
104             ... # handle errors
105             }
106             ...
107             }
108             );
109              
110             =head3 Arguments
111              
112             =over
113              
114             =item host & port
115              
116             Address where tarantool is started.
117              
118             =item spaces
119              
120             A hash with space description or a L reference.
121              
122             =item reconnect_period & reconnect_always
123              
124             See L for more details.
125              
126             =back
127              
128             =cut
129              
130             sub connect {
131 0     0 1   my $class = shift;
132 0           my ($cb, %opts);
133 0 0         if ( @_ % 2 ) {
134 0           $cb = pop;
135 0           %opts = @_;
136             } else {
137 0           %opts = @_;
138 0           $cb = delete $opts{cb};
139             }
140              
141 0           $class->_llc->_check_cb( $cb );
142              
143 0   0       my $host = $opts{host} || 'localhost';
144 0 0         my $port = $opts{port} or croak "port isn't defined";
145              
146 0 0         my $spaces = blessed($opts{spaces}) ?
147             $opts{spaces} : DR::Tarantool::Spaces->new($opts{spaces});
148 0   0       my $reconnect_period = $opts{reconnect_period} || 0;
149 0   0       my $reconnect_always = $opts{reconnect_always} || 0;
150              
151             DR::Tarantool::LLClient->connect(
152             host => $host,
153             port => $port,
154             reconnect_period => $reconnect_period,
155             reconnect_always => $reconnect_always,
156             sub {
157 0     0     my ($client) = @_;
158 0           my $self;
159 0 0         if (ref $client) {
160 0   0       $self = bless {
161             llc => $client,
162             spaces => $spaces,
163             } => ref($class) || $class;
164             } else {
165 0           $self = $client;
166             }
167              
168 0           $cb->( $self );
169             }
170 0           );
171              
172 0           return;
173             }
174              
175             =head1 Attributes
176              
177             =head2 space
178              
179             Returns a space object by space name or numeric id. See perldoc
180             L for more details.
181              
182             =cut
183              
184             sub space {
185 0     0 1   my ($self, $name) = @_;
186 0           return $self->{spaces}->space($name);
187             }
188              
189              
190             sub disconnect {
191 0     0 0   my ($self, $cb) = @_;
192 0           $self->_llc->disconnect( $cb );
193             }
194              
195              
196 0 0   0     sub _llc { return $_[0]{llc} if ref $_[0]; return 'DR::Tarantool::LLClient' }
  0            
197              
198             sub _cb_default {
199 0     0     my ($res, $s, $cb) = @_;
200 0 0         if ($res->{status} ne 'ok') {
201 0           $cb->($res->{status} => $res->{code}, $res->{errstr});
202 0           return;
203             }
204              
205 0 0         if ($s) {
206 0           $cb->( ok => $s->tuple_class->unpack( $res->{tuples}, $s ),
207             $res->{code}
208             );
209             } else {
210 0           $cb->( 'ok', $res->{tuples}, $res->{code} );
211             }
212 0           return;
213             }
214              
215              
216             =head1 Worker methods
217              
218             All methods accept callbacks which are invoked with the following arguments:
219              
220             =over
221              
222             =item status
223              
224             On success, this field has value 'B'. The value
225             of this parameter determines the contents of the rest of the callback
226             arguments.
227              
228             =item a tuple or tuples or an error code
229              
230             On success, the second argument contains tuple(s) produced by
231             the request. On error, it contains the server error code.
232              
233             =item errorstr
234              
235             Error string in case of an error.
236              
237             =back
238              
239              
240             sub {
241             if ($_[0] eq 'ok') {
242             my ($status, $tuples) = @_;
243             ...
244             } else {
245             my ($status, $code, $errstr) = @_;
246             }
247             }
248              
249              
250             =head2 ping
251              
252             Ping the server.
253              
254             $client->ping(sub { ... });
255              
256             =head3 Arguments
257              
258             =over
259              
260             =item cb
261              
262             =back
263              
264             =cut
265              
266             sub ping {
267 0     0 1   my ($self, $cb, %opts) = &_split_args;
268 0     0     $self->_llc->ping(sub { _cb_default($_[0], undef, $cb) });
  0            
269             }
270              
271              
272              
273             =head2 insert
274              
275             Insert a tuple into a space.
276              
277             $client->insert('space', [ 'user', 10, 'password' ], sub { ... });
278             $client->insert('space', \@tuple, $flags, sub { ... });
279              
280              
281             =head3 Arguments
282              
283             =over
284              
285             =item space_name
286              
287             =item tuple
288              
289             =item flags (optional)
290              
291             Possible flags are described in perldoc L.
292              
293             =item callback
294              
295             =back
296              
297             =cut
298              
299             sub insert {
300 0     0 1   my $self = shift;
301 0           $self->_llc->_check_cb( my $cb = pop );
302 0           my $space = shift;
303 0           $self->_llc->_check_tuple( my $tuple = shift );
304 0   0       my $flags = pop || 0;
305              
306 0           my $s = $self->{spaces}->space($space);
307              
308             $self->_llc->insert(
309             $s->number,
310             $s->pack_tuple( $tuple ),
311             $flags,
312             sub {
313 0     0     my ($res) = @_;
314 0           _cb_default($res, $s, $cb);
315             }
316 0           );
317 0           return;
318             }
319              
320              
321             =head2 call_lua
322              
323             Call a Lua function. All arguments are passed to Lua as binary strings.
324             Returned tuples can be unpacked using either a space description
325             or a format specification.
326              
327              
328             $client->call_lua(foo => ['arg1', 'arg2'], sub { });
329             $client->call_lua(foo => [], 'space_name', sub { ... });
330             $client->call_lua(foo => \@args,
331             flags => $f,
332             space => $space_name,
333             sub { ... }
334             );
335             $client->call_lua(foo => \@args,
336             fields => [ qw(a b c) ],
337             sub { ... }
338             );
339             $client->call_lua(foo => \@args,
340             fields => [ qw(a b c), { type => 'NUM', name => 'abc'} ... ],
341             sub { ... }
342             );
343              
344             =head3 Arguments
345              
346             =over
347              
348             =item function name
349              
350             =item function arguments
351              
352             =item space or fields
353              
354             Is optional. If given, this space description
355             will be used to interpret contents of tuples
356             returned by the procedure. Alternatively, instead
357             of providing a reference to a space, the format
358             can be set explicitly with B argument.
359              
360             =item callback
361              
362             =back
363              
364             =head4 Optional arguments
365              
366             =over
367              
368             =item space
369              
370             Space name. Use the argument if your function returns tuple(s) from a
371             space described on L.
372              
373             =item fields
374              
375             Output format of the returned tuple (like 'B' in L method).
376              
377             =item flags
378              
379             Reserved option.
380              
381             =item args
382              
383             Format description for stored procedure arguments.
384              
385             =back
386              
387             =cut
388              
389             sub call_lua {
390 0     0 1   my $self = shift;
391 0           my $lua_name = shift;
392 0           my $args = shift;
393 0           $self->_llc->_check_cb( my $cb = pop );
394              
395 0 0         unshift @_ => 'space' if @_ == 1;
396 0           my %opts = @_;
397              
398 0   0       my $flags = $opts{flags} || 0;
399 0           my $space_name = $opts{space};
400 0           my $fields = $opts{fields};
401              
402 0           my $s;
403 0 0 0       croak "You can't use 'fields' and 'space' at the same time"
404             if $fields and $space_name;
405              
406 0 0         if ($space_name) {
    0          
407 0           $s = $self->space( $space_name );
408             } elsif ( $fields ) {
409 0           $s = DR::Tarantool::Space->new(
410             0 =>
411             {
412             name => 'temp_space',
413             fields => $fields,
414             indexes => {}
415             },
416             );
417             } else {
418 0           $s = DR::Tarantool::Space->new(
419             0 =>
420             {
421             name => 'temp_space',
422             fields => [],
423             indexes => {}
424             },
425             );
426             }
427              
428 0 0         if ($opts{args}) {
429 0           my $sa = DR::Tarantool::Space->new(
430             0 =>
431             {
432             name => 'temp_space_args',
433             fields => $opts{args},
434             indexes => {}
435             },
436             );
437 0           $args = $sa->pack_tuple( $args );
438             }
439              
440             $self->_llc->call_lua(
441             $lua_name,
442             $args,
443             $flags,
444 0     0     sub { _cb_default($_[0], $s, $cb) }
445 0           );
446             }
447              
448              
449             =head2 select
450              
451             Select a tuple from a space by index.
452              
453             $tuples = $client->select('space', 1, sub { ... });
454             $tuples = $client->select('space', [1, 2], sub { ... });
455              
456             $tuples = $client->select('space_name',
457             [1,2,3] => 'index_name', sub { ... });
458              
459             =head3 Arguments
460              
461             =over
462              
463             =item space name
464              
465             =item key(s)
466              
467             =item optional arguments
468              
469             =item callback
470              
471             =back
472              
473             =head3 optional arguments
474              
475             This section can contain only one element, which is either an index name,
476             or a hash with the following fields:
477              
478             =over
479              
480             =item index
481              
482             index name or number
483              
484             =item limit
485              
486             =item offset
487              
488             =back
489              
490             =cut
491              
492             sub select {
493 0     0 1   my $self = shift;
494 0           my $space = shift;
495 0           my $keys = shift;
496              
497 0           my $cb = pop;
498              
499 0           my ($index, $limit, $offset);
500              
501 0 0         if (@_ == 1) {
    0          
    0          
502 0           $index = shift;
503             } elsif (@_ == 3) {
504 0           ($index, $limit, $offset) = @_;
505             } elsif (@_) {
506 0           my %opts = @_;
507 0           $index = $opts{index};
508 0           $limit = $opts{limit};
509 0           $offset = $opts{offset};
510             }
511              
512 0   0       $index ||= 0;
513              
514 0           my $s = $self->space($space);
515              
516             $self->_llc->select(
517             $s->number,
518             $s->_index( $index )->{no},
519             $s->pack_keys( $keys, $index ),
520             $limit,
521             $offset,
522              
523 0     0     sub { _cb_default($_[0], $s, $cb) }
524 0           );
525             }
526              
527              
528             =head2 delete
529              
530             Delete a tuple.
531              
532             $client->delete('space', 1, sub { ... });
533             $client->delete('space', $key, $flags, sub { ... });
534              
535             Tuple is always deleted by primary key.
536              
537             =head3 Arguments
538              
539             =over
540              
541             =item space name
542              
543             =item key
544              
545             =item flags (optional)
546              
547             Server flags, as described in perldoc L.
548              
549             =item callback
550              
551             =back
552              
553             =cut
554              
555             sub delete :method {
556 0     0 1   my $self = shift;
557 0           my $space = shift;
558 0           my $key = shift;
559 0           $self->_llc->_check_cb( my $cb = pop );
560 0   0       my $flags = shift || 0;
561              
562 0           my $s = $self->space($space);
563              
564             $self->_llc->delete(
565             $s->number,
566             $s->pack_primary_key( $key ),
567             $flags,
568 0     0     sub { _cb_default($_[0], $s, $cb) }
569 0           );
570             }
571              
572              
573             =head2 update
574              
575             Update a tuple.
576              
577             $client->update('space', 1, [ passwd => set => 'abc' ], sub { .. });
578             $client->update(
579             'space',
580             1,
581             [ [ passwd => set => 'abc' ], [ login => 'delete' ] ],
582             sub { ... }
583             );
584              
585             =head3 Arguments
586              
587             =over
588              
589             =item space name
590              
591             =item key
592              
593             =item operation list
594              
595             =item flags (optional)
596              
597             Server flags, as described in perldoc L.
598              
599             =item callback
600              
601             =back
602              
603             =cut
604              
605             sub update {
606 0     0 1   my $self = shift;
607 0           my $space = shift;
608 0           my $key = shift;
609 0           my $op = shift;
610 0           $self->_llc->_check_cb( my $cb = pop );
611 0   0       my $flags = shift || 0;
612              
613 0           my $s = $self->space($space);
614              
615             $self->_llc->update(
616             $s->number,
617             $s->pack_primary_key( $key ),
618             $s->pack_operations( $op ),
619             $flags,
620 0     0     sub { _cb_default($_[0], $s, $cb) }
621 0           );
622             }
623              
624              
625             =head2 last_code
626              
627             The error code returned by the last request
628             (see L).
629              
630             =cut
631              
632 0     0 1   sub last_code { $_[0]->_llc->last_code }
633              
634              
635             =head2 last_error_string
636              
637             The error message associated with the last request
638             (see L), if
639             there was an error.
640              
641             =cut
642              
643 0     0 1   sub last_error_string { $_[0]->_llc->last_error_string }
644              
645              
646             =head1 COPYRIGHT AND LICENSE
647              
648             Copyright (C) 2011 Dmitry E. Oboukhov
649             Copyright (C) 2011 Roman V. Nikolaev
650              
651             This program is free software, you can redistribute it and/or
652             modify it under the terms of the Artistic License.
653              
654             =head1 VCS
655              
656             The project is placed git repo on github:
657             L.
658              
659             =cut
660              
661             1;