redis 4.1.4 → 4.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 83f1f7270db68603d63e86ec43e68348cb5ccb2b4e6759642d89898566bdbaf6
4
- data.tar.gz: 45c5bcc92629ec7d85cdc2b913e7922cd5425f2e6691891efc379aeec73026b3
3
+ metadata.gz: 9796f6646b7d3aaeeb5ef37629fb1a43422285724d8d2901a219ef4f2882eff5
4
+ data.tar.gz: b89f4f1d6a3c9ee93202ce08cdcd3ed184694b5693f7461be7cf7e517139a278
5
5
  SHA512:
6
- metadata.gz: 692dfc5c73c6410492589f38f279976a023f6a2ff13f7b1476806011eb387f41bed784bdeac746de5f4b990b6d22bf297b36dddc7b8e448a842241a389f50796
7
- data.tar.gz: 55a9e305c7563f5dd7d38f50dc7b919967dbb0f6a131ebc5e1569f49f196ab458203b6594394fa9a33ea9e337b741113e781378113783683dd36b87196607b8f
6
+ metadata.gz: be709e1aad1acee8d7c3e121e946060ce9b693ac80e25af200e2a579988f3952d6522cb9855917dc76628ae2038365bb8754b49a7cc6e90395706ba30fc87a86
7
+ data.tar.gz: 00102b01b4b37daab76fc90990f980a4710bec0e797a70d65a7544ef304f01f9b9babbdcc680da71513dd8f79649bd5f529e6804e71297b26978625bea746367
@@ -1,5 +1,18 @@
1
1
  # Unreleased
2
2
 
3
+ # 4.2.0
4
+
5
+ * Convert commands to accept keyword arguments rather than option hashes. This both help catching typos, and reduce needless allocations.
6
+ * Deprecate the synchrony driver. It will be removed in 5.0 and hopefully maintained as a separate gem. See #915.
7
+ * Make `Redis#exists` variadic, will return an Integer if called with multiple keys.
8
+ * Add `Redis#exists?` to get a Boolean if any of the keys exists.
9
+ * `Redis#exists` when called with a single key will warn that future versions will return an Integer.
10
+ Set `Redis.exists_returns_integer = true` to opt-in to the new behavior.
11
+ * Support `keepttl` ooption in `set`. See #913.
12
+ * Optimized initialization of Redis::Cluster. See #912.
13
+ * Accept sentinel options even with string key. See #599.
14
+ * Verify TLS connections by default. See #900.
15
+
3
16
  # 4.1.4
4
17
 
5
18
  * Alias `Redis#disconnect` as `#close`. See #901.
@@ -9,6 +22,7 @@
9
22
  * Increase buffer size in the ruby connector. See #880.
10
23
  * Fix thread safety of `Redis.queue`. See #878.
11
24
  * Deprecate `Redis::Future#==` as it's likely to be a mistake. See #876.
25
+ * Support `KEEPTTL` option for SET command. See #913.
12
26
 
13
27
  # 4.1.3
14
28
 
data/README.md CHANGED
@@ -3,6 +3,7 @@
3
3
  A Ruby client that tries to match [Redis][redis-home]' API one-to-one, while still
4
4
  providing an idiomatic interface.
5
5
 
6
+ See [RubyDoc.info][rubydoc] for the API docs of the latest published gem.
6
7
 
7
8
  ## Getting started
8
9
 
@@ -34,6 +35,9 @@ You can also specify connection options as a [`redis://` URL][redis-url]:
34
35
  redis = Redis.new(url: "redis://:p4ssw0rd@10.0.1.1:6380/15")
35
36
  ```
36
37
 
38
+ The client expects passwords with special chracters to be URL-encoded (i.e.
39
+ `CGI.escape(password)`).
40
+
37
41
  By default, the client will try to read the `REDIS_URL` environment variable
38
42
  and use that as URL to connect to. The above statement is therefore equivalent
39
43
  to setting this environment variable and calling `Redis.new` without arguments.
@@ -147,8 +151,8 @@ redis.mget('{key}1', '{key}2')
147
151
 
148
152
  ## Storing objects
149
153
 
150
- Redis only stores strings as values. If you want to store an object, you
151
- can use a serialization mechanism such as JSON:
154
+ Redis "string" types can be used to store serialized Ruby objects, for
155
+ example with JSON:
152
156
 
153
157
  ```ruby
154
158
  require "json"
@@ -322,7 +326,7 @@ This library supports natively terminating client side SSL/TLS connections
322
326
  when talking to Redis via a server-side proxy such as [stunnel], [hitch],
323
327
  or [ghostunnel].
324
328
 
325
- To enable SSL support, pass the `:ssl => :true` option when configuring the
329
+ To enable SSL support, pass the `:ssl => true` option when configuring the
326
330
  Redis client, or pass in `:url => "rediss://..."` (like HTTPS for Redis).
327
331
  You will also need to pass in an `:ssl_params => { ... }` hash used to
328
332
  configure the `OpenSSL::SSL::SSLContext` object used for the connection:
@@ -451,7 +455,7 @@ client and evangelized Redis in Rubyland. Thank you, Ezra.
451
455
  ## Contributing
452
456
 
453
457
  [Fork the project](https://github.com/redis/redis-rb) and send pull
454
- requests. You can also ask for help at `#redis-rb` on Freenode.
458
+ requests.
455
459
 
456
460
 
457
461
  [inchpages-image]: https://inch-ci.org/github/redis/redis-rb.svg
@@ -4,12 +4,13 @@ require "monitor"
4
4
  require_relative "redis/errors"
5
5
 
6
6
  class Redis
7
- def self.current
8
- @current ||= Redis.new
7
+ class << self
8
+ attr_accessor :exists_returns_integer
9
+ attr_writer :current
9
10
  end
10
11
 
11
- def self.current=(redis)
12
- @current = redis
12
+ def self.current
13
+ @current ||= Redis.new
13
14
  end
14
15
 
15
16
  include MonitorMixin
@@ -17,7 +18,9 @@ class Redis
17
18
  # Create a new client instance
18
19
  #
19
20
  # @param [Hash] options
20
- # @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.
21
+ # @option options [String] :url (value of the environment variable REDIS_URL) a Redis URL, for a TCP connection:
22
+ # `redis://:[password]@[hostname]:[port]/[db]` (password, port and database are optional), for a unix socket
23
+ # connection: `unix://[path to Redis socket]`. This overrides all other options.
21
24
  # @option options [String] :host ("127.0.0.1") server hostname
22
25
  # @option options [Integer] :port (6379) server port
23
26
  # @option options [String] :path path to server socket (overrides host and port)
@@ -26,8 +29,10 @@ class Redis
26
29
  # @option options [String] :password Password to authenticate against server
27
30
  # @option options [Integer] :db (0) Database to select after initial connect
28
31
  # @option options [Symbol] :driver Driver to use, currently supported: `:ruby`, `:hiredis`, `:synchrony`
29
- # @option options [String] :id ID for the client connection, assigns name to current connection by sending `CLIENT SETNAME`
30
- # @option options [Hash, Integer] :tcp_keepalive Keepalive values, if Integer `intvl` and `probe` are calculated based on the value, if Hash `time`, `intvl` and `probes` can be specified as a Integer
32
+ # @option options [String] :id ID for the client connection, assigns name to current connection by sending
33
+ # `CLIENT SETNAME`
34
+ # @option options [Hash, Integer] :tcp_keepalive Keepalive values, if Integer `intvl` and `probe` are calculated
35
+ # based on the value, if Hash `time`, `intvl` and `probes` can be specified as a Integer
31
36
  # @option options [Integer] :reconnect_attempts Number of attempts trying to connect
32
37
  # @option options [Boolean] :inherit_socket (false) Whether to use socket in forked process or not
33
38
  # @option options [Array] :sentinels List of sentinels to contact
@@ -52,7 +57,7 @@ class Redis
52
57
  end
53
58
 
54
59
  # Run code with the client reconnecting
55
- def with_reconnect(val=true, &blk)
60
+ def with_reconnect(val = true, &blk)
56
61
  synchronize do |client|
57
62
  client.with_reconnect(val, &blk)
58
63
  end
@@ -205,7 +210,7 @@ class Redis
205
210
  def config(action, *args)
206
211
  synchronize do |client|
207
212
  client.call([:config, action] + args) do |reply|
208
- if reply.kind_of?(Array) && action == :get
213
+ if reply.is_a?(Array) && action == :get
209
214
  Hashify.call(reply)
210
215
  else
211
216
  reply
@@ -256,7 +261,7 @@ class Redis
256
261
  def flushall(options = nil)
257
262
  synchronize do |client|
258
263
  if options && options[:async]
259
- client.call([:flushall, :async])
264
+ client.call(%i[flushall async])
260
265
  else
261
266
  client.call([:flushall])
262
267
  end
@@ -271,7 +276,7 @@ class Redis
271
276
  def flushdb(options = nil)
272
277
  synchronize do |client|
273
278
  if options && options[:async]
274
- client.call([:flushdb, :async])
279
+ client.call(%i[flushdb async])
275
280
  else
276
281
  client.call([:flushdb])
277
282
  end
@@ -285,7 +290,7 @@ class Redis
285
290
  def info(cmd = nil)
286
291
  synchronize do |client|
287
292
  client.call([:info, cmd].compact) do |reply|
288
- if reply.kind_of?(String)
293
+ if reply.is_a?(String)
289
294
  reply = HashifyInfo.call(reply)
290
295
 
291
296
  if cmd && cmd.to_s == "commandstats"
@@ -358,7 +363,7 @@ class Redis
358
363
  # @param [String] subcommand e.g. `get`, `len`, `reset`
359
364
  # @param [Integer] length maximum number of entries to return
360
365
  # @return [Array<String>, Integer, String] depends on subcommand
361
- def slowlog(subcommand, length=nil)
366
+ def slowlog(subcommand, length = nil)
362
367
  synchronize do |client|
363
368
  args = [:slowlog, subcommand]
364
369
  args << length if length
@@ -383,7 +388,7 @@ class Redis
383
388
  def time
384
389
  synchronize do |client|
385
390
  client.call([:time]) do |reply|
386
- reply.map(&:to_i) if reply
391
+ reply&.map(&:to_i)
387
392
  end
388
393
  end
389
394
  end
@@ -496,9 +501,9 @@ class Redis
496
501
  # - `:replace => Boolean`: if false, raises an error if key already exists
497
502
  # @raise [Redis::CommandError]
498
503
  # @return [String] `"OK"`
499
- def restore(key, ttl, serialized_value, options = {})
504
+ def restore(key, ttl, serialized_value, replace: nil)
500
505
  args = [:restore, key, ttl, serialized_value]
501
- args << 'REPLACE' if options[:replace]
506
+ args << 'REPLACE' if replace
502
507
 
503
508
  synchronize do |client|
504
509
  client.call(args)
@@ -550,13 +555,36 @@ class Redis
550
555
  end
551
556
  end
552
557
 
553
- # Determine if a key exists.
558
+ # Determine how many of the keys exists.
554
559
  #
555
- # @param [String] key
560
+ # @param [String, Array<String>] keys
561
+ # @return [Integer]
562
+ def exists(*keys)
563
+ if !Redis.exists_returns_integer && keys.size == 1
564
+ message = "`Redis#exists(key)` will return an Integer in redis-rb 4.3, if you want to keep the old behavior, " \
565
+ "use `exists?` instead. To opt-in to the new behavior now you can set Redis.exists_returns_integer = true. " \
566
+ "(#{::Kernel.caller(1, 1).first})\n"
567
+
568
+ ::Kernel.warn(message)
569
+ exists?(*keys)
570
+ else
571
+ _exists(*keys)
572
+ end
573
+ end
574
+
575
+ def _exists(*keys)
576
+ synchronize do |client|
577
+ client.call([:exists, *keys])
578
+ end
579
+ end
580
+
581
+ # Determine if any of the keys exists.
582
+ #
583
+ # @param [String, Array<String>] keys
556
584
  # @return [Boolean]
557
- def exists(key)
585
+ def exists?(*keys)
558
586
  synchronize do |client|
559
- client.call([:exists, key], &Boolify)
587
+ client.call([:exists, *keys], &Boolify)
560
588
  end
561
589
  end
562
590
 
@@ -567,7 +595,7 @@ class Redis
567
595
  def keys(pattern = "*")
568
596
  synchronize do |client|
569
597
  client.call([:keys, pattern]) do |reply|
570
- if reply.kind_of?(String)
598
+ if reply.is_a?(String)
571
599
  reply.split(" ")
572
600
  else
573
601
  reply
@@ -663,30 +691,27 @@ class Redis
663
691
  # elements where every element is an array with the result for every
664
692
  # element specified in `:get`
665
693
  # - when `:store` is specified, the number of elements in the stored result
666
- def sort(key, options = {})
667
- args = []
668
-
669
- by = options[:by]
670
- args.concat(["BY", by]) if by
694
+ def sort(key, by: nil, limit: nil, get: nil, order: nil, store: nil)
695
+ args = [:sort, key]
696
+ args << "BY" << by if by
671
697
 
672
- limit = options[:limit]
673
- args.concat(["LIMIT"] + limit) if limit
698
+ if limit
699
+ args << "LIMIT"
700
+ args.concat(limit)
701
+ end
674
702
 
675
- get = Array(options[:get])
676
- args.concat(["GET"].product(get).flatten) unless get.empty?
703
+ get = Array(get)
704
+ get.each do |item|
705
+ args << "GET" << item
706
+ end
677
707
 
678
- order = options[:order]
679
708
  args.concat(order.split(" ")) if order
680
-
681
- store = options[:store]
682
- args.concat(["STORE", store]) if store
709
+ args << "STORE" << store if store
683
710
 
684
711
  synchronize do |client|
685
- client.call([:sort, key] + args) do |reply|
712
+ client.call(args) do |reply|
686
713
  if get.size > 1 && !store
687
- if reply
688
- reply.each_slice(get.size).to_a
689
- end
714
+ reply.each_slice(get.size).to_a if reply
690
715
  else
691
716
  reply
692
717
  end
@@ -786,27 +811,21 @@ class Redis
786
811
  # - `:px => Integer`: Set the specified expire time, in milliseconds.
787
812
  # - `:nx => true`: Only set the key if it does not already exist.
788
813
  # - `:xx => true`: Only set the key if it already exist.
814
+ # - `:keepttl => true`: Retain the time to live associated with the key.
789
815
  # @return [String, Boolean] `"OK"` or true, false if `:nx => true` or `:xx => true`
790
- def set(key, value, options = {})
791
- args = []
792
-
793
- ex = options[:ex]
794
- args.concat(["EX", ex]) if ex
795
-
796
- px = options[:px]
797
- args.concat(["PX", px]) if px
798
-
799
- nx = options[:nx]
800
- args.concat(["NX"]) if nx
801
-
802
- xx = options[:xx]
803
- args.concat(["XX"]) if xx
816
+ def set(key, value, ex: nil, px: nil, nx: nil, xx: nil, keepttl: nil)
817
+ args = [:set, key, value.to_s]
818
+ args << "EX" << ex if ex
819
+ args << "PX" << px if px
820
+ args << "NX" if nx
821
+ args << "XX" if xx
822
+ args << "KEEPTTL" if keepttl
804
823
 
805
824
  synchronize do |client|
806
825
  if nx || xx
807
- client.call([:set, key, value.to_s] + args, &BoolifySet)
826
+ client.call(args, &BoolifySet)
808
827
  else
809
- client.call([:set, key, value.to_s] + args)
828
+ client.call(args)
810
829
  end
811
830
  end
812
831
  end
@@ -888,7 +907,7 @@ class Redis
888
907
  # @see #mapped_msetnx
889
908
  def msetnx(*args)
890
909
  synchronize do |client|
891
- client.call([:msetnx] + args, &Boolify)
910
+ client.call([:msetnx, *args], &Boolify)
892
911
  end
893
912
  end
894
913
 
@@ -928,7 +947,7 @@ class Redis
928
947
  # @see #mapped_mget
929
948
  def mget(*keys, &blk)
930
949
  synchronize do |client|
931
- client.call([:mget] + keys, &blk)
950
+ client.call([:mget, *keys], &blk)
932
951
  end
933
952
  end
934
953
 
@@ -944,7 +963,7 @@ class Redis
944
963
  # @see #mget
945
964
  def mapped_mget(*keys)
946
965
  mget(*keys) do |reply|
947
- if reply.kind_of?(Array)
966
+ if reply.is_a?(Array)
948
967
  Hash[keys.zip(reply)]
949
968
  else
950
969
  reply
@@ -1031,7 +1050,7 @@ class Redis
1031
1050
  # @return [Integer] the length of the string stored in `destkey`
1032
1051
  def bitop(operation, destkey, *keys)
1033
1052
  synchronize do |client|
1034
- client.call([:bitop, operation, destkey] + keys)
1053
+ client.call([:bitop, operation, destkey, *keys])
1035
1054
  end
1036
1055
  end
1037
1056
 
@@ -1043,10 +1062,8 @@ class Redis
1043
1062
  # @param [Integer] stop stop index
1044
1063
  # @return [Integer] the position of the first 1/0 bit.
1045
1064
  # -1 if looking for 1 and it is not found or start and stop are given.
1046
- def bitpos(key, bit, start=nil, stop=nil)
1047
- if stop and not start
1048
- raise(ArgumentError, 'stop parameter specified without start parameter')
1049
- end
1065
+ def bitpos(key, bit, start = nil, stop = nil)
1066
+ raise(ArgumentError, 'stop parameter specified without start parameter') if stop && !start
1050
1067
 
1051
1068
  synchronize do |client|
1052
1069
  command = [:bitpos, key, bit]
@@ -1240,15 +1257,7 @@ class Redis
1240
1257
  # @return [nil, String]
1241
1258
  # - `nil` when the operation timed out
1242
1259
  # - the element was popped and pushed otherwise
1243
- def brpoplpush(source, destination, options = {})
1244
- case options
1245
- when Integer
1246
- # Issue deprecation notice in obnoxious mode...
1247
- options = { :timeout => options }
1248
- end
1249
-
1250
- timeout = options[:timeout] || 0
1251
-
1260
+ def brpoplpush(source, destination, deprecated_timeout = 0, timeout: deprecated_timeout)
1252
1261
  synchronize do |client|
1253
1262
  command = [:brpoplpush, source, destination, timeout]
1254
1263
  timeout += client.timeout if timeout > 0
@@ -1455,7 +1464,7 @@ class Redis
1455
1464
  # @return [Array<String>] members in the difference
1456
1465
  def sdiff(*keys)
1457
1466
  synchronize do |client|
1458
- client.call([:sdiff] + keys)
1467
+ client.call([:sdiff, *keys])
1459
1468
  end
1460
1469
  end
1461
1470
 
@@ -1466,7 +1475,7 @@ class Redis
1466
1475
  # @return [Integer] number of elements in the resulting set
1467
1476
  def sdiffstore(destination, *keys)
1468
1477
  synchronize do |client|
1469
- client.call([:sdiffstore, destination] + keys)
1478
+ client.call([:sdiffstore, destination, *keys])
1470
1479
  end
1471
1480
  end
1472
1481
 
@@ -1476,7 +1485,7 @@ class Redis
1476
1485
  # @return [Array<String>] members in the intersection
1477
1486
  def sinter(*keys)
1478
1487
  synchronize do |client|
1479
- client.call([:sinter] + keys)
1488
+ client.call([:sinter, *keys])
1480
1489
  end
1481
1490
  end
1482
1491
 
@@ -1487,7 +1496,7 @@ class Redis
1487
1496
  # @return [Integer] number of elements in the resulting set
1488
1497
  def sinterstore(destination, *keys)
1489
1498
  synchronize do |client|
1490
- client.call([:sinterstore, destination] + keys)
1499
+ client.call([:sinterstore, destination, *keys])
1491
1500
  end
1492
1501
  end
1493
1502
 
@@ -1497,7 +1506,7 @@ class Redis
1497
1506
  # @return [Array<String>] members in the union
1498
1507
  def sunion(*keys)
1499
1508
  synchronize do |client|
1500
- client.call([:sunion] + keys)
1509
+ client.call([:sunion, *keys])
1501
1510
  end
1502
1511
  end
1503
1512
 
@@ -1508,7 +1517,7 @@ class Redis
1508
1517
  # @return [Integer] number of elements in the resulting set
1509
1518
  def sunionstore(destination, *keys)
1510
1519
  synchronize do |client|
1511
- client.call([:sunionstore, destination] + keys)
1520
+ client.call([:sunionstore, destination, *keys])
1512
1521
  end
1513
1522
  end
1514
1523
 
@@ -1557,31 +1566,20 @@ class Redis
1557
1566
  # pairs that were **added** to the sorted set.
1558
1567
  # - `Float` when option :incr is specified, holding the score of the member
1559
1568
  # after incrementing it.
1560
- def zadd(key, *args) #, options
1561
- zadd_options = []
1562
- if args.last.is_a?(Hash)
1563
- options = args.pop
1564
-
1565
- nx = options[:nx]
1566
- zadd_options << "NX" if nx
1567
-
1568
- xx = options[:xx]
1569
- zadd_options << "XX" if xx
1570
-
1571
- ch = options[:ch]
1572
- zadd_options << "CH" if ch
1573
-
1574
- incr = options[:incr]
1575
- zadd_options << "INCR" if incr
1576
- end
1569
+ def zadd(key, *args, nx: nil, xx: nil, ch: nil, incr: nil)
1570
+ command = [:zadd, key]
1571
+ command << "NX" if nx
1572
+ command << "XX" if xx
1573
+ command << "CH" if ch
1574
+ command << "INCR" if incr
1577
1575
 
1578
1576
  synchronize do |client|
1579
1577
  if args.size == 1 && args[0].is_a?(Array)
1580
1578
  # Variadic: return float if INCR, integer if !INCR
1581
- client.call([:zadd, key] + zadd_options + args[0], &(incr ? Floatify : nil))
1579
+ client.call(command + args[0], &(incr ? Floatify : nil))
1582
1580
  elsif args.size == 2
1583
1581
  # Single pair: return float if INCR, boolean if !INCR
1584
- client.call([:zadd, key] + zadd_options + args, &(incr ? Floatify : Boolify))
1582
+ client.call(command + args, &(incr ? Floatify : Boolify))
1585
1583
  else
1586
1584
  raise ArgumentError, "wrong number of arguments"
1587
1585
  end
@@ -1752,10 +1750,8 @@ class Redis
1752
1750
  # @return [Array<String>, Array<[String, Float]>]
1753
1751
  # - when `:with_scores` is not specified, an array of members
1754
1752
  # - when `:with_scores` is specified, an array with `[member, score]` pairs
1755
- def zrange(key, start, stop, options = {})
1756
- args = []
1757
-
1758
- with_scores = options[:with_scores] || options[:withscores]
1753
+ def zrange(key, start, stop, withscores: false, with_scores: withscores)
1754
+ args = [:zrange, key, start, stop]
1759
1755
 
1760
1756
  if with_scores
1761
1757
  args << "WITHSCORES"
@@ -1763,7 +1759,7 @@ class Redis
1763
1759
  end
1764
1760
 
1765
1761
  synchronize do |client|
1766
- client.call([:zrange, key, start, stop] + args, &block)
1762
+ client.call(args, &block)
1767
1763
  end
1768
1764
  end
1769
1765
 
@@ -1778,10 +1774,8 @@ class Redis
1778
1774
  # # => [["b", 64.0], ["a", 32.0]]
1779
1775
  #
1780
1776
  # @see #zrange
1781
- def zrevrange(key, start, stop, options = {})
1782
- args = []
1783
-
1784
- with_scores = options[:with_scores] || options[:withscores]
1777
+ def zrevrange(key, start, stop, withscores: false, with_scores: withscores)
1778
+ args = [:zrevrange, key, start, stop]
1785
1779
 
1786
1780
  if with_scores
1787
1781
  args << "WITHSCORES"
@@ -1789,7 +1783,7 @@ class Redis
1789
1783
  end
1790
1784
 
1791
1785
  synchronize do |client|
1792
- client.call([:zrevrange, key, start, stop] + args, &block)
1786
+ client.call(args, &block)
1793
1787
  end
1794
1788
  end
1795
1789
 
@@ -1880,14 +1874,16 @@ class Redis
1880
1874
  # `count` members
1881
1875
  #
1882
1876
  # @return [Array<String>, Array<[String, Float]>]
1883
- def zrangebylex(key, min, max, options = {})
1884
- args = []
1877
+ def zrangebylex(key, min, max, limit: nil)
1878
+ args = [:zrangebylex, key, min, max]
1885
1879
 
1886
- limit = options[:limit]
1887
- args.concat(["LIMIT"] + limit) if limit
1880
+ if limit
1881
+ args << "LIMIT"
1882
+ args.concat(limit)
1883
+ end
1888
1884
 
1889
1885
  synchronize do |client|
1890
- client.call([:zrangebylex, key, min, max] + args)
1886
+ client.call(args)
1891
1887
  end
1892
1888
  end
1893
1889
 
@@ -1902,14 +1898,16 @@ class Redis
1902
1898
  # # => ["abbygail", "abby"]
1903
1899
  #
1904
1900
  # @see #zrangebylex
1905
- def zrevrangebylex(key, max, min, options = {})
1906
- args = []
1901
+ def zrevrangebylex(key, max, min, limit: nil)
1902
+ args = [:zrevrangebylex, key, max, min]
1907
1903
 
1908
- limit = options[:limit]
1909
- args.concat(["LIMIT"] + limit) if limit
1904
+ if limit
1905
+ args << "LIMIT"
1906
+ args.concat(limit)
1907
+ end
1910
1908
 
1911
1909
  synchronize do |client|
1912
- client.call([:zrevrangebylex, key, max, min] + args)
1910
+ client.call(args)
1913
1911
  end
1914
1912
  end
1915
1913
 
@@ -1940,21 +1938,21 @@ class Redis
1940
1938
  # @return [Array<String>, Array<[String, Float]>]
1941
1939
  # - when `:with_scores` is not specified, an array of members
1942
1940
  # - when `:with_scores` is specified, an array with `[member, score]` pairs
1943
- def zrangebyscore(key, min, max, options = {})
1944
- args = []
1945
-
1946
- with_scores = options[:with_scores] || options[:withscores]
1941
+ def zrangebyscore(key, min, max, withscores: false, with_scores: withscores, limit: nil)
1942
+ args = [:zrangebyscore, key, min, max]
1947
1943
 
1948
1944
  if with_scores
1949
1945
  args << "WITHSCORES"
1950
1946
  block = FloatifyPairs
1951
1947
  end
1952
1948
 
1953
- limit = options[:limit]
1954
- args.concat(["LIMIT"] + limit) if limit
1949
+ if limit
1950
+ args << "LIMIT"
1951
+ args.concat(limit)
1952
+ end
1955
1953
 
1956
1954
  synchronize do |client|
1957
- client.call([:zrangebyscore, key, min, max] + args, &block)
1955
+ client.call(args, &block)
1958
1956
  end
1959
1957
  end
1960
1958
 
@@ -1972,21 +1970,21 @@ class Redis
1972
1970
  # # => [["b", 64.0], ["a", 32.0]]
1973
1971
  #
1974
1972
  # @see #zrangebyscore
1975
- def zrevrangebyscore(key, max, min, options = {})
1976
- args = []
1977
-
1978
- with_scores = options[:with_scores] || options[:withscores]
1973
+ def zrevrangebyscore(key, max, min, withscores: false, with_scores: withscores, limit: nil)
1974
+ args = [:zrevrangebyscore, key, max, min]
1979
1975
 
1980
1976
  if with_scores
1981
- args << ["WITHSCORES"]
1977
+ args << "WITHSCORES"
1982
1978
  block = FloatifyPairs
1983
1979
  end
1984
1980
 
1985
- limit = options[:limit]
1986
- args.concat(["LIMIT"] + limit) if limit
1981
+ if limit
1982
+ args << "LIMIT"
1983
+ args.concat(limit)
1984
+ end
1987
1985
 
1988
1986
  synchronize do |client|
1989
- client.call([:zrevrangebyscore, key, max, min] + args, &block)
1987
+ client.call(args, &block)
1990
1988
  end
1991
1989
  end
1992
1990
 
@@ -2050,17 +2048,18 @@ class Redis
2050
2048
  # sorted sets
2051
2049
  # - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
2052
2050
  # @return [Integer] number of elements in the resulting sorted set
2053
- def zinterstore(destination, keys, options = {})
2054
- args = []
2051
+ def zinterstore(destination, keys, weights: nil, aggregate: nil)
2052
+ args = [:zinterstore, destination, keys.size, *keys]
2055
2053
 
2056
- weights = options[:weights]
2057
- args.concat(["WEIGHTS"] + weights) if weights
2054
+ if weights
2055
+ args << "WEIGHTS"
2056
+ args.concat(weights)
2057
+ end
2058
2058
 
2059
- aggregate = options[:aggregate]
2060
- args.concat(["AGGREGATE", aggregate]) if aggregate
2059
+ args << "AGGREGATE" << aggregate if aggregate
2061
2060
 
2062
2061
  synchronize do |client|
2063
- client.call([:zinterstore, destination, keys.size] + keys + args)
2062
+ client.call(args)
2064
2063
  end
2065
2064
  end
2066
2065
 
@@ -2077,17 +2076,18 @@ class Redis
2077
2076
  # sorted sets
2078
2077
  # - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
2079
2078
  # @return [Integer] number of elements in the resulting sorted set
2080
- def zunionstore(destination, keys, options = {})
2081
- args = []
2079
+ def zunionstore(destination, keys, weights: nil, aggregate: nil)
2080
+ args = [:zunionstore, destination, keys.size, *keys]
2082
2081
 
2083
- weights = options[:weights]
2084
- args.concat(["WEIGHTS"] + weights) if weights
2082
+ if weights
2083
+ args << "WEIGHTS"
2084
+ args.concat(weights)
2085
+ end
2085
2086
 
2086
- aggregate = options[:aggregate]
2087
- args.concat(["AGGREGATE", aggregate]) if aggregate
2087
+ args << "AGGREGATE" << aggregate if aggregate
2088
2088
 
2089
2089
  synchronize do |client|
2090
- client.call([:zunionstore, destination, keys.size] + keys + args)
2090
+ client.call(args)
2091
2091
  end
2092
2092
  end
2093
2093
 
@@ -2101,15 +2101,20 @@ class Redis
2101
2101
  end
2102
2102
  end
2103
2103
 
2104
- # Set the string value of a hash field.
2104
+ # Set one or more hash values.
2105
+ #
2106
+ # @example
2107
+ # redis.hset("hash", "f1", "v1", "f2", "v2") # => 2
2108
+ # redis.hset("hash", { "f1" => "v1", "f2" => "v2" }) # => 2
2105
2109
  #
2106
2110
  # @param [String] key
2107
- # @param [String] field
2108
- # @param [String] value
2109
- # @return [Boolean] whether or not the field was **added** to the hash
2110
- def hset(key, field, value)
2111
+ # @param [Array<String> | Hash<String, String>] attrs array or hash of fields and values
2112
+ # @return [Integer] The number of fields that were added to the hash
2113
+ def hset(key, *attrs)
2114
+ attrs = attrs.first.flatten if attrs.size == 1 && attrs.first.is_a?(Hash)
2115
+
2111
2116
  synchronize do |client|
2112
- client.call([:hset, key, field, value], &Boolify)
2117
+ client.call([:hset, key, *attrs])
2113
2118
  end
2114
2119
  end
2115
2120
 
@@ -2198,7 +2203,7 @@ class Redis
2198
2203
  # @see #hmget
2199
2204
  def mapped_hmget(key, *fields)
2200
2205
  hmget(key, *fields) do |reply|
2201
- if reply.kind_of?(Array)
2206
+ if reply.is_a?(Array)
2202
2207
  Hash[fields.zip(reply)]
2203
2208
  else
2204
2209
  reply
@@ -2291,20 +2296,21 @@ class Redis
2291
2296
 
2292
2297
  def subscribed?
2293
2298
  synchronize do |client|
2294
- client.kind_of? SubscribedClient
2299
+ client.is_a? SubscribedClient
2295
2300
  end
2296
2301
  end
2297
2302
 
2298
2303
  # Listen for messages published to the given channels.
2299
2304
  def subscribe(*channels, &block)
2300
- synchronize do |client|
2305
+ synchronize do |_client|
2301
2306
  _subscription(:subscribe, 0, channels, block)
2302
2307
  end
2303
2308
  end
2304
2309
 
2305
- # Listen for messages published to the given channels. Throw a timeout error if there is no messages for a timeout period.
2310
+ # Listen for messages published to the given channels. Throw a timeout error
2311
+ # if there is no messages for a timeout period.
2306
2312
  def subscribe_with_timeout(timeout, *channels, &block)
2307
- synchronize do |client|
2313
+ synchronize do |_client|
2308
2314
  _subscription(:subscribe_with_timeout, timeout, channels, block)
2309
2315
  end
2310
2316
  end
@@ -2312,21 +2318,23 @@ class Redis
2312
2318
  # Stop listening for messages posted to the given channels.
2313
2319
  def unsubscribe(*channels)
2314
2320
  synchronize do |client|
2315
- raise RuntimeError, "Can't unsubscribe if not subscribed." unless subscribed?
2321
+ raise "Can't unsubscribe if not subscribed." unless subscribed?
2322
+
2316
2323
  client.unsubscribe(*channels)
2317
2324
  end
2318
2325
  end
2319
2326
 
2320
2327
  # Listen for messages published to channels matching the given patterns.
2321
2328
  def psubscribe(*channels, &block)
2322
- synchronize do |client|
2329
+ synchronize do |_client|
2323
2330
  _subscription(:psubscribe, 0, channels, block)
2324
2331
  end
2325
2332
  end
2326
2333
 
2327
- # Listen for messages published to channels matching the given patterns. Throw a timeout error if there is no messages for a timeout period.
2334
+ # Listen for messages published to channels matching the given patterns.
2335
+ # Throw a timeout error if there is no messages for a timeout period.
2328
2336
  def psubscribe_with_timeout(timeout, *channels, &block)
2329
- synchronize do |client|
2337
+ synchronize do |_client|
2330
2338
  _subscription(:psubscribe_with_timeout, timeout, channels, block)
2331
2339
  end
2332
2340
  end
@@ -2334,7 +2342,8 @@ class Redis
2334
2342
  # Stop listening for messages posted to channels matching the given patterns.
2335
2343
  def punsubscribe(*channels)
2336
2344
  synchronize do |client|
2337
- raise RuntimeError, "Can't unsubscribe if not subscribed." unless subscribed?
2345
+ raise "Can't unsubscribe if not subscribed." unless subscribed?
2346
+
2338
2347
  client.punsubscribe(*channels)
2339
2348
  end
2340
2349
  end
@@ -2379,7 +2388,7 @@ class Redis
2379
2388
  # @see #multi
2380
2389
  def watch(*keys)
2381
2390
  synchronize do |client|
2382
- res = client.call([:watch] + keys)
2391
+ res = client.call([:watch, *keys])
2383
2392
 
2384
2393
  if block_given?
2385
2394
  begin
@@ -2409,7 +2418,7 @@ class Redis
2409
2418
  end
2410
2419
 
2411
2420
  def pipelined
2412
- synchronize do |client|
2421
+ synchronize do |_client|
2413
2422
  begin
2414
2423
  pipeline = Pipeline.new(@client)
2415
2424
  original, @client = @client, pipeline
@@ -2609,18 +2618,12 @@ class Redis
2609
2618
  _eval(:evalsha, args)
2610
2619
  end
2611
2620
 
2612
- def _scan(command, cursor, args, options = {}, &block)
2621
+ def _scan(command, cursor, args, match: nil, count: nil, &block)
2613
2622
  # SSCAN/ZSCAN/HSCAN already prepend the key to +args+.
2614
2623
 
2615
2624
  args << cursor
2616
-
2617
- if match = options[:match]
2618
- args.concat(["MATCH", match])
2619
- end
2620
-
2621
- if count = options[:count]
2622
- args.concat(["COUNT", count])
2623
- end
2625
+ args << "MATCH" << match if match
2626
+ args << "COUNT" << count if count
2624
2627
 
2625
2628
  synchronize do |client|
2626
2629
  client.call([command] + args, &block)
@@ -2642,8 +2645,8 @@ class Redis
2642
2645
  # - `:count => Integer`: return count keys at most per iteration
2643
2646
  #
2644
2647
  # @return [String, Array<String>] the next cursor and all found keys
2645
- def scan(cursor, options={})
2646
- _scan(:scan, cursor, [], options)
2648
+ def scan(cursor, **options)
2649
+ _scan(:scan, cursor, [], **options)
2647
2650
  end
2648
2651
 
2649
2652
  # Scan the keyspace
@@ -2661,11 +2664,12 @@ class Redis
2661
2664
  # - `:count => Integer`: return count keys at most per iteration
2662
2665
  #
2663
2666
  # @return [Enumerator] an enumerator for all found keys
2664
- def scan_each(options={}, &block)
2665
- return to_enum(:scan_each, options) unless block_given?
2667
+ def scan_each(**options, &block)
2668
+ return to_enum(:scan_each, **options) unless block_given?
2669
+
2666
2670
  cursor = 0
2667
2671
  loop do
2668
- cursor, keys = scan(cursor, options)
2672
+ cursor, keys = scan(cursor, **options)
2669
2673
  keys.each(&block)
2670
2674
  break if cursor == "0"
2671
2675
  end
@@ -2682,8 +2686,8 @@ class Redis
2682
2686
  # - `:count => Integer`: return count keys at most per iteration
2683
2687
  #
2684
2688
  # @return [String, Array<[String, String]>] the next cursor and all found keys
2685
- def hscan(key, cursor, options={})
2686
- _scan(:hscan, cursor, [key], options) do |reply|
2689
+ def hscan(key, cursor, **options)
2690
+ _scan(:hscan, cursor, [key], **options) do |reply|
2687
2691
  [reply[0], reply[1].each_slice(2).to_a]
2688
2692
  end
2689
2693
  end
@@ -2699,11 +2703,12 @@ class Redis
2699
2703
  # - `:count => Integer`: return count keys at most per iteration
2700
2704
  #
2701
2705
  # @return [Enumerator] an enumerator for all found keys
2702
- def hscan_each(key, options={}, &block)
2703
- return to_enum(:hscan_each, key, options) unless block_given?
2706
+ def hscan_each(key, **options, &block)
2707
+ return to_enum(:hscan_each, key, **options) unless block_given?
2708
+
2704
2709
  cursor = 0
2705
2710
  loop do
2706
- cursor, values = hscan(key, cursor, options)
2711
+ cursor, values = hscan(key, cursor, **options)
2707
2712
  values.each(&block)
2708
2713
  break if cursor == "0"
2709
2714
  end
@@ -2721,8 +2726,8 @@ class Redis
2721
2726
  #
2722
2727
  # @return [String, Array<[String, Float]>] the next cursor and all found
2723
2728
  # members and scores
2724
- def zscan(key, cursor, options={})
2725
- _scan(:zscan, cursor, [key], options) do |reply|
2729
+ def zscan(key, cursor, **options)
2730
+ _scan(:zscan, cursor, [key], **options) do |reply|
2726
2731
  [reply[0], FloatifyPairs.call(reply[1])]
2727
2732
  end
2728
2733
  end
@@ -2738,11 +2743,12 @@ class Redis
2738
2743
  # - `:count => Integer`: return count keys at most per iteration
2739
2744
  #
2740
2745
  # @return [Enumerator] an enumerator for all found scores and members
2741
- def zscan_each(key, options={}, &block)
2742
- return to_enum(:zscan_each, key, options) unless block_given?
2746
+ def zscan_each(key, **options, &block)
2747
+ return to_enum(:zscan_each, key, **options) unless block_given?
2748
+
2743
2749
  cursor = 0
2744
2750
  loop do
2745
- cursor, values = zscan(key, cursor, options)
2751
+ cursor, values = zscan(key, cursor, **options)
2746
2752
  values.each(&block)
2747
2753
  break if cursor == "0"
2748
2754
  end
@@ -2759,8 +2765,8 @@ class Redis
2759
2765
  # - `:count => Integer`: return count keys at most per iteration
2760
2766
  #
2761
2767
  # @return [String, Array<String>] the next cursor and all found members
2762
- def sscan(key, cursor, options={})
2763
- _scan(:sscan, cursor, [key], options)
2768
+ def sscan(key, cursor, **options)
2769
+ _scan(:sscan, cursor, [key], **options)
2764
2770
  end
2765
2771
 
2766
2772
  # Scan a set
@@ -2774,11 +2780,12 @@ class Redis
2774
2780
  # - `:count => Integer`: return count keys at most per iteration
2775
2781
  #
2776
2782
  # @return [Enumerator] an enumerator for all keys in the set
2777
- def sscan_each(key, options={}, &block)
2778
- return to_enum(:sscan_each, key, options) unless block_given?
2783
+ def sscan_each(key, **options, &block)
2784
+ return to_enum(:sscan_each, key, **options) unless block_given?
2785
+
2779
2786
  cursor = 0
2780
2787
  loop do
2781
- cursor, keys = sscan(key, cursor, options)
2788
+ cursor, keys = sscan(key, cursor, **options)
2782
2789
  keys.each(&block)
2783
2790
  break if cursor == "0"
2784
2791
  end
@@ -2842,12 +2849,12 @@ class Redis
2842
2849
  end
2843
2850
  end
2844
2851
 
2845
-
2846
2852
  # Query a sorted set representing a geospatial index to fetch members matching a
2847
2853
  # given maximum distance from a point
2848
2854
  #
2849
2855
  # @param [Array] args key, longitude, latitude, radius, unit(m|km|ft|mi)
2850
- # @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest or the farthest to the nearest relative to the center
2856
+ # @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest
2857
+ # or the farthest to the nearest relative to the center
2851
2858
  # @param [Integer] count limit the results to the first N matching items
2852
2859
  # @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
2853
2860
  # @return [Array<String>] may be changed with `options`
@@ -2864,7 +2871,8 @@ class Redis
2864
2871
  # given maximum distance from an already existing member
2865
2872
  #
2866
2873
  # @param [Array] args key, member, radius, unit(m|km|ft|mi)
2867
- # @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest or the farthest to the nearest relative to the center
2874
+ # @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest or the farthest
2875
+ # to the nearest relative to the center
2868
2876
  # @param [Integer] count limit the results to the first N matching items
2869
2877
  # @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
2870
2878
  # @return [Array<String>] may be changed with `options`
@@ -2881,7 +2889,8 @@ class Redis
2881
2889
  #
2882
2890
  # @param [String] key
2883
2891
  # @param [String, Array<String>] member one member or array of members
2884
- # @return [Array<Array<String>, nil>] returns array of elements, where each element is either array of longitude and latitude or nil
2892
+ # @return [Array<Array<String>, nil>] returns array of elements, where each
2893
+ # element is either array of longitude and latitude or nil
2885
2894
  def geopos(key, member)
2886
2895
  synchronize do |client|
2887
2896
  client.call([:geopos, key, member])
@@ -2945,10 +2954,14 @@ class Redis
2945
2954
  # @option opts [Boolean] :approximate whether to add `~` modifier of maxlen or not
2946
2955
  #
2947
2956
  # @return [String] the entry id
2948
- def xadd(key, entry, opts = {})
2957
+ def xadd(key, entry, approximate: nil, maxlen: nil, id: '*')
2949
2958
  args = [:xadd, key]
2950
- args.concat(['MAXLEN', (opts[:approximate] ? '~' : nil), opts[:maxlen]].compact) if opts[:maxlen]
2951
- args << (opts[:id] || '*')
2959
+ if maxlen
2960
+ args << "MAXLEN"
2961
+ args << "~" if approximate
2962
+ args << maxlen
2963
+ end
2964
+ args << id
2952
2965
  args.concat(entry.to_a.flatten)
2953
2966
  synchronize { |client| client.call(args) }
2954
2967
  end
@@ -3003,8 +3016,8 @@ class Redis
3003
3016
  # @param count [Integer] the number of entries as limit
3004
3017
  #
3005
3018
  # @return [Array<Array<String, Hash>>] the ids and entries pairs
3006
- def xrange(key, start = '-', _end = '+', count: nil)
3007
- args = [:xrange, key, start, _end]
3019
+ def xrange(key, start = '-', range_end = '+', count: nil)
3020
+ args = [:xrange, key, start, range_end]
3008
3021
  args.concat(['COUNT', count]) if count
3009
3022
  synchronize { |client| client.call(args, &HashifyStreamEntries) }
3010
3023
  end
@@ -3026,8 +3039,8 @@ class Redis
3026
3039
  # @params count [Integer] the number of entries as limit
3027
3040
  #
3028
3041
  # @return [Array<Array<String, Hash>>] the ids and entries pairs
3029
- def xrevrange(key, _end = '+', start = '-', count: nil)
3030
- args = [:xrevrange, key, _end, start]
3042
+ def xrevrange(key, range_end = '+', start = '-', count: nil)
3043
+ args = [:xrevrange, key, range_end, start]
3031
3044
  args.concat(['COUNT', count]) if count
3032
3045
  synchronize { |client| client.call(args, &HashifyStreamEntries) }
3033
3046
  end
@@ -3119,12 +3132,12 @@ class Redis
3119
3132
  # @option opts [Boolean] :noack whether message loss is acceptable or not
3120
3133
  #
3121
3134
  # @return [Hash{String => Hash{String => Hash}}] the entries
3122
- def xreadgroup(group, consumer, keys, ids, opts = {})
3135
+ def xreadgroup(group, consumer, keys, ids, count: nil, block: nil, noack: nil)
3123
3136
  args = [:xreadgroup, 'GROUP', group, consumer]
3124
- args << 'COUNT' << opts[:count] if opts[:count]
3125
- args << 'BLOCK' << opts[:block].to_i if opts[:block]
3126
- args << 'NOACK' if opts[:noack]
3127
- _xread(args, keys, ids, opts[:block])
3137
+ args << 'COUNT' << count if count
3138
+ args << 'BLOCK' << block.to_i if block
3139
+ args << 'NOACK' if noack
3140
+ _xread(args, keys, ids, block)
3128
3141
  end
3129
3142
 
3130
3143
  # Removes one or multiple entries from the pending entries list of a stream consumer group.
@@ -3234,8 +3247,8 @@ class Redis
3234
3247
  when "get-master-addr-by-name"
3235
3248
  reply
3236
3249
  else
3237
- if reply.kind_of?(Array)
3238
- if reply[0].kind_of?(Array)
3250
+ if reply.is_a?(Array)
3251
+ if reply[0].is_a?(Array)
3239
3252
  reply.map(&Hashify)
3240
3253
  else
3241
3254
  Hashify.call(reply)
@@ -3259,12 +3272,17 @@ class Redis
3259
3272
  def cluster(subcommand, *args)
3260
3273
  subcommand = subcommand.to_s.downcase
3261
3274
  block = case subcommand
3262
- when 'slots' then HashifyClusterSlots
3263
- when 'nodes' then HashifyClusterNodes
3264
- when 'slaves' then HashifyClusterSlaves
3265
- when 'info' then HashifyInfo
3266
- else Noop
3267
- end
3275
+ when 'slots'
3276
+ HashifyClusterSlots
3277
+ when 'nodes'
3278
+ HashifyClusterNodes
3279
+ when 'slaves'
3280
+ HashifyClusterSlaves
3281
+ when 'info'
3282
+ HashifyInfo
3283
+ else
3284
+ Noop
3285
+ end
3268
3286
 
3269
3287
  # @see https://github.com/antirez/redis/blob/unstable/src/redis-trib.rb#L127 raw reply expected
3270
3288
  block = Noop unless @cluster_mode
@@ -3299,21 +3317,21 @@ class Redis
3299
3317
  return @original_client.connection_info if @cluster_mode
3300
3318
 
3301
3319
  {
3302
- host: @original_client.host,
3303
- port: @original_client.port,
3304
- db: @original_client.db,
3305
- id: @original_client.id,
3320
+ host: @original_client.host,
3321
+ port: @original_client.port,
3322
+ db: @original_client.db,
3323
+ id: @original_client.id,
3306
3324
  location: @original_client.location
3307
3325
  }
3308
3326
  end
3309
3327
 
3310
- def method_missing(command, *args)
3328
+ def method_missing(command, *args) # rubocop:disable Style/MissingRespondToMissing
3311
3329
  synchronize do |client|
3312
3330
  client.call([command] + args)
3313
3331
  end
3314
3332
  end
3315
3333
 
3316
- private
3334
+ private
3317
3335
 
3318
3336
  # Commands returning 1 for true and 0 for false may be executed in a pipeline
3319
3337
  # where the method call will return nil. Propagate the nil instead of falsely
@@ -3393,10 +3411,10 @@ private
3393
3411
 
3394
3412
  HashifyStreamPendings = lambda { |reply|
3395
3413
  {
3396
- 'size' => reply[0],
3414
+ 'size' => reply[0],
3397
3415
  'min_entry_id' => reply[1],
3398
3416
  'max_entry_id' => reply[2],
3399
- 'consumers' => reply[3].nil? ? {} : reply[3].to_h
3417
+ 'consumers' => reply[3].nil? ? {} : reply[3].to_h
3400
3418
  }
3401
3419
  }
3402
3420
 
@@ -3405,8 +3423,8 @@ private
3405
3423
  {
3406
3424
  'entry_id' => arr[0],
3407
3425
  'consumer' => arr[1],
3408
- 'elapsed' => arr[2],
3409
- 'count' => arr[3]
3426
+ 'elapsed' => arr[2],
3427
+ 'count' => arr[3]
3410
3428
  }
3411
3429
  end
3412
3430
  }
@@ -3414,15 +3432,15 @@ private
3414
3432
  HashifyClusterNodeInfo = lambda { |str|
3415
3433
  arr = str.split(' ')
3416
3434
  {
3417
- 'node_id' => arr[0],
3418
- 'ip_port' => arr[1],
3419
- 'flags' => arr[2].split(','),
3435
+ 'node_id' => arr[0],
3436
+ 'ip_port' => arr[1],
3437
+ 'flags' => arr[2].split(','),
3420
3438
  'master_node_id' => arr[3],
3421
- 'ping_sent' => arr[4],
3422
- 'pong_recv' => arr[5],
3423
- 'config_epoch' => arr[6],
3424
- 'link_state' => arr[7],
3425
- 'slots' => arr[8].nil? ? nil : Range.new(*arr[8].split('-'))
3439
+ 'ping_sent' => arr[4],
3440
+ 'pong_recv' => arr[5],
3441
+ 'config_epoch' => arr[6],
3442
+ 'link_state' => arr[7],
3443
+ 'slots' => arr[8].nil? ? nil : Range.new(*arr[8].split('-'))
3426
3444
  }
3427
3445
  }
3428
3446
 
@@ -3433,9 +3451,9 @@ private
3433
3451
  replicas = arr[3..-1].map { |r| { 'ip' => r[0], 'port' => r[1], 'node_id' => r[2] } }
3434
3452
  {
3435
3453
  'start_slot' => first_slot,
3436
- 'end_slot' => last_slot,
3437
- 'master' => master,
3438
- 'replicas' => replicas
3454
+ 'end_slot' => last_slot,
3455
+ 'master' => master,
3456
+ 'replicas' => replicas
3439
3457
  }
3440
3458
  end
3441
3459
  }