redis 5.0.8 → 5.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 68e827844bdac2fa5954e99fd259060a232719927db426788368408b02011eff
4
- data.tar.gz: 46f7f0f74538f29ac4b8f72bc0e2cecaeeea44568174c25d61cf3532869c9f3d
3
+ metadata.gz: 485e29928983863d4725f47cf52718493c36724b1b736951603fb81648fd1ffb
4
+ data.tar.gz: 17663d08f23fe1106e89f3b06805402f5f1ff7b5bcee2e4bb9593cc3329646f7
5
5
  SHA512:
6
- metadata.gz: 996e87442dda9f5750a529b9a14627c371b8e042bc6a7c0a5a32d0c36effa24728d10404a983cea0ef07f19c6e6f1571ac75af1d14a5480021445e23a7eb24e5
7
- data.tar.gz: d36cc39b9d847badfe8d63a94ac74ee588a4fa856581e93f113261156be4b6d6e802b207af6fbe526ff7fb3a00b00f27e4f935a2164da18eb329b6eb007dfb4d
6
+ metadata.gz: 71bed198d46e435677b3ebf725ba65a55af5d5d7a73ff1593d2ac7044972d2898f7c5503fc69d3aa202d8a00781174e0c7348cefa06102c948b9df19cd22d81c
7
+ data.tar.gz: 607288d9a58459346db9c6e79041d35c98cc2c3268c74ffceecd395799833b9ba8e0f1c29c236d6c5afdc917bd66089721fdf5977efcab0799f46cfedb18e826
data/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # Unreleased
2
2
 
3
+ # 5.4.0
4
+
5
+ - Fix `blmpop` method to actually use `BLMPOP`, it was mistakenly issuing `LMPOP` commands.
6
+ - `xadd` now accepts a `minid:` argument.
7
+ - `zrank` and `zrevrank` now accepts `with_score:` argument.
8
+ - `Redis#call` now accept a block, allowing to use `Redis` instances where `RedisClient` is expected.
9
+
10
+ # 5.3.0
11
+
12
+ - Fix the return type of `hgetall` when used inside a `multi` transaction which is itself inside a pipeline.
13
+
14
+ # 5.2.0
15
+
16
+ - Now require Ruby 2.6 because `redis-client` does.
17
+ - Eagerly close subscribed connection when using `subscribe_with_timeout`. See #1259.
18
+ - Add `exception` flag in `pipelined` allowing failed commands to be returned in the result array when set to `false`.
19
+
20
+ # 5.1.0
21
+
22
+ - `multi` now accept a `watch` keyword argument like `redis-client`. See #1236.
23
+ - `bitcount` and `bitpos` now accept a `scale:` argument on Redis 7+. See #1242
24
+ - Added `expiretime` and `pexpiretime`. See #1248.
25
+
3
26
  # 5.0.8
4
27
 
5
28
  - Fix `Redis#without_reconnect` for sentinel clients. Fix #1212.
data/README.md CHANGED
@@ -34,7 +34,7 @@ You can also specify connection options as a [`redis://` URL][redis-url]:
34
34
  redis = Redis.new(url: "redis://:p4ssw0rd@10.0.1.1:6380/15")
35
35
  ```
36
36
 
37
- The client expects passwords with special chracters to be URL-encoded (i.e.
37
+ The client expects passwords with special characters to be URL-encoded (i.e.
38
38
  `CGI.escape(password)`).
39
39
 
40
40
  To connect to Redis listening on a Unix socket, try:
@@ -77,7 +77,7 @@ The client does not provide connection pooling. Each `Redis` instance
77
77
  has one and only one connection to the server, and use of this connection
78
78
  is protected by a mutex.
79
79
 
80
- As such it is heavilly recommended to use the [`connection_pool` gem](https://github.com/mperham/connection_pool), e.g.:
80
+ As such it is heavily recommended to use the [`connection_pool` gem](https://github.com/mperham/connection_pool), e.g.:
81
81
 
82
82
  ```ruby
83
83
  module MyApp
@@ -139,7 +139,7 @@ SENTINELS = [{ host: '127.0.0.1', port: 26380 },
139
139
  redis = Redis.new(name: 'mymaster', sentinels: SENTINELS, role: :master, password: 'mysecret')
140
140
  ```
141
141
 
142
- So you have to provide Sentinel credential and Redis explictly even they are the same
142
+ So you have to provide Sentinel credential and Redis explicitly even they are the same
143
143
 
144
144
  ```ruby
145
145
  # Use 'mysecret' to authenticate against the mymaster instance and sentinel
@@ -191,6 +191,28 @@ end
191
191
  # => ["OK"]
192
192
  ```
193
193
 
194
+ ### Exception management
195
+
196
+ The `exception` flag in the `#pipelined` is a feature that modifies the pipeline execution behavior. When set
197
+ to `false`, it doesn't raise an exception when a command error occurs. Instead, it allows the pipeline to execute all
198
+ commands, and any failed command will be available in the returned array. (Defaults to `true`)
199
+
200
+ ```ruby
201
+ results = redis.pipelined(exception: false) do |pipeline|
202
+ pipeline.set('key1', 'value1')
203
+ pipeline.lpush('key1', 'something') # This will fail
204
+ pipeline.set('key2', 'value2')
205
+ end
206
+ # results => ["OK", #<RedisClient::WrongTypeError: WRONGTYPE Operation against a key holding the wrong kind of value>, "OK"]
207
+
208
+ results.each do |result|
209
+ if result.is_a?(Redis::CommandError)
210
+ # Do something with the failed result
211
+ end
212
+ end
213
+ ```
214
+
215
+
194
216
  ### Executing commands atomically
195
217
 
196
218
  You can use `MULTI/EXEC` to run a number of commands in an atomic
@@ -251,6 +273,7 @@ See lib/redis/errors.rb for information about what exceptions are possible.
251
273
  ## Timeouts
252
274
 
253
275
  The client allows you to configure connect, read, and write timeouts.
276
+ Starting in version 5.0, the default for each is 1. Before that, it was 5.
254
277
  Passing a single `timeout` option will set all three values:
255
278
 
256
279
  ```ruby
@@ -383,7 +406,7 @@ gem "hiredis-client"
383
406
  ```
384
407
 
385
408
  If your application doesn't call `Bundler.require`, you may have
386
- to require it explictly:
409
+ to require it explicitly:
387
410
 
388
411
  ```ruby
389
412
  require "hiredis-client"
data/lib/redis/client.rb CHANGED
@@ -27,18 +27,18 @@ class Redis
27
27
  super(protocol: 2, **kwargs, client_implementation: ::RedisClient)
28
28
  end
29
29
 
30
- def translate_error!(error)
31
- redis_error = translate_error_class(error.class)
30
+ def translate_error!(error, mapping: ERROR_MAPPING)
31
+ redis_error = translate_error_class(error.class, mapping: mapping)
32
32
  raise redis_error, error.message, error.backtrace
33
33
  end
34
34
 
35
35
  private
36
36
 
37
- def translate_error_class(error_class)
38
- ERROR_MAPPING.fetch(error_class)
37
+ def translate_error_class(error_class, mapping: ERROR_MAPPING)
38
+ mapping.fetch(error_class)
39
39
  rescue IndexError
40
- if (client_error = error_class.ancestors.find { |a| ERROR_MAPPING[a] })
41
- ERROR_MAPPING[error_class] = ERROR_MAPPING[client_error]
40
+ if (client_error = error_class.ancestors.find { |a| mapping[a] })
41
+ mapping[error_class] = mapping[client_error]
42
42
  else
43
43
  raise
44
44
  end
@@ -105,13 +105,13 @@ class Redis
105
105
  Client.translate_error!(error)
106
106
  end
107
107
 
108
- def pipelined
108
+ def pipelined(exception: true)
109
109
  super
110
110
  rescue ::RedisClient::Error => error
111
111
  Client.translate_error!(error)
112
112
  end
113
113
 
114
- def multi
114
+ def multi(watch: nil)
115
115
  super
116
116
  rescue ::RedisClient::Error => error
117
117
  Client.translate_error!(error)
@@ -27,9 +27,13 @@ class Redis
27
27
  # @param [String] key
28
28
  # @param [Integer] start start index
29
29
  # @param [Integer] stop stop index
30
+ # @param [String, Symbol] scale the scale of the offset range
31
+ # e.g. 'BYTE' - interpreted as a range of bytes, 'BIT' - interpreted as a range of bits
30
32
  # @return [Integer] the number of bits set to 1
31
- def bitcount(key, start = 0, stop = -1)
32
- send_command([:bitcount, key, start, stop])
33
+ def bitcount(key, start = 0, stop = -1, scale: nil)
34
+ command = [:bitcount, key, start, stop]
35
+ command << scale if scale
36
+ send_command(command)
33
37
  end
34
38
 
35
39
  # Perform a bitwise operation between strings and store the resulting string in a key.
@@ -51,14 +55,17 @@ class Redis
51
55
  # @param [Integer] bit whether to look for the first 1 or 0 bit
52
56
  # @param [Integer] start start index
53
57
  # @param [Integer] stop stop index
58
+ # @param [String, Symbol] scale the scale of the offset range
59
+ # e.g. 'BYTE' - interpreted as a range of bytes, 'BIT' - interpreted as a range of bits
54
60
  # @return [Integer] the position of the first 1/0 bit.
55
61
  # -1 if looking for 1 and it is not found or start and stop are given.
56
- def bitpos(key, bit, start = nil, stop = nil)
62
+ def bitpos(key, bit, start = nil, stop = nil, scale: nil)
57
63
  raise(ArgumentError, 'stop parameter specified without start parameter') if stop && !start
58
64
 
59
65
  command = [:bitpos, key, bit]
60
66
  command << start if start
61
67
  command << stop if stop
68
+ command << scale if scale
62
69
  send_command(command)
63
70
  end
64
71
  end
@@ -222,6 +222,8 @@ class Redis
222
222
  # - `:count => Integer`: return count keys at most per iteration
223
223
  #
224
224
  # @return [String, Array<[String, String]>] the next cursor and all found keys
225
+ #
226
+ # See the [Redis Server HSCAN documentation](https://redis.io/docs/latest/commands/hscan/) for further details
225
227
  def hscan(key, cursor, **options)
226
228
  _scan(:hscan, cursor, [key], **options) do |reply|
227
229
  [reply[0], reply[1].each_slice(2).to_a]
@@ -239,6 +241,8 @@ class Redis
239
241
  # - `:count => Integer`: return count keys at most per iteration
240
242
  #
241
243
  # @return [Enumerator] an enumerator for all found keys
244
+ #
245
+ # See the [Redis Server HSCAN documentation](https://redis.io/docs/latest/commands/hscan/) for further details
242
246
  def hscan_each(key, **options, &block)
243
247
  return to_enum(:hscan_each, key, **options) unless block_given?
244
248
 
@@ -22,6 +22,8 @@ class Redis
22
22
  # - `:type => String`: return keys only of the given type
23
23
  #
24
24
  # @return [String, Array<String>] the next cursor and all found keys
25
+ #
26
+ # See the [Redis Server SCAN documentation](https://redis.io/docs/latest/commands/scan/) for further details
25
27
  def scan(cursor, **options)
26
28
  _scan(:scan, cursor, [], **options)
27
29
  end
@@ -46,6 +48,8 @@ class Redis
46
48
  # - `:type => String`: return keys only of the given type
47
49
  #
48
50
  # @return [Enumerator] an enumerator for all found keys
51
+ #
52
+ # See the [Redis Server SCAN documentation](https://redis.io/docs/latest/commands/scan/) for further details
49
53
  def scan_each(**options, &block)
50
54
  return to_enum(:scan_each, **options) unless block_given?
51
55
 
@@ -105,6 +109,14 @@ class Redis
105
109
  send_command(args, &Boolify)
106
110
  end
107
111
 
112
+ # Get a key's expiry time specified as number of seconds from UNIX Epoch
113
+ #
114
+ # @param [String] key
115
+ # @return [Integer] expiry time specified as number of seconds from UNIX Epoch
116
+ def expiretime(key)
117
+ send_command([:expiretime, key])
118
+ end
119
+
108
120
  # Get the time to live (in seconds) for a key.
109
121
  #
110
122
  # @param [String] key
@@ -161,6 +173,14 @@ class Redis
161
173
  send_command(args, &Boolify)
162
174
  end
163
175
 
176
+ # Get a key's expiry time specified as number of milliseconds from UNIX Epoch
177
+ #
178
+ # @param [String] key
179
+ # @return [Integer] expiry time specified as number of milliseconds from UNIX Epoch
180
+ def pexpiretime(key)
181
+ send_command([:pexpiretime, key])
182
+ end
183
+
164
184
  # Get the time to live (in milliseconds) for a key.
165
185
  #
166
186
  # @param [String] key
@@ -266,6 +286,8 @@ class Redis
266
286
  #
267
287
  # @param [String] pattern
268
288
  # @return [Array<String>]
289
+ #
290
+ # See the [Redis Server KEYS documentation](https://redis.io/docs/latest/commands/keys/) for further details
269
291
  def keys(pattern = "*")
270
292
  send_command([:keys, pattern]) do |reply|
271
293
  if reply.is_a?(String)
@@ -205,7 +205,7 @@ class Redis
205
205
  def blmpop(timeout, *keys, modifier: "LEFT", count: nil)
206
206
  raise ArgumentError, "Pick either LEFT or RIGHT" unless modifier == "LEFT" || modifier == "RIGHT"
207
207
 
208
- args = [:lmpop, keys.size, *keys, modifier]
208
+ args = [:blmpop, timeout, keys.size, *keys, modifier]
209
209
  args << "COUNT" << Integer(count) if count
210
210
 
211
211
  send_blocking_command(args, timeout)
@@ -29,17 +29,23 @@ class Redis
29
29
  end
30
30
 
31
31
  # Listen for messages published to channels matching the given patterns.
32
+ # See the [Redis Server PSUBSCRIBE documentation](https://redis.io/docs/latest/commands/psubscribe/)
33
+ # for further details
32
34
  def psubscribe(*channels, &block)
33
35
  _subscription(:psubscribe, 0, channels, block)
34
36
  end
35
37
 
36
38
  # Listen for messages published to channels matching the given patterns.
37
39
  # Throw a timeout error if there is no messages for a timeout period.
40
+ # See the [Redis Server PSUBSCRIBE documentation](https://redis.io/docs/latest/commands/psubscribe/)
41
+ # for further details
38
42
  def psubscribe_with_timeout(timeout, *channels, &block)
39
43
  _subscription(:psubscribe_with_timeout, timeout, channels, block)
40
44
  end
41
45
 
42
46
  # Stop listening for messages posted to channels matching the given patterns.
47
+ # See the [Redis Server PUNSUBSCRIBE documentation](https://redis.io/docs/latest/commands/punsubscribe/)
48
+ # for further details
43
49
  def punsubscribe(*channels)
44
50
  _subscription(:punsubscribe, 0, channels, nil)
45
51
  end
@@ -184,6 +184,8 @@ class Redis
184
184
  # - `:count => Integer`: return count keys at most per iteration
185
185
  #
186
186
  # @return [String, Array<String>] the next cursor and all found members
187
+ #
188
+ # See the [Redis Server SSCAN documentation](https://redis.io/docs/latest/commands/sscan/) for further details
187
189
  def sscan(key, cursor, **options)
188
190
  _scan(:sscan, cursor, [key], **options)
189
191
  end
@@ -199,6 +201,8 @@ class Redis
199
201
  # - `:count => Integer`: return count keys at most per iteration
200
202
  #
201
203
  # @return [Enumerator] an enumerator for all keys in the set
204
+ #
205
+ # See the [Redis Server SSCAN documentation](https://redis.io/docs/latest/commands/sscan/) for further details
202
206
  def sscan_each(key, **options, &block)
203
207
  return to_enum(:sscan_each, key, **options) unless block_given?
204
208
 
@@ -454,21 +454,55 @@ class Redis
454
454
 
455
455
  # Determine the index of a member in a sorted set.
456
456
  #
457
+ # @example Retrieve member rank
458
+ # redis.zrank("zset", "a")
459
+ # # => 3
460
+ # @example Retrieve member rank with their score
461
+ # redis.zrank("zset", "a", :with_score => true)
462
+ # # => [3, 32.0]
463
+ #
457
464
  # @param [String] key
458
465
  # @param [String] member
459
- # @return [Integer]
460
- def zrank(key, member)
461
- send_command([:zrank, key, member])
466
+ #
467
+ # @return [Integer, [Integer, Float]]
468
+ # - when `:with_score` is not specified, an Integer
469
+ # - when `:with_score` is specified, a `[rank, score]` pair
470
+ def zrank(key, member, withscore: false, with_score: withscore)
471
+ args = [:zrank, key, member]
472
+
473
+ if with_score
474
+ args << "WITHSCORE"
475
+ block = FloatifyPair
476
+ end
477
+
478
+ send_command(args, &block)
462
479
  end
463
480
 
464
481
  # Determine the index of a member in a sorted set, with scores ordered from
465
482
  # high to low.
466
483
  #
484
+ # @example Retrieve member rank
485
+ # redis.zrevrank("zset", "a")
486
+ # # => 3
487
+ # @example Retrieve member rank with their score
488
+ # redis.zrevrank("zset", "a", :with_score => true)
489
+ # # => [3, 32.0]
490
+ #
467
491
  # @param [String] key
468
492
  # @param [String] member
469
- # @return [Integer]
470
- def zrevrank(key, member)
471
- send_command([:zrevrank, key, member])
493
+ #
494
+ # @return [Integer, [Integer, Float]]
495
+ # - when `:with_score` is not specified, an Integer
496
+ # - when `:with_score` is specified, a `[rank, score]` pair
497
+ def zrevrank(key, member, withscore: false, with_score: withscore)
498
+ args = [:zrevrank, key, member]
499
+
500
+ if with_score
501
+ args << "WITHSCORE"
502
+ block = FloatifyPair
503
+ end
504
+
505
+ send_command(args, &block)
472
506
  end
473
507
 
474
508
  # Remove all members in a sorted set within the given indexes.
@@ -817,6 +851,8 @@ class Redis
817
851
  #
818
852
  # @return [String, Array<[String, Float]>] the next cursor and all found
819
853
  # members and scores
854
+ #
855
+ # See the [Redis Server ZSCAN documentation](https://redis.io/docs/latest/commands/zscan/) for further details
820
856
  def zscan(key, cursor, **options)
821
857
  _scan(:zscan, cursor, [key], **options) do |reply|
822
858
  [reply[0], FloatifyPairs.call(reply[1])]
@@ -834,6 +870,8 @@ class Redis
834
870
  # - `:count => Integer`: return count keys at most per iteration
835
871
  #
836
872
  # @return [Enumerator] an enumerator for all found scores and members
873
+ #
874
+ # See the [Redis Server ZSCAN documentation](https://redis.io/docs/latest/commands/zscan/) for further details
837
875
  def zscan_each(key, **options, &block)
838
876
  return to_enum(:zscan_each, key, **options) unless block_given?
839
877
 
@@ -41,18 +41,25 @@ class Redis
41
41
  # @param opts [Hash] several options for `XADD` command
42
42
  #
43
43
  # @option opts [String] :id the entry id, default value is `*`, it means auto generation
44
- # @option opts [Integer] :maxlen max length of entries
45
- # @option opts [Boolean] :approximate whether to add `~` modifier of maxlen or not
44
+ # @option opts [Integer] :maxlen max length of entries to keep
45
+ # @option opts [Integer] :minid min id of entries to keep
46
+ # @option opts [Boolean] :approximate whether to add `~` modifier of maxlen/minid or not
46
47
  # @option opts [Boolean] :nomkstream whether to add NOMKSTREAM, default is not to add
47
48
  #
48
49
  # @return [String] the entry id
49
- def xadd(key, entry, approximate: nil, maxlen: nil, nomkstream: nil, id: '*')
50
+ def xadd(key, entry, approximate: nil, maxlen: nil, minid: nil, nomkstream: nil, id: '*')
50
51
  args = [:xadd, key]
51
52
  args << 'NOMKSTREAM' if nomkstream
52
53
  if maxlen
54
+ raise ArgumentError, "can't supply both maxlen and minid" if minid
55
+
53
56
  args << "MAXLEN"
54
57
  args << "~" if approximate
55
58
  args << maxlen
59
+ elsif minid
60
+ args << "MINID"
61
+ args << "~" if approximate
62
+ args << minid
56
63
  end
57
64
  args << id
58
65
  args.concat(entry.flatten)
@@ -83,12 +83,14 @@ class Redis
83
83
  end
84
84
  }
85
85
 
86
+ FloatifyPair = lambda { |(first, score)|
87
+ [first, Floatify.call(score)]
88
+ }
89
+
86
90
  FloatifyPairs = lambda { |value|
87
91
  return value unless value.respond_to?(:each_slice)
88
92
 
89
- value.each_slice(2).map do |member, score|
90
- [member, Floatify.call(score)]
91
- end
93
+ value.each_slice(2).map(&FloatifyPair)
92
94
  }
93
95
 
94
96
  HashifyInfo = lambda { |reply|
@@ -199,8 +201,8 @@ class Redis
199
201
  # hash, are up to consumers.
200
202
  #
201
203
  # Redis error replies are raised as Ruby exceptions.
202
- def call(*command)
203
- send_command(command)
204
+ def call(*command, &block)
205
+ send_command(command, &block)
204
206
  end
205
207
 
206
208
  # Interact with the sentinel command (masters, master, slaves, failover)
@@ -130,6 +130,11 @@ class Redis
130
130
  node_for(key).expireat(key, unix_time, **kwargs)
131
131
  end
132
132
 
133
+ # Get the expiration for a key as a UNIX timestamp.
134
+ def expiretime(key)
135
+ node_for(key).expiretime(key)
136
+ end
137
+
133
138
  # Get the time to live (in seconds) for a key.
134
139
  def ttl(key)
135
140
  node_for(key).ttl(key)
@@ -145,6 +150,11 @@ class Redis
145
150
  node_for(key).pexpireat(key, ms_unix_time, **kwarg)
146
151
  end
147
152
 
153
+ # Get the expiration for a key as number of milliseconds from UNIX Epoch.
154
+ def pexpiretime(key)
155
+ node_for(key).pexpiretime(key)
156
+ end
157
+
148
158
  # Get the time to live (in milliseconds) for a key.
149
159
  def pttl(key)
150
160
  node_for(key).pttl(key)
@@ -370,8 +380,8 @@ class Redis
370
380
  end
371
381
 
372
382
  # Count the number of set bits in a range of the string value stored at key.
373
- def bitcount(key, start = 0, stop = -1)
374
- node_for(key).bitcount(key, start, stop)
383
+ def bitcount(key, start = 0, stop = -1, scale: nil)
384
+ node_for(key).bitcount(key, start, stop, scale: scale)
375
385
  end
376
386
 
377
387
  # Perform a bitwise operation between strings and store the resulting string in a key.
@@ -383,8 +393,8 @@ class Redis
383
393
  end
384
394
 
385
395
  # Return the position of the first bit set to 1 or 0 in a string.
386
- def bitpos(key, bit, start = nil, stop = nil)
387
- node_for(key).bitpos(key, bit, start, stop)
396
+ def bitpos(key, bit, start = nil, stop = nil, scale: nil)
397
+ node_for(key).bitpos(key, bit, start, stop, scale: scale)
388
398
  end
389
399
 
390
400
  # Set the string value of a key and return its old value.
@@ -742,14 +752,14 @@ class Redis
742
752
  end
743
753
 
744
754
  # Determine the index of a member in a sorted set.
745
- def zrank(key, member)
746
- node_for(key).zrank(key, member)
755
+ def zrank(key, member, **options)
756
+ node_for(key).zrank(key, member, **options)
747
757
  end
748
758
 
749
759
  # Determine the index of a member in a sorted set, with scores ordered from
750
760
  # high to low.
751
- def zrevrank(key, member)
752
- node_for(key).zrevrank(key, member)
761
+ def zrevrank(key, member, **options)
762
+ node_for(key).zrevrank(key, member, **options)
753
763
  end
754
764
 
755
765
  # Remove all members in a sorted set within the given indexes.
@@ -938,12 +948,16 @@ class Redis
938
948
  end
939
949
 
940
950
  # Listen for messages published to channels matching the given patterns.
951
+ # See the [Redis Server PSUBSCRIBE documentation](https://redis.io/docs/latest/commands/psubscribe/)
952
+ # for further details
941
953
  def psubscribe(*channels, &block)
942
954
  raise NotImplementedError
943
955
  end
944
956
 
945
957
  # Stop listening for messages posted to channels matching the given
946
958
  # patterns.
959
+ # See the [Redis Server PUNSUBSCRIBE documentation](https://redis.io/docs/latest/commands/punsubscribe/)
960
+ # for further details
947
961
  def punsubscribe(*channels)
948
962
  raise NotImplementedError
949
963
  end
@@ -6,9 +6,10 @@ class Redis
6
6
  class PipelinedConnection
7
7
  attr_accessor :db
8
8
 
9
- def initialize(pipeline, futures = [])
9
+ def initialize(pipeline, futures = [], exception: true)
10
10
  @pipeline = pipeline
11
11
  @futures = futures
12
+ @exception = exception
12
13
  end
13
14
 
14
15
  include Commands
@@ -37,7 +38,7 @@ class Redis
37
38
  end
38
39
 
39
40
  def send_command(command, &block)
40
- future = Future.new(command, block)
41
+ future = Future.new(command, block, @exception)
41
42
  @pipeline.call_v(command) do |result|
42
43
  future._set(result)
43
44
  end
@@ -46,7 +47,7 @@ class Redis
46
47
  end
47
48
 
48
49
  def send_blocking_command(command, timeout, &block)
49
- future = Future.new(command, block)
50
+ future = Future.new(command, block, @exception)
50
51
  @pipeline.blocking_call_v(timeout, command) do |result|
51
52
  future._set(result)
52
53
  end
@@ -57,7 +58,7 @@ class Redis
57
58
 
58
59
  class MultiConnection < PipelinedConnection
59
60
  def multi
60
- raise Redis::Error, "Can't nest multi transaction"
61
+ raise Redis::BaseError, "Can't nest multi transaction"
61
62
  end
62
63
 
63
64
  private
@@ -79,10 +80,11 @@ class Redis
79
80
  class Future < BasicObject
80
81
  FutureNotReady = ::Redis::FutureNotReady.new
81
82
 
82
- def initialize(command, coerce)
83
+ def initialize(command, coerce, exception)
83
84
  @command = command
84
85
  @object = FutureNotReady
85
86
  @coerce = coerce
87
+ @exception = exception
86
88
  end
87
89
 
88
90
  def inspect
@@ -95,7 +97,7 @@ class Redis
95
97
  end
96
98
 
97
99
  def value
98
- ::Kernel.raise(@object) if @object.is_a?(::StandardError)
100
+ ::Kernel.raise(@object) if @exception && @object.is_a?(::StandardError)
99
101
  @object
100
102
  end
101
103
 
@@ -116,12 +118,14 @@ class Redis
116
118
  end
117
119
 
118
120
  def _set(replies)
119
- if replies
120
- @futures.each_with_index do |future, index|
121
+ @object = if replies
122
+ @futures.map.with_index do |future, index|
121
123
  future._set(replies[index])
124
+ future.value
122
125
  end
126
+ else
127
+ replies
123
128
  end
124
- @object = replies
125
129
  end
126
130
  end
127
131
  end
data/lib/redis/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Redis
4
- VERSION = '5.0.8'
4
+ VERSION = '5.4.0'
5
5
  end
data/lib/redis.rb CHANGED
@@ -99,10 +99,10 @@ class Redis
99
99
  @client
100
100
  end
101
101
 
102
- def pipelined
102
+ def pipelined(exception: true)
103
103
  synchronize do |client|
104
- client.pipelined do |raw_pipeline|
105
- yield PipelinedConnection.new(raw_pipeline)
104
+ client.pipelined(exception: exception) do |raw_pipeline|
105
+ yield PipelinedConnection.new(raw_pipeline, exception: exception)
106
106
  end
107
107
  end
108
108
  end
@@ -175,6 +175,7 @@ class Redis
175
175
  @subscription_client.send(method, *channels, &block)
176
176
  end
177
177
  ensure
178
+ @subscription_client&.close
178
179
  @subscription_client = nil
179
180
  end
180
181
  else
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: 5.0.8
4
+ version: 5.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ezra Zygmuntowicz
@@ -13,10 +13,9 @@ authors:
13
13
  - Michel Martens
14
14
  - Damian Janowski
15
15
  - Pieter Noordhuis
16
- autorequire:
17
16
  bindir: bin
18
17
  cert_chain: []
19
- date: 2023-10-23 00:00:00.000000000 Z
18
+ date: 2025-02-20 00:00:00.000000000 Z
20
19
  dependencies:
21
20
  - !ruby/object:Gem::Dependency
22
21
  name: redis-client
@@ -24,14 +23,14 @@ dependencies:
24
23
  requirements:
25
24
  - - ">="
26
25
  - !ruby/object:Gem::Version
27
- version: 0.17.0
26
+ version: 0.22.0
28
27
  type: :runtime
29
28
  prerelease: false
30
29
  version_requirements: !ruby/object:Gem::Requirement
31
30
  requirements:
32
31
  - - ">="
33
32
  - !ruby/object:Gem::Version
34
- version: 0.17.0
33
+ version: 0.22.0
35
34
  description: |2
36
35
  A Ruby client that tries to match Redis' API one-to-one, while still
37
36
  providing an idiomatic interface.
@@ -75,10 +74,9 @@ licenses:
75
74
  metadata:
76
75
  bug_tracker_uri: https://github.com/redis/redis-rb/issues
77
76
  changelog_uri: https://github.com/redis/redis-rb/blob/master/CHANGELOG.md
78
- documentation_uri: https://www.rubydoc.info/gems/redis/5.0.8
77
+ documentation_uri: https://www.rubydoc.info/gems/redis/5.4.0
79
78
  homepage_uri: https://github.com/redis/redis-rb
80
- source_code_uri: https://github.com/redis/redis-rb/tree/v5.0.8
81
- post_install_message:
79
+ source_code_uri: https://github.com/redis/redis-rb/tree/v5.4.0
82
80
  rdoc_options: []
83
81
  require_paths:
84
82
  - lib
@@ -86,15 +84,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
86
84
  requirements:
87
85
  - - ">="
88
86
  - !ruby/object:Gem::Version
89
- version: 2.5.0
87
+ version: 2.6.0
90
88
  required_rubygems_version: !ruby/object:Gem::Requirement
91
89
  requirements:
92
90
  - - ">="
93
91
  - !ruby/object:Gem::Version
94
92
  version: '0'
95
93
  requirements: []
96
- rubygems_version: 3.3.7
97
- signing_key:
94
+ rubygems_version: 3.6.2
98
95
  specification_version: 4
99
96
  summary: A Ruby client library for Redis
100
97
  test_files: []