]> git.saurik.com Git - redis.git/blob - client-libraries/ruby/lib/redis.rb
first commit
[redis.git] / client-libraries / ruby / lib / redis.rb
1 require 'socket'
2 require File.join(File.dirname(__FILE__),'better_timeout')
3 require 'set'
4
5 class RedisError < StandardError
6 end
7
8 class Redis
9 OK = "+OK".freeze
10 ERRCODE = "-".freeze
11 NIL = 'nil'.freeze
12 CTRLF = "\r\n".freeze
13
14 def to_s
15 "#{host}:#{port}"
16 end
17
18 def port
19 @opts[:port]
20 end
21
22 def host
23 @opts[:host]
24 end
25
26 def initialize(opts={})
27 @opts = {:host => 'localhost', :port => '6379'}.merge(opts)
28 end
29
30 # SET key value
31 # Time complexity: O(1)
32 # Set the string value as value of the key. The string can't be longer
33 # than 1073741824 bytes (1 GB).
34 #
35 # Return value: status code reply
36 def []=(key, val)
37 val = redis_marshal(val)
38 timeout_retry(3, 3){
39 write "SET #{key} #{val.to_s.size}\r\n#{val}\r\n"
40 status_code_reply
41 }
42 end
43
44 # SETNX key value
45 #
46 # Time complexity: O(1)
47 # SETNX works exactly like SET with the only difference that if the key
48 # already exists no operation is performed. SETNX actually means "SET if Not eXists".
49 #
50 # *Return value: integer reply, specifically:
51 #
52 # 1 if the key was set 0 if the key was not set
53 def set_unless_exists(key, val)
54 val = redis_marshal(val)
55 timeout_retry(3, 3){
56 write "SETNX #{key} #{val.to_s.size}\r\n#{val}\r\n"
57 integer_reply == 1
58 }
59 end
60
61 # GET key
62 # Time complexity: O(1)
63 # Get the value of the specified key. If the key does not exist the special value
64 # 'nil' is returned. If the value stored at key is not a string an error is
65 # returned because GET can only handle string values.
66 #
67 # Return value: bulk reply
68 def [](key)
69 timeout_retry(3, 3){
70 write "GET #{key}\r\n"
71 redis_unmarshal(bulk_reply)
72 }
73 end
74
75 # INCR key
76 # INCRBY key value
77 # Time complexity: O(1)
78 # Increment the number stored at key by one. If the key does not exist or contains
79 # a value of a wrong type, set the key to the value of "1" (like if the previous
80 # value was zero).
81 #
82 # INCRBY works just like INCR but instead to increment by 1 the increment is value.
83 #
84 # Return value: integer reply
85 def incr(key, increment=nil)
86 timeout_retry(3, 3){
87 if increment
88 write "INCRBY #{key} #{increment}\r\n"
89 else
90 write "INCR #{key}\r\n"
91 end
92 integer_reply
93 }
94 end
95
96
97 # DECR key
98 #
99 # DECRBY key value
100 #
101 # Time complexity: O(1) Like INCR/INCRBY but decrementing instead of incrementing.
102 def decr(key, increment=nil)
103 timeout_retry(3, 3){
104 if increment
105 write "DECRBY #{key} #{increment}\r\n"
106 else
107 write "DECR #{key}\r\n"
108 end
109 integer_reply
110 }
111 end
112
113 # RANDOMKEY
114 # Time complexity: O(1)
115 # Returns a random key from the currently seleted DB.
116 #
117 # Return value: single line reply
118 def randkey
119 timeout_retry(3, 3){
120 write "RANDOMKEY\r\n"
121 single_line_reply
122 }
123 end
124
125 # RENAME oldkey newkey
126 #
127 # Atomically renames the key oldkey to newkey. If the source and destination
128 # name are the same an error is returned. If newkey already exists it is
129 # overwritten.
130 #
131 # Return value: status code reply
132 def rename!(oldkey, newkey)
133 timeout_retry(3, 3){
134 write "RENAME #{oldkey} #{newkey}\r\n"
135 status_code_reply
136 }
137 end
138
139 # RENAMENX oldkey newkey
140 # Just like RENAME but fails if the destination key newkey already exists.
141 #
142 # *Return value: integer reply, specifically:
143 #
144 # 1 if the key was renamed 0 if the target key already exist -1 if the
145 # source key does not exist -3 if source and destination keys are the same
146 def rename(oldkey, newkey)
147 timeout_retry(3, 3){
148 write "RENAMENX #{oldkey} #{newkey}\r\n"
149 case integer_reply
150 when -1
151 raise RedisError, "source key: #{oldkey} does not exist"
152 when 0
153 raise RedisError, "target key: #{oldkey} already exists"
154 when -3
155 raise RedisError, "source and destination keys are the same"
156 when 1
157 true
158 end
159 }
160 end
161
162 # EXISTS key
163 # Time complexity: O(1)
164 # Test if the specified key exists. The command returns "0" if the key
165 # exists, otherwise "1" is returned. Note that even keys set with an empty
166 # string as value will return "1".
167 #
168 # *Return value: integer reply, specifically:
169 #
170 # 1 if the key exists 0 if the key does not exist
171 def key?(key)
172 timeout_retry(3, 3){
173 write "EXISTS #{key}\r\n"
174 integer_reply == 1
175 }
176 end
177
178 # DEL key
179 # Time complexity: O(1)
180 # Remove the specified key. If the key does not exist no operation is
181 # performed. The command always returns success.
182 #
183 # *Return value: integer reply, specifically:
184 #
185 # 1 if the key was removed 0 if the key does not exist
186 def delete(key)
187 timeout_retry(3, 3){
188 write "DEL #{key}\r\n"
189 integer_reply == 1
190 }
191 end
192
193 # KEYS pattern
194 # Time complexity: O(n) (with n being the number of keys in the DB)
195 # Returns all the keys matching the glob-style pattern as space separated strings.
196 # For example if you have in the database the keys "foo" and "foobar" the command
197 # "KEYS foo*" will return "foo foobar".
198 #
199 # Note that while the time complexity for this operation is O(n) the constant times
200 # are pretty low. For example Redis running on an entry level laptop can scan a 1
201 # million keys database in 40 milliseconds. Still it's better to consider this one
202 # of the slow commands that may ruin the DB performance if not used with care.
203 #
204 # Return value: bulk reply
205 def keys(glob)
206 timeout_retry(3, 3){
207 write "KEYS #{glob}\r\n"
208 bulk_reply.split(' ')
209 }
210 end
211
212 # TYPE key
213 #
214 # Time complexity: O(1) Return the type of the value stored at key in form of
215 # a string. The type can be one of "none", "string", "list", "set". "none" is
216 # returned if the key does not exist.
217 #
218 # Return value: single line reply
219 def type?(key)
220 timeout_retry(3, 3){
221 write "TYPE #{key}\r\n"
222 single_line_reply
223 }
224 end
225
226 # RPUSH key string
227 #
228 # Time complexity: O(1)
229 # Add the given string to the tail of the list contained at key. If the key
230 # does not exist an empty list is created just before the append operation.
231 # If the key exists but is not a List an error is returned.
232 #
233 # Return value: status code reply
234 def push_tail(key, string)
235 timeout_retry(3, 3){
236 write "RPUSH #{key} #{string.to_s.size}\r\n#{string.to_s}\r\n"
237 status_code_reply
238 }
239 end
240
241 # LPUSH key string
242 # Time complexity: O(1)
243 # Add the given string to the head of the list contained at key. If the
244 # key does not exist an empty list is created just before the append operation.
245 # If the key exists but is not a List an error is returned.
246 #
247 # Return value: status code reply
248 def push_head(key, string)
249 timeout_retry(3, 3){
250 write "LPUSH #{key} #{string.to_s.size}\r\n#{string.to_s}\r\n"
251 status_code_reply
252 }
253 end
254
255 # LPOP key
256 #
257 # Time complexity: O(1)
258 # Atomically return and remove the first element of the list. For example if
259 # the list contains the elements "a","b","c" LPOP will return "a" and the
260 # list will become "b","c".
261 #
262 # If the key does not exist or the list is already empty the special value
263 # 'nil' is returned.
264 #
265 # Return value: bulk reply
266 def pop_head(key)
267 timeout_retry(3, 3){
268 write "LPOP #{key}\r\n"
269 bulk_reply
270 }
271 end
272
273 # RPOP key
274 # This command works exactly like LPOP, but the last element instead
275 # of the first element of the list is returned/deleted.
276 def pop_tail(key)
277 timeout_retry(3, 3){
278 write "RPOP #{key}\r\n"
279 bulk_reply
280 }
281 end
282
283 # LSET key index value
284 # Time complexity: O(N) (with N being the length of the list)
285 # Set the list element at index (see LINDEX for information about the index argument) with the new value. Out of range indexes will generate an error. Note that setting the first or last elements of the list is O(1).
286 #
287 # Return value: status code reply
288 def list_set(key, index, val)
289 timeout_retry(3, 3){
290 write "LSET #{key} #{index} #{val.to_s.size}\r\n#{val}\r\n"
291 status_code_reply
292 }
293 end
294
295
296 # LLEN key
297 # Time complexity: O(1)
298 # Return the length of the list stored at the specified key. If the key does not
299 # exist zero is returned (the same behaviour as for empty lists). If the value
300 # stored at key is not a list the special value -1 is returned. Note: client
301 # library should raise an exception when -1 is returned instead to pass the
302 # value back to the caller like a normal list length value.
303 #
304 # *Return value: integer reply, specifically:
305 #
306 # the length of the list as an integer
307 # >=
308 # 0 if the operation succeeded -2 if the specified key does not hold a list valu
309 def list_length(key)
310 timeout_retry(3, 3){
311 write "LLEN #{key}\r\n"
312 case i = integer_reply
313 when -2
314 raise RedisError, "key: #{key} does not hold a list value"
315 else
316 i
317 end
318 }
319 end
320
321 # LRANGE key start end
322 # Time complexity: O(n) (with n being the length of the range)
323 # Return the specified elements of the list stored at the specified key. Start
324 # and end are zero-based indexes. 0 is the first element of the list (the list head),
325 # 1 the next element and so on.
326 #
327 # For example LRANGE foobar 0 2 will return the first three elements of the list.
328 #
329 # start and end can also be negative numbers indicating offsets from the end of the list.
330 # For example -1 is the last element of the list, -2 the penultimate element and so on.
331 #
332 # Indexes out of range will not produce an error: if start is over the end of the list,
333 # or start > end, an empty list is returned. If end is over the end of the list Redis
334 # will threat it just like the last element of the list.
335 #
336 # Return value: multi bulk reply
337 def list_range(key, start, ending)
338 timeout_retry(3, 3){
339 write "LRANGE #{key} #{start} #{ending}\r\n"
340 multi_bulk_reply
341 }
342 end
343
344
345 # LTRIM key start end
346 # Time complexity: O(n) (with n being len of list - len of range)
347 # Trim an existing list so that it will contain only the specified range of
348 # elements specified. Start and end are zero-based indexes. 0 is the first
349 # element of the list (the list head), 1 the next element and so on.
350 #
351 # For example LTRIM foobar 0 2 will modify the list stored at foobar key so that
352 # only the first three elements of the list will remain.
353 #
354 # start and end can also be negative numbers indicating offsets from the end of
355 # the list. For example -1 is the last element of the list, -2 the penultimate
356 # element and so on.
357 #
358 # Indexes out of range will not produce an error: if start is over the end of
359 # the list, or start > end, an empty list is left as value. If end over the
360 # end of the list Redis will threat it just like the last element of the list.
361 #
362 # Hint: the obvious use of LTRIM is together with LPUSH/RPUSH. For example:
363 #
364 # LPUSH mylist <someelement> LTRIM mylist 0 99
365 # The above two commands will push elements in the list taking care that the
366 # list will not grow without limits. This is very useful when using Redis
367 # to store logs for example. It is important to note that when used in this
368 # way LTRIM is an O(1) operation because in the average case just one element
369 # is removed from the tail of the list.
370 #
371 # Return value: status code reply
372 def list_trim(key, start, ending)
373 timeout_retry(3, 3){
374 write "LTRIM #{key} #{start} #{ending}\r\n"
375 status_code_reply
376 }
377 end
378
379 # LINDEX key index
380 # Time complexity: O(n) (with n being the length of the list)
381 # Return the specified element of the list stored at the specified key. 0 is
382 # the first element, 1 the second and so on. Negative indexes are supported,
383 # for example -1 is the last element, -2 the penultimate and so on.
384 #
385 # If the value stored at key is not of list type an error is returned. If
386 # the index is out of range an empty string is returned.
387 #
388 # Note that even if the average time complexity is O(n) asking for the first
389 # or the last element of the list is O(1).
390 #
391 # Return value: bulk reply
392 def list_index(key, index)
393 timeout_retry(3, 3){
394 write "LINDEX #{key} #{index}\r\n"
395 bulk_reply
396 }
397 end
398
399 # SADD key member
400 # Time complexity O(1)
401 # Add the specified member to the set value stored at key. If member is
402 # already a member of the set no operation is performed. If key does not
403 # exist a new set with the specified member as sole member is crated. If
404 # the key exists but does not hold a set value an error is returned.
405 #
406 # *Return value: integer reply, specifically:
407 #
408 # 1 if the new element was added 0 if the new element was already a member
409 # of the set -2 if the key contains a non set value
410 def set_add(key, member)
411 timeout_retry(3, 3){
412 write "SADD #{key} #{member.to_s.size}\r\n#{member}\r\n"
413 case integer_reply
414 when 1
415 true
416 when 0
417 false
418 when -2
419 raise RedisError, "key: #{key} contains a non set value"
420 end
421 }
422 end
423
424 # SREM key member
425 #
426 # Time complexity O(1)
427 # Remove the specified member from the set value stored at key. If member
428 # was not a member of the set no operation is performed. If key does not
429 # exist or does not hold a set value an error is returned.
430 #
431 # *Return value: integer reply, specifically:
432 #
433 # 1 if the new element was removed 0 if the new element was not a member
434 # of the set -2 if the key does not hold a set value
435 def set_delete(key, member)
436 timeout_retry(3, 3){
437 write "SREM #{key} #{member.to_s.size}\r\n#{member}\r\n"
438 case integer_reply
439 when 1
440 true
441 when 0
442 false
443 when -2
444 raise RedisError, "key: #{key} contains a non set value"
445 end
446 }
447 end
448
449 # SCARD key
450 # Time complexity O(1)
451 # Return the set cardinality (number of elements). If the key does not
452 # exist 0 is returned, like for empty sets. If the key does not hold a
453 # set value -1 is returned. Client libraries should raise an error when -1
454 # is returned instead to pass the value to the caller.
455 #
456 # *Return value: integer reply, specifically:
457 #
458 # the cardinality (number of elements) of the set as an integer
459 # >=
460 # 0 if the operation succeeded -2 if the specified key does not hold a set value
461 def set_count(key)
462 timeout_retry(3, 3){
463 write "SCARD #{key}\r\n"
464 case i = integer_reply
465 when -2
466 raise RedisError, "key: #{key} contains a non set value"
467 else
468 i
469 end
470 }
471 end
472
473 # SISMEMBER key member
474 #
475 # Time complexity O(1)
476 # Return 1 if member is a member of the set stored at key, otherwise 0 is
477 # returned. On error a negative value is returned. Client libraries should
478 # raise an error when a negative value is returned instead to pass the value
479 # to the caller.
480 #
481 # *Return value: integer reply, specifically:
482 #
483 # 1 if the element is a member of the set 0 if the element is not a member of
484 # the set OR if the key does not exist -2 if the key does not hold a set value
485 def set_member?(key, member)
486 timeout_retry(3, 3){
487 write "SISMEMBER #{key} #{member.to_s.size}\r\n#{member}\r\n"
488 case integer_reply
489 when 1
490 true
491 when 0
492 false
493 when -2
494 raise RedisError, "key: #{key} contains a non set value"
495 end
496 }
497 end
498
499 # SINTER key1 key2 ... keyN
500 # Time complexity O(N*M) worst case where N is the cardinality of the smallest
501 # set and M the number of sets
502 # Return the members of a set resulting from the intersection of all the sets
503 # hold at the specified keys. Like in LRANGE the result is sent to the client
504 # as a multi-bulk reply (see the protocol specification for more information).
505 # If just a single key is specified, then this command produces the same
506 # result as SELEMENTS. Actually SELEMENTS is just syntax sugar for SINTERSECT.
507 #
508 # If at least one of the specified keys does not exist or does not hold a set
509 # value an error is returned.
510 #
511 # Return value: multi bulk reply
512 def set_intersect(*keys)
513 timeout_retry(3, 3){
514 write "SINTER #{keys.join(' ')}\r\n"
515 Set.new(multi_bulk_reply)
516 }
517 end
518
519 # SINTERSTORE dstkey key1 key2 ... keyN
520 #
521 # Time complexity O(N*M) worst case where N is the cardinality of the smallest set and M the number of sets
522 # This commnad works exactly like SINTER but instead of being returned the resulting set is sotred as dstkey.
523 #
524 # Return value: status code reply
525 def set_inter_store(destkey, *keys)
526 timeout_retry(3, 3){
527 write "SINTERSTORE #{destkey} #{keys.join(' ')}\r\n"
528 status_code_reply
529 }
530 end
531
532 # SMEMBERS key
533 #
534 # Time complexity O(N)
535 # Return all the members (elements) of the set value stored at key.
536 # This is just syntax glue for SINTERSECT.
537 def set_members(key)
538 timeout_retry(3, 3){
539 write "SMEMBERS #{key}\r\n"
540 Set.new(multi_bulk_reply)
541 }
542 end
543
544
545 # SORT key [BY pattern] [GET|DEL|INCR|DECR pattern] [ASC|DESC] [LIMIT start count]
546 # Sort the elements contained in the List or Set value at key. By default sorting is
547 # numeric with elements being compared as double precision floating point numbers.
548 # This is the simplest form of SORT.
549 # SORT mylist
550 #
551 # Assuming mylist contains a list of numbers, the return value will be the list of
552 # numbers ordered from the smallest to the bigger number. In order to get the sorting
553 # in reverse order use DESC:
554 # SORT mylist DESC
555 #
556 # ASC is also supported but it's the default so you don't really need it. If you
557 # want to sort lexicographically use ALPHA. Note that Redis is utf-8 aware
558 # assuming you set the right value for the LC_COLLATE environment variable.
559 #
560 # Sort is able to limit the number of results using the LIMIT option:
561 # SORT mylist LIMIT 0 10
562 # In the above example SORT will return only 10 elements, starting from the first one
563 # (star is zero-based). Almost all the sort options can be mixed together. For example:
564 # SORT mylist LIMIT 0 10 ALPHA DESC
565 # Will sort mylist lexicographically, in descending order, returning only the first
566 # 10 elements.
567 # Sometimes you want to sort elements using external keys as weights to compare
568 # instead to compare the actual List or Set elements. For example the list mylist
569 # may contain the elements 1, 2, 3, 4, that are just the unique IDs of objects
570 # stored at object_1, object_2, object_3 and object_4, while the keys weight_1,
571 # weight_2, weight_3 and weight_4 can contain weights we want to use to sort the
572 # list of objects identifiers. We can use the following command:
573 # SORT mylist BY weight_*
574 # the BY option takes a pattern (weight_* in our example) that is used in order to
575 # generate the key names of the weights used for sorting. Weight key names are obtained
576 # substituting the first occurrence of * with the actual value of the elements on the
577 # list (1,2,3,4 in our example).
578 # Still our previous example will return just the sorted IDs. Often it is needed to
579 # get the actual objects sorted (object_1, ..., object_4 in the example). We can do
580 # it with the following command:
581 # SORT mylist BY weight_* GET object_*
582 # Note that GET can be used multiple times in order to get more key for every
583 # element of the original List or Set sorted.
584
585 # redis.sort 'index', :by => 'weight_*',
586 # :order => 'DESC ALPHA',
587 # :limit => [0,10],
588 # :get => 'obj_*'
589 def sort(key, opts={})
590 cmd = "SORT #{key}"
591 cmd << " BY #{opts[:by]}" if opts[:by]
592 cmd << " GET #{opts[:get]}" if opts[:get]
593 cmd << " INCR #{opts[:incr]}" if opts[:incr]
594 cmd << " DEL #{opts[:del]}" if opts[:del]
595 cmd << " DECR #{opts[:decr]}" if opts[:decr]
596 cmd << " #{opts[:order]}" if opts[:order]
597 cmd << " LIMIT #{opts[:limit].join(' ')}" if opts[:limit]
598 cmd << "\r\n"
599 write cmd
600 multi_bulk_reply
601 end
602
603 # ADMIN functions for redis
604
605 # SELECT index
606 #
607 # Select the DB with having the specified zero-based numeric index.
608 # For default every new client connection is automatically selected to DB 0.
609 # Return value: status code reply
610 def select_db(index)
611 timeout_retry(3, 3){
612 write "SELECT #{index}\r\n"
613 status_code_reply
614 }
615 end
616
617 # MOVE key dbindex
618 #
619 # Move the specified key from the currently selected DB to the specified
620 # destination DB. Note that this command returns 1 only if the key was
621 # successfully moved, and 0 if the target key was already there or if
622 # the source key was not found at all, so it is possible to use MOVE
623 # as a locking primitive.
624 #
625 # *Return value: integer reply, specifically:
626 #
627 # 1 if the key was moved 0 if the key was not moved because already
628 # present on the target DB or was not found in the current DB. -3
629 # if the destination DB is the same as the source DB -4 if the database
630 # index if out of range
631 def move(key, index)
632 timeout_retry(3, 3){
633 write "MOVE #{index}\r\n"
634 case integer_reply
635 when 1
636 true
637 when 0
638 false
639 when -3
640 raise RedisError, "destination db same as source db"
641 when -4
642 raise RedisError, "db index if out of range"
643 end
644 }
645 end
646
647 # SAVE
648 #
649 # Save the DB on disk. The server hangs while the saving is not completed,
650 # no connection is served in the meanwhile. An OK code is returned when
651 # the DB was fully stored in disk.
652 # Return value: status code reply
653 def save
654 timeout_retry(3, 3){
655 write "SAVE\r\n"
656 status_code_reply
657 }
658 end
659
660 # BGSAVE
661 #
662 # Save the DB in background. The OK code is immediately returned. Redis
663 # forks, the parent continues to server the clients, the child saves
664 # the DB on disk then exit. A client my be able to check if the operation
665 # succeeded using the LASTSAVE command.
666 # Return value: status code reply
667 def bgsave
668 timeout_retry(3, 3){
669 write "BGSAVE\r\n"
670 status_code_reply
671 }
672 end
673
674 # LASTSAVE
675 #
676 # Return the UNIX TIME of the last DB save executed with success. A client
677 # may check if a BGSAVE command succeeded reading the LASTSAVE value, then
678 # issuing a BGSAVE command and checking at regular intervals every N seconds
679 # if LASTSAVE changed.
680 #
681 # Return value: integer reply (UNIX timestamp)
682 def lastsave
683 timeout_retry(3, 3){
684 write "LASTSAVE\r\n"
685 integer_reply
686 }
687 end
688
689 def quit
690 timeout_retry(3, 3){
691 write "QUIT\r\n"
692 status_code_reply
693 }
694 end
695
696 private
697
698 def redis_unmarshal(obj)
699 if obj[0] == 4
700 Marshal.load(obj)
701 else
702 obj
703 end
704 end
705
706 def redis_marshal(obj)
707 case obj
708 when String, Integer
709 obj
710 else
711 Marshal.dump(obj)
712 end
713 end
714
715 def close
716 socket.close unless socket.closed?
717 end
718
719 def timeout_retry(time, retries, &block)
720 timeout(time, &block)
721 rescue TimeoutError
722 retries -= 1
723 retry unless retries < 0
724 end
725
726 def socket
727 connect if (!@socket or @socket.closed?)
728 @socket
729 end
730
731 def connect
732 @socket = TCPSocket.new(@opts[:host], @opts[:port])
733 @socket.sync = true
734 @socket
735 end
736
737 def read(length, nodebug=true)
738 retries = 3
739 res = socket.read(length)
740 puts "read: #{res}" if @opts[:debug] && nodebug
741 res
742 rescue
743 retries -= 1
744 if retries > 0
745 connect
746 retry
747 end
748 end
749
750 def write(data)
751 puts "write: #{data}" if @opts[:debug]
752 retries = 3
753 socket.write(data)
754 rescue
755 retries -= 1
756 if retries > 0
757 connect
758 retry
759 end
760 end
761
762 def nibble_end
763 read(2)
764 end
765
766 def read_proto
767 print "read proto: " if @opts[:debug]
768 buff = ""
769 while (char = read(1, false))
770 print char if @opts[:debug]
771 buff << char
772 break if buff[-2..-1] == CTRLF
773 end
774 puts if @opts[:debug]
775 buff[0..-3]
776 end
777
778
779 def status_code_reply
780 res = read_proto
781 if res.index(ERRCODE) == 0
782 raise RedisError, res
783 else
784 true
785 end
786 end
787
788 def bulk_reply
789 res = read_proto
790 if res.index(ERRCODE) == 0
791 err = read(res.to_i.abs)
792 nibble_end
793 raise RedisError, err
794 elsif res != NIL
795 val = read(res.to_i.abs)
796 nibble_end
797 val
798 else
799 nil
800 end
801 end
802
803
804 def multi_bulk_reply
805 res = read_proto
806 if res.index(ERRCODE) == 0
807 err = read(res.to_i.abs)
808 nibble_end
809 raise RedisError, err
810 elsif res == NIL
811 nil
812 else
813 items = Integer(res)
814 list = []
815 items.times do
816 len = Integer(read_proto)
817 if len == -1
818 nil
819 else
820 list << read(len)
821 end
822 nibble_end
823 end
824 list
825 end
826 end
827
828 def single_line_reply
829 read_proto
830 end
831
832 def integer_reply
833 Integer(read_proto)
834 end
835
836 end