redis 4.2.5 → 4.4.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: 862e8133262fead707e4ca317b29d0e35425e3a7861ea1a52033bc91ba61bf6d
4
- data.tar.gz: 5b2b32250d783fe58b0af54cc386f2568241644a0badc0b7247bb29b1ac94a93
3
+ metadata.gz: d5ff2ee4b6a6f2b087ac26bf96a3c1769cf42f70ea90008361157f7ef04cdb14
4
+ data.tar.gz: f2c24654294c4fa81a5cff4ddb545bc59e6f85b64ee61273278cb1e286a4edb8
5
5
  SHA512:
6
- metadata.gz: 2aab289f4f22b2f3a804ca7b0da4cf95e352a8d246611490d8d803edebbbc7e7c299355cd0b90e6f3ac8bc0d11e9bf3792a328c95847d32e1d984729afe66ed2
7
- data.tar.gz: d9ec8ba4d314d099e909cdddf6dfb4c3c14b853b46f45c4909ac3ba10fb1880bd9a7b0465257fa91f9a4ea6c9f82723c16c177810ac15c89fab3790d7af31ad0
6
+ metadata.gz: 0d88c6621659a178dca04d3ca62f25520f5f1a846b6300f93fb008966551fb4795a3e6c37810f99ea18f77a6292b955fa180ae3e06f8c90df7e6a3ca27087ae1
7
+ data.tar.gz: 1427cc268e867872388f214184d23a5c6062104fcb9d1e50d6026dd9daf6e51b9833866fee9fcf80c7a64e4de705295de8399d8e8ac22fa890bf358bd63ff707
data/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # Unreleased
2
2
 
3
+ # 4.4.0
4
+
5
+ * Redis cluster: fix cross-slot validation in pipelines. Fix ##1019.
6
+ * Add support for `XAUTOCLAIM`. See #1018.
7
+ * Properly issue `READONLY` when reconnecting to replicas. Fix #1017.
8
+ * Make `del` a noop if passed an empty list of keys. See #998.
9
+ * Add support for `ZINTER`. See #995.
10
+
11
+ # 4.3.1
12
+
13
+ * Fix password authentication against redis server 5 and older.
14
+
15
+ # 4.3.0
16
+
17
+ * Add the TYPE argument to scan and scan_each. See #985.
18
+ * Support AUTH command for ACL. See #967.
19
+
3
20
  # 4.2.5
4
21
 
5
22
  * Optimize the ruby connector write buffering. See #964.
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # redis-rb [![Build Status][travis-image]][travis-link] [![Inline docs][inchpages-image]][inchpages-link] ![](https://github.com/redis/redis-rb/workflows/Test/badge.svg?branch=master)
1
+ # redis-rb [![Build Status][gh-actions-image]][gh-actions-link] [![Inline docs][inchpages-image]][inchpages-link]
2
2
 
3
3
  A Ruby client that tries to match [Redis][redis-home]' API one-to-one, while still
4
4
  providing an idiomatic interface.
@@ -54,6 +54,12 @@ To connect to a password protected Redis instance, use:
54
54
  redis = Redis.new(password: "mysecret")
55
55
  ```
56
56
 
57
+ To connect a Redis instance using [ACL](https://redis.io/topics/acl), use:
58
+
59
+ ```ruby
60
+ redis = Redis.new(username: 'myname', password: 'mysecret')
61
+ ```
62
+
57
63
  The Redis class exports methods that are named identical to the commands
58
64
  they execute. The arguments these methods accept are often identical to
59
65
  the arguments specified on the [Redis website][redis-commands]. For
@@ -440,7 +446,7 @@ redis = Redis.new(:driver => :synchrony)
440
446
  ## Testing
441
447
 
442
448
  This library is tested against recent Ruby and Redis versions.
443
- Check [Travis][travis-link] for the exact versions supported.
449
+ Check [Github Actions][gh-actions-link] for the exact versions supported.
444
450
 
445
451
  ## See Also
446
452
 
@@ -459,12 +465,11 @@ client and evangelized Redis in Rubyland. Thank you, Ezra.
459
465
  requests.
460
466
 
461
467
 
462
- [inchpages-image]: https://inch-ci.org/github/redis/redis-rb.svg
463
- [inchpages-link]: https://inch-ci.org/github/redis/redis-rb
464
- [redis-commands]: https://redis.io/commands
465
- [redis-home]: https://redis.io
466
- [redis-url]: http://www.iana.org/assignments/uri-schemes/prov/redis
467
- [travis-home]: https://travis-ci.org/
468
- [travis-image]: https://secure.travis-ci.org/redis/redis-rb.svg?branch=master
469
- [travis-link]: https://travis-ci.org/redis/redis-rb
470
- [rubydoc]: http://www.rubydoc.info/gems/redis
468
+ [inchpages-image]: https://inch-ci.org/github/redis/redis-rb.svg
469
+ [inchpages-link]: https://inch-ci.org/github/redis/redis-rb
470
+ [redis-commands]: https://redis.io/commands
471
+ [redis-home]: https://redis.io
472
+ [redis-url]: http://www.iana.org/assignments/uri-schemes/prov/redis
473
+ [gh-actions-image]: https://github.com/redis/redis-rb/workflows/Test/badge.svg
474
+ [gh-actions-link]: https://github.com/redis/redis-rb/actions
475
+ [rubydoc]: http://www.rubydoc.info/gems/redis
data/lib/redis/client.rb CHANGED
@@ -17,6 +17,7 @@ class Redis
17
17
  write_timeout: nil,
18
18
  connect_timeout: nil,
19
19
  timeout: 5.0,
20
+ username: nil,
20
21
  password: nil,
21
22
  db: 0,
22
23
  driver: nil,
@@ -61,6 +62,10 @@ class Redis
61
62
  @options[:read_timeout]
62
63
  end
63
64
 
65
+ def username
66
+ @options[:username]
67
+ end
68
+
64
69
  def password
65
70
  @options[:password]
66
71
  end
@@ -110,7 +115,19 @@ class Redis
110
115
  # Don't try to reconnect when the connection is fresh
111
116
  with_reconnect(false) do
112
117
  establish_connection
113
- call [:auth, password] if password
118
+ if password
119
+ if username
120
+ begin
121
+ call [:auth, username, password]
122
+ rescue CommandError # Likely on Redis < 6
123
+ call [:auth, password]
124
+ end
125
+ else
126
+ call [:auth, password]
127
+ end
128
+ end
129
+
130
+ call [:readonly] if @options[:readonly]
114
131
  call [:select, db] if db != 0
115
132
  call [:client, :setname, @options[:id]] if @options[:id]
116
133
  @connector.check(self)
@@ -131,7 +148,7 @@ class Redis
131
148
  reply = process([command]) { read }
132
149
  raise reply if reply.is_a?(CommandError)
133
150
 
134
- if block_given?
151
+ if block_given? && reply != 'QUEUED'
135
152
  yield reply
136
153
  else
137
154
  reply
@@ -434,7 +451,8 @@ class Redis
434
451
  defaults[:scheme] = uri.scheme
435
452
  defaults[:host] = uri.host if uri.host
436
453
  defaults[:port] = uri.port if uri.port
437
- defaults[:password] = CGI.unescape(uri.password) if uri.password
454
+ defaults[:username] = CGI.unescape(uri.user) if uri.user && !uri.user.empty?
455
+ defaults[:password] = CGI.unescape(uri.password) if uri.password && !uri.password.empty?
438
456
  defaults[:db] = uri.path[1..-1].to_i if uri.path
439
457
  defaults[:role] = :master
440
458
  else
@@ -510,7 +528,7 @@ class Redis
510
528
  require_relative "connection/#{driver}"
511
529
  rescue LoadError, NameError
512
530
  begin
513
- require "connection/#{driver}"
531
+ require "redis/connection/#{driver}"
514
532
  rescue LoadError, NameError => error
515
533
  raise "Cannot load driver #{driver.inspect}: #{error.message}"
516
534
  end
@@ -579,6 +597,7 @@ class Redis
579
597
  client = Client.new(@options.merge({
580
598
  host: sentinel[:host] || sentinel["host"],
581
599
  port: sentinel[:port] || sentinel["port"],
600
+ username: sentinel[:username] || sentinel["username"],
582
601
  password: sentinel[:password] || sentinel["password"],
583
602
  reconnect_attempts: 0
584
603
  }))
@@ -76,8 +76,9 @@ class Redis
76
76
  clients = options.map do |node_key, option|
77
77
  next if replica_disabled? && slave?(node_key)
78
78
 
79
+ option = option.merge(readonly: true) if slave?(node_key)
80
+
79
81
  client = Client.new(option)
80
- client.call(%i[readonly]) if slave?(node_key)
81
82
  [node_key, client]
82
83
  end
83
84
 
@@ -18,6 +18,7 @@ class Redis
18
18
  @node_opts = build_node_options(node_addrs)
19
19
  @replica = options.delete(:replica) == true
20
20
  add_common_node_option_if_needed(options, @node_opts, :scheme)
21
+ add_common_node_option_if_needed(options, @node_opts, :username)
21
22
  add_common_node_option_if_needed(options, @node_opts, :password)
22
23
  @options = options
23
24
  end
@@ -63,7 +64,9 @@ class Redis
63
64
  raise InvalidClientOptionError, "Invalid uri scheme #{addr}" unless VALID_SCHEMES.include?(uri.scheme)
64
65
 
65
66
  db = uri.path.split('/')[1]&.to_i
66
- { scheme: uri.scheme, password: uri.password, host: uri.host, port: uri.port, db: db }.reject { |_, v| v.nil? }
67
+
68
+ { scheme: uri.scheme, username: uri.user, password: uri.password, host: uri.host, port: uri.port, db: db }
69
+ .reject { |_, v| v.nil? || v == '' }
67
70
  rescue URI::InvalidURIError => err
68
71
  raise InvalidClientOptionError, err.message
69
72
  end
@@ -79,7 +82,7 @@ class Redis
79
82
 
80
83
  # Redis cluster node returns only host and port information.
81
84
  # So we should complement additional information such as:
82
- # scheme, password and so on.
85
+ # scheme, username, password and so on.
83
86
  def add_common_node_option_if_needed(options, node_opts, key)
84
87
  return options if options[key].nil? && node_opts.first[key].nil?
85
88
 
data/lib/redis/cluster.rb CHANGED
@@ -78,11 +78,13 @@ class Redis
78
78
  end
79
79
 
80
80
  def call_pipeline(pipeline)
81
- node_keys, command_keys = extract_keys_in_pipeline(pipeline)
82
- raise CrossSlotPipeliningError, command_keys if node_keys.size > 1
81
+ node_keys = pipeline.commands.map { |cmd| find_node_key(cmd, primary_only: true) }.compact.uniq
82
+ if node_keys.size > 1
83
+ raise(CrossSlotPipeliningError,
84
+ pipeline.commands.map { |cmd| @command.extract_first_key(cmd) }.reject(&:empty?).uniq)
85
+ end
83
86
 
84
- node = find_node(node_keys.first)
85
- try_send(node, :call_pipeline, pipeline)
87
+ try_send(find_node(node_keys.first), :call_pipeline, pipeline)
86
88
  end
87
89
 
88
90
  def call_with_timeout(command, timeout, &block)
@@ -128,7 +130,7 @@ class Redis
128
130
  def send_command(command, &block)
129
131
  cmd = command.first.to_s.downcase
130
132
  case cmd
131
- when 'auth', 'bgrewriteaof', 'bgsave', 'quit', 'save'
133
+ when 'acl', 'auth', 'bgrewriteaof', 'bgsave', 'quit', 'save'
132
134
  @node.call_all(command, &block).first
133
135
  when 'flushall', 'flushdb'
134
136
  @node.call_master(command, &block).first
@@ -253,14 +255,14 @@ class Redis
253
255
  find_node(node_key)
254
256
  end
255
257
 
256
- def find_node_key(command)
258
+ def find_node_key(command, primary_only: false)
257
259
  key = @command.extract_first_key(command)
258
260
  return if key.empty?
259
261
 
260
262
  slot = KeySlotConverter.convert(key)
261
263
  return unless @slot.exists?(slot)
262
264
 
263
- if @command.should_send_to_master?(command)
265
+ if @command.should_send_to_master?(command) || primary_only
264
266
  @slot.find_node_key_of_master(slot)
265
267
  else
266
268
  @slot.find_node_key_of_slave(slot)
@@ -285,11 +287,5 @@ class Redis
285
287
  @node.map(&:disconnect)
286
288
  @node, @slot = fetch_cluster_info!(@option)
287
289
  end
288
-
289
- def extract_keys_in_pipeline(pipeline)
290
- node_keys = pipeline.commands.map { |cmd| find_node_key(cmd) }.compact.uniq
291
- command_keys = pipeline.commands.map { |cmd| @command.extract_first_key(cmd) }.reject(&:empty?)
292
- [node_keys, command_keys]
293
- end
294
290
  end
295
291
  end
@@ -413,14 +413,14 @@ class Redis
413
413
  node_for(key).rpushx(key, value)
414
414
  end
415
415
 
416
- # Remove and get the first element in a list.
417
- def lpop(key)
418
- node_for(key).lpop(key)
416
+ # Remove and get the first elements in a list.
417
+ def lpop(key, count = nil)
418
+ node_for(key).lpop(key, count)
419
419
  end
420
420
 
421
- # Remove and get the last element in a list.
422
- def rpop(key)
423
- node_for(key).rpop(key)
421
+ # Remove and get the last elements in a list.
422
+ def rpop(key, count = nil)
423
+ node_for(key).rpop(key, count)
424
424
  end
425
425
 
426
426
  # Remove the last element in a list, append it to another list and return
@@ -674,6 +674,13 @@ class Redis
674
674
  node_for(key).zcount(key, min, max)
675
675
  end
676
676
 
677
+ # Get the intersection of multiple sorted sets
678
+ def zinter(*keys, **options)
679
+ ensure_same_node(:zinter, keys) do |node|
680
+ node.zinter(*keys, **options)
681
+ end
682
+ end
683
+
677
684
  # Intersect multiple sorted sets and store the resulting sorted set in a new
678
685
  # key.
679
686
  def zinterstore(destination, keys, **options)
data/lib/redis/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Redis
4
- VERSION = '4.2.5'
4
+ VERSION = '4.4.0'
5
5
  end
data/lib/redis.rb CHANGED
@@ -39,6 +39,7 @@ class Redis
39
39
  # @option options [String] :path path to server socket (overrides host and port)
40
40
  # @option options [Float] :timeout (5.0) timeout in seconds
41
41
  # @option options [Float] :connect_timeout (same as timeout) timeout for initial connect in seconds
42
+ # @option options [String] :username Username to authenticate against server
42
43
  # @option options [String] :password Password to authenticate against server
43
44
  # @option options [Integer] :db (0) Database to select after initial connect
44
45
  # @option options [Symbol] :driver Driver to use, currently supported: `:ruby`, `:hiredis`, `:synchrony`
@@ -143,12 +144,13 @@ class Redis
143
144
 
144
145
  # Authenticate to the server.
145
146
  #
146
- # @param [String] password must match the password specified in the
147
- # `requirepass` directive in the configuration file
147
+ # @param [Array<String>] args includes both username and password
148
+ # or only password
148
149
  # @return [String] `OK`
149
- def auth(password)
150
+ # @see https://redis.io/commands/auth AUTH command
151
+ def auth(*args)
150
152
  synchronize do |client|
151
- client.call([:auth, password])
153
+ client.call([:auth, *args])
152
154
  end
153
155
  end
154
156
 
@@ -553,6 +555,9 @@ class Redis
553
555
  # @param [String, Array<String>] keys
554
556
  # @return [Integer] number of keys that were deleted
555
557
  def del(*keys)
558
+ keys.flatten!(1)
559
+ return 0 if keys.empty?
560
+
556
561
  synchronize do |client|
557
562
  client.call([:del] + keys)
558
563
  end
@@ -1170,23 +1175,29 @@ class Redis
1170
1175
  end
1171
1176
  end
1172
1177
 
1173
- # Remove and get the first element in a list.
1178
+ # Remove and get the first elements in a list.
1174
1179
  #
1175
1180
  # @param [String] key
1176
- # @return [String]
1177
- def lpop(key)
1181
+ # @param [Integer] count number of elements to remove
1182
+ # @return [String, Array<String>] the values of the first elements
1183
+ def lpop(key, count = nil)
1178
1184
  synchronize do |client|
1179
- client.call([:lpop, key])
1185
+ command = [:lpop, key]
1186
+ command << count if count
1187
+ client.call(command)
1180
1188
  end
1181
1189
  end
1182
1190
 
1183
- # Remove and get the last element in a list.
1191
+ # Remove and get the last elements in a list.
1184
1192
  #
1185
1193
  # @param [String] key
1186
- # @return [String]
1187
- def rpop(key)
1194
+ # @param [Integer] count number of elements to remove
1195
+ # @return [String, Array<String>] the values of the last elements
1196
+ def rpop(key, count = nil)
1188
1197
  synchronize do |client|
1189
- client.call([:rpop, key])
1198
+ command = [:rpop, key]
1199
+ command << count if count
1200
+ client.call(command)
1190
1201
  end
1191
1202
  end
1192
1203
 
@@ -2054,6 +2065,45 @@ class Redis
2054
2065
  end
2055
2066
  end
2056
2067
 
2068
+ # Return the intersection of multiple sorted sets
2069
+ #
2070
+ # @example Retrieve the intersection of `2*zsetA` and `1*zsetB`
2071
+ # redis.zinter("zsetA", "zsetB", :weights => [2.0, 1.0])
2072
+ # # => ["v1", "v2"]
2073
+ # @example Retrieve the intersection of `2*zsetA` and `1*zsetB`, and their scores
2074
+ # redis.zinter("zsetA", "zsetB", :weights => [2.0, 1.0], :with_scores => true)
2075
+ # # => [["v1", 3.0], ["v2", 6.0]]
2076
+ #
2077
+ # @param [String, Array<String>] keys one or more keys to intersect
2078
+ # @param [Hash] options
2079
+ # - `:weights => [Float, Float, ...]`: weights to associate with source
2080
+ # sorted sets
2081
+ # - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
2082
+ # - `:with_scores => true`: include scores in output
2083
+ #
2084
+ # @return [Array<String>, Array<[String, Float]>]
2085
+ # - when `:with_scores` is not specified, an array of members
2086
+ # - when `:with_scores` is specified, an array with `[member, score]` pairs
2087
+ def zinter(*keys, weights: nil, aggregate: nil, with_scores: false)
2088
+ args = [:zinter, keys.size, *keys]
2089
+
2090
+ if weights
2091
+ args << "WEIGHTS"
2092
+ args.concat(weights)
2093
+ end
2094
+
2095
+ args << "AGGREGATE" << aggregate if aggregate
2096
+
2097
+ if with_scores
2098
+ args << "WITHSCORES"
2099
+ block = FloatifyPairs
2100
+ end
2101
+
2102
+ synchronize do |client|
2103
+ client.call(args, &block)
2104
+ end
2105
+ end
2106
+
2057
2107
  # Intersect multiple sorted sets and store the resulting sorted set in a new
2058
2108
  # key.
2059
2109
  #
@@ -2636,12 +2686,13 @@ class Redis
2636
2686
  _eval(:evalsha, args)
2637
2687
  end
2638
2688
 
2639
- def _scan(command, cursor, args, match: nil, count: nil, &block)
2689
+ def _scan(command, cursor, args, match: nil, count: nil, type: nil, &block)
2640
2690
  # SSCAN/ZSCAN/HSCAN already prepend the key to +args+.
2641
2691
 
2642
2692
  args << cursor
2643
2693
  args << "MATCH" << match if match
2644
2694
  args << "COUNT" << count if count
2695
+ args << "TYPE" << type if type
2645
2696
 
2646
2697
  synchronize do |client|
2647
2698
  client.call([command] + args, &block)
@@ -2656,11 +2707,15 @@ class Redis
2656
2707
  # @example Retrieve a batch of keys matching a pattern
2657
2708
  # redis.scan(4, :match => "key:1?")
2658
2709
  # # => ["92", ["key:13", "key:18"]]
2710
+ # @example Retrieve a batch of keys of a certain type
2711
+ # redis.scan(92, :type => "zset")
2712
+ # # => ["173", ["sortedset:14", "sortedset:78"]]
2659
2713
  #
2660
2714
  # @param [String, Integer] cursor the cursor of the iteration
2661
2715
  # @param [Hash] options
2662
2716
  # - `:match => String`: only return keys matching the pattern
2663
2717
  # - `:count => Integer`: return count keys at most per iteration
2718
+ # - `:type => String`: return keys only of the given type
2664
2719
  #
2665
2720
  # @return [String, Array<String>] the next cursor and all found keys
2666
2721
  def scan(cursor, **options)
@@ -2676,10 +2731,15 @@ class Redis
2676
2731
  # redis.scan_each(:match => "key:1?") {|key| puts key}
2677
2732
  # # => key:13
2678
2733
  # # => key:18
2734
+ # @example Execute block for each key of a type
2735
+ # redis.scan_each(:type => "hash") {|key| puts redis.type(key)}
2736
+ # # => "hash"
2737
+ # # => "hash"
2679
2738
  #
2680
2739
  # @param [Hash] options
2681
2740
  # - `:match => String`: only return keys matching the pattern
2682
2741
  # - `:count => Integer`: return count keys at most per iteration
2742
+ # - `:type => String`: return keys only of the given type
2683
2743
  #
2684
2744
  # @return [Enumerator] an enumerator for all found keys
2685
2745
  def scan_each(**options, &block)
@@ -3220,6 +3280,38 @@ class Redis
3220
3280
  synchronize { |client| client.call(args, &blk) }
3221
3281
  end
3222
3282
 
3283
+ # Transfers ownership of pending stream entries that match the specified criteria.
3284
+ #
3285
+ # @example Claim next pending message stuck > 5 minutes and mark as retry
3286
+ # redis.xautoclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0')
3287
+ # @example Claim 50 next pending messages stuck > 5 minutes and mark as retry
3288
+ # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0', count: 50)
3289
+ # @example Claim next pending message stuck > 5 minutes and don't mark as retry
3290
+ # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0', justid: true)
3291
+ # @example Claim next pending message after this id stuck > 5 minutes and mark as retry
3292
+ # redis.xautoclaim('mystream', 'mygroup', 'consumer1', 3600000, '1641321233-0')
3293
+ #
3294
+ # @param key [String] the stream key
3295
+ # @param group [String] the consumer group name
3296
+ # @param consumer [String] the consumer name
3297
+ # @param min_idle_time [Integer] the number of milliseconds
3298
+ # @param start [String] entry id to start scanning from or 0-0 for everything
3299
+ # @param count [Integer] number of messages to claim (default 1)
3300
+ # @param justid [Boolean] whether to fetch just an array of entry ids or not.
3301
+ # Does not increment retry count when true
3302
+ #
3303
+ # @return [Hash{String => Hash}] the entries successfully claimed
3304
+ # @return [Array<String>] the entry ids successfully claimed if justid option is `true`
3305
+ def xautoclaim(key, group, consumer, min_idle_time, start, count: nil, justid: false)
3306
+ args = [:xautoclaim, key, group, consumer, min_idle_time, start]
3307
+ if count
3308
+ args << 'COUNT' << count.to_s
3309
+ end
3310
+ args << 'JUSTID' if justid
3311
+ blk = justid ? HashifyStreamAutoclaimJustId : HashifyStreamAutoclaim
3312
+ synchronize { |client| client.call(args, &blk) }
3313
+ end
3314
+
3223
3315
  # Fetches not acknowledging pending entries
3224
3316
  #
3225
3317
  # @example With key and group
@@ -3430,6 +3522,20 @@ class Redis
3430
3522
  end
3431
3523
  }
3432
3524
 
3525
+ HashifyStreamAutoclaim = lambda { |reply|
3526
+ {
3527
+ 'next' => reply[0],
3528
+ 'entries' => reply[1].map { |entry| [entry[0], entry[1].each_slice(2).to_h] }
3529
+ }
3530
+ }
3531
+
3532
+ HashifyStreamAutoclaimJustId = lambda { |reply|
3533
+ {
3534
+ 'next' => reply[0],
3535
+ 'entries' => reply[1]
3536
+ }
3537
+ }
3538
+
3433
3539
  HashifyStreamPendings = lambda { |reply|
3434
3540
  {
3435
3541
  'size' => reply[0],
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.2.5
4
+ version: 4.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ezra Zygmuntowicz
@@ -16,7 +16,7 @@ authors:
16
16
  autorequire:
17
17
  bindir: bin
18
18
  cert_chain: []
19
- date: 2020-11-20 00:00:00.000000000 Z
19
+ date: 2021-07-28 00:00:00.000000000 Z
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
22
  name: em-synchrony
@@ -102,9 +102,9 @@ licenses:
102
102
  metadata:
103
103
  bug_tracker_uri: https://github.com/redis/redis-rb/issues
104
104
  changelog_uri: https://github.com/redis/redis-rb/blob/master/CHANGELOG.md
105
- documentation_uri: https://www.rubydoc.info/gems/redis/4.2.5
105
+ documentation_uri: https://www.rubydoc.info/gems/redis/4.4.0
106
106
  homepage_uri: https://github.com/redis/redis-rb
107
- source_code_uri: https://github.com/redis/redis-rb/tree/v4.2.5
107
+ source_code_uri: https://github.com/redis/redis-rb/tree/v4.4.0
108
108
  post_install_message:
109
109
  rdoc_options: []
110
110
  require_paths: