redis 4.1.4 → 4.2.4

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: d7c23f28cc1a4b66b70d9caf4b890ce521145a51435921e65d5676ce7145c2bf
4
+ data.tar.gz: b88ecef94de49b0419ed1e1152fc05b5a6999a53883235b09b2eef0df3d37d9c
5
5
  SHA512:
6
- metadata.gz: 692dfc5c73c6410492589f38f279976a023f6a2ff13f7b1476806011eb387f41bed784bdeac746de5f4b990b6d22bf297b36dddc7b8e448a842241a389f50796
7
- data.tar.gz: 55a9e305c7563f5dd7d38f50dc7b919967dbb0f6a131ebc5e1569f49f196ab458203b6594394fa9a33ea9e337b741113e781378113783683dd36b87196607b8f
6
+ metadata.gz: 4904a1b642fe757601a0c3bb34cb1d1160ca6bdf269624ae73a9d73599c11ad2d8e52a93ba1396bd76c71f31456211d7332d3c8fa16dd44d2ceb92826cbe86cc
7
+ data.tar.gz: d491bf50bca0c804d1b594ebf2de1d5b5a1d464873fe0d03986464006638028e7dd2b2056ed24919bb70a8c60c9a9fabf0e73663e8065bd474baf52abbf95d98
@@ -1,5 +1,39 @@
1
1
  # Unreleased
2
2
 
3
+ # 4.2.4
4
+
5
+ * Fix bytesize calculations in the ruby connector, and work on a copy of the buffer. Fix #961, #962.
6
+
7
+ # 4.2.3
8
+
9
+ * Use io/wait instead of IO.select in the ruby connector. See #960.
10
+ * Use exception free non blocking IOs in the ruby connector. See #926.
11
+ * Prevent corruption of the client when an interrupt happen during inside a pipeline block. See #945.
12
+
13
+ # 4.2.2
14
+
15
+ * Fix `WATCH` support for `Redis::Distributed`. See #941.
16
+ * Fix handling of empty stream responses. See #905, #929.
17
+
18
+ # 4.2.1
19
+
20
+ * Fix `exists?` returning an actual boolean when called with multiple keys. See #918.
21
+ * Setting `Redis.exists_returns_integer = false` disables warning message about new behaviour. See #920.
22
+
23
+ # 4.2.0
24
+
25
+ * Convert commands to accept keyword arguments rather than option hashes. This both help catching typos, and reduce needless allocations.
26
+ * Deprecate the synchrony driver. It will be removed in 5.0 and hopefully maintained as a separate gem. See #915.
27
+ * Make `Redis#exists` variadic, will return an Integer if called with multiple keys.
28
+ * Add `Redis#exists?` to get a Boolean if any of the keys exists.
29
+ * `Redis#exists` when called with a single key will warn that future versions will return an Integer.
30
+ Set `Redis.exists_returns_integer = true` to opt-in to the new behavior.
31
+ * Support `keepttl` ooption in `set`. See #913.
32
+ * Optimized initialization of Redis::Cluster. See #912.
33
+ * Accept sentinel options even with string key. See #599.
34
+ * Verify TLS connections by default. See #900.
35
+ * Make `Redis#hset` variadic. It now returns an integer, not a boolean. See #910.
36
+
3
37
  # 4.1.4
4
38
 
5
39
  * Alias `Redis#disconnect` as `#close`. See #901.
@@ -9,6 +43,7 @@
9
43
  * Increase buffer size in the ruby connector. See #880.
10
44
  * Fix thread safety of `Redis.queue`. See #878.
11
45
  * Deprecate `Redis::Future#==` as it's likely to be a mistake. See #876.
46
+ * Support `KEEPTTL` option for SET command. See #913.
12
47
 
13
48
  # 4.1.3
14
49
 
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"
@@ -261,6 +265,7 @@ All timeout values are specified in seconds.
261
265
  When using pub/sub, you can subscribe to a channel using a timeout as well:
262
266
 
263
267
  ```ruby
268
+ redis = Redis.new(reconnect_attempts: 0)
264
269
  redis.subscribe_with_timeout(5, "news") do |on|
265
270
  on.message do |channel, message|
266
271
  # ...
@@ -322,7 +327,7 @@ This library supports natively terminating client side SSL/TLS connections
322
327
  when talking to Redis via a server-side proxy such as [stunnel], [hitch],
323
328
  or [ghostunnel].
324
329
 
325
- To enable SSL support, pass the `:ssl => :true` option when configuring the
330
+ To enable SSL support, pass the `:ssl => true` option when configuring the
326
331
  Redis client, or pass in `:url => "rediss://..."` (like HTTPS for Redis).
327
332
  You will also need to pass in an `:ssl_params => { ... }` hash used to
328
333
  configure the `OpenSSL::SSL::SSLContext` object used for the connection:
@@ -451,7 +456,7 @@ client and evangelized Redis in Rubyland. Thank you, Ezra.
451
456
  ## Contributing
452
457
 
453
458
  [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.
459
+ requests.
455
460
 
456
461
 
457
462
  [inchpages-image]: https://inch-ci.org/github/redis/redis-rb.svg
@@ -4,12 +4,26 @@ 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_reader :exists_returns_integer
9
+
10
+ def exists_returns_integer=(value)
11
+ unless value
12
+ message = "`Redis#exists(key)` will return an Integer by default in redis-rb 4.3. The option to explicitly " \
13
+ "disable this behaviour via `Redis.exists_returns_integer` will be removed in 5.0. You should use " \
14
+ "`exists?` instead."
15
+
16
+ ::Kernel.warn(message)
17
+ end
18
+
19
+ @exists_returns_integer = value
20
+ end
21
+
22
+ attr_writer :current
9
23
  end
10
24
 
11
- def self.current=(redis)
12
- @current = redis
25
+ def self.current
26
+ @current ||= Redis.new
13
27
  end
14
28
 
15
29
  include MonitorMixin
@@ -17,7 +31,9 @@ class Redis
17
31
  # Create a new client instance
18
32
  #
19
33
  # @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.
34
+ # @option options [String] :url (value of the environment variable REDIS_URL) a Redis URL, for a TCP connection:
35
+ # `redis://:[password]@[hostname]:[port]/[db]` (password, port and database are optional), for a unix socket
36
+ # connection: `unix://[path to Redis socket]`. This overrides all other options.
21
37
  # @option options [String] :host ("127.0.0.1") server hostname
22
38
  # @option options [Integer] :port (6379) server port
23
39
  # @option options [String] :path path to server socket (overrides host and port)
@@ -26,8 +42,10 @@ class Redis
26
42
  # @option options [String] :password Password to authenticate against server
27
43
  # @option options [Integer] :db (0) Database to select after initial connect
28
44
  # @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
45
+ # @option options [String] :id ID for the client connection, assigns name to current connection by sending
46
+ # `CLIENT SETNAME`
47
+ # @option options [Hash, Integer] :tcp_keepalive Keepalive values, if Integer `intvl` and `probe` are calculated
48
+ # based on the value, if Hash `time`, `intvl` and `probes` can be specified as a Integer
31
49
  # @option options [Integer] :reconnect_attempts Number of attempts trying to connect
32
50
  # @option options [Boolean] :inherit_socket (false) Whether to use socket in forked process or not
33
51
  # @option options [Array] :sentinels List of sentinels to contact
@@ -52,7 +70,7 @@ class Redis
52
70
  end
53
71
 
54
72
  # Run code with the client reconnecting
55
- def with_reconnect(val=true, &blk)
73
+ def with_reconnect(val = true, &blk)
56
74
  synchronize do |client|
57
75
  client.with_reconnect(val, &blk)
58
76
  end
@@ -205,7 +223,7 @@ class Redis
205
223
  def config(action, *args)
206
224
  synchronize do |client|
207
225
  client.call([:config, action] + args) do |reply|
208
- if reply.kind_of?(Array) && action == :get
226
+ if reply.is_a?(Array) && action == :get
209
227
  Hashify.call(reply)
210
228
  else
211
229
  reply
@@ -256,7 +274,7 @@ class Redis
256
274
  def flushall(options = nil)
257
275
  synchronize do |client|
258
276
  if options && options[:async]
259
- client.call([:flushall, :async])
277
+ client.call(%i[flushall async])
260
278
  else
261
279
  client.call([:flushall])
262
280
  end
@@ -271,7 +289,7 @@ class Redis
271
289
  def flushdb(options = nil)
272
290
  synchronize do |client|
273
291
  if options && options[:async]
274
- client.call([:flushdb, :async])
292
+ client.call(%i[flushdb async])
275
293
  else
276
294
  client.call([:flushdb])
277
295
  end
@@ -285,7 +303,7 @@ class Redis
285
303
  def info(cmd = nil)
286
304
  synchronize do |client|
287
305
  client.call([:info, cmd].compact) do |reply|
288
- if reply.kind_of?(String)
306
+ if reply.is_a?(String)
289
307
  reply = HashifyInfo.call(reply)
290
308
 
291
309
  if cmd && cmd.to_s == "commandstats"
@@ -358,7 +376,7 @@ class Redis
358
376
  # @param [String] subcommand e.g. `get`, `len`, `reset`
359
377
  # @param [Integer] length maximum number of entries to return
360
378
  # @return [Array<String>, Integer, String] depends on subcommand
361
- def slowlog(subcommand, length=nil)
379
+ def slowlog(subcommand, length = nil)
362
380
  synchronize do |client|
363
381
  args = [:slowlog, subcommand]
364
382
  args << length if length
@@ -383,7 +401,7 @@ class Redis
383
401
  def time
384
402
  synchronize do |client|
385
403
  client.call([:time]) do |reply|
386
- reply.map(&:to_i) if reply
404
+ reply&.map(&:to_i)
387
405
  end
388
406
  end
389
407
  end
@@ -496,9 +514,9 @@ class Redis
496
514
  # - `:replace => Boolean`: if false, raises an error if key already exists
497
515
  # @raise [Redis::CommandError]
498
516
  # @return [String] `"OK"`
499
- def restore(key, ttl, serialized_value, options = {})
517
+ def restore(key, ttl, serialized_value, replace: nil)
500
518
  args = [:restore, key, ttl, serialized_value]
501
- args << 'REPLACE' if options[:replace]
519
+ args << 'REPLACE' if replace
502
520
 
503
521
  synchronize do |client|
504
522
  client.call(args)
@@ -550,13 +568,43 @@ class Redis
550
568
  end
551
569
  end
552
570
 
553
- # Determine if a key exists.
571
+ # Determine how many of the keys exists.
554
572
  #
555
- # @param [String] key
573
+ # @param [String, Array<String>] keys
574
+ # @return [Integer]
575
+ def exists(*keys)
576
+ if !Redis.exists_returns_integer && keys.size == 1
577
+ if Redis.exists_returns_integer.nil?
578
+ message = "`Redis#exists(key)` will return an Integer in redis-rb 4.3. `exists?` returns a boolean, you " \
579
+ "should use it instead. To opt-in to the new behavior now you can set Redis.exists_returns_integer = " \
580
+ "true. To disable this message and keep the current (boolean) behaviour of 'exists' you can set " \
581
+ "`Redis.exists_returns_integer = false`, but this option will be removed in 5.0. " \
582
+ "(#{::Kernel.caller(1, 1).first})\n"
583
+
584
+ ::Kernel.warn(message)
585
+ end
586
+
587
+ exists?(*keys)
588
+ else
589
+ _exists(*keys)
590
+ end
591
+ end
592
+
593
+ def _exists(*keys)
594
+ synchronize do |client|
595
+ client.call([:exists, *keys])
596
+ end
597
+ end
598
+
599
+ # Determine if any of the keys exists.
600
+ #
601
+ # @param [String, Array<String>] keys
556
602
  # @return [Boolean]
557
- def exists(key)
603
+ def exists?(*keys)
558
604
  synchronize do |client|
559
- client.call([:exists, key], &Boolify)
605
+ client.call([:exists, *keys]) do |value|
606
+ value > 0
607
+ end
560
608
  end
561
609
  end
562
610
 
@@ -567,7 +615,7 @@ class Redis
567
615
  def keys(pattern = "*")
568
616
  synchronize do |client|
569
617
  client.call([:keys, pattern]) do |reply|
570
- if reply.kind_of?(String)
618
+ if reply.is_a?(String)
571
619
  reply.split(" ")
572
620
  else
573
621
  reply
@@ -663,30 +711,27 @@ class Redis
663
711
  # elements where every element is an array with the result for every
664
712
  # element specified in `:get`
665
713
  # - when `:store` is specified, the number of elements in the stored result
666
- def sort(key, options = {})
667
- args = []
714
+ def sort(key, by: nil, limit: nil, get: nil, order: nil, store: nil)
715
+ args = [:sort, key]
716
+ args << "BY" << by if by
668
717
 
669
- by = options[:by]
670
- args.concat(["BY", by]) if by
671
-
672
- limit = options[:limit]
673
- args.concat(["LIMIT"] + limit) if limit
718
+ if limit
719
+ args << "LIMIT"
720
+ args.concat(limit)
721
+ end
674
722
 
675
- get = Array(options[:get])
676
- args.concat(["GET"].product(get).flatten) unless get.empty?
723
+ get = Array(get)
724
+ get.each do |item|
725
+ args << "GET" << item
726
+ end
677
727
 
678
- order = options[:order]
679
728
  args.concat(order.split(" ")) if order
680
-
681
- store = options[:store]
682
- args.concat(["STORE", store]) if store
729
+ args << "STORE" << store if store
683
730
 
684
731
  synchronize do |client|
685
- client.call([:sort, key] + args) do |reply|
732
+ client.call(args) do |reply|
686
733
  if get.size > 1 && !store
687
- if reply
688
- reply.each_slice(get.size).to_a
689
- end
734
+ reply.each_slice(get.size).to_a if reply
690
735
  else
691
736
  reply
692
737
  end
@@ -786,27 +831,21 @@ class Redis
786
831
  # - `:px => Integer`: Set the specified expire time, in milliseconds.
787
832
  # - `:nx => true`: Only set the key if it does not already exist.
788
833
  # - `:xx => true`: Only set the key if it already exist.
834
+ # - `:keepttl => true`: Retain the time to live associated with the key.
789
835
  # @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
836
+ def set(key, value, ex: nil, px: nil, nx: nil, xx: nil, keepttl: nil)
837
+ args = [:set, key, value.to_s]
838
+ args << "EX" << ex if ex
839
+ args << "PX" << px if px
840
+ args << "NX" if nx
841
+ args << "XX" if xx
842
+ args << "KEEPTTL" if keepttl
804
843
 
805
844
  synchronize do |client|
806
845
  if nx || xx
807
- client.call([:set, key, value.to_s] + args, &BoolifySet)
846
+ client.call(args, &BoolifySet)
808
847
  else
809
- client.call([:set, key, value.to_s] + args)
848
+ client.call(args)
810
849
  end
811
850
  end
812
851
  end
@@ -888,7 +927,7 @@ class Redis
888
927
  # @see #mapped_msetnx
889
928
  def msetnx(*args)
890
929
  synchronize do |client|
891
- client.call([:msetnx] + args, &Boolify)
930
+ client.call([:msetnx, *args], &Boolify)
892
931
  end
893
932
  end
894
933
 
@@ -928,7 +967,7 @@ class Redis
928
967
  # @see #mapped_mget
929
968
  def mget(*keys, &blk)
930
969
  synchronize do |client|
931
- client.call([:mget] + keys, &blk)
970
+ client.call([:mget, *keys], &blk)
932
971
  end
933
972
  end
934
973
 
@@ -944,7 +983,7 @@ class Redis
944
983
  # @see #mget
945
984
  def mapped_mget(*keys)
946
985
  mget(*keys) do |reply|
947
- if reply.kind_of?(Array)
986
+ if reply.is_a?(Array)
948
987
  Hash[keys.zip(reply)]
949
988
  else
950
989
  reply
@@ -1031,7 +1070,7 @@ class Redis
1031
1070
  # @return [Integer] the length of the string stored in `destkey`
1032
1071
  def bitop(operation, destkey, *keys)
1033
1072
  synchronize do |client|
1034
- client.call([:bitop, operation, destkey] + keys)
1073
+ client.call([:bitop, operation, destkey, *keys])
1035
1074
  end
1036
1075
  end
1037
1076
 
@@ -1043,10 +1082,8 @@ class Redis
1043
1082
  # @param [Integer] stop stop index
1044
1083
  # @return [Integer] the position of the first 1/0 bit.
1045
1084
  # -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
1085
+ def bitpos(key, bit, start = nil, stop = nil)
1086
+ raise(ArgumentError, 'stop parameter specified without start parameter') if stop && !start
1050
1087
 
1051
1088
  synchronize do |client|
1052
1089
  command = [:bitpos, key, bit]
@@ -1240,15 +1277,7 @@ class Redis
1240
1277
  # @return [nil, String]
1241
1278
  # - `nil` when the operation timed out
1242
1279
  # - 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
-
1280
+ def brpoplpush(source, destination, deprecated_timeout = 0, timeout: deprecated_timeout)
1252
1281
  synchronize do |client|
1253
1282
  command = [:brpoplpush, source, destination, timeout]
1254
1283
  timeout += client.timeout if timeout > 0
@@ -1455,7 +1484,7 @@ class Redis
1455
1484
  # @return [Array<String>] members in the difference
1456
1485
  def sdiff(*keys)
1457
1486
  synchronize do |client|
1458
- client.call([:sdiff] + keys)
1487
+ client.call([:sdiff, *keys])
1459
1488
  end
1460
1489
  end
1461
1490
 
@@ -1466,7 +1495,7 @@ class Redis
1466
1495
  # @return [Integer] number of elements in the resulting set
1467
1496
  def sdiffstore(destination, *keys)
1468
1497
  synchronize do |client|
1469
- client.call([:sdiffstore, destination] + keys)
1498
+ client.call([:sdiffstore, destination, *keys])
1470
1499
  end
1471
1500
  end
1472
1501
 
@@ -1476,7 +1505,7 @@ class Redis
1476
1505
  # @return [Array<String>] members in the intersection
1477
1506
  def sinter(*keys)
1478
1507
  synchronize do |client|
1479
- client.call([:sinter] + keys)
1508
+ client.call([:sinter, *keys])
1480
1509
  end
1481
1510
  end
1482
1511
 
@@ -1487,7 +1516,7 @@ class Redis
1487
1516
  # @return [Integer] number of elements in the resulting set
1488
1517
  def sinterstore(destination, *keys)
1489
1518
  synchronize do |client|
1490
- client.call([:sinterstore, destination] + keys)
1519
+ client.call([:sinterstore, destination, *keys])
1491
1520
  end
1492
1521
  end
1493
1522
 
@@ -1497,7 +1526,7 @@ class Redis
1497
1526
  # @return [Array<String>] members in the union
1498
1527
  def sunion(*keys)
1499
1528
  synchronize do |client|
1500
- client.call([:sunion] + keys)
1529
+ client.call([:sunion, *keys])
1501
1530
  end
1502
1531
  end
1503
1532
 
@@ -1508,7 +1537,7 @@ class Redis
1508
1537
  # @return [Integer] number of elements in the resulting set
1509
1538
  def sunionstore(destination, *keys)
1510
1539
  synchronize do |client|
1511
- client.call([:sunionstore, destination] + keys)
1540
+ client.call([:sunionstore, destination, *keys])
1512
1541
  end
1513
1542
  end
1514
1543
 
@@ -1557,31 +1586,20 @@ class Redis
1557
1586
  # pairs that were **added** to the sorted set.
1558
1587
  # - `Float` when option :incr is specified, holding the score of the member
1559
1588
  # 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
1589
+ def zadd(key, *args, nx: nil, xx: nil, ch: nil, incr: nil)
1590
+ command = [:zadd, key]
1591
+ command << "NX" if nx
1592
+ command << "XX" if xx
1593
+ command << "CH" if ch
1594
+ command << "INCR" if incr
1577
1595
 
1578
1596
  synchronize do |client|
1579
1597
  if args.size == 1 && args[0].is_a?(Array)
1580
1598
  # Variadic: return float if INCR, integer if !INCR
1581
- client.call([:zadd, key] + zadd_options + args[0], &(incr ? Floatify : nil))
1599
+ client.call(command + args[0], &(incr ? Floatify : nil))
1582
1600
  elsif args.size == 2
1583
1601
  # Single pair: return float if INCR, boolean if !INCR
1584
- client.call([:zadd, key] + zadd_options + args, &(incr ? Floatify : Boolify))
1602
+ client.call(command + args, &(incr ? Floatify : Boolify))
1585
1603
  else
1586
1604
  raise ArgumentError, "wrong number of arguments"
1587
1605
  end
@@ -1752,10 +1770,8 @@ class Redis
1752
1770
  # @return [Array<String>, Array<[String, Float]>]
1753
1771
  # - when `:with_scores` is not specified, an array of members
1754
1772
  # - 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]
1773
+ def zrange(key, start, stop, withscores: false, with_scores: withscores)
1774
+ args = [:zrange, key, start, stop]
1759
1775
 
1760
1776
  if with_scores
1761
1777
  args << "WITHSCORES"
@@ -1763,7 +1779,7 @@ class Redis
1763
1779
  end
1764
1780
 
1765
1781
  synchronize do |client|
1766
- client.call([:zrange, key, start, stop] + args, &block)
1782
+ client.call(args, &block)
1767
1783
  end
1768
1784
  end
1769
1785
 
@@ -1778,10 +1794,8 @@ class Redis
1778
1794
  # # => [["b", 64.0], ["a", 32.0]]
1779
1795
  #
1780
1796
  # @see #zrange
1781
- def zrevrange(key, start, stop, options = {})
1782
- args = []
1783
-
1784
- with_scores = options[:with_scores] || options[:withscores]
1797
+ def zrevrange(key, start, stop, withscores: false, with_scores: withscores)
1798
+ args = [:zrevrange, key, start, stop]
1785
1799
 
1786
1800
  if with_scores
1787
1801
  args << "WITHSCORES"
@@ -1789,7 +1803,7 @@ class Redis
1789
1803
  end
1790
1804
 
1791
1805
  synchronize do |client|
1792
- client.call([:zrevrange, key, start, stop] + args, &block)
1806
+ client.call(args, &block)
1793
1807
  end
1794
1808
  end
1795
1809
 
@@ -1880,14 +1894,16 @@ class Redis
1880
1894
  # `count` members
1881
1895
  #
1882
1896
  # @return [Array<String>, Array<[String, Float]>]
1883
- def zrangebylex(key, min, max, options = {})
1884
- args = []
1897
+ def zrangebylex(key, min, max, limit: nil)
1898
+ args = [:zrangebylex, key, min, max]
1885
1899
 
1886
- limit = options[:limit]
1887
- args.concat(["LIMIT"] + limit) if limit
1900
+ if limit
1901
+ args << "LIMIT"
1902
+ args.concat(limit)
1903
+ end
1888
1904
 
1889
1905
  synchronize do |client|
1890
- client.call([:zrangebylex, key, min, max] + args)
1906
+ client.call(args)
1891
1907
  end
1892
1908
  end
1893
1909
 
@@ -1902,14 +1918,16 @@ class Redis
1902
1918
  # # => ["abbygail", "abby"]
1903
1919
  #
1904
1920
  # @see #zrangebylex
1905
- def zrevrangebylex(key, max, min, options = {})
1906
- args = []
1921
+ def zrevrangebylex(key, max, min, limit: nil)
1922
+ args = [:zrevrangebylex, key, max, min]
1907
1923
 
1908
- limit = options[:limit]
1909
- args.concat(["LIMIT"] + limit) if limit
1924
+ if limit
1925
+ args << "LIMIT"
1926
+ args.concat(limit)
1927
+ end
1910
1928
 
1911
1929
  synchronize do |client|
1912
- client.call([:zrevrangebylex, key, max, min] + args)
1930
+ client.call(args)
1913
1931
  end
1914
1932
  end
1915
1933
 
@@ -1940,21 +1958,21 @@ class Redis
1940
1958
  # @return [Array<String>, Array<[String, Float]>]
1941
1959
  # - when `:with_scores` is not specified, an array of members
1942
1960
  # - 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]
1961
+ def zrangebyscore(key, min, max, withscores: false, with_scores: withscores, limit: nil)
1962
+ args = [:zrangebyscore, key, min, max]
1947
1963
 
1948
1964
  if with_scores
1949
1965
  args << "WITHSCORES"
1950
1966
  block = FloatifyPairs
1951
1967
  end
1952
1968
 
1953
- limit = options[:limit]
1954
- args.concat(["LIMIT"] + limit) if limit
1969
+ if limit
1970
+ args << "LIMIT"
1971
+ args.concat(limit)
1972
+ end
1955
1973
 
1956
1974
  synchronize do |client|
1957
- client.call([:zrangebyscore, key, min, max] + args, &block)
1975
+ client.call(args, &block)
1958
1976
  end
1959
1977
  end
1960
1978
 
@@ -1972,21 +1990,21 @@ class Redis
1972
1990
  # # => [["b", 64.0], ["a", 32.0]]
1973
1991
  #
1974
1992
  # @see #zrangebyscore
1975
- def zrevrangebyscore(key, max, min, options = {})
1976
- args = []
1977
-
1978
- with_scores = options[:with_scores] || options[:withscores]
1993
+ def zrevrangebyscore(key, max, min, withscores: false, with_scores: withscores, limit: nil)
1994
+ args = [:zrevrangebyscore, key, max, min]
1979
1995
 
1980
1996
  if with_scores
1981
- args << ["WITHSCORES"]
1997
+ args << "WITHSCORES"
1982
1998
  block = FloatifyPairs
1983
1999
  end
1984
2000
 
1985
- limit = options[:limit]
1986
- args.concat(["LIMIT"] + limit) if limit
2001
+ if limit
2002
+ args << "LIMIT"
2003
+ args.concat(limit)
2004
+ end
1987
2005
 
1988
2006
  synchronize do |client|
1989
- client.call([:zrevrangebyscore, key, max, min] + args, &block)
2007
+ client.call(args, &block)
1990
2008
  end
1991
2009
  end
1992
2010
 
@@ -2050,17 +2068,18 @@ class Redis
2050
2068
  # sorted sets
2051
2069
  # - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
2052
2070
  # @return [Integer] number of elements in the resulting sorted set
2053
- def zinterstore(destination, keys, options = {})
2054
- args = []
2071
+ def zinterstore(destination, keys, weights: nil, aggregate: nil)
2072
+ args = [:zinterstore, destination, keys.size, *keys]
2055
2073
 
2056
- weights = options[:weights]
2057
- args.concat(["WEIGHTS"] + weights) if weights
2074
+ if weights
2075
+ args << "WEIGHTS"
2076
+ args.concat(weights)
2077
+ end
2058
2078
 
2059
- aggregate = options[:aggregate]
2060
- args.concat(["AGGREGATE", aggregate]) if aggregate
2079
+ args << "AGGREGATE" << aggregate if aggregate
2061
2080
 
2062
2081
  synchronize do |client|
2063
- client.call([:zinterstore, destination, keys.size] + keys + args)
2082
+ client.call(args)
2064
2083
  end
2065
2084
  end
2066
2085
 
@@ -2077,17 +2096,18 @@ class Redis
2077
2096
  # sorted sets
2078
2097
  # - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
2079
2098
  # @return [Integer] number of elements in the resulting sorted set
2080
- def zunionstore(destination, keys, options = {})
2081
- args = []
2099
+ def zunionstore(destination, keys, weights: nil, aggregate: nil)
2100
+ args = [:zunionstore, destination, keys.size, *keys]
2082
2101
 
2083
- weights = options[:weights]
2084
- args.concat(["WEIGHTS"] + weights) if weights
2102
+ if weights
2103
+ args << "WEIGHTS"
2104
+ args.concat(weights)
2105
+ end
2085
2106
 
2086
- aggregate = options[:aggregate]
2087
- args.concat(["AGGREGATE", aggregate]) if aggregate
2107
+ args << "AGGREGATE" << aggregate if aggregate
2088
2108
 
2089
2109
  synchronize do |client|
2090
- client.call([:zunionstore, destination, keys.size] + keys + args)
2110
+ client.call(args)
2091
2111
  end
2092
2112
  end
2093
2113
 
@@ -2101,15 +2121,20 @@ class Redis
2101
2121
  end
2102
2122
  end
2103
2123
 
2104
- # Set the string value of a hash field.
2124
+ # Set one or more hash values.
2125
+ #
2126
+ # @example
2127
+ # redis.hset("hash", "f1", "v1", "f2", "v2") # => 2
2128
+ # redis.hset("hash", { "f1" => "v1", "f2" => "v2" }) # => 2
2105
2129
  #
2106
2130
  # @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)
2131
+ # @param [Array<String> | Hash<String, String>] attrs array or hash of fields and values
2132
+ # @return [Integer] The number of fields that were added to the hash
2133
+ def hset(key, *attrs)
2134
+ attrs = attrs.first.flatten if attrs.size == 1 && attrs.first.is_a?(Hash)
2135
+
2111
2136
  synchronize do |client|
2112
- client.call([:hset, key, field, value], &Boolify)
2137
+ client.call([:hset, key, *attrs])
2113
2138
  end
2114
2139
  end
2115
2140
 
@@ -2198,7 +2223,7 @@ class Redis
2198
2223
  # @see #hmget
2199
2224
  def mapped_hmget(key, *fields)
2200
2225
  hmget(key, *fields) do |reply|
2201
- if reply.kind_of?(Array)
2226
+ if reply.is_a?(Array)
2202
2227
  Hash[fields.zip(reply)]
2203
2228
  else
2204
2229
  reply
@@ -2291,20 +2316,21 @@ class Redis
2291
2316
 
2292
2317
  def subscribed?
2293
2318
  synchronize do |client|
2294
- client.kind_of? SubscribedClient
2319
+ client.is_a? SubscribedClient
2295
2320
  end
2296
2321
  end
2297
2322
 
2298
2323
  # Listen for messages published to the given channels.
2299
2324
  def subscribe(*channels, &block)
2300
- synchronize do |client|
2325
+ synchronize do |_client|
2301
2326
  _subscription(:subscribe, 0, channels, block)
2302
2327
  end
2303
2328
  end
2304
2329
 
2305
- # Listen for messages published to the given channels. Throw a timeout error if there is no messages for a timeout period.
2330
+ # Listen for messages published to the given channels. Throw a timeout error
2331
+ # if there is no messages for a timeout period.
2306
2332
  def subscribe_with_timeout(timeout, *channels, &block)
2307
- synchronize do |client|
2333
+ synchronize do |_client|
2308
2334
  _subscription(:subscribe_with_timeout, timeout, channels, block)
2309
2335
  end
2310
2336
  end
@@ -2312,21 +2338,23 @@ class Redis
2312
2338
  # Stop listening for messages posted to the given channels.
2313
2339
  def unsubscribe(*channels)
2314
2340
  synchronize do |client|
2315
- raise RuntimeError, "Can't unsubscribe if not subscribed." unless subscribed?
2341
+ raise "Can't unsubscribe if not subscribed." unless subscribed?
2342
+
2316
2343
  client.unsubscribe(*channels)
2317
2344
  end
2318
2345
  end
2319
2346
 
2320
2347
  # Listen for messages published to channels matching the given patterns.
2321
2348
  def psubscribe(*channels, &block)
2322
- synchronize do |client|
2349
+ synchronize do |_client|
2323
2350
  _subscription(:psubscribe, 0, channels, block)
2324
2351
  end
2325
2352
  end
2326
2353
 
2327
- # Listen for messages published to channels matching the given patterns. Throw a timeout error if there is no messages for a timeout period.
2354
+ # Listen for messages published to channels matching the given patterns.
2355
+ # Throw a timeout error if there is no messages for a timeout period.
2328
2356
  def psubscribe_with_timeout(timeout, *channels, &block)
2329
- synchronize do |client|
2357
+ synchronize do |_client|
2330
2358
  _subscription(:psubscribe_with_timeout, timeout, channels, block)
2331
2359
  end
2332
2360
  end
@@ -2334,7 +2362,8 @@ class Redis
2334
2362
  # Stop listening for messages posted to channels matching the given patterns.
2335
2363
  def punsubscribe(*channels)
2336
2364
  synchronize do |client|
2337
- raise RuntimeError, "Can't unsubscribe if not subscribed." unless subscribed?
2365
+ raise "Can't unsubscribe if not subscribed." unless subscribed?
2366
+
2338
2367
  client.punsubscribe(*channels)
2339
2368
  end
2340
2369
  end
@@ -2379,7 +2408,7 @@ class Redis
2379
2408
  # @see #multi
2380
2409
  def watch(*keys)
2381
2410
  synchronize do |client|
2382
- res = client.call([:watch] + keys)
2411
+ res = client.call([:watch, *keys])
2383
2412
 
2384
2413
  if block_given?
2385
2414
  begin
@@ -2409,14 +2438,13 @@ class Redis
2409
2438
  end
2410
2439
 
2411
2440
  def pipelined
2412
- synchronize do |client|
2441
+ synchronize do |prior_client|
2413
2442
  begin
2414
- pipeline = Pipeline.new(@client)
2415
- original, @client = @client, pipeline
2443
+ @client = Pipeline.new(prior_client)
2416
2444
  yield(self)
2417
- original.call_pipeline(@client)
2445
+ prior_client.call_pipeline(@client)
2418
2446
  ensure
2419
- @client = original
2447
+ @client = prior_client
2420
2448
  end
2421
2449
  end
2422
2450
  end
@@ -2452,17 +2480,16 @@ class Redis
2452
2480
  # @see #watch
2453
2481
  # @see #unwatch
2454
2482
  def multi
2455
- synchronize do |client|
2483
+ synchronize do |prior_client|
2456
2484
  if !block_given?
2457
- client.call([:multi])
2485
+ prior_client.call([:multi])
2458
2486
  else
2459
2487
  begin
2460
- pipeline = Pipeline::Multi.new(@client)
2461
- original, @client = @client, pipeline
2488
+ @client = Pipeline::Multi.new(prior_client)
2462
2489
  yield(self)
2463
- original.call_pipeline(pipeline)
2490
+ prior_client.call_pipeline(@client)
2464
2491
  ensure
2465
- @client = original
2492
+ @client = prior_client
2466
2493
  end
2467
2494
  end
2468
2495
  end
@@ -2609,18 +2636,12 @@ class Redis
2609
2636
  _eval(:evalsha, args)
2610
2637
  end
2611
2638
 
2612
- def _scan(command, cursor, args, options = {}, &block)
2639
+ def _scan(command, cursor, args, match: nil, count: nil, &block)
2613
2640
  # SSCAN/ZSCAN/HSCAN already prepend the key to +args+.
2614
2641
 
2615
2642
  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
2643
+ args << "MATCH" << match if match
2644
+ args << "COUNT" << count if count
2624
2645
 
2625
2646
  synchronize do |client|
2626
2647
  client.call([command] + args, &block)
@@ -2642,8 +2663,8 @@ class Redis
2642
2663
  # - `:count => Integer`: return count keys at most per iteration
2643
2664
  #
2644
2665
  # @return [String, Array<String>] the next cursor and all found keys
2645
- def scan(cursor, options={})
2646
- _scan(:scan, cursor, [], options)
2666
+ def scan(cursor, **options)
2667
+ _scan(:scan, cursor, [], **options)
2647
2668
  end
2648
2669
 
2649
2670
  # Scan the keyspace
@@ -2661,11 +2682,12 @@ class Redis
2661
2682
  # - `:count => Integer`: return count keys at most per iteration
2662
2683
  #
2663
2684
  # @return [Enumerator] an enumerator for all found keys
2664
- def scan_each(options={}, &block)
2665
- return to_enum(:scan_each, options) unless block_given?
2685
+ def scan_each(**options, &block)
2686
+ return to_enum(:scan_each, **options) unless block_given?
2687
+
2666
2688
  cursor = 0
2667
2689
  loop do
2668
- cursor, keys = scan(cursor, options)
2690
+ cursor, keys = scan(cursor, **options)
2669
2691
  keys.each(&block)
2670
2692
  break if cursor == "0"
2671
2693
  end
@@ -2682,8 +2704,8 @@ class Redis
2682
2704
  # - `:count => Integer`: return count keys at most per iteration
2683
2705
  #
2684
2706
  # @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|
2707
+ def hscan(key, cursor, **options)
2708
+ _scan(:hscan, cursor, [key], **options) do |reply|
2687
2709
  [reply[0], reply[1].each_slice(2).to_a]
2688
2710
  end
2689
2711
  end
@@ -2699,11 +2721,12 @@ class Redis
2699
2721
  # - `:count => Integer`: return count keys at most per iteration
2700
2722
  #
2701
2723
  # @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?
2724
+ def hscan_each(key, **options, &block)
2725
+ return to_enum(:hscan_each, key, **options) unless block_given?
2726
+
2704
2727
  cursor = 0
2705
2728
  loop do
2706
- cursor, values = hscan(key, cursor, options)
2729
+ cursor, values = hscan(key, cursor, **options)
2707
2730
  values.each(&block)
2708
2731
  break if cursor == "0"
2709
2732
  end
@@ -2721,8 +2744,8 @@ class Redis
2721
2744
  #
2722
2745
  # @return [String, Array<[String, Float]>] the next cursor and all found
2723
2746
  # members and scores
2724
- def zscan(key, cursor, options={})
2725
- _scan(:zscan, cursor, [key], options) do |reply|
2747
+ def zscan(key, cursor, **options)
2748
+ _scan(:zscan, cursor, [key], **options) do |reply|
2726
2749
  [reply[0], FloatifyPairs.call(reply[1])]
2727
2750
  end
2728
2751
  end
@@ -2738,11 +2761,12 @@ class Redis
2738
2761
  # - `:count => Integer`: return count keys at most per iteration
2739
2762
  #
2740
2763
  # @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?
2764
+ def zscan_each(key, **options, &block)
2765
+ return to_enum(:zscan_each, key, **options) unless block_given?
2766
+
2743
2767
  cursor = 0
2744
2768
  loop do
2745
- cursor, values = zscan(key, cursor, options)
2769
+ cursor, values = zscan(key, cursor, **options)
2746
2770
  values.each(&block)
2747
2771
  break if cursor == "0"
2748
2772
  end
@@ -2759,8 +2783,8 @@ class Redis
2759
2783
  # - `:count => Integer`: return count keys at most per iteration
2760
2784
  #
2761
2785
  # @return [String, Array<String>] the next cursor and all found members
2762
- def sscan(key, cursor, options={})
2763
- _scan(:sscan, cursor, [key], options)
2786
+ def sscan(key, cursor, **options)
2787
+ _scan(:sscan, cursor, [key], **options)
2764
2788
  end
2765
2789
 
2766
2790
  # Scan a set
@@ -2774,11 +2798,12 @@ class Redis
2774
2798
  # - `:count => Integer`: return count keys at most per iteration
2775
2799
  #
2776
2800
  # @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?
2801
+ def sscan_each(key, **options, &block)
2802
+ return to_enum(:sscan_each, key, **options) unless block_given?
2803
+
2779
2804
  cursor = 0
2780
2805
  loop do
2781
- cursor, keys = sscan(key, cursor, options)
2806
+ cursor, keys = sscan(key, cursor, **options)
2782
2807
  keys.each(&block)
2783
2808
  break if cursor == "0"
2784
2809
  end
@@ -2842,12 +2867,12 @@ class Redis
2842
2867
  end
2843
2868
  end
2844
2869
 
2845
-
2846
2870
  # Query a sorted set representing a geospatial index to fetch members matching a
2847
2871
  # given maximum distance from a point
2848
2872
  #
2849
2873
  # @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
2874
+ # @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest
2875
+ # or the farthest to the nearest relative to the center
2851
2876
  # @param [Integer] count limit the results to the first N matching items
2852
2877
  # @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
2853
2878
  # @return [Array<String>] may be changed with `options`
@@ -2864,7 +2889,8 @@ class Redis
2864
2889
  # given maximum distance from an already existing member
2865
2890
  #
2866
2891
  # @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
2892
+ # @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest or the farthest
2893
+ # to the nearest relative to the center
2868
2894
  # @param [Integer] count limit the results to the first N matching items
2869
2895
  # @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
2870
2896
  # @return [Array<String>] may be changed with `options`
@@ -2881,7 +2907,8 @@ class Redis
2881
2907
  #
2882
2908
  # @param [String] key
2883
2909
  # @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
2910
+ # @return [Array<Array<String>, nil>] returns array of elements, where each
2911
+ # element is either array of longitude and latitude or nil
2885
2912
  def geopos(key, member)
2886
2913
  synchronize do |client|
2887
2914
  client.call([:geopos, key, member])
@@ -2945,10 +2972,14 @@ class Redis
2945
2972
  # @option opts [Boolean] :approximate whether to add `~` modifier of maxlen or not
2946
2973
  #
2947
2974
  # @return [String] the entry id
2948
- def xadd(key, entry, opts = {})
2975
+ def xadd(key, entry, approximate: nil, maxlen: nil, id: '*')
2949
2976
  args = [:xadd, key]
2950
- args.concat(['MAXLEN', (opts[:approximate] ? '~' : nil), opts[:maxlen]].compact) if opts[:maxlen]
2951
- args << (opts[:id] || '*')
2977
+ if maxlen
2978
+ args << "MAXLEN"
2979
+ args << "~" if approximate
2980
+ args << maxlen
2981
+ end
2982
+ args << id
2952
2983
  args.concat(entry.to_a.flatten)
2953
2984
  synchronize { |client| client.call(args) }
2954
2985
  end
@@ -3003,8 +3034,8 @@ class Redis
3003
3034
  # @param count [Integer] the number of entries as limit
3004
3035
  #
3005
3036
  # @return [Array<Array<String, Hash>>] the ids and entries pairs
3006
- def xrange(key, start = '-', _end = '+', count: nil)
3007
- args = [:xrange, key, start, _end]
3037
+ def xrange(key, start = '-', range_end = '+', count: nil)
3038
+ args = [:xrange, key, start, range_end]
3008
3039
  args.concat(['COUNT', count]) if count
3009
3040
  synchronize { |client| client.call(args, &HashifyStreamEntries) }
3010
3041
  end
@@ -3026,8 +3057,8 @@ class Redis
3026
3057
  # @params count [Integer] the number of entries as limit
3027
3058
  #
3028
3059
  # @return [Array<Array<String, Hash>>] the ids and entries pairs
3029
- def xrevrange(key, _end = '+', start = '-', count: nil)
3030
- args = [:xrevrange, key, _end, start]
3060
+ def xrevrange(key, range_end = '+', start = '-', count: nil)
3061
+ args = [:xrevrange, key, range_end, start]
3031
3062
  args.concat(['COUNT', count]) if count
3032
3063
  synchronize { |client| client.call(args, &HashifyStreamEntries) }
3033
3064
  end
@@ -3119,12 +3150,12 @@ class Redis
3119
3150
  # @option opts [Boolean] :noack whether message loss is acceptable or not
3120
3151
  #
3121
3152
  # @return [Hash{String => Hash{String => Hash}}] the entries
3122
- def xreadgroup(group, consumer, keys, ids, opts = {})
3153
+ def xreadgroup(group, consumer, keys, ids, count: nil, block: nil, noack: nil)
3123
3154
  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])
3155
+ args << 'COUNT' << count if count
3156
+ args << 'BLOCK' << block.to_i if block
3157
+ args << 'NOACK' if noack
3158
+ _xread(args, keys, ids, block)
3128
3159
  end
3129
3160
 
3130
3161
  # Removes one or multiple entries from the pending entries list of a stream consumer group.
@@ -3234,8 +3265,8 @@ class Redis
3234
3265
  when "get-master-addr-by-name"
3235
3266
  reply
3236
3267
  else
3237
- if reply.kind_of?(Array)
3238
- if reply[0].kind_of?(Array)
3268
+ if reply.is_a?(Array)
3269
+ if reply[0].is_a?(Array)
3239
3270
  reply.map(&Hashify)
3240
3271
  else
3241
3272
  Hashify.call(reply)
@@ -3259,12 +3290,17 @@ class Redis
3259
3290
  def cluster(subcommand, *args)
3260
3291
  subcommand = subcommand.to_s.downcase
3261
3292
  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
3293
+ when 'slots'
3294
+ HashifyClusterSlots
3295
+ when 'nodes'
3296
+ HashifyClusterNodes
3297
+ when 'slaves'
3298
+ HashifyClusterSlaves
3299
+ when 'info'
3300
+ HashifyInfo
3301
+ else
3302
+ Noop
3303
+ end
3268
3304
 
3269
3305
  # @see https://github.com/antirez/redis/blob/unstable/src/redis-trib.rb#L127 raw reply expected
3270
3306
  block = Noop unless @cluster_mode
@@ -3299,21 +3335,21 @@ class Redis
3299
3335
  return @original_client.connection_info if @cluster_mode
3300
3336
 
3301
3337
  {
3302
- host: @original_client.host,
3303
- port: @original_client.port,
3304
- db: @original_client.db,
3305
- id: @original_client.id,
3338
+ host: @original_client.host,
3339
+ port: @original_client.port,
3340
+ db: @original_client.db,
3341
+ id: @original_client.id,
3306
3342
  location: @original_client.location
3307
3343
  }
3308
3344
  end
3309
3345
 
3310
- def method_missing(command, *args)
3346
+ def method_missing(command, *args) # rubocop:disable Style/MissingRespondToMissing
3311
3347
  synchronize do |client|
3312
3348
  client.call([command] + args)
3313
3349
  end
3314
3350
  end
3315
3351
 
3316
- private
3352
+ private
3317
3353
 
3318
3354
  # Commands returning 1 for true and 0 for false may be executed in a pipeline
3319
3355
  # where the method call will return nil. Propagate the nil instead of falsely
@@ -3385,18 +3421,21 @@ private
3385
3421
  end
3386
3422
  }
3387
3423
 
3424
+ EMPTY_STREAM_RESPONSE = [nil].freeze
3425
+ private_constant :EMPTY_STREAM_RESPONSE
3426
+
3388
3427
  HashifyStreamEntries = lambda { |reply|
3389
- reply.map do |entry_id, values|
3428
+ reply.compact.map do |entry_id, values|
3390
3429
  [entry_id, values.each_slice(2).to_h]
3391
3430
  end
3392
3431
  }
3393
3432
 
3394
3433
  HashifyStreamPendings = lambda { |reply|
3395
3434
  {
3396
- 'size' => reply[0],
3435
+ 'size' => reply[0],
3397
3436
  'min_entry_id' => reply[1],
3398
3437
  'max_entry_id' => reply[2],
3399
- 'consumers' => reply[3].nil? ? {} : reply[3].to_h
3438
+ 'consumers' => reply[3].nil? ? {} : reply[3].to_h
3400
3439
  }
3401
3440
  }
3402
3441
 
@@ -3405,8 +3444,8 @@ private
3405
3444
  {
3406
3445
  'entry_id' => arr[0],
3407
3446
  'consumer' => arr[1],
3408
- 'elapsed' => arr[2],
3409
- 'count' => arr[3]
3447
+ 'elapsed' => arr[2],
3448
+ 'count' => arr[3]
3410
3449
  }
3411
3450
  end
3412
3451
  }
@@ -3414,15 +3453,15 @@ private
3414
3453
  HashifyClusterNodeInfo = lambda { |str|
3415
3454
  arr = str.split(' ')
3416
3455
  {
3417
- 'node_id' => arr[0],
3418
- 'ip_port' => arr[1],
3419
- 'flags' => arr[2].split(','),
3456
+ 'node_id' => arr[0],
3457
+ 'ip_port' => arr[1],
3458
+ 'flags' => arr[2].split(','),
3420
3459
  '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('-'))
3460
+ 'ping_sent' => arr[4],
3461
+ 'pong_recv' => arr[5],
3462
+ 'config_epoch' => arr[6],
3463
+ 'link_state' => arr[7],
3464
+ 'slots' => arr[8].nil? ? nil : Range.new(*arr[8].split('-'))
3426
3465
  }
3427
3466
  }
3428
3467
 
@@ -3433,9 +3472,9 @@ private
3433
3472
  replicas = arr[3..-1].map { |r| { 'ip' => r[0], 'port' => r[1], 'node_id' => r[2] } }
3434
3473
  {
3435
3474
  'start_slot' => first_slot,
3436
- 'end_slot' => last_slot,
3437
- 'master' => master,
3438
- 'replicas' => replicas
3475
+ 'end_slot' => last_slot,
3476
+ 'master' => master,
3477
+ 'replicas' => replicas
3439
3478
  }
3440
3479
  end
3441
3480
  }