redis 4.1.0 → 4.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/redis.rb CHANGED
@@ -1,14 +1,31 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "monitor"
2
4
  require_relative "redis/errors"
3
5
 
4
6
  class Redis
7
+ @exists_returns_integer = true
5
8
 
6
- def self.current
7
- @current ||= Redis.new
9
+ class << self
10
+ attr_reader :exists_returns_integer
11
+
12
+ def exists_returns_integer=(value)
13
+ unless value
14
+ message = "`Redis#exists(key)` will return an Integer by default in redis-rb 4.3. The option to explicitly " \
15
+ "disable this behaviour via `Redis.exists_returns_integer` will be removed in 5.0. You should use " \
16
+ "`exists?` instead."
17
+
18
+ ::Kernel.warn(message)
19
+ end
20
+
21
+ @exists_returns_integer = value
22
+ end
23
+
24
+ attr_writer :current
8
25
  end
9
26
 
10
- def self.current=(redis)
11
- @current = redis
27
+ def self.current
28
+ @current ||= Redis.new
12
29
  end
13
30
 
14
31
  include MonitorMixin
@@ -16,18 +33,23 @@ class Redis
16
33
  # Create a new client instance
17
34
  #
18
35
  # @param [Hash] options
19
- # @option options [String] :url (value of the environment variable REDIS_URL) a Redis URL, for a TCP connection: `redis://:[password]@[hostname]:[port]/[db]` (password, port and database are optional), for a unix socket connection: `unix://[path to Redis socket]`. This overrides all other options.
36
+ # @option options [String] :url (value of the environment variable REDIS_URL) a Redis URL, for a TCP connection:
37
+ # `redis://:[password]@[hostname]:[port]/[db]` (password, port and database are optional), for a unix socket
38
+ # connection: `unix://[path to Redis socket]`. This overrides all other options.
20
39
  # @option options [String] :host ("127.0.0.1") server hostname
21
- # @option options [Fixnum] :port (6379) server port
40
+ # @option options [Integer] :port (6379) server port
22
41
  # @option options [String] :path path to server socket (overrides host and port)
23
42
  # @option options [Float] :timeout (5.0) timeout in seconds
24
43
  # @option options [Float] :connect_timeout (same as timeout) timeout for initial connect in seconds
44
+ # @option options [String] :username Username to authenticate against server
25
45
  # @option options [String] :password Password to authenticate against server
26
- # @option options [Fixnum] :db (0) Database to select after initial connect
46
+ # @option options [Integer] :db (0) Database to select after initial connect
27
47
  # @option options [Symbol] :driver Driver to use, currently supported: `:ruby`, `:hiredis`, `:synchrony`
28
- # @option options [String] :id ID for the client connection, assigns name to current connection by sending `CLIENT SETNAME`
29
- # @option options [Hash, Fixnum] :tcp_keepalive Keepalive values, if Fixnum `intvl` and `probe` are calculated based on the value, if Hash `time`, `intvl` and `probes` can be specified as a Fixnum
30
- # @option options [Fixnum] :reconnect_attempts Number of attempts trying to connect
48
+ # @option options [String] :id ID for the client connection, assigns name to current connection by sending
49
+ # `CLIENT SETNAME`
50
+ # @option options [Hash, Integer] :tcp_keepalive Keepalive values, if Integer `intvl` and `probe` are calculated
51
+ # based on the value, if Hash `time`, `intvl` and `probes` can be specified as a Integer
52
+ # @option options [Integer] :reconnect_attempts Number of attempts trying to connect
31
53
  # @option options [Boolean] :inherit_socket (false) Whether to use socket in forked process or not
32
54
  # @option options [Array] :sentinels List of sentinels to contact
33
55
  # @option options [Symbol] :role (:master) Role to fetch via Sentinel, either `:master` or `:slave`
@@ -51,7 +73,7 @@ class Redis
51
73
  end
52
74
 
53
75
  # Run code with the client reconnecting
54
- def with_reconnect(val=true, &blk)
76
+ def with_reconnect(val = true, &blk)
55
77
  synchronize do |client|
56
78
  client.with_reconnect(val, &blk)
57
79
  end
@@ -94,7 +116,9 @@ class Redis
94
116
  # See http://redis.io/topics/pipelining for more details.
95
117
  #
96
118
  def queue(*command)
97
- @queue[Thread.current.object_id] << command
119
+ synchronize do
120
+ @queue[Thread.current.object_id] << command
121
+ end
98
122
  end
99
123
 
100
124
  # Sends all commands in the queue.
@@ -104,7 +128,12 @@ class Redis
104
128
  def commit
105
129
  synchronize do |client|
106
130
  begin
107
- client.call_pipelined(@queue[Thread.current.object_id])
131
+ pipeline = Pipeline.new(client)
132
+ @queue[Thread.current.object_id].each do |command|
133
+ pipeline.call(command)
134
+ end
135
+
136
+ client.call_pipelined(pipeline)
108
137
  ensure
109
138
  @queue.delete(Thread.current.object_id)
110
139
  end
@@ -117,18 +146,19 @@ class Redis
117
146
 
118
147
  # Authenticate to the server.
119
148
  #
120
- # @param [String] password must match the password specified in the
121
- # `requirepass` directive in the configuration file
149
+ # @param [Array<String>] args includes both username and password
150
+ # or only password
122
151
  # @return [String] `OK`
123
- def auth(password)
152
+ # @see https://redis.io/commands/auth AUTH command
153
+ def auth(*args)
124
154
  synchronize do |client|
125
- client.call([:auth, password])
155
+ client.call([:auth, *args])
126
156
  end
127
157
  end
128
158
 
129
159
  # Change the selected database for the current connection.
130
160
  #
131
- # @param [Fixnum] db zero-based index of the DB to use (0 to 15)
161
+ # @param [Integer] db zero-based index of the DB to use (0 to 15)
132
162
  # @return [String] `OK`
133
163
  def select(db)
134
164
  synchronize do |client|
@@ -197,7 +227,7 @@ class Redis
197
227
  def config(action, *args)
198
228
  synchronize do |client|
199
229
  client.call([:config, action] + args) do |reply|
200
- if reply.kind_of?(Array) && action == :get
230
+ if reply.is_a?(Array) && action == :get
201
231
  Hashify.call(reply)
202
232
  else
203
233
  reply
@@ -227,7 +257,7 @@ class Redis
227
257
 
228
258
  # Return the number of keys in the selected database.
229
259
  #
230
- # @return [Fixnum]
260
+ # @return [Integer]
231
261
  def dbsize
232
262
  synchronize do |client|
233
263
  client.call([:dbsize])
@@ -248,7 +278,7 @@ class Redis
248
278
  def flushall(options = nil)
249
279
  synchronize do |client|
250
280
  if options && options[:async]
251
- client.call([:flushall, :async])
281
+ client.call(%i[flushall async])
252
282
  else
253
283
  client.call([:flushall])
254
284
  end
@@ -263,7 +293,7 @@ class Redis
263
293
  def flushdb(options = nil)
264
294
  synchronize do |client|
265
295
  if options && options[:async]
266
- client.call([:flushdb, :async])
296
+ client.call(%i[flushdb async])
267
297
  else
268
298
  client.call([:flushdb])
269
299
  end
@@ -277,7 +307,7 @@ class Redis
277
307
  def info(cmd = nil)
278
308
  synchronize do |client|
279
309
  client.call([:info, cmd].compact) do |reply|
280
- if reply.kind_of?(String)
310
+ if reply.is_a?(String)
281
311
  reply = HashifyInfo.call(reply)
282
312
 
283
313
  if cmd && cmd.to_s == "commandstats"
@@ -296,7 +326,7 @@ class Redis
296
326
 
297
327
  # Get the UNIX time stamp of the last successful save to disk.
298
328
  #
299
- # @return [Fixnum]
329
+ # @return [Integer]
300
330
  def lastsave
301
331
  synchronize do |client|
302
332
  client.call([:lastsave])
@@ -348,9 +378,9 @@ class Redis
348
378
  # Interact with the slowlog (get, len, reset)
349
379
  #
350
380
  # @param [String] subcommand e.g. `get`, `len`, `reset`
351
- # @param [Fixnum] length maximum number of entries to return
352
- # @return [Array<String>, Fixnum, String] depends on subcommand
353
- def slowlog(subcommand, length=nil)
381
+ # @param [Integer] length maximum number of entries to return
382
+ # @return [Array<String>, Integer, String] depends on subcommand
383
+ def slowlog(subcommand, length = nil)
354
384
  synchronize do |client|
355
385
  args = [:slowlog, subcommand]
356
386
  args << length if length
@@ -370,12 +400,12 @@ class Redis
370
400
  # @example
371
401
  # r.time # => [ 1333093196, 606806 ]
372
402
  #
373
- # @return [Array<Fixnum>] tuple of seconds since UNIX epoch and
403
+ # @return [Array<Integer>] tuple of seconds since UNIX epoch and
374
404
  # microseconds in the current second
375
405
  def time
376
406
  synchronize do |client|
377
407
  client.call([:time]) do |reply|
378
- reply.map(&:to_i) if reply
408
+ reply&.map(&:to_i)
379
409
  end
380
410
  end
381
411
  end
@@ -393,7 +423,7 @@ class Redis
393
423
  # Set a key's time to live in seconds.
394
424
  #
395
425
  # @param [String] key
396
- # @param [Fixnum] seconds time to live
426
+ # @param [Integer] seconds time to live
397
427
  # @return [Boolean] whether the timeout was set or not
398
428
  def expire(key, seconds)
399
429
  synchronize do |client|
@@ -404,7 +434,7 @@ class Redis
404
434
  # Set the expiration for a key as a UNIX timestamp.
405
435
  #
406
436
  # @param [String] key
407
- # @param [Fixnum] unix_time expiry time specified as a UNIX timestamp
437
+ # @param [Integer] unix_time expiry time specified as a UNIX timestamp
408
438
  # @return [Boolean] whether the timeout was set or not
409
439
  def expireat(key, unix_time)
410
440
  synchronize do |client|
@@ -415,7 +445,7 @@ class Redis
415
445
  # Get the time to live (in seconds) for a key.
416
446
  #
417
447
  # @param [String] key
418
- # @return [Fixnum] remaining time to live in seconds.
448
+ # @return [Integer] remaining time to live in seconds.
419
449
  #
420
450
  # In Redis 2.6 or older the command returns -1 if the key does not exist or if
421
451
  # the key exist but has no associated expire.
@@ -433,7 +463,7 @@ class Redis
433
463
  # Set a key's time to live in milliseconds.
434
464
  #
435
465
  # @param [String] key
436
- # @param [Fixnum] milliseconds time to live
466
+ # @param [Integer] milliseconds time to live
437
467
  # @return [Boolean] whether the timeout was set or not
438
468
  def pexpire(key, milliseconds)
439
469
  synchronize do |client|
@@ -444,7 +474,7 @@ class Redis
444
474
  # Set the expiration for a key as number of milliseconds from UNIX Epoch.
445
475
  #
446
476
  # @param [String] key
447
- # @param [Fixnum] ms_unix_time expiry time specified as number of milliseconds from UNIX Epoch.
477
+ # @param [Integer] ms_unix_time expiry time specified as number of milliseconds from UNIX Epoch.
448
478
  # @return [Boolean] whether the timeout was set or not
449
479
  def pexpireat(key, ms_unix_time)
450
480
  synchronize do |client|
@@ -455,7 +485,7 @@ class Redis
455
485
  # Get the time to live (in milliseconds) for a key.
456
486
  #
457
487
  # @param [String] key
458
- # @return [Fixnum] remaining time to live in milliseconds
488
+ # @return [Integer] remaining time to live in milliseconds
459
489
  # In Redis 2.6 or older the command returns -1 if the key does not exist or if
460
490
  # the key exist but has no associated expire.
461
491
  #
@@ -488,9 +518,9 @@ class Redis
488
518
  # - `:replace => Boolean`: if false, raises an error if key already exists
489
519
  # @raise [Redis::CommandError]
490
520
  # @return [String] `"OK"`
491
- def restore(key, ttl, serialized_value, options = {})
521
+ def restore(key, ttl, serialized_value, replace: nil)
492
522
  args = [:restore, key, ttl, serialized_value]
493
- args << 'REPLACE' if options[:replace]
523
+ args << 'REPLACE' if replace
494
524
 
495
525
  synchronize do |client|
496
526
  client.call(args)
@@ -525,8 +555,11 @@ class Redis
525
555
  # Delete one or more keys.
526
556
  #
527
557
  # @param [String, Array<String>] keys
528
- # @return [Fixnum] number of keys that were deleted
558
+ # @return [Integer] number of keys that were deleted
529
559
  def del(*keys)
560
+ keys.flatten!(1)
561
+ return 0 if keys.empty?
562
+
530
563
  synchronize do |client|
531
564
  client.call([:del] + keys)
532
565
  end
@@ -535,20 +568,50 @@ class Redis
535
568
  # Unlink one or more keys.
536
569
  #
537
570
  # @param [String, Array<String>] keys
538
- # @return [Fixnum] number of keys that were unlinked
571
+ # @return [Integer] number of keys that were unlinked
539
572
  def unlink(*keys)
540
573
  synchronize do |client|
541
574
  client.call([:unlink] + keys)
542
575
  end
543
576
  end
544
577
 
545
- # Determine if a key exists.
578
+ # Determine how many of the keys exists.
546
579
  #
547
- # @param [String] key
580
+ # @param [String, Array<String>] keys
581
+ # @return [Integer]
582
+ def exists(*keys)
583
+ if !Redis.exists_returns_integer && keys.size == 1
584
+ if Redis.exists_returns_integer.nil?
585
+ message = "`Redis#exists(key)` will return an Integer in redis-rb 4.3. `exists?` returns a boolean, you " \
586
+ "should use it instead. To opt-in to the new behavior now you can set Redis.exists_returns_integer = " \
587
+ "true. To disable this message and keep the current (boolean) behaviour of 'exists' you can set " \
588
+ "`Redis.exists_returns_integer = false`, but this option will be removed in 5.0. " \
589
+ "(#{::Kernel.caller(1, 1).first})\n"
590
+
591
+ ::Kernel.warn(message)
592
+ end
593
+
594
+ exists?(*keys)
595
+ else
596
+ _exists(*keys)
597
+ end
598
+ end
599
+
600
+ def _exists(*keys)
601
+ synchronize do |client|
602
+ client.call([:exists, *keys])
603
+ end
604
+ end
605
+
606
+ # Determine if any of the keys exists.
607
+ #
608
+ # @param [String, Array<String>] keys
548
609
  # @return [Boolean]
549
- def exists(key)
610
+ def exists?(*keys)
550
611
  synchronize do |client|
551
- client.call([:exists, key], &Boolify)
612
+ client.call([:exists, *keys]) do |value|
613
+ value > 0
614
+ end
552
615
  end
553
616
  end
554
617
 
@@ -559,7 +622,7 @@ class Redis
559
622
  def keys(pattern = "*")
560
623
  synchronize do |client|
561
624
  client.call([:keys, pattern]) do |reply|
562
- if reply.kind_of?(String)
625
+ if reply.is_a?(String)
563
626
  reply.split(" ")
564
627
  else
565
628
  reply
@@ -585,7 +648,7 @@ class Redis
585
648
  # # => "bar"
586
649
  #
587
650
  # @param [String] key
588
- # @param [Fixnum] db
651
+ # @param [Integer] db
589
652
  # @return [Boolean] whether the key was moved or not
590
653
  def move(key, db)
591
654
  synchronize do |client|
@@ -649,36 +712,33 @@ class Redis
649
712
  # - `:order => String`: combination of `ASC`, `DESC` and optionally `ALPHA`
650
713
  # - `:store => String`: key to store the result at
651
714
  #
652
- # @return [Array<String>, Array<Array<String>>, Fixnum]
715
+ # @return [Array<String>, Array<Array<String>>, Integer]
653
716
  # - when `:get` is not specified, or holds a single element, an array of elements
654
717
  # - when `:get` is specified, and holds more than one element, an array of
655
718
  # elements where every element is an array with the result for every
656
719
  # element specified in `:get`
657
720
  # - when `:store` is specified, the number of elements in the stored result
658
- def sort(key, options = {})
659
- args = []
660
-
661
- by = options[:by]
662
- args.concat(["BY", by]) if by
721
+ def sort(key, by: nil, limit: nil, get: nil, order: nil, store: nil)
722
+ args = [:sort, key]
723
+ args << "BY" << by if by
663
724
 
664
- limit = options[:limit]
665
- args.concat(["LIMIT"] + limit) if limit
725
+ if limit
726
+ args << "LIMIT"
727
+ args.concat(limit)
728
+ end
666
729
 
667
- get = Array(options[:get])
668
- args.concat(["GET"].product(get).flatten) unless get.empty?
730
+ get = Array(get)
731
+ get.each do |item|
732
+ args << "GET" << item
733
+ end
669
734
 
670
- order = options[:order]
671
735
  args.concat(order.split(" ")) if order
672
-
673
- store = options[:store]
674
- args.concat(["STORE", store]) if store
736
+ args << "STORE" << store if store
675
737
 
676
738
  synchronize do |client|
677
- client.call([:sort, key] + args) do |reply|
739
+ client.call(args) do |reply|
678
740
  if get.size > 1 && !store
679
- if reply
680
- reply.each_slice(get.size).to_a
681
- end
741
+ reply.each_slice(get.size).to_a if reply
682
742
  else
683
743
  reply
684
744
  end
@@ -703,7 +763,7 @@ class Redis
703
763
  # # => 4
704
764
  #
705
765
  # @param [String] key
706
- # @return [Fixnum] value after decrementing it
766
+ # @return [Integer] value after decrementing it
707
767
  def decr(key)
708
768
  synchronize do |client|
709
769
  client.call([:decr, key])
@@ -717,8 +777,8 @@ class Redis
717
777
  # # => 0
718
778
  #
719
779
  # @param [String] key
720
- # @param [Fixnum] decrement
721
- # @return [Fixnum] value after decrementing it
780
+ # @param [Integer] decrement
781
+ # @return [Integer] value after decrementing it
722
782
  def decrby(key, decrement)
723
783
  synchronize do |client|
724
784
  client.call([:decrby, key, decrement])
@@ -732,7 +792,7 @@ class Redis
732
792
  # # => 6
733
793
  #
734
794
  # @param [String] key
735
- # @return [Fixnum] value after incrementing it
795
+ # @return [Integer] value after incrementing it
736
796
  def incr(key)
737
797
  synchronize do |client|
738
798
  client.call([:incr, key])
@@ -746,8 +806,8 @@ class Redis
746
806
  # # => 10
747
807
  #
748
808
  # @param [String] key
749
- # @param [Fixnum] increment
750
- # @return [Fixnum] value after incrementing it
809
+ # @param [Integer] increment
810
+ # @return [Integer] value after incrementing it
751
811
  def incrby(key, increment)
752
812
  synchronize do |client|
753
813
  client.call([:incrby, key, increment])
@@ -774,31 +834,31 @@ class Redis
774
834
  # @param [String] key
775
835
  # @param [String] value
776
836
  # @param [Hash] options
777
- # - `:ex => Fixnum`: Set the specified expire time, in seconds.
778
- # - `:px => Fixnum`: Set the specified expire time, in milliseconds.
837
+ # - `:ex => Integer`: Set the specified expire time, in seconds.
838
+ # - `:px => Integer`: Set the specified expire time, in milliseconds.
839
+ # - `:exat => Integer` : Set the specified Unix time at which the key will expire, in seconds.
840
+ # - `:pxat => Integer` : Set the specified Unix time at which the key will expire, in milliseconds.
779
841
  # - `:nx => true`: Only set the key if it does not already exist.
780
842
  # - `:xx => true`: Only set the key if it already exist.
843
+ # - `:keepttl => true`: Retain the time to live associated with the key.
844
+ # - `:get => true`: Return the old string stored at key, or nil if key did not exist.
781
845
  # @return [String, Boolean] `"OK"` or true, false if `:nx => true` or `:xx => true`
782
- def set(key, value, options = {})
783
- args = []
784
-
785
- ex = options[:ex]
786
- args.concat(["EX", ex]) if ex
787
-
788
- px = options[:px]
789
- args.concat(["PX", px]) if px
790
-
791
- nx = options[:nx]
792
- args.concat(["NX"]) if nx
793
-
794
- xx = options[:xx]
795
- args.concat(["XX"]) if xx
846
+ def set(key, value, ex: nil, px: nil, exat: nil, pxat: nil, nx: nil, xx: nil, keepttl: nil, get: nil)
847
+ args = [:set, key, value.to_s]
848
+ args << "EX" << ex if ex
849
+ args << "PX" << px if px
850
+ args << "EXAT" << exat if exat
851
+ args << "PXAT" << pxat if pxat
852
+ args << "NX" if nx
853
+ args << "XX" if xx
854
+ args << "KEEPTTL" if keepttl
855
+ args << "GET" if get
796
856
 
797
857
  synchronize do |client|
798
858
  if nx || xx
799
- client.call([:set, key, value.to_s] + args, &BoolifySet)
859
+ client.call(args, &BoolifySet)
800
860
  else
801
- client.call([:set, key, value.to_s] + args)
861
+ client.call(args)
802
862
  end
803
863
  end
804
864
  end
@@ -806,7 +866,7 @@ class Redis
806
866
  # Set the time to live in seconds of a key.
807
867
  #
808
868
  # @param [String] key
809
- # @param [Fixnum] ttl
869
+ # @param [Integer] ttl
810
870
  # @param [String] value
811
871
  # @return [String] `"OK"`
812
872
  def setex(key, ttl, value)
@@ -818,7 +878,7 @@ class Redis
818
878
  # Set the time to live in milliseconds of a key.
819
879
  #
820
880
  # @param [String] key
821
- # @param [Fixnum] ttl
881
+ # @param [Integer] ttl
822
882
  # @param [String] value
823
883
  # @return [String] `"OK"`
824
884
  def psetex(key, ttl, value)
@@ -880,7 +940,7 @@ class Redis
880
940
  # @see #mapped_msetnx
881
941
  def msetnx(*args)
882
942
  synchronize do |client|
883
- client.call([:msetnx] + args, &Boolify)
943
+ client.call([:msetnx, *args], &Boolify)
884
944
  end
885
945
  end
886
946
 
@@ -911,7 +971,7 @@ class Redis
911
971
  # Get the values of all the given keys.
912
972
  #
913
973
  # @example
914
- # redis.mget("key1", "key1")
974
+ # redis.mget("key1", "key2")
915
975
  # # => ["v1", "v2"]
916
976
  #
917
977
  # @param [Array<String>] keys
@@ -920,7 +980,7 @@ class Redis
920
980
  # @see #mapped_mget
921
981
  def mget(*keys, &blk)
922
982
  synchronize do |client|
923
- client.call([:mget] + keys, &blk)
983
+ client.call([:mget, *keys], &blk)
924
984
  end
925
985
  end
926
986
 
@@ -936,7 +996,7 @@ class Redis
936
996
  # @see #mget
937
997
  def mapped_mget(*keys)
938
998
  mget(*keys) do |reply|
939
- if reply.kind_of?(Array)
999
+ if reply.is_a?(Array)
940
1000
  Hash[keys.zip(reply)]
941
1001
  else
942
1002
  reply
@@ -947,9 +1007,9 @@ class Redis
947
1007
  # Overwrite part of a string at key starting at the specified offset.
948
1008
  #
949
1009
  # @param [String] key
950
- # @param [Fixnum] offset byte offset
1010
+ # @param [Integer] offset byte offset
951
1011
  # @param [String] value
952
- # @return [Fixnum] length of the string after it was modified
1012
+ # @return [Integer] length of the string after it was modified
953
1013
  def setrange(key, offset, value)
954
1014
  synchronize do |client|
955
1015
  client.call([:setrange, key, offset, value.to_s])
@@ -959,10 +1019,10 @@ class Redis
959
1019
  # Get a substring of the string stored at a key.
960
1020
  #
961
1021
  # @param [String] key
962
- # @param [Fixnum] start zero-based start offset
963
- # @param [Fixnum] stop zero-based end offset. Use -1 for representing
1022
+ # @param [Integer] start zero-based start offset
1023
+ # @param [Integer] stop zero-based end offset. Use -1 for representing
964
1024
  # the end of the string
965
- # @return [Fixnum] `0` or `1`
1025
+ # @return [Integer] `0` or `1`
966
1026
  def getrange(key, start, stop)
967
1027
  synchronize do |client|
968
1028
  client.call([:getrange, key, start, stop])
@@ -972,9 +1032,9 @@ class Redis
972
1032
  # Sets or clears the bit at offset in the string value stored at key.
973
1033
  #
974
1034
  # @param [String] key
975
- # @param [Fixnum] offset bit offset
976
- # @param [Fixnum] value bit value `0` or `1`
977
- # @return [Fixnum] the original bit value stored at `offset`
1035
+ # @param [Integer] offset bit offset
1036
+ # @param [Integer] value bit value `0` or `1`
1037
+ # @return [Integer] the original bit value stored at `offset`
978
1038
  def setbit(key, offset, value)
979
1039
  synchronize do |client|
980
1040
  client.call([:setbit, key, offset, value])
@@ -984,8 +1044,8 @@ class Redis
984
1044
  # Returns the bit value at offset in the string value stored at key.
985
1045
  #
986
1046
  # @param [String] key
987
- # @param [Fixnum] offset bit offset
988
- # @return [Fixnum] `0` or `1`
1047
+ # @param [Integer] offset bit offset
1048
+ # @return [Integer] `0` or `1`
989
1049
  def getbit(key, offset)
990
1050
  synchronize do |client|
991
1051
  client.call([:getbit, key, offset])
@@ -996,7 +1056,7 @@ class Redis
996
1056
  #
997
1057
  # @param [String] key
998
1058
  # @param [String] value value to append
999
- # @return [Fixnum] length of the string after appending
1059
+ # @return [Integer] length of the string after appending
1000
1060
  def append(key, value)
1001
1061
  synchronize do |client|
1002
1062
  client.call([:append, key, value])
@@ -1006,9 +1066,9 @@ class Redis
1006
1066
  # Count the number of set bits in a range of the string value stored at key.
1007
1067
  #
1008
1068
  # @param [String] key
1009
- # @param [Fixnum] start start index
1010
- # @param [Fixnum] stop stop index
1011
- # @return [Fixnum] the number of bits set to 1
1069
+ # @param [Integer] start start index
1070
+ # @param [Integer] stop stop index
1071
+ # @return [Integer] the number of bits set to 1
1012
1072
  def bitcount(key, start = 0, stop = -1)
1013
1073
  synchronize do |client|
1014
1074
  client.call([:bitcount, key, start, stop])
@@ -1020,25 +1080,23 @@ class Redis
1020
1080
  # @param [String] operation e.g. `and`, `or`, `xor`, `not`
1021
1081
  # @param [String] destkey destination key
1022
1082
  # @param [String, Array<String>] keys one or more source keys to perform `operation`
1023
- # @return [Fixnum] the length of the string stored in `destkey`
1083
+ # @return [Integer] the length of the string stored in `destkey`
1024
1084
  def bitop(operation, destkey, *keys)
1025
1085
  synchronize do |client|
1026
- client.call([:bitop, operation, destkey] + keys)
1086
+ client.call([:bitop, operation, destkey, *keys])
1027
1087
  end
1028
1088
  end
1029
1089
 
1030
1090
  # Return the position of the first bit set to 1 or 0 in a string.
1031
1091
  #
1032
1092
  # @param [String] key
1033
- # @param [Fixnum] bit whether to look for the first 1 or 0 bit
1034
- # @param [Fixnum] start start index
1035
- # @param [Fixnum] stop stop index
1036
- # @return [Fixnum] the position of the first 1/0 bit.
1093
+ # @param [Integer] bit whether to look for the first 1 or 0 bit
1094
+ # @param [Integer] start start index
1095
+ # @param [Integer] stop stop index
1096
+ # @return [Integer] the position of the first 1/0 bit.
1037
1097
  # -1 if looking for 1 and it is not found or start and stop are given.
1038
- def bitpos(key, bit, start=nil, stop=nil)
1039
- if stop and not start
1040
- raise(ArgumentError, 'stop parameter specified without start parameter')
1041
- end
1098
+ def bitpos(key, bit, start = nil, stop = nil)
1099
+ raise(ArgumentError, 'stop parameter specified without start parameter') if stop && !start
1042
1100
 
1043
1101
  synchronize do |client|
1044
1102
  command = [:bitpos, key, bit]
@@ -1060,10 +1118,49 @@ class Redis
1060
1118
  end
1061
1119
  end
1062
1120
 
1121
+ # Get the value of key and delete the key. This command is similar to GET,
1122
+ # except for the fact that it also deletes the key on success.
1123
+ #
1124
+ # @param [String] key
1125
+ # @return [String] the old value stored in the key, or `nil` if the key
1126
+ # did not exist
1127
+ def getdel(key)
1128
+ synchronize do |client|
1129
+ client.call([:getdel, key])
1130
+ end
1131
+ end
1132
+
1133
+ # Get the value of key and optionally set its expiration. GETEX is similar to
1134
+ # GET, but is a write command with additional options. When no options are
1135
+ # provided, GETEX behaves like GET.
1136
+ #
1137
+ # @param [String] key
1138
+ # @param [Hash] options
1139
+ # - `:ex => Integer`: Set the specified expire time, in seconds.
1140
+ # - `:px => Integer`: Set the specified expire time, in milliseconds.
1141
+ # - `:exat => true`: Set the specified Unix time at which the key will
1142
+ # expire, in seconds.
1143
+ # - `:pxat => true`: Set the specified Unix time at which the key will
1144
+ # expire, in milliseconds.
1145
+ # - `:persist => true`: Remove the time to live associated with the key.
1146
+ # @return [String] The value of key, or nil when key does not exist.
1147
+ def getex(key, ex: nil, px: nil, exat: nil, pxat: nil, persist: false)
1148
+ args = [:getex, key]
1149
+ args << "EX" << ex if ex
1150
+ args << "PX" << px if px
1151
+ args << "EXAT" << exat if exat
1152
+ args << "PXAT" << pxat if pxat
1153
+ args << "PERSIST" if persist
1154
+
1155
+ synchronize do |client|
1156
+ client.call(args)
1157
+ end
1158
+ end
1159
+
1063
1160
  # Get the length of the value stored in a key.
1064
1161
  #
1065
1162
  # @param [String] key
1066
- # @return [Fixnum] the length of the value stored in the key, or 0
1163
+ # @return [Integer] the length of the value stored in the key, or 0
1067
1164
  # if the key does not exist
1068
1165
  def strlen(key)
1069
1166
  synchronize do |client|
@@ -1074,18 +1171,71 @@ class Redis
1074
1171
  # Get the length of a list.
1075
1172
  #
1076
1173
  # @param [String] key
1077
- # @return [Fixnum]
1174
+ # @return [Integer]
1078
1175
  def llen(key)
1079
1176
  synchronize do |client|
1080
1177
  client.call([:llen, key])
1081
1178
  end
1082
1179
  end
1083
1180
 
1181
+ # Remove the first/last element in a list, append/prepend it to another list and return it.
1182
+ #
1183
+ # @param [String] source source key
1184
+ # @param [String] destination destination key
1185
+ # @param [String, Symbol] where_source from where to remove the element from the source list
1186
+ # e.g. 'LEFT' - from head, 'RIGHT' - from tail
1187
+ # @param [String, Symbol] where_destination where to push the element to the source list
1188
+ # e.g. 'LEFT' - to head, 'RIGHT' - to tail
1189
+ #
1190
+ # @return [nil, String] the element, or nil when the source key does not exist
1191
+ #
1192
+ # @note This command comes in place of the now deprecated RPOPLPUSH.
1193
+ # Doing LMOVE RIGHT LEFT is equivalent.
1194
+ def lmove(source, destination, where_source, where_destination)
1195
+ where_source, where_destination = _normalize_move_wheres(where_source, where_destination)
1196
+
1197
+ synchronize do |client|
1198
+ client.call([:lmove, source, destination, where_source, where_destination])
1199
+ end
1200
+ end
1201
+
1202
+ # Remove the first/last element in a list and append/prepend it
1203
+ # to another list and return it, or block until one is available.
1204
+ #
1205
+ # @example With timeout
1206
+ # element = redis.blmove("foo", "bar", "LEFT", "RIGHT", timeout: 5)
1207
+ # # => nil on timeout
1208
+ # # => "element" on success
1209
+ # @example Without timeout
1210
+ # element = redis.blmove("foo", "bar", "LEFT", "RIGHT")
1211
+ # # => "element"
1212
+ #
1213
+ # @param [String] source source key
1214
+ # @param [String] destination destination key
1215
+ # @param [String, Symbol] where_source from where to remove the element from the source list
1216
+ # e.g. 'LEFT' - from head, 'RIGHT' - from tail
1217
+ # @param [String, Symbol] where_destination where to push the element to the source list
1218
+ # e.g. 'LEFT' - to head, 'RIGHT' - to tail
1219
+ # @param [Hash] options
1220
+ # - `:timeout => Numeric`: timeout in seconds, defaults to no timeout
1221
+ #
1222
+ # @return [nil, String] the element, or nil when the source key does not exist or the timeout expired
1223
+ #
1224
+ def blmove(source, destination, where_source, where_destination, timeout: 0)
1225
+ where_source, where_destination = _normalize_move_wheres(where_source, where_destination)
1226
+
1227
+ synchronize do |client|
1228
+ command = [:blmove, source, destination, where_source, where_destination, timeout]
1229
+ timeout += client.timeout if timeout > 0
1230
+ client.call_with_timeout(command, timeout)
1231
+ end
1232
+ end
1233
+
1084
1234
  # Prepend one or more values to a list, creating the list if it doesn't exist
1085
1235
  #
1086
1236
  # @param [String] key
1087
1237
  # @param [String, Array<String>] value string value, or array of string values to push
1088
- # @return [Fixnum] the length of the list after the push operation
1238
+ # @return [Integer] the length of the list after the push operation
1089
1239
  def lpush(key, value)
1090
1240
  synchronize do |client|
1091
1241
  client.call([:lpush, key, value])
@@ -1096,7 +1246,7 @@ class Redis
1096
1246
  #
1097
1247
  # @param [String] key
1098
1248
  # @param [String] value
1099
- # @return [Fixnum] the length of the list after the push operation
1249
+ # @return [Integer] the length of the list after the push operation
1100
1250
  def lpushx(key, value)
1101
1251
  synchronize do |client|
1102
1252
  client.call([:lpushx, key, value])
@@ -1107,7 +1257,7 @@ class Redis
1107
1257
  #
1108
1258
  # @param [String] key
1109
1259
  # @param [String, Array<String>] value string value, or array of string values to push
1110
- # @return [Fixnum] the length of the list after the push operation
1260
+ # @return [Integer] the length of the list after the push operation
1111
1261
  def rpush(key, value)
1112
1262
  synchronize do |client|
1113
1263
  client.call([:rpush, key, value])
@@ -1118,30 +1268,36 @@ class Redis
1118
1268
  #
1119
1269
  # @param [String] key
1120
1270
  # @param [String] value
1121
- # @return [Fixnum] the length of the list after the push operation
1271
+ # @return [Integer] the length of the list after the push operation
1122
1272
  def rpushx(key, value)
1123
1273
  synchronize do |client|
1124
1274
  client.call([:rpushx, key, value])
1125
1275
  end
1126
1276
  end
1127
1277
 
1128
- # Remove and get the first element in a list.
1278
+ # Remove and get the first elements in a list.
1129
1279
  #
1130
1280
  # @param [String] key
1131
- # @return [String]
1132
- def lpop(key)
1281
+ # @param [Integer] count number of elements to remove
1282
+ # @return [String, Array<String>] the values of the first elements
1283
+ def lpop(key, count = nil)
1133
1284
  synchronize do |client|
1134
- client.call([:lpop, key])
1285
+ command = [:lpop, key]
1286
+ command << count if count
1287
+ client.call(command)
1135
1288
  end
1136
1289
  end
1137
1290
 
1138
- # Remove and get the last element in a list.
1291
+ # Remove and get the last elements in a list.
1139
1292
  #
1140
1293
  # @param [String] key
1141
- # @return [String]
1142
- def rpop(key)
1294
+ # @param [Integer] count number of elements to remove
1295
+ # @return [String, Array<String>] the values of the last elements
1296
+ def rpop(key, count = nil)
1143
1297
  synchronize do |client|
1144
- client.call([:rpop, key])
1298
+ command = [:rpop, key]
1299
+ command << count if count
1300
+ client.call(command)
1145
1301
  end
1146
1302
  end
1147
1303
 
@@ -1157,21 +1313,21 @@ class Redis
1157
1313
  end
1158
1314
 
1159
1315
  def _bpop(cmd, args, &blk)
1160
- options = {}
1161
-
1162
- if args.last.is_a?(Hash)
1316
+ timeout = if args.last.is_a?(Hash)
1163
1317
  options = args.pop
1318
+ options[:timeout]
1164
1319
  elsif args.last.respond_to?(:to_int)
1165
1320
  # Issue deprecation notice in obnoxious mode...
1166
- options[:timeout] = args.pop.to_int
1321
+ args.pop.to_int
1167
1322
  end
1168
1323
 
1324
+ timeout ||= 0
1325
+
1169
1326
  if args.size > 1
1170
1327
  # Issue deprecation notice in obnoxious mode...
1171
1328
  end
1172
1329
 
1173
1330
  keys = args.flatten
1174
- timeout = options[:timeout] || 0
1175
1331
 
1176
1332
  synchronize do |client|
1177
1333
  command = [cmd, keys, timeout]
@@ -1196,7 +1352,7 @@ class Redis
1196
1352
  # @param [String, Array<String>] keys one or more keys to perform the
1197
1353
  # blocking pop on
1198
1354
  # @param [Hash] options
1199
- # - `:timeout => Fixnum`: timeout in seconds, defaults to no timeout
1355
+ # - `:timeout => Integer`: timeout in seconds, defaults to no timeout
1200
1356
  #
1201
1357
  # @return [nil, [String, String]]
1202
1358
  # - `nil` when the operation timed out
@@ -1210,7 +1366,7 @@ class Redis
1210
1366
  # @param [String, Array<String>] keys one or more keys to perform the
1211
1367
  # blocking pop on
1212
1368
  # @param [Hash] options
1213
- # - `:timeout => Fixnum`: timeout in seconds, defaults to no timeout
1369
+ # - `:timeout => Integer`: timeout in seconds, defaults to no timeout
1214
1370
  #
1215
1371
  # @return [nil, [String, String]]
1216
1372
  # - `nil` when the operation timed out
@@ -1227,20 +1383,12 @@ class Redis
1227
1383
  # @param [String] source source key
1228
1384
  # @param [String] destination destination key
1229
1385
  # @param [Hash] options
1230
- # - `:timeout => Fixnum`: timeout in seconds, defaults to no timeout
1386
+ # - `:timeout => Integer`: timeout in seconds, defaults to no timeout
1231
1387
  #
1232
1388
  # @return [nil, String]
1233
1389
  # - `nil` when the operation timed out
1234
1390
  # - the element was popped and pushed otherwise
1235
- def brpoplpush(source, destination, options = {})
1236
- case options
1237
- when Integer
1238
- # Issue deprecation notice in obnoxious mode...
1239
- options = { :timeout => options }
1240
- end
1241
-
1242
- timeout = options[:timeout] || 0
1243
-
1391
+ def brpoplpush(source, destination, deprecated_timeout = 0, timeout: deprecated_timeout)
1244
1392
  synchronize do |client|
1245
1393
  command = [:brpoplpush, source, destination, timeout]
1246
1394
  timeout += client.timeout if timeout > 0
@@ -1251,7 +1399,7 @@ class Redis
1251
1399
  # Get an element from a list by its index.
1252
1400
  #
1253
1401
  # @param [String] key
1254
- # @param [Fixnum] index
1402
+ # @param [Integer] index
1255
1403
  # @return [String]
1256
1404
  def lindex(key, index)
1257
1405
  synchronize do |client|
@@ -1265,7 +1413,7 @@ class Redis
1265
1413
  # @param [String, Symbol] where `BEFORE` or `AFTER`
1266
1414
  # @param [String] pivot reference element
1267
1415
  # @param [String] value
1268
- # @return [Fixnum] length of the list after the insert operation, or `-1`
1416
+ # @return [Integer] length of the list after the insert operation, or `-1`
1269
1417
  # when the element `pivot` was not found
1270
1418
  def linsert(key, where, pivot, value)
1271
1419
  synchronize do |client|
@@ -1276,8 +1424,8 @@ class Redis
1276
1424
  # Get a range of elements from a list.
1277
1425
  #
1278
1426
  # @param [String] key
1279
- # @param [Fixnum] start start index
1280
- # @param [Fixnum] stop stop index
1427
+ # @param [Integer] start start index
1428
+ # @param [Integer] stop stop index
1281
1429
  # @return [Array<String>]
1282
1430
  def lrange(key, start, stop)
1283
1431
  synchronize do |client|
@@ -1288,12 +1436,12 @@ class Redis
1288
1436
  # Remove elements from a list.
1289
1437
  #
1290
1438
  # @param [String] key
1291
- # @param [Fixnum] count number of elements to remove. Use a positive
1439
+ # @param [Integer] count number of elements to remove. Use a positive
1292
1440
  # value to remove the first `count` occurrences of `value`. A negative
1293
1441
  # value to remove the last `count` occurrences of `value`. Or zero, to
1294
1442
  # remove all occurrences of `value` from the list.
1295
1443
  # @param [String] value
1296
- # @return [Fixnum] the number of removed elements
1444
+ # @return [Integer] the number of removed elements
1297
1445
  def lrem(key, count, value)
1298
1446
  synchronize do |client|
1299
1447
  client.call([:lrem, key, count, value])
@@ -1303,7 +1451,7 @@ class Redis
1303
1451
  # Set the value of an element in a list by its index.
1304
1452
  #
1305
1453
  # @param [String] key
1306
- # @param [Fixnum] index
1454
+ # @param [Integer] index
1307
1455
  # @param [String] value
1308
1456
  # @return [String] `OK`
1309
1457
  def lset(key, index, value)
@@ -1315,8 +1463,8 @@ class Redis
1315
1463
  # Trim a list to the specified range.
1316
1464
  #
1317
1465
  # @param [String] key
1318
- # @param [Fixnum] start start index
1319
- # @param [Fixnum] stop stop index
1466
+ # @param [Integer] start start index
1467
+ # @param [Integer] stop stop index
1320
1468
  # @return [String] `OK`
1321
1469
  def ltrim(key, start, stop)
1322
1470
  synchronize do |client|
@@ -1327,7 +1475,7 @@ class Redis
1327
1475
  # Get the number of members in a set.
1328
1476
  #
1329
1477
  # @param [String] key
1330
- # @return [Fixnum]
1478
+ # @return [Integer]
1331
1479
  def scard(key)
1332
1480
  synchronize do |client|
1333
1481
  client.call([:scard, key])
@@ -1338,8 +1486,8 @@ class Redis
1338
1486
  #
1339
1487
  # @param [String] key
1340
1488
  # @param [String, Array<String>] member one member, or array of members
1341
- # @return [Boolean, Fixnum] `Boolean` when a single member is specified,
1342
- # holding whether or not adding the member succeeded, or `Fixnum` when an
1489
+ # @return [Boolean, Integer] `Boolean` when a single member is specified,
1490
+ # holding whether or not adding the member succeeded, or `Integer` when an
1343
1491
  # array of members is specified, holding the number of members that were
1344
1492
  # successfully added
1345
1493
  def sadd(key, member)
@@ -1360,8 +1508,8 @@ class Redis
1360
1508
  #
1361
1509
  # @param [String] key
1362
1510
  # @param [String, Array<String>] member one member, or array of members
1363
- # @return [Boolean, Fixnum] `Boolean` when a single member is specified,
1364
- # holding whether or not removing the member succeeded, or `Fixnum` when an
1511
+ # @return [Boolean, Integer] `Boolean` when a single member is specified,
1512
+ # holding whether or not removing the member succeeded, or `Integer` when an
1365
1513
  # array of members is specified, holding the number of members that were
1366
1514
  # successfully removed
1367
1515
  def srem(key, member)
@@ -1382,7 +1530,7 @@ class Redis
1382
1530
  #
1383
1531
  # @param [String] key
1384
1532
  # @return [String]
1385
- # @param [Fixnum] count
1533
+ # @param [Integer] count
1386
1534
  def spop(key, count = nil)
1387
1535
  synchronize do |client|
1388
1536
  if count.nil?
@@ -1396,7 +1544,7 @@ class Redis
1396
1544
  # Get one or more random members from a set.
1397
1545
  #
1398
1546
  # @param [String] key
1399
- # @param [Fixnum] count
1547
+ # @param [Integer] count
1400
1548
  # @return [String]
1401
1549
  def srandmember(key, count = nil)
1402
1550
  synchronize do |client|
@@ -1431,6 +1579,19 @@ class Redis
1431
1579
  end
1432
1580
  end
1433
1581
 
1582
+ # Determine if multiple values are members of a set.
1583
+ #
1584
+ # @param [String] key
1585
+ # @param [String, Array<String>] members
1586
+ # @return [Array<Boolean>]
1587
+ def smismember(key, *members)
1588
+ synchronize do |client|
1589
+ client.call([:smismember, key, *members]) do |reply|
1590
+ reply.map(&Boolify)
1591
+ end
1592
+ end
1593
+ end
1594
+
1434
1595
  # Get all the members in a set.
1435
1596
  #
1436
1597
  # @param [String] key
@@ -1447,7 +1608,7 @@ class Redis
1447
1608
  # @return [Array<String>] members in the difference
1448
1609
  def sdiff(*keys)
1449
1610
  synchronize do |client|
1450
- client.call([:sdiff] + keys)
1611
+ client.call([:sdiff, *keys])
1451
1612
  end
1452
1613
  end
1453
1614
 
@@ -1455,10 +1616,10 @@ class Redis
1455
1616
  #
1456
1617
  # @param [String] destination destination key
1457
1618
  # @param [String, Array<String>] keys keys pointing to sets to subtract
1458
- # @return [Fixnum] number of elements in the resulting set
1619
+ # @return [Integer] number of elements in the resulting set
1459
1620
  def sdiffstore(destination, *keys)
1460
1621
  synchronize do |client|
1461
- client.call([:sdiffstore, destination] + keys)
1622
+ client.call([:sdiffstore, destination, *keys])
1462
1623
  end
1463
1624
  end
1464
1625
 
@@ -1468,7 +1629,7 @@ class Redis
1468
1629
  # @return [Array<String>] members in the intersection
1469
1630
  def sinter(*keys)
1470
1631
  synchronize do |client|
1471
- client.call([:sinter] + keys)
1632
+ client.call([:sinter, *keys])
1472
1633
  end
1473
1634
  end
1474
1635
 
@@ -1476,10 +1637,10 @@ class Redis
1476
1637
  #
1477
1638
  # @param [String] destination destination key
1478
1639
  # @param [String, Array<String>] keys keys pointing to sets to intersect
1479
- # @return [Fixnum] number of elements in the resulting set
1640
+ # @return [Integer] number of elements in the resulting set
1480
1641
  def sinterstore(destination, *keys)
1481
1642
  synchronize do |client|
1482
- client.call([:sinterstore, destination] + keys)
1643
+ client.call([:sinterstore, destination, *keys])
1483
1644
  end
1484
1645
  end
1485
1646
 
@@ -1489,7 +1650,7 @@ class Redis
1489
1650
  # @return [Array<String>] members in the union
1490
1651
  def sunion(*keys)
1491
1652
  synchronize do |client|
1492
- client.call([:sunion] + keys)
1653
+ client.call([:sunion, *keys])
1493
1654
  end
1494
1655
  end
1495
1656
 
@@ -1497,10 +1658,10 @@ class Redis
1497
1658
  #
1498
1659
  # @param [String] destination destination key
1499
1660
  # @param [String, Array<String>] keys keys pointing to sets to unify
1500
- # @return [Fixnum] number of elements in the resulting set
1661
+ # @return [Integer] number of elements in the resulting set
1501
1662
  def sunionstore(destination, *keys)
1502
1663
  synchronize do |client|
1503
- client.call([:sunionstore, destination] + keys)
1664
+ client.call([:sunionstore, destination, *keys])
1504
1665
  end
1505
1666
  end
1506
1667
 
@@ -1511,7 +1672,7 @@ class Redis
1511
1672
  # # => 4
1512
1673
  #
1513
1674
  # @param [String] key
1514
- # @return [Fixnum]
1675
+ # @return [Integer]
1515
1676
  def zcard(key)
1516
1677
  synchronize do |client|
1517
1678
  client.call([:zcard, key])
@@ -1535,6 +1696,10 @@ class Redis
1535
1696
  # add elements)
1536
1697
  # - `:nx => true`: Don't update already existing elements (always
1537
1698
  # add new elements)
1699
+ # - `:lt => true`: Only update existing elements if the new score
1700
+ # is less than the current score
1701
+ # - `:gt => true`: Only update existing elements if the new score
1702
+ # is greater than the current score
1538
1703
  # - `:ch => true`: Modify the return value from the number of new
1539
1704
  # elements added, to the total number of elements changed (CH is an
1540
1705
  # abbreviation of changed); changed elements are new elements added
@@ -1542,38 +1707,29 @@ class Redis
1542
1707
  # - `:incr => true`: When this option is specified ZADD acts like
1543
1708
  # ZINCRBY; only one score-element pair can be specified in this mode
1544
1709
  #
1545
- # @return [Boolean, Fixnum, Float]
1710
+ # @return [Boolean, Integer, Float]
1546
1711
  # - `Boolean` when a single pair is specified, holding whether or not it was
1547
1712
  # **added** to the sorted set.
1548
- # - `Fixnum` when an array of pairs is specified, holding the number of
1713
+ # - `Integer` when an array of pairs is specified, holding the number of
1549
1714
  # pairs that were **added** to the sorted set.
1550
1715
  # - `Float` when option :incr is specified, holding the score of the member
1551
1716
  # after incrementing it.
1552
- def zadd(key, *args) #, options
1553
- zadd_options = []
1554
- if args.last.is_a?(Hash)
1555
- options = args.pop
1556
-
1557
- nx = options[:nx]
1558
- zadd_options << "NX" if nx
1559
-
1560
- xx = options[:xx]
1561
- zadd_options << "XX" if xx
1562
-
1563
- ch = options[:ch]
1564
- zadd_options << "CH" if ch
1565
-
1566
- incr = options[:incr]
1567
- zadd_options << "INCR" if incr
1568
- end
1717
+ def zadd(key, *args, nx: nil, xx: nil, lt: nil, gt: nil, ch: nil, incr: nil)
1718
+ command = [:zadd, key]
1719
+ command << "NX" if nx
1720
+ command << "XX" if xx
1721
+ command << "LT" if lt
1722
+ command << "GT" if gt
1723
+ command << "CH" if ch
1724
+ command << "INCR" if incr
1569
1725
 
1570
1726
  synchronize do |client|
1571
1727
  if args.size == 1 && args[0].is_a?(Array)
1572
1728
  # Variadic: return float if INCR, integer if !INCR
1573
- client.call([:zadd, key] + zadd_options + args[0], &(incr ? Floatify : nil))
1729
+ client.call(command + args[0], &(incr ? Floatify : nil))
1574
1730
  elsif args.size == 2
1575
1731
  # Single pair: return float if INCR, boolean if !INCR
1576
- client.call([:zadd, key] + zadd_options + args, &(incr ? Floatify : Boolify))
1732
+ client.call(command + args, &(incr ? Floatify : Boolify))
1577
1733
  else
1578
1734
  raise ArgumentError, "wrong number of arguments"
1579
1735
  end
@@ -1608,10 +1764,10 @@ class Redis
1608
1764
  # - a single member
1609
1765
  # - an array of members
1610
1766
  #
1611
- # @return [Boolean, Fixnum]
1767
+ # @return [Boolean, Integer]
1612
1768
  # - `Boolean` when a single member is specified, holding whether or not it
1613
1769
  # was removed from the sorted set
1614
- # - `Fixnum` when an array of pairs is specified, holding the number of
1770
+ # - `Integer` when an array of pairs is specified, holding the number of
1615
1771
  # members that were removed to the sorted set
1616
1772
  def zrem(key, member)
1617
1773
  synchronize do |client|
@@ -1726,6 +1882,63 @@ class Redis
1726
1882
  end
1727
1883
  end
1728
1884
 
1885
+ # Get the scores associated with the given members in a sorted set.
1886
+ #
1887
+ # @example Get the scores for members "a" and "b"
1888
+ # redis.zmscore("zset", "a", "b")
1889
+ # # => [32.0, 48.0]
1890
+ #
1891
+ # @param [String] key
1892
+ # @param [String, Array<String>] members
1893
+ # @return [Array<Float>] scores of the members
1894
+ def zmscore(key, *members)
1895
+ synchronize do |client|
1896
+ client.call([:zmscore, key, *members]) do |reply|
1897
+ reply.map(&Floatify)
1898
+ end
1899
+ end
1900
+ end
1901
+
1902
+ # Get one or more random members from a sorted set.
1903
+ #
1904
+ # @example Get one random member
1905
+ # redis.zrandmember("zset")
1906
+ # # => "a"
1907
+ # @example Get multiple random members
1908
+ # redis.zrandmember("zset", 2)
1909
+ # # => ["a", "b"]
1910
+ # @example Gem multiple random members with scores
1911
+ # redis.zrandmember("zset", 2, with_scores: true)
1912
+ # # => [["a", 2.0], ["b", 3.0]]
1913
+ #
1914
+ # @param [String] key
1915
+ # @param [Integer] count
1916
+ # @param [Hash] options
1917
+ # - `:with_scores => true`: include scores in output
1918
+ #
1919
+ # @return [nil, String, Array<String>, Array<[String, Float]>]
1920
+ # - when `key` does not exist or set is empty, `nil`
1921
+ # - when `count` is not specified, a member
1922
+ # - when `count` is specified and `:with_scores` is not specified, an array of members
1923
+ # - when `:with_scores` is specified, an array with `[member, score]` pairs
1924
+ def zrandmember(key, count = nil, withscores: false, with_scores: withscores)
1925
+ if with_scores && count.nil?
1926
+ raise ArgumentError, "count argument must be specified"
1927
+ end
1928
+
1929
+ args = [:zrandmember, key]
1930
+ args << count if count
1931
+
1932
+ if with_scores
1933
+ args << "WITHSCORES"
1934
+ block = FloatifyPairs
1935
+ end
1936
+
1937
+ synchronize do |client|
1938
+ client.call(args, &block)
1939
+ end
1940
+ end
1941
+
1729
1942
  # Return a range of members in a sorted set, by index.
1730
1943
  #
1731
1944
  # @example Retrieve all members from a sorted set
@@ -1736,18 +1949,16 @@ class Redis
1736
1949
  # # => [["a", 32.0], ["b", 64.0]]
1737
1950
  #
1738
1951
  # @param [String] key
1739
- # @param [Fixnum] start start index
1740
- # @param [Fixnum] stop stop index
1952
+ # @param [Integer] start start index
1953
+ # @param [Integer] stop stop index
1741
1954
  # @param [Hash] options
1742
1955
  # - `:with_scores => true`: include scores in output
1743
1956
  #
1744
1957
  # @return [Array<String>, Array<[String, Float]>]
1745
1958
  # - when `:with_scores` is not specified, an array of members
1746
1959
  # - when `:with_scores` is specified, an array with `[member, score]` pairs
1747
- def zrange(key, start, stop, options = {})
1748
- args = []
1749
-
1750
- with_scores = options[:with_scores] || options[:withscores]
1960
+ def zrange(key, start, stop, withscores: false, with_scores: withscores)
1961
+ args = [:zrange, key, start, stop]
1751
1962
 
1752
1963
  if with_scores
1753
1964
  args << "WITHSCORES"
@@ -1755,7 +1966,7 @@ class Redis
1755
1966
  end
1756
1967
 
1757
1968
  synchronize do |client|
1758
- client.call([:zrange, key, start, stop] + args, &block)
1969
+ client.call(args, &block)
1759
1970
  end
1760
1971
  end
1761
1972
 
@@ -1770,10 +1981,8 @@ class Redis
1770
1981
  # # => [["b", 64.0], ["a", 32.0]]
1771
1982
  #
1772
1983
  # @see #zrange
1773
- def zrevrange(key, start, stop, options = {})
1774
- args = []
1775
-
1776
- with_scores = options[:with_scores] || options[:withscores]
1984
+ def zrevrange(key, start, stop, withscores: false, with_scores: withscores)
1985
+ args = [:zrevrange, key, start, stop]
1777
1986
 
1778
1987
  if with_scores
1779
1988
  args << "WITHSCORES"
@@ -1781,7 +1990,7 @@ class Redis
1781
1990
  end
1782
1991
 
1783
1992
  synchronize do |client|
1784
- client.call([:zrevrange, key, start, stop] + args, &block)
1993
+ client.call(args, &block)
1785
1994
  end
1786
1995
  end
1787
1996
 
@@ -1789,7 +1998,7 @@ class Redis
1789
1998
  #
1790
1999
  # @param [String] key
1791
2000
  # @param [String] member
1792
- # @return [Fixnum]
2001
+ # @return [Integer]
1793
2002
  def zrank(key, member)
1794
2003
  synchronize do |client|
1795
2004
  client.call([:zrank, key, member])
@@ -1801,7 +2010,7 @@ class Redis
1801
2010
  #
1802
2011
  # @param [String] key
1803
2012
  # @param [String] member
1804
- # @return [Fixnum]
2013
+ # @return [Integer]
1805
2014
  def zrevrank(key, member)
1806
2015
  synchronize do |client|
1807
2016
  client.call([:zrevrank, key, member])
@@ -1818,9 +2027,9 @@ class Redis
1818
2027
  # # => 5
1819
2028
  #
1820
2029
  # @param [String] key
1821
- # @param [Fixnum] start start index
1822
- # @param [Fixnum] stop stop index
1823
- # @return [Fixnum] number of members that were removed
2030
+ # @param [Integer] start start index
2031
+ # @param [Integer] stop stop index
2032
+ # @return [Integer] number of members that were removed
1824
2033
  def zremrangebyrank(key, start, stop)
1825
2034
  synchronize do |client|
1826
2035
  client.call([:zremrangebyrank, key, start, stop])
@@ -1844,7 +2053,7 @@ class Redis
1844
2053
  # - inclusive maximum is specified by prefixing `(`
1845
2054
  # - exclusive maximum is specified by prefixing `[`
1846
2055
  #
1847
- # @return [Fixnum] number of members within the specified lexicographical range
2056
+ # @return [Integer] number of members within the specified lexicographical range
1848
2057
  def zlexcount(key, min, max)
1849
2058
  synchronize do |client|
1850
2059
  client.call([:zlexcount, key, min, max])
@@ -1872,14 +2081,16 @@ class Redis
1872
2081
  # `count` members
1873
2082
  #
1874
2083
  # @return [Array<String>, Array<[String, Float]>]
1875
- def zrangebylex(key, min, max, options = {})
1876
- args = []
2084
+ def zrangebylex(key, min, max, limit: nil)
2085
+ args = [:zrangebylex, key, min, max]
1877
2086
 
1878
- limit = options[:limit]
1879
- args.concat(["LIMIT"] + limit) if limit
2087
+ if limit
2088
+ args << "LIMIT"
2089
+ args.concat(limit)
2090
+ end
1880
2091
 
1881
2092
  synchronize do |client|
1882
- client.call([:zrangebylex, key, min, max] + args)
2093
+ client.call(args)
1883
2094
  end
1884
2095
  end
1885
2096
 
@@ -1894,14 +2105,16 @@ class Redis
1894
2105
  # # => ["abbygail", "abby"]
1895
2106
  #
1896
2107
  # @see #zrangebylex
1897
- def zrevrangebylex(key, max, min, options = {})
1898
- args = []
2108
+ def zrevrangebylex(key, max, min, limit: nil)
2109
+ args = [:zrevrangebylex, key, max, min]
1899
2110
 
1900
- limit = options[:limit]
1901
- args.concat(["LIMIT"] + limit) if limit
2111
+ if limit
2112
+ args << "LIMIT"
2113
+ args.concat(limit)
2114
+ end
1902
2115
 
1903
2116
  synchronize do |client|
1904
- client.call([:zrevrangebylex, key, max, min] + args)
2117
+ client.call(args)
1905
2118
  end
1906
2119
  end
1907
2120
 
@@ -1932,21 +2145,21 @@ class Redis
1932
2145
  # @return [Array<String>, Array<[String, Float]>]
1933
2146
  # - when `:with_scores` is not specified, an array of members
1934
2147
  # - when `:with_scores` is specified, an array with `[member, score]` pairs
1935
- def zrangebyscore(key, min, max, options = {})
1936
- args = []
1937
-
1938
- with_scores = options[:with_scores] || options[:withscores]
2148
+ def zrangebyscore(key, min, max, withscores: false, with_scores: withscores, limit: nil)
2149
+ args = [:zrangebyscore, key, min, max]
1939
2150
 
1940
2151
  if with_scores
1941
2152
  args << "WITHSCORES"
1942
2153
  block = FloatifyPairs
1943
2154
  end
1944
2155
 
1945
- limit = options[:limit]
1946
- args.concat(["LIMIT"] + limit) if limit
2156
+ if limit
2157
+ args << "LIMIT"
2158
+ args.concat(limit)
2159
+ end
1947
2160
 
1948
2161
  synchronize do |client|
1949
- client.call([:zrangebyscore, key, min, max] + args, &block)
2162
+ client.call(args, &block)
1950
2163
  end
1951
2164
  end
1952
2165
 
@@ -1964,21 +2177,21 @@ class Redis
1964
2177
  # # => [["b", 64.0], ["a", 32.0]]
1965
2178
  #
1966
2179
  # @see #zrangebyscore
1967
- def zrevrangebyscore(key, max, min, options = {})
1968
- args = []
1969
-
1970
- with_scores = options[:with_scores] || options[:withscores]
2180
+ def zrevrangebyscore(key, max, min, withscores: false, with_scores: withscores, limit: nil)
2181
+ args = [:zrevrangebyscore, key, max, min]
1971
2182
 
1972
2183
  if with_scores
1973
- args << ["WITHSCORES"]
2184
+ args << "WITHSCORES"
1974
2185
  block = FloatifyPairs
1975
2186
  end
1976
2187
 
1977
- limit = options[:limit]
1978
- args.concat(["LIMIT"] + limit) if limit
2188
+ if limit
2189
+ args << "LIMIT"
2190
+ args.concat(limit)
2191
+ end
1979
2192
 
1980
2193
  synchronize do |client|
1981
- client.call([:zrevrangebyscore, key, max, min] + args, &block)
2194
+ client.call(args, &block)
1982
2195
  end
1983
2196
  end
1984
2197
 
@@ -1998,7 +2211,7 @@ class Redis
1998
2211
  # @param [String] max
1999
2212
  # - inclusive maximum score is specified verbatim
2000
2213
  # - exclusive maximum score is specified by prefixing `(`
2001
- # @return [Fixnum] number of members that were removed
2214
+ # @return [Integer] number of members that were removed
2002
2215
  def zremrangebyscore(key, min, max)
2003
2216
  synchronize do |client|
2004
2217
  client.call([:zremrangebyscore, key, min, max])
@@ -2021,13 +2234,52 @@ class Redis
2021
2234
  # @param [String] max
2022
2235
  # - inclusive maximum score is specified verbatim
2023
2236
  # - exclusive maximum score is specified by prefixing `(`
2024
- # @return [Fixnum] number of members in within the specified range
2237
+ # @return [Integer] number of members in within the specified range
2025
2238
  def zcount(key, min, max)
2026
2239
  synchronize do |client|
2027
2240
  client.call([:zcount, key, min, max])
2028
2241
  end
2029
2242
  end
2030
2243
 
2244
+ # Return the intersection of multiple sorted sets
2245
+ #
2246
+ # @example Retrieve the intersection of `2*zsetA` and `1*zsetB`
2247
+ # redis.zinter("zsetA", "zsetB", :weights => [2.0, 1.0])
2248
+ # # => ["v1", "v2"]
2249
+ # @example Retrieve the intersection of `2*zsetA` and `1*zsetB`, and their scores
2250
+ # redis.zinter("zsetA", "zsetB", :weights => [2.0, 1.0], :with_scores => true)
2251
+ # # => [["v1", 3.0], ["v2", 6.0]]
2252
+ #
2253
+ # @param [String, Array<String>] keys one or more keys to intersect
2254
+ # @param [Hash] options
2255
+ # - `:weights => [Float, Float, ...]`: weights to associate with source
2256
+ # sorted sets
2257
+ # - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
2258
+ # - `:with_scores => true`: include scores in output
2259
+ #
2260
+ # @return [Array<String>, Array<[String, Float]>]
2261
+ # - when `:with_scores` is not specified, an array of members
2262
+ # - when `:with_scores` is specified, an array with `[member, score]` pairs
2263
+ def zinter(*keys, weights: nil, aggregate: nil, with_scores: false)
2264
+ args = [:zinter, keys.size, *keys]
2265
+
2266
+ if weights
2267
+ args << "WEIGHTS"
2268
+ args.concat(weights)
2269
+ end
2270
+
2271
+ args << "AGGREGATE" << aggregate if aggregate
2272
+
2273
+ if with_scores
2274
+ args << "WITHSCORES"
2275
+ block = FloatifyPairs
2276
+ end
2277
+
2278
+ synchronize do |client|
2279
+ client.call(args, &block)
2280
+ end
2281
+ end
2282
+
2031
2283
  # Intersect multiple sorted sets and store the resulting sorted set in a new
2032
2284
  # key.
2033
2285
  #
@@ -2041,18 +2293,19 @@ class Redis
2041
2293
  # - `:weights => [Float, Float, ...]`: weights to associate with source
2042
2294
  # sorted sets
2043
2295
  # - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
2044
- # @return [Fixnum] number of elements in the resulting sorted set
2045
- def zinterstore(destination, keys, options = {})
2046
- args = []
2296
+ # @return [Integer] number of elements in the resulting sorted set
2297
+ def zinterstore(destination, keys, weights: nil, aggregate: nil)
2298
+ args = [:zinterstore, destination, keys.size, *keys]
2047
2299
 
2048
- weights = options[:weights]
2049
- args.concat(["WEIGHTS"] + weights) if weights
2300
+ if weights
2301
+ args << "WEIGHTS"
2302
+ args.concat(weights)
2303
+ end
2050
2304
 
2051
- aggregate = options[:aggregate]
2052
- args.concat(["AGGREGATE", aggregate]) if aggregate
2305
+ args << "AGGREGATE" << aggregate if aggregate
2053
2306
 
2054
2307
  synchronize do |client|
2055
- client.call([:zinterstore, destination, keys.size] + keys + args)
2308
+ client.call(args)
2056
2309
  end
2057
2310
  end
2058
2311
 
@@ -2068,40 +2321,46 @@ class Redis
2068
2321
  # - `:weights => [Float, Float, ...]`: weights to associate with source
2069
2322
  # sorted sets
2070
2323
  # - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
2071
- # @return [Fixnum] number of elements in the resulting sorted set
2072
- def zunionstore(destination, keys, options = {})
2073
- args = []
2324
+ # @return [Integer] number of elements in the resulting sorted set
2325
+ def zunionstore(destination, keys, weights: nil, aggregate: nil)
2326
+ args = [:zunionstore, destination, keys.size, *keys]
2074
2327
 
2075
- weights = options[:weights]
2076
- args.concat(["WEIGHTS"] + weights) if weights
2328
+ if weights
2329
+ args << "WEIGHTS"
2330
+ args.concat(weights)
2331
+ end
2077
2332
 
2078
- aggregate = options[:aggregate]
2079
- args.concat(["AGGREGATE", aggregate]) if aggregate
2333
+ args << "AGGREGATE" << aggregate if aggregate
2080
2334
 
2081
2335
  synchronize do |client|
2082
- client.call([:zunionstore, destination, keys.size] + keys + args)
2336
+ client.call(args)
2083
2337
  end
2084
2338
  end
2085
2339
 
2086
2340
  # Get the number of fields in a hash.
2087
2341
  #
2088
2342
  # @param [String] key
2089
- # @return [Fixnum] number of fields in the hash
2343
+ # @return [Integer] number of fields in the hash
2090
2344
  def hlen(key)
2091
2345
  synchronize do |client|
2092
2346
  client.call([:hlen, key])
2093
2347
  end
2094
2348
  end
2095
2349
 
2096
- # Set the string value of a hash field.
2350
+ # Set one or more hash values.
2351
+ #
2352
+ # @example
2353
+ # redis.hset("hash", "f1", "v1", "f2", "v2") # => 2
2354
+ # redis.hset("hash", { "f1" => "v1", "f2" => "v2" }) # => 2
2097
2355
  #
2098
2356
  # @param [String] key
2099
- # @param [String] field
2100
- # @param [String] value
2101
- # @return [Boolean] whether or not the field was **added** to the hash
2102
- def hset(key, field, value)
2357
+ # @param [Array<String> | Hash<String, String>] attrs array or hash of fields and values
2358
+ # @return [Integer] The number of fields that were added to the hash
2359
+ def hset(key, *attrs)
2360
+ attrs = attrs.first.flatten if attrs.size == 1 && attrs.first.is_a?(Hash)
2361
+
2103
2362
  synchronize do |client|
2104
- client.call([:hset, key, field, value], &Boolify)
2363
+ client.call([:hset, key, *attrs])
2105
2364
  end
2106
2365
  end
2107
2366
 
@@ -2190,7 +2449,7 @@ class Redis
2190
2449
  # @see #hmget
2191
2450
  def mapped_hmget(key, *fields)
2192
2451
  hmget(key, *fields) do |reply|
2193
- if reply.kind_of?(Array)
2452
+ if reply.is_a?(Array)
2194
2453
  Hash[fields.zip(reply)]
2195
2454
  else
2196
2455
  reply
@@ -2202,7 +2461,7 @@ class Redis
2202
2461
  #
2203
2462
  # @param [String] key
2204
2463
  # @param [String, Array<String>] field
2205
- # @return [Fixnum] the number of fields that were removed from the hash
2464
+ # @return [Integer] the number of fields that were removed from the hash
2206
2465
  def hdel(key, *fields)
2207
2466
  synchronize do |client|
2208
2467
  client.call([:hdel, key, *fields])
@@ -2224,8 +2483,8 @@ class Redis
2224
2483
  #
2225
2484
  # @param [String] key
2226
2485
  # @param [String] field
2227
- # @param [Fixnum] increment
2228
- # @return [Fixnum] value of the field after incrementing it
2486
+ # @param [Integer] increment
2487
+ # @return [Integer] value of the field after incrementing it
2229
2488
  def hincrby(key, field, increment)
2230
2489
  synchronize do |client|
2231
2490
  client.call([:hincrby, key, field, increment])
@@ -2283,20 +2542,21 @@ class Redis
2283
2542
 
2284
2543
  def subscribed?
2285
2544
  synchronize do |client|
2286
- client.kind_of? SubscribedClient
2545
+ client.is_a? SubscribedClient
2287
2546
  end
2288
2547
  end
2289
2548
 
2290
2549
  # Listen for messages published to the given channels.
2291
2550
  def subscribe(*channels, &block)
2292
- synchronize do |client|
2551
+ synchronize do |_client|
2293
2552
  _subscription(:subscribe, 0, channels, block)
2294
2553
  end
2295
2554
  end
2296
2555
 
2297
- # Listen for messages published to the given channels. Throw a timeout error if there is no messages for a timeout period.
2556
+ # Listen for messages published to the given channels. Throw a timeout error
2557
+ # if there is no messages for a timeout period.
2298
2558
  def subscribe_with_timeout(timeout, *channels, &block)
2299
- synchronize do |client|
2559
+ synchronize do |_client|
2300
2560
  _subscription(:subscribe_with_timeout, timeout, channels, block)
2301
2561
  end
2302
2562
  end
@@ -2304,21 +2564,23 @@ class Redis
2304
2564
  # Stop listening for messages posted to the given channels.
2305
2565
  def unsubscribe(*channels)
2306
2566
  synchronize do |client|
2307
- raise RuntimeError, "Can't unsubscribe if not subscribed." unless subscribed?
2567
+ raise "Can't unsubscribe if not subscribed." unless subscribed?
2568
+
2308
2569
  client.unsubscribe(*channels)
2309
2570
  end
2310
2571
  end
2311
2572
 
2312
2573
  # Listen for messages published to channels matching the given patterns.
2313
2574
  def psubscribe(*channels, &block)
2314
- synchronize do |client|
2575
+ synchronize do |_client|
2315
2576
  _subscription(:psubscribe, 0, channels, block)
2316
2577
  end
2317
2578
  end
2318
2579
 
2319
- # Listen for messages published to channels matching the given patterns. Throw a timeout error if there is no messages for a timeout period.
2580
+ # Listen for messages published to channels matching the given patterns.
2581
+ # Throw a timeout error if there is no messages for a timeout period.
2320
2582
  def psubscribe_with_timeout(timeout, *channels, &block)
2321
- synchronize do |client|
2583
+ synchronize do |_client|
2322
2584
  _subscription(:psubscribe_with_timeout, timeout, channels, block)
2323
2585
  end
2324
2586
  end
@@ -2326,7 +2588,8 @@ class Redis
2326
2588
  # Stop listening for messages posted to channels matching the given patterns.
2327
2589
  def punsubscribe(*channels)
2328
2590
  synchronize do |client|
2329
- raise RuntimeError, "Can't unsubscribe if not subscribed." unless subscribed?
2591
+ raise "Can't unsubscribe if not subscribed." unless subscribed?
2592
+
2330
2593
  client.punsubscribe(*channels)
2331
2594
  end
2332
2595
  end
@@ -2371,7 +2634,7 @@ class Redis
2371
2634
  # @see #multi
2372
2635
  def watch(*keys)
2373
2636
  synchronize do |client|
2374
- res = client.call([:watch] + keys)
2637
+ res = client.call([:watch, *keys])
2375
2638
 
2376
2639
  if block_given?
2377
2640
  begin
@@ -2401,13 +2664,13 @@ class Redis
2401
2664
  end
2402
2665
 
2403
2666
  def pipelined
2404
- synchronize do |client|
2667
+ synchronize do |prior_client|
2405
2668
  begin
2406
- original, @client = @client, Pipeline.new
2669
+ @client = Pipeline.new(prior_client)
2407
2670
  yield(self)
2408
- original.call_pipeline(@client)
2671
+ prior_client.call_pipeline(@client)
2409
2672
  ensure
2410
- @client = original
2673
+ @client = prior_client
2411
2674
  end
2412
2675
  end
2413
2676
  end
@@ -2443,17 +2706,16 @@ class Redis
2443
2706
  # @see #watch
2444
2707
  # @see #unwatch
2445
2708
  def multi
2446
- synchronize do |client|
2709
+ synchronize do |prior_client|
2447
2710
  if !block_given?
2448
- client.call([:multi])
2711
+ prior_client.call([:multi])
2449
2712
  else
2450
2713
  begin
2451
- pipeline = Pipeline::Multi.new
2452
- original, @client = @client, pipeline
2714
+ @client = Pipeline::Multi.new(prior_client)
2453
2715
  yield(self)
2454
- original.call_pipeline(pipeline)
2716
+ prior_client.call_pipeline(@client)
2455
2717
  ensure
2456
- @client = original
2718
+ @client = prior_client
2457
2719
  end
2458
2720
  end
2459
2721
  end
@@ -2600,18 +2862,13 @@ class Redis
2600
2862
  _eval(:evalsha, args)
2601
2863
  end
2602
2864
 
2603
- def _scan(command, cursor, args, options = {}, &block)
2865
+ def _scan(command, cursor, args, match: nil, count: nil, type: nil, &block)
2604
2866
  # SSCAN/ZSCAN/HSCAN already prepend the key to +args+.
2605
2867
 
2606
2868
  args << cursor
2607
-
2608
- if match = options[:match]
2609
- args.concat(["MATCH", match])
2610
- end
2611
-
2612
- if count = options[:count]
2613
- args.concat(["COUNT", count])
2614
- end
2869
+ args << "MATCH" << match if match
2870
+ args << "COUNT" << count if count
2871
+ args << "TYPE" << type if type
2615
2872
 
2616
2873
  synchronize do |client|
2617
2874
  client.call([command] + args, &block)
@@ -2626,15 +2883,19 @@ class Redis
2626
2883
  # @example Retrieve a batch of keys matching a pattern
2627
2884
  # redis.scan(4, :match => "key:1?")
2628
2885
  # # => ["92", ["key:13", "key:18"]]
2886
+ # @example Retrieve a batch of keys of a certain type
2887
+ # redis.scan(92, :type => "zset")
2888
+ # # => ["173", ["sortedset:14", "sortedset:78"]]
2629
2889
  #
2630
2890
  # @param [String, Integer] cursor the cursor of the iteration
2631
2891
  # @param [Hash] options
2632
2892
  # - `:match => String`: only return keys matching the pattern
2633
2893
  # - `:count => Integer`: return count keys at most per iteration
2894
+ # - `:type => String`: return keys only of the given type
2634
2895
  #
2635
2896
  # @return [String, Array<String>] the next cursor and all found keys
2636
- def scan(cursor, options={})
2637
- _scan(:scan, cursor, [], options)
2897
+ def scan(cursor, **options)
2898
+ _scan(:scan, cursor, [], **options)
2638
2899
  end
2639
2900
 
2640
2901
  # Scan the keyspace
@@ -2646,17 +2907,23 @@ class Redis
2646
2907
  # redis.scan_each(:match => "key:1?") {|key| puts key}
2647
2908
  # # => key:13
2648
2909
  # # => key:18
2910
+ # @example Execute block for each key of a type
2911
+ # redis.scan_each(:type => "hash") {|key| puts redis.type(key)}
2912
+ # # => "hash"
2913
+ # # => "hash"
2649
2914
  #
2650
2915
  # @param [Hash] options
2651
2916
  # - `:match => String`: only return keys matching the pattern
2652
2917
  # - `:count => Integer`: return count keys at most per iteration
2918
+ # - `:type => String`: return keys only of the given type
2653
2919
  #
2654
2920
  # @return [Enumerator] an enumerator for all found keys
2655
- def scan_each(options={}, &block)
2656
- return to_enum(:scan_each, options) unless block_given?
2921
+ def scan_each(**options, &block)
2922
+ return to_enum(:scan_each, **options) unless block_given?
2923
+
2657
2924
  cursor = 0
2658
2925
  loop do
2659
- cursor, keys = scan(cursor, options)
2926
+ cursor, keys = scan(cursor, **options)
2660
2927
  keys.each(&block)
2661
2928
  break if cursor == "0"
2662
2929
  end
@@ -2673,8 +2940,8 @@ class Redis
2673
2940
  # - `:count => Integer`: return count keys at most per iteration
2674
2941
  #
2675
2942
  # @return [String, Array<[String, String]>] the next cursor and all found keys
2676
- def hscan(key, cursor, options={})
2677
- _scan(:hscan, cursor, [key], options) do |reply|
2943
+ def hscan(key, cursor, **options)
2944
+ _scan(:hscan, cursor, [key], **options) do |reply|
2678
2945
  [reply[0], reply[1].each_slice(2).to_a]
2679
2946
  end
2680
2947
  end
@@ -2690,11 +2957,12 @@ class Redis
2690
2957
  # - `:count => Integer`: return count keys at most per iteration
2691
2958
  #
2692
2959
  # @return [Enumerator] an enumerator for all found keys
2693
- def hscan_each(key, options={}, &block)
2694
- return to_enum(:hscan_each, key, options) unless block_given?
2960
+ def hscan_each(key, **options, &block)
2961
+ return to_enum(:hscan_each, key, **options) unless block_given?
2962
+
2695
2963
  cursor = 0
2696
2964
  loop do
2697
- cursor, values = hscan(key, cursor, options)
2965
+ cursor, values = hscan(key, cursor, **options)
2698
2966
  values.each(&block)
2699
2967
  break if cursor == "0"
2700
2968
  end
@@ -2712,8 +2980,8 @@ class Redis
2712
2980
  #
2713
2981
  # @return [String, Array<[String, Float]>] the next cursor and all found
2714
2982
  # members and scores
2715
- def zscan(key, cursor, options={})
2716
- _scan(:zscan, cursor, [key], options) do |reply|
2983
+ def zscan(key, cursor, **options)
2984
+ _scan(:zscan, cursor, [key], **options) do |reply|
2717
2985
  [reply[0], FloatifyPairs.call(reply[1])]
2718
2986
  end
2719
2987
  end
@@ -2729,11 +2997,12 @@ class Redis
2729
2997
  # - `:count => Integer`: return count keys at most per iteration
2730
2998
  #
2731
2999
  # @return [Enumerator] an enumerator for all found scores and members
2732
- def zscan_each(key, options={}, &block)
2733
- return to_enum(:zscan_each, key, options) unless block_given?
3000
+ def zscan_each(key, **options, &block)
3001
+ return to_enum(:zscan_each, key, **options) unless block_given?
3002
+
2734
3003
  cursor = 0
2735
3004
  loop do
2736
- cursor, values = zscan(key, cursor, options)
3005
+ cursor, values = zscan(key, cursor, **options)
2737
3006
  values.each(&block)
2738
3007
  break if cursor == "0"
2739
3008
  end
@@ -2750,8 +3019,8 @@ class Redis
2750
3019
  # - `:count => Integer`: return count keys at most per iteration
2751
3020
  #
2752
3021
  # @return [String, Array<String>] the next cursor and all found members
2753
- def sscan(key, cursor, options={})
2754
- _scan(:sscan, cursor, [key], options)
3022
+ def sscan(key, cursor, **options)
3023
+ _scan(:sscan, cursor, [key], **options)
2755
3024
  end
2756
3025
 
2757
3026
  # Scan a set
@@ -2765,11 +3034,12 @@ class Redis
2765
3034
  # - `:count => Integer`: return count keys at most per iteration
2766
3035
  #
2767
3036
  # @return [Enumerator] an enumerator for all keys in the set
2768
- def sscan_each(key, options={}, &block)
2769
- return to_enum(:sscan_each, key, options) unless block_given?
3037
+ def sscan_each(key, **options, &block)
3038
+ return to_enum(:sscan_each, key, **options) unless block_given?
3039
+
2770
3040
  cursor = 0
2771
3041
  loop do
2772
- cursor, keys = sscan(key, cursor, options)
3042
+ cursor, keys = sscan(key, cursor, **options)
2773
3043
  keys.each(&block)
2774
3044
  break if cursor == "0"
2775
3045
  end
@@ -2792,7 +3062,7 @@ class Redis
2792
3062
  # union of the HyperLogLogs contained in the keys.
2793
3063
  #
2794
3064
  # @param [String, Array<String>] keys
2795
- # @return [Fixnum]
3065
+ # @return [Integer]
2796
3066
  def pfcount(*keys)
2797
3067
  synchronize do |client|
2798
3068
  client.call([:pfcount] + keys)
@@ -2815,10 +3085,10 @@ class Redis
2815
3085
  #
2816
3086
  # @param [String] key
2817
3087
  # @param [Array] member arguemnts for member or members: longitude, latitude, name
2818
- # @return [Intger] number of elements added to the sorted set
3088
+ # @return [Integer] number of elements added to the sorted set
2819
3089
  def geoadd(key, *member)
2820
3090
  synchronize do |client|
2821
- client.call([:geoadd, key, member])
3091
+ client.call([:geoadd, key, *member])
2822
3092
  end
2823
3093
  end
2824
3094
 
@@ -2833,12 +3103,12 @@ class Redis
2833
3103
  end
2834
3104
  end
2835
3105
 
2836
-
2837
3106
  # Query a sorted set representing a geospatial index to fetch members matching a
2838
3107
  # given maximum distance from a point
2839
3108
  #
2840
3109
  # @param [Array] args key, longitude, latitude, radius, unit(m|km|ft|mi)
2841
- # @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest or the farthest to the nearest relative to the center
3110
+ # @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest
3111
+ # or the farthest to the nearest relative to the center
2842
3112
  # @param [Integer] count limit the results to the first N matching items
2843
3113
  # @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
2844
3114
  # @return [Array<String>] may be changed with `options`
@@ -2855,7 +3125,8 @@ class Redis
2855
3125
  # given maximum distance from an already existing member
2856
3126
  #
2857
3127
  # @param [Array] args key, member, radius, unit(m|km|ft|mi)
2858
- # @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest or the farthest to the nearest relative to the center
3128
+ # @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest or the farthest
3129
+ # to the nearest relative to the center
2859
3130
  # @param [Integer] count limit the results to the first N matching items
2860
3131
  # @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
2861
3132
  # @return [Array<String>] may be changed with `options`
@@ -2872,7 +3143,8 @@ class Redis
2872
3143
  #
2873
3144
  # @param [String] key
2874
3145
  # @param [String, Array<String>] member one member or array of members
2875
- # @return [Array<Array<String>, nil>] returns array of elements, where each element is either array of longitude and latitude or nil
3146
+ # @return [Array<Array<String>, nil>] returns array of elements, where each
3147
+ # element is either array of longitude and latitude or nil
2876
3148
  def geopos(key, member)
2877
3149
  synchronize do |client|
2878
3150
  client.call([:geopos, key, member])
@@ -2936,10 +3208,14 @@ class Redis
2936
3208
  # @option opts [Boolean] :approximate whether to add `~` modifier of maxlen or not
2937
3209
  #
2938
3210
  # @return [String] the entry id
2939
- def xadd(key, entry, opts = {})
3211
+ def xadd(key, entry, approximate: nil, maxlen: nil, id: '*')
2940
3212
  args = [:xadd, key]
2941
- args.concat(['MAXLEN', (opts[:approximate] ? '~' : nil), opts[:maxlen]].compact) if opts[:maxlen]
2942
- args << (opts[:id] || '*')
3213
+ if maxlen
3214
+ args << "MAXLEN"
3215
+ args << "~" if approximate
3216
+ args << maxlen
3217
+ end
3218
+ args << id
2943
3219
  args.concat(entry.to_a.flatten)
2944
3220
  synchronize { |client| client.call(args) }
2945
3221
  end
@@ -2977,24 +3253,6 @@ class Redis
2977
3253
  synchronize { |client| client.call(args) }
2978
3254
  end
2979
3255
 
2980
- # Fetches entries of the stream.
2981
- #
2982
- # @example Without options
2983
- # redis.xrange('mystream')
2984
- # @example With first entry id option
2985
- # redis.xrange('mystream', first: '0-1')
2986
- # @example With first and last entry id options
2987
- # redis.xrange('mystream', first: '0-1', last: '0-3')
2988
- # @example With count options
2989
- # redis.xrange('mystream', count: 10)
2990
- #
2991
- # @param key [String] the stream key
2992
- # @param start [String] first entry id of range, default value is `+`
2993
- # @param end [String] last entry id of range, default value is `-`
2994
- # @param count [Integer] the number of entries as limit
2995
- #
2996
- # @return [Hash{String => Hash}] the entries
2997
-
2998
3256
  # Fetches entries of the stream in ascending order.
2999
3257
  #
3000
3258
  # @example Without options
@@ -3012,8 +3270,8 @@ class Redis
3012
3270
  # @param count [Integer] the number of entries as limit
3013
3271
  #
3014
3272
  # @return [Array<Array<String, Hash>>] the ids and entries pairs
3015
- def xrange(key, start = '-', _end = '+', count: nil)
3016
- args = [:xrange, key, start, _end]
3273
+ def xrange(key, start = '-', range_end = '+', count: nil)
3274
+ args = [:xrange, key, start, range_end]
3017
3275
  args.concat(['COUNT', count]) if count
3018
3276
  synchronize { |client| client.call(args, &HashifyStreamEntries) }
3019
3277
  end
@@ -3035,8 +3293,8 @@ class Redis
3035
3293
  # @params count [Integer] the number of entries as limit
3036
3294
  #
3037
3295
  # @return [Array<Array<String, Hash>>] the ids and entries pairs
3038
- def xrevrange(key, _end = '+', start = '-', count: nil)
3039
- args = [:xrevrange, key, _end, start]
3296
+ def xrevrange(key, range_end = '+', start = '-', count: nil)
3297
+ args = [:xrevrange, key, range_end, start]
3040
3298
  args.concat(['COUNT', count]) if count
3041
3299
  synchronize { |client| client.call(args, &HashifyStreamEntries) }
3042
3300
  end
@@ -3128,12 +3386,12 @@ class Redis
3128
3386
  # @option opts [Boolean] :noack whether message loss is acceptable or not
3129
3387
  #
3130
3388
  # @return [Hash{String => Hash{String => Hash}}] the entries
3131
- def xreadgroup(group, consumer, keys, ids, opts = {})
3389
+ def xreadgroup(group, consumer, keys, ids, count: nil, block: nil, noack: nil)
3132
3390
  args = [:xreadgroup, 'GROUP', group, consumer]
3133
- args << 'COUNT' << opts[:count] if opts[:count]
3134
- args << 'BLOCK' << opts[:block].to_i if opts[:block]
3135
- args << 'NOACK' if opts[:noack]
3136
- _xread(args, keys, ids, opts[:block])
3391
+ args << 'COUNT' << count if count
3392
+ args << 'BLOCK' << block.to_i if block
3393
+ args << 'NOACK' if noack
3394
+ _xread(args, keys, ids, block)
3137
3395
  end
3138
3396
 
3139
3397
  # Removes one or multiple entries from the pending entries list of a stream consumer group.
@@ -3198,6 +3456,38 @@ class Redis
3198
3456
  synchronize { |client| client.call(args, &blk) }
3199
3457
  end
3200
3458
 
3459
+ # Transfers ownership of pending stream entries that match the specified criteria.
3460
+ #
3461
+ # @example Claim next pending message stuck > 5 minutes and mark as retry
3462
+ # redis.xautoclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0')
3463
+ # @example Claim 50 next pending messages stuck > 5 minutes and mark as retry
3464
+ # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0', count: 50)
3465
+ # @example Claim next pending message stuck > 5 minutes and don't mark as retry
3466
+ # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0', justid: true)
3467
+ # @example Claim next pending message after this id stuck > 5 minutes and mark as retry
3468
+ # redis.xautoclaim('mystream', 'mygroup', 'consumer1', 3600000, '1641321233-0')
3469
+ #
3470
+ # @param key [String] the stream key
3471
+ # @param group [String] the consumer group name
3472
+ # @param consumer [String] the consumer name
3473
+ # @param min_idle_time [Integer] the number of milliseconds
3474
+ # @param start [String] entry id to start scanning from or 0-0 for everything
3475
+ # @param count [Integer] number of messages to claim (default 1)
3476
+ # @param justid [Boolean] whether to fetch just an array of entry ids or not.
3477
+ # Does not increment retry count when true
3478
+ #
3479
+ # @return [Hash{String => Hash}] the entries successfully claimed
3480
+ # @return [Array<String>] the entry ids successfully claimed if justid option is `true`
3481
+ def xautoclaim(key, group, consumer, min_idle_time, start, count: nil, justid: false)
3482
+ args = [:xautoclaim, key, group, consumer, min_idle_time, start]
3483
+ if count
3484
+ args << 'COUNT' << count.to_s
3485
+ end
3486
+ args << 'JUSTID' if justid
3487
+ blk = justid ? HashifyStreamAutoclaimJustId : HashifyStreamAutoclaim
3488
+ synchronize { |client| client.call(args, &blk) }
3489
+ end
3490
+
3201
3491
  # Fetches not acknowledging pending entries
3202
3492
  #
3203
3493
  # @example With key and group
@@ -3243,8 +3533,8 @@ class Redis
3243
3533
  when "get-master-addr-by-name"
3244
3534
  reply
3245
3535
  else
3246
- if reply.kind_of?(Array)
3247
- if reply[0].kind_of?(Array)
3536
+ if reply.is_a?(Array)
3537
+ if reply[0].is_a?(Array)
3248
3538
  reply.map(&Hashify)
3249
3539
  else
3250
3540
  Hashify.call(reply)
@@ -3268,12 +3558,17 @@ class Redis
3268
3558
  def cluster(subcommand, *args)
3269
3559
  subcommand = subcommand.to_s.downcase
3270
3560
  block = case subcommand
3271
- when 'slots' then HashifyClusterSlots
3272
- when 'nodes' then HashifyClusterNodes
3273
- when 'slaves' then HashifyClusterSlaves
3274
- when 'info' then HashifyInfo
3275
- else Noop
3276
- end
3561
+ when 'slots'
3562
+ HashifyClusterSlots
3563
+ when 'nodes'
3564
+ HashifyClusterNodes
3565
+ when 'slaves'
3566
+ HashifyClusterSlaves
3567
+ when 'info'
3568
+ HashifyInfo
3569
+ else
3570
+ Noop
3571
+ end
3277
3572
 
3278
3573
  # @see https://github.com/antirez/redis/blob/unstable/src/redis-trib.rb#L127 raw reply expected
3279
3574
  block = Noop unless @cluster_mode
@@ -3308,150 +3603,171 @@ class Redis
3308
3603
  return @original_client.connection_info if @cluster_mode
3309
3604
 
3310
3605
  {
3311
- host: @original_client.host,
3312
- port: @original_client.port,
3313
- db: @original_client.db,
3314
- id: @original_client.id,
3606
+ host: @original_client.host,
3607
+ port: @original_client.port,
3608
+ db: @original_client.db,
3609
+ id: @original_client.id,
3315
3610
  location: @original_client.location
3316
3611
  }
3317
3612
  end
3318
3613
 
3319
- def method_missing(command, *args)
3614
+ def method_missing(command, *args) # rubocop:disable Style/MissingRespondToMissing
3320
3615
  synchronize do |client|
3321
3616
  client.call([command] + args)
3322
3617
  end
3323
3618
  end
3324
3619
 
3325
- private
3620
+ private
3326
3621
 
3327
3622
  # Commands returning 1 for true and 0 for false may be executed in a pipeline
3328
3623
  # where the method call will return nil. Propagate the nil instead of falsely
3329
3624
  # returning false.
3330
- Boolify =
3331
- lambda { |value|
3332
- value == 1 if value
3333
- }
3625
+ Boolify = lambda { |value|
3626
+ case value
3627
+ when 1
3628
+ true
3629
+ when 0
3630
+ false
3631
+ else
3632
+ value
3633
+ end
3634
+ }
3334
3635
 
3335
- BoolifySet =
3336
- lambda { |value|
3337
- if value && "OK" == value
3338
- true
3339
- else
3340
- false
3341
- end
3342
- }
3636
+ BoolifySet = lambda { |value|
3637
+ case value
3638
+ when "OK"
3639
+ true
3640
+ when nil
3641
+ false
3642
+ else
3643
+ value
3644
+ end
3645
+ }
3343
3646
 
3344
- Hashify =
3345
- lambda { |array|
3346
- hash = Hash.new
3347
- array.each_slice(2) do |field, value|
3348
- hash[field] = value
3349
- end
3350
- hash
3351
- }
3647
+ Hashify = lambda { |value|
3648
+ if value.respond_to?(:each_slice)
3649
+ value.each_slice(2).to_h
3650
+ else
3651
+ value
3652
+ end
3653
+ }
3654
+
3655
+ Floatify = lambda { |value|
3656
+ case value
3657
+ when "inf"
3658
+ Float::INFINITY
3659
+ when "-inf"
3660
+ -Float::INFINITY
3661
+ when String
3662
+ Float(value)
3663
+ else
3664
+ value
3665
+ end
3666
+ }
3352
3667
 
3353
- Floatify =
3354
- lambda { |str|
3355
- if str
3356
- if (inf = str.match(/^(-)?inf/i))
3357
- (inf[1] ? -1.0 : 1.0) / 0.0
3358
- else
3359
- Float(str)
3360
- end
3361
- end
3362
- }
3668
+ FloatifyPairs = lambda { |value|
3669
+ return value unless value.respond_to?(:each_slice)
3363
3670
 
3364
- FloatifyPairs =
3365
- lambda { |result|
3366
- result.each_slice(2).map do |member, score|
3367
- [member, Floatify.call(score)]
3368
- end
3369
- }
3671
+ value.each_slice(2).map do |member, score|
3672
+ [member, Floatify.call(score)]
3673
+ end
3674
+ }
3675
+
3676
+ HashifyInfo = lambda { |reply|
3677
+ lines = reply.split("\r\n").grep_v(/^(#|$)/)
3678
+ lines.map! { |line| line.split(':', 2) }
3679
+ lines.compact!
3680
+ lines.to_h
3681
+ }
3682
+
3683
+ HashifyStreams = lambda { |reply|
3684
+ case reply
3685
+ when nil
3686
+ {}
3687
+ else
3688
+ reply.map { |key, entries| [key, HashifyStreamEntries.call(entries)] }.to_h
3689
+ end
3690
+ }
3370
3691
 
3371
- HashifyInfo =
3372
- lambda { |reply|
3373
- Hash[reply.split("\r\n").map do |line|
3374
- line.split(':', 2) unless line =~ /^(#|$)/
3375
- end.compact]
3692
+ EMPTY_STREAM_RESPONSE = [nil].freeze
3693
+ private_constant :EMPTY_STREAM_RESPONSE
3694
+
3695
+ HashifyStreamEntries = lambda { |reply|
3696
+ reply.compact.map do |entry_id, values|
3697
+ [entry_id, values&.each_slice(2)&.to_h]
3698
+ end
3699
+ }
3700
+
3701
+ HashifyStreamAutoclaim = lambda { |reply|
3702
+ {
3703
+ 'next' => reply[0],
3704
+ 'entries' => reply[1].map { |entry| [entry[0], entry[1].each_slice(2).to_h] }
3376
3705
  }
3706
+ }
3377
3707
 
3378
- HashifyStreams =
3379
- lambda { |reply|
3380
- return {} if reply.nil?
3381
- reply.map do |stream_key, entries|
3382
- [stream_key, HashifyStreamEntries.call(entries)]
3383
- end.to_h
3708
+ HashifyStreamAutoclaimJustId = lambda { |reply|
3709
+ {
3710
+ 'next' => reply[0],
3711
+ 'entries' => reply[1]
3384
3712
  }
3713
+ }
3385
3714
 
3386
- HashifyStreamEntries =
3387
- lambda { |reply|
3388
- reply.map do |entry_id, values|
3389
- [entry_id, values.each_slice(2).to_h]
3390
- end
3715
+ HashifyStreamPendings = lambda { |reply|
3716
+ {
3717
+ 'size' => reply[0],
3718
+ 'min_entry_id' => reply[1],
3719
+ 'max_entry_id' => reply[2],
3720
+ 'consumers' => reply[3].nil? ? {} : reply[3].to_h
3391
3721
  }
3722
+ }
3392
3723
 
3393
- HashifyStreamPendings =
3394
- lambda { |reply|
3724
+ HashifyStreamPendingDetails = lambda { |reply|
3725
+ reply.map do |arr|
3395
3726
  {
3396
- 'size' => reply[0],
3397
- 'min_entry_id' => reply[1],
3398
- 'max_entry_id' => reply[2],
3399
- 'consumers' => reply[3].nil? ? {} : Hash[reply[3]]
3727
+ 'entry_id' => arr[0],
3728
+ 'consumer' => arr[1],
3729
+ 'elapsed' => arr[2],
3730
+ 'count' => arr[3]
3400
3731
  }
3401
- }
3732
+ end
3733
+ }
3402
3734
 
3403
- HashifyStreamPendingDetails =
3404
- lambda { |reply|
3405
- reply.map do |arr|
3406
- {
3407
- 'entry_id' => arr[0],
3408
- 'consumer' => arr[1],
3409
- 'elapsed' => arr[2],
3410
- 'count' => arr[3]
3411
- }
3412
- end
3735
+ HashifyClusterNodeInfo = lambda { |str|
3736
+ arr = str.split(' ')
3737
+ {
3738
+ 'node_id' => arr[0],
3739
+ 'ip_port' => arr[1],
3740
+ 'flags' => arr[2].split(','),
3741
+ 'master_node_id' => arr[3],
3742
+ 'ping_sent' => arr[4],
3743
+ 'pong_recv' => arr[5],
3744
+ 'config_epoch' => arr[6],
3745
+ 'link_state' => arr[7],
3746
+ 'slots' => arr[8].nil? ? nil : Range.new(*arr[8].split('-'))
3413
3747
  }
3748
+ }
3414
3749
 
3415
- HashifyClusterNodeInfo =
3416
- lambda { |str|
3417
- arr = str.split(' ')
3750
+ HashifyClusterSlots = lambda { |reply|
3751
+ reply.map do |arr|
3752
+ first_slot, last_slot = arr[0..1]
3753
+ master = { 'ip' => arr[2][0], 'port' => arr[2][1], 'node_id' => arr[2][2] }
3754
+ replicas = arr[3..-1].map { |r| { 'ip' => r[0], 'port' => r[1], 'node_id' => r[2] } }
3418
3755
  {
3419
- 'node_id' => arr[0],
3420
- 'ip_port' => arr[1],
3421
- 'flags' => arr[2].split(','),
3422
- 'master_node_id' => arr[3],
3423
- 'ping_sent' => arr[4],
3424
- 'pong_recv' => arr[5],
3425
- 'config_epoch' => arr[6],
3426
- 'link_state' => arr[7],
3427
- 'slots' => arr[8].nil? ? nil : Range.new(*arr[8].split('-'))
3756
+ 'start_slot' => first_slot,
3757
+ 'end_slot' => last_slot,
3758
+ 'master' => master,
3759
+ 'replicas' => replicas
3428
3760
  }
3429
- }
3430
-
3431
- HashifyClusterSlots =
3432
- lambda { |reply|
3433
- reply.map do |arr|
3434
- first_slot, last_slot = arr[0..1]
3435
- master = { 'ip' => arr[2][0], 'port' => arr[2][1], 'node_id' => arr[2][2] }
3436
- replicas = arr[3..-1].map { |r| { 'ip' => r[0], 'port' => r[1], 'node_id' => r[2] } }
3437
- {
3438
- 'start_slot' => first_slot,
3439
- 'end_slot' => last_slot,
3440
- 'master' => master,
3441
- 'replicas' => replicas
3442
- }
3443
- end
3444
- }
3761
+ end
3762
+ }
3445
3763
 
3446
- HashifyClusterNodes =
3447
- lambda { |reply|
3448
- reply.split(/[\r\n]+/).map { |str| HashifyClusterNodeInfo.call(str) }
3449
- }
3764
+ HashifyClusterNodes = lambda { |reply|
3765
+ reply.split(/[\r\n]+/).map { |str| HashifyClusterNodeInfo.call(str) }
3766
+ }
3450
3767
 
3451
- HashifyClusterSlaves =
3452
- lambda { |reply|
3453
- reply.map { |str| HashifyClusterNodeInfo.call(str) }
3454
- }
3768
+ HashifyClusterSlaves = lambda { |reply|
3769
+ reply.map { |str| HashifyClusterNodeInfo.call(str) }
3770
+ }
3455
3771
 
3456
3772
  Noop = ->(reply) { reply }
3457
3773
 
@@ -3459,8 +3775,7 @@ private
3459
3775
  args.push sort if sort
3460
3776
  args.push 'count', count if count
3461
3777
  args.push options if options
3462
-
3463
- args.uniq
3778
+ args
3464
3779
  end
3465
3780
 
3466
3781
  def _subscription(method, timeout, channels, block)
@@ -3488,12 +3803,29 @@ private
3488
3803
  synchronize do |client|
3489
3804
  if blocking_timeout_msec.nil?
3490
3805
  client.call(args, &HashifyStreams)
3806
+ elsif blocking_timeout_msec.to_f.zero?
3807
+ client.call_without_timeout(args, &HashifyStreams)
3491
3808
  else
3492
3809
  timeout = client.timeout.to_f + blocking_timeout_msec.to_f / 1000.0
3493
3810
  client.call_with_timeout(args, timeout, &HashifyStreams)
3494
3811
  end
3495
3812
  end
3496
3813
  end
3814
+
3815
+ def _normalize_move_wheres(where_source, where_destination)
3816
+ where_source = where_source.to_s.upcase
3817
+ where_destination = where_destination.to_s.upcase
3818
+
3819
+ if where_source != "LEFT" && where_source != "RIGHT"
3820
+ raise ArgumentError, "where_source must be 'LEFT' or 'RIGHT'"
3821
+ end
3822
+
3823
+ if where_destination != "LEFT" && where_destination != "RIGHT"
3824
+ raise ArgumentError, "where_destination must be 'LEFT' or 'RIGHT'"
3825
+ end
3826
+
3827
+ [where_source, where_destination]
3828
+ end
3497
3829
  end
3498
3830
 
3499
3831
  require_relative "redis/version"