redis 4.2.5 → 4.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +32 -0
- data/README.md +16 -11
- data/lib/redis/client.rb +27 -4
- data/lib/redis/cluster/command_loader.rb +6 -7
- data/lib/redis/cluster/node.rb +2 -1
- data/lib/redis/cluster/option.rb +5 -2
- data/lib/redis/cluster.rb +9 -13
- data/lib/redis/connection/command_helper.rb +2 -0
- data/lib/redis/connection/ruby.rb +24 -6
- data/lib/redis/distributed.rb +53 -6
- data/lib/redis/version.rb +1 -1
- data/lib/redis.rb +313 -16
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b1e483240f70aa8a92a7fe1f38968262ea82e84a97e436a9c7c695c032584313
|
4
|
+
data.tar.gz: 1cb164b2d588ef2f2701b91371a12a0d90299a78994f0d614cb14ab0daa36243
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 72c3e91839061ae26e27cbb08330f752acbba5f440c8549cad4325b2c2f517a907a0db11330ca9ba221255aa7b570efe2050bde9cbb905217794c4754c53dadb
|
7
|
+
data.tar.gz: e92cc58111fb9e1553a3363c56569ef6064c3851263d5eb86147c907878650b697db5c598b2b2046f9c9025cc40ca7eda5ce16f4f21049ac49f09385cf80a92f
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,37 @@
|
|
1
1
|
# Unreleased
|
2
2
|
|
3
|
+
# 4.5.0
|
4
|
+
|
5
|
+
* Handle parts of the command using incompatible encodings. See #1037.
|
6
|
+
* Add GET option to SET command. See #1036.
|
7
|
+
* Add ZRANDMEMBER command. See #1035.
|
8
|
+
* Add LMOVE/BLMOVE commands. See #1034.
|
9
|
+
* Add ZMSCORE command. See #1032.
|
10
|
+
* Add LT/GT options to ZADD. See #1033.
|
11
|
+
* Add SMISMEMBER command. See #1031.
|
12
|
+
* Add EXAT/PXAT options to SET. See #1028.
|
13
|
+
* Add GETDEL/GETEX commands. See #1024.
|
14
|
+
* `Redis#exists` now returns an Integer by default, as warned since 4.2.0. The old behavior can be restored with `Redis.exists_returns_integer = false`.
|
15
|
+
* Fix Redis < 6 detection during connect. See #1025.
|
16
|
+
* Fix fetching command details in Redis cluster when the first node is unhealthy. See #1026.
|
17
|
+
|
18
|
+
# 4.4.0
|
19
|
+
|
20
|
+
* Redis cluster: fix cross-slot validation in pipelines. Fix ##1019.
|
21
|
+
* Add support for `XAUTOCLAIM`. See #1018.
|
22
|
+
* Properly issue `READONLY` when reconnecting to replicas. Fix #1017.
|
23
|
+
* Make `del` a noop if passed an empty list of keys. See #998.
|
24
|
+
* Add support for `ZINTER`. See #995.
|
25
|
+
|
26
|
+
# 4.3.1
|
27
|
+
|
28
|
+
* Fix password authentication against redis server 5 and older.
|
29
|
+
|
30
|
+
# 4.3.0
|
31
|
+
|
32
|
+
* Add the TYPE argument to scan and scan_each. See #985.
|
33
|
+
* Support AUTH command for ACL. See #967.
|
34
|
+
|
3
35
|
# 4.2.5
|
4
36
|
|
5
37
|
* Optimize the ruby connector write buffering. See #964.
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# redis-rb [![Build Status][
|
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 [
|
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]:
|
463
|
-
[inchpages-link]:
|
464
|
-
[redis-commands]:
|
465
|
-
[redis-home]:
|
466
|
-
[redis-url]:
|
467
|
-
[
|
468
|
-
[
|
469
|
-
[
|
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,23 @@ 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
|
-
|
118
|
+
if password
|
119
|
+
if username
|
120
|
+
begin
|
121
|
+
call [:auth, username, password]
|
122
|
+
rescue CommandError => err # Likely on Redis < 6
|
123
|
+
if err.message.match?(/ERR wrong number of arguments for \'auth\' command/)
|
124
|
+
call [:auth, password]
|
125
|
+
else
|
126
|
+
raise
|
127
|
+
end
|
128
|
+
end
|
129
|
+
else
|
130
|
+
call [:auth, password]
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
call [:readonly] if @options[:readonly]
|
114
135
|
call [:select, db] if db != 0
|
115
136
|
call [:client, :setname, @options[:id]] if @options[:id]
|
116
137
|
@connector.check(self)
|
@@ -131,7 +152,7 @@ class Redis
|
|
131
152
|
reply = process([command]) { read }
|
132
153
|
raise reply if reply.is_a?(CommandError)
|
133
154
|
|
134
|
-
if block_given?
|
155
|
+
if block_given? && reply != 'QUEUED'
|
135
156
|
yield reply
|
136
157
|
else
|
137
158
|
reply
|
@@ -434,7 +455,8 @@ class Redis
|
|
434
455
|
defaults[:scheme] = uri.scheme
|
435
456
|
defaults[:host] = uri.host if uri.host
|
436
457
|
defaults[:port] = uri.port if uri.port
|
437
|
-
defaults[:
|
458
|
+
defaults[:username] = CGI.unescape(uri.user) if uri.user && !uri.user.empty?
|
459
|
+
defaults[:password] = CGI.unescape(uri.password) if uri.password && !uri.password.empty?
|
438
460
|
defaults[:db] = uri.path[1..-1].to_i if uri.path
|
439
461
|
defaults[:role] = :master
|
440
462
|
else
|
@@ -510,7 +532,7 @@ class Redis
|
|
510
532
|
require_relative "connection/#{driver}"
|
511
533
|
rescue LoadError, NameError
|
512
534
|
begin
|
513
|
-
require "connection/#{driver}"
|
535
|
+
require "redis/connection/#{driver}"
|
514
536
|
rescue LoadError, NameError => error
|
515
537
|
raise "Cannot load driver #{driver.inspect}: #{error.message}"
|
516
538
|
end
|
@@ -579,6 +601,7 @@ class Redis
|
|
579
601
|
client = Client.new(@options.merge({
|
580
602
|
host: sentinel[:host] || sentinel["host"],
|
581
603
|
port: sentinel[:port] || sentinel["port"],
|
604
|
+
username: sentinel[:username] || sentinel["username"],
|
582
605
|
password: sentinel[:password] || sentinel["password"],
|
583
606
|
reconnect_attempts: 0
|
584
607
|
}))
|
@@ -10,22 +10,21 @@ class Redis
|
|
10
10
|
module_function
|
11
11
|
|
12
12
|
def load(nodes)
|
13
|
-
details = {}
|
14
|
-
|
15
13
|
nodes.each do |node|
|
16
|
-
|
17
|
-
|
14
|
+
begin
|
15
|
+
return fetch_command_details(node)
|
16
|
+
rescue CannotConnectError, ConnectionError, CommandError
|
17
|
+
next # can retry on another node
|
18
|
+
end
|
18
19
|
end
|
19
20
|
|
20
|
-
|
21
|
+
raise CannotConnectError, 'Redis client could not connect to any cluster nodes'
|
21
22
|
end
|
22
23
|
|
23
24
|
def fetch_command_details(node)
|
24
25
|
node.call(%i[command]).map do |reply|
|
25
26
|
[reply[0], { arity: reply[1], flags: reply[2], first: reply[3], last: reply[4], step: reply[5] }]
|
26
27
|
end.to_h
|
27
|
-
rescue CannotConnectError, ConnectionError, CommandError
|
28
|
-
{} # can retry on another node
|
29
28
|
end
|
30
29
|
|
31
30
|
private_class_method :fetch_command_details
|
data/lib/redis/cluster/node.rb
CHANGED
@@ -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
|
|
data/lib/redis/cluster/option.rb
CHANGED
@@ -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
|
-
|
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
|
82
|
-
|
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
|
-
|
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
|
@@ -12,11 +12,13 @@ class Redis
|
|
12
12
|
if i.is_a? Array
|
13
13
|
i.each do |j|
|
14
14
|
j = j.to_s
|
15
|
+
j = j.encoding == Encoding::BINARY ? j : j.b
|
15
16
|
command << "$#{j.bytesize}"
|
16
17
|
command << j
|
17
18
|
end
|
18
19
|
else
|
19
20
|
i = i.to_s
|
21
|
+
i = i.encoding == Encoding::BINARY ? i : i.b
|
20
22
|
command << "$#{i.bytesize}"
|
21
23
|
command << i
|
22
24
|
end
|
@@ -32,12 +32,30 @@ class Redis
|
|
32
32
|
@write_timeout = (timeout if timeout && timeout > 0)
|
33
33
|
end
|
34
34
|
|
35
|
-
|
36
|
-
|
35
|
+
string_capacity_support = begin
|
36
|
+
String.new(capacity: 0)
|
37
|
+
true # Ruby 2.4+
|
38
|
+
rescue ArgumentError
|
39
|
+
false # Ruby 2.3
|
40
|
+
end
|
41
|
+
|
42
|
+
if string_capacity_support
|
43
|
+
def read(nbytes)
|
44
|
+
result = @buffer.slice!(0, nbytes)
|
37
45
|
|
38
|
-
|
46
|
+
buffer = String.new(capacity: nbytes, encoding: Encoding::ASCII_8BIT)
|
47
|
+
result << _read_from_socket(nbytes - result.bytesize, buffer) while result.bytesize < nbytes
|
39
48
|
|
40
|
-
|
49
|
+
result
|
50
|
+
end
|
51
|
+
else
|
52
|
+
def read(nbytes)
|
53
|
+
result = @buffer.slice!(0, nbytes)
|
54
|
+
|
55
|
+
result << _read_from_socket(nbytes - result.bytesize, "".b) while result.bytesize < nbytes
|
56
|
+
|
57
|
+
result
|
58
|
+
end
|
41
59
|
end
|
42
60
|
|
43
61
|
def gets
|
@@ -48,9 +66,9 @@ class Redis
|
|
48
66
|
@buffer.slice!(0, crlf + CRLF.bytesize)
|
49
67
|
end
|
50
68
|
|
51
|
-
def _read_from_socket(nbytes)
|
69
|
+
def _read_from_socket(nbytes, buffer = nil)
|
52
70
|
loop do
|
53
|
-
case chunk = read_nonblock(nbytes, exception: false)
|
71
|
+
case chunk = read_nonblock(nbytes, buffer, exception: false)
|
54
72
|
when :wait_readable
|
55
73
|
unless wait_readable(@timeout)
|
56
74
|
raise Redis::TimeoutError
|
data/lib/redis/distributed.rb
CHANGED
@@ -316,6 +316,16 @@ class Redis
|
|
316
316
|
node_for(key).get(key)
|
317
317
|
end
|
318
318
|
|
319
|
+
# Get the value of a key and delete it.
|
320
|
+
def getdel(key)
|
321
|
+
node_for(key).getdel(key)
|
322
|
+
end
|
323
|
+
|
324
|
+
# Get the value of a key and sets its time to live based on options.
|
325
|
+
def getex(key, **options)
|
326
|
+
node_for(key).getex(key, **options)
|
327
|
+
end
|
328
|
+
|
319
329
|
# Get the values of all the given keys as an Array.
|
320
330
|
def mget(*keys)
|
321
331
|
mapped_mget(*keys).values_at(*keys)
|
@@ -393,6 +403,21 @@ class Redis
|
|
393
403
|
node_for(key).llen(key)
|
394
404
|
end
|
395
405
|
|
406
|
+
# Remove the first/last element in a list, append/prepend it to another list and return it.
|
407
|
+
def lmove(source, destination, where_source, where_destination)
|
408
|
+
ensure_same_node(:lmove, [source, destination]) do |node|
|
409
|
+
node.lmove(source, destination, where_source, where_destination)
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
# Remove the first/last element in a list and append/prepend it
|
414
|
+
# to another list and return it, or block until one is available.
|
415
|
+
def blmove(source, destination, where_source, where_destination, timeout: 0)
|
416
|
+
ensure_same_node(:lmove, [source, destination]) do |node|
|
417
|
+
node.blmove(source, destination, where_source, where_destination, timeout: timeout)
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
396
421
|
# Prepend one or more values to a list.
|
397
422
|
def lpush(key, value)
|
398
423
|
node_for(key).lpush(key, value)
|
@@ -413,14 +438,14 @@ class Redis
|
|
413
438
|
node_for(key).rpushx(key, value)
|
414
439
|
end
|
415
440
|
|
416
|
-
# Remove and get the first
|
417
|
-
def lpop(key)
|
418
|
-
node_for(key).lpop(key)
|
441
|
+
# Remove and get the first elements in a list.
|
442
|
+
def lpop(key, count = nil)
|
443
|
+
node_for(key).lpop(key, count)
|
419
444
|
end
|
420
445
|
|
421
|
-
# Remove and get the last
|
422
|
-
def rpop(key)
|
423
|
-
node_for(key).rpop(key)
|
446
|
+
# Remove and get the last elements in a list.
|
447
|
+
def rpop(key, count = nil)
|
448
|
+
node_for(key).rpop(key, count)
|
424
449
|
end
|
425
450
|
|
426
451
|
# Remove the last element in a list, append it to another list and return
|
@@ -542,6 +567,11 @@ class Redis
|
|
542
567
|
node_for(key).sismember(key, member)
|
543
568
|
end
|
544
569
|
|
570
|
+
# Determine if multiple values are members of a set.
|
571
|
+
def smismember(key, *members)
|
572
|
+
node_for(key).smismember(key, *members)
|
573
|
+
end
|
574
|
+
|
545
575
|
# Get all the members in a set.
|
546
576
|
def smembers(key)
|
547
577
|
node_for(key).smembers(key)
|
@@ -626,6 +656,16 @@ class Redis
|
|
626
656
|
node_for(key).zscore(key, member)
|
627
657
|
end
|
628
658
|
|
659
|
+
# Get one or more random members from a sorted set.
|
660
|
+
def zrandmember(key, count = nil, **options)
|
661
|
+
node_for(key).zrandmember(key, count, **options)
|
662
|
+
end
|
663
|
+
|
664
|
+
# Get the scores associated with the given members in a sorted set.
|
665
|
+
def zmscore(key, *members)
|
666
|
+
node_for(key).zmscore(key, *members)
|
667
|
+
end
|
668
|
+
|
629
669
|
# Return a range of members in a sorted set, by index.
|
630
670
|
def zrange(key, start, stop, **options)
|
631
671
|
node_for(key).zrange(key, start, stop, **options)
|
@@ -674,6 +714,13 @@ class Redis
|
|
674
714
|
node_for(key).zcount(key, min, max)
|
675
715
|
end
|
676
716
|
|
717
|
+
# Get the intersection of multiple sorted sets
|
718
|
+
def zinter(*keys, **options)
|
719
|
+
ensure_same_node(:zinter, keys) do |node|
|
720
|
+
node.zinter(*keys, **options)
|
721
|
+
end
|
722
|
+
end
|
723
|
+
|
677
724
|
# Intersect multiple sorted sets and store the resulting sorted set in a new
|
678
725
|
# key.
|
679
726
|
def zinterstore(destination, keys, **options)
|
data/lib/redis/version.rb
CHANGED
data/lib/redis.rb
CHANGED
@@ -4,6 +4,8 @@ require "monitor"
|
|
4
4
|
require_relative "redis/errors"
|
5
5
|
|
6
6
|
class Redis
|
7
|
+
@exists_returns_integer = true
|
8
|
+
|
7
9
|
class << self
|
8
10
|
attr_reader :exists_returns_integer
|
9
11
|
|
@@ -39,6 +41,7 @@ class Redis
|
|
39
41
|
# @option options [String] :path path to server socket (overrides host and port)
|
40
42
|
# @option options [Float] :timeout (5.0) timeout in seconds
|
41
43
|
# @option options [Float] :connect_timeout (same as timeout) timeout for initial connect in seconds
|
44
|
+
# @option options [String] :username Username to authenticate against server
|
42
45
|
# @option options [String] :password Password to authenticate against server
|
43
46
|
# @option options [Integer] :db (0) Database to select after initial connect
|
44
47
|
# @option options [Symbol] :driver Driver to use, currently supported: `:ruby`, `:hiredis`, `:synchrony`
|
@@ -143,12 +146,13 @@ class Redis
|
|
143
146
|
|
144
147
|
# Authenticate to the server.
|
145
148
|
#
|
146
|
-
# @param [String]
|
147
|
-
#
|
149
|
+
# @param [Array<String>] args includes both username and password
|
150
|
+
# or only password
|
148
151
|
# @return [String] `OK`
|
149
|
-
|
152
|
+
# @see https://redis.io/commands/auth AUTH command
|
153
|
+
def auth(*args)
|
150
154
|
synchronize do |client|
|
151
|
-
client.call([:auth,
|
155
|
+
client.call([:auth, *args])
|
152
156
|
end
|
153
157
|
end
|
154
158
|
|
@@ -553,6 +557,9 @@ class Redis
|
|
553
557
|
# @param [String, Array<String>] keys
|
554
558
|
# @return [Integer] number of keys that were deleted
|
555
559
|
def del(*keys)
|
560
|
+
keys.flatten!(1)
|
561
|
+
return 0 if keys.empty?
|
562
|
+
|
556
563
|
synchronize do |client|
|
557
564
|
client.call([:del] + keys)
|
558
565
|
end
|
@@ -829,17 +836,23 @@ class Redis
|
|
829
836
|
# @param [Hash] options
|
830
837
|
# - `:ex => Integer`: Set the specified expire time, in seconds.
|
831
838
|
# - `:px => Integer`: Set the specified expire time, in milliseconds.
|
839
|
+
# - `:exat => Integer` : Set the specified Unix time at which the key will expire, in seconds.
|
840
|
+
# - `:pxat => Integer` : Set the specified Unix time at which the key will expire, in milliseconds.
|
832
841
|
# - `:nx => true`: Only set the key if it does not already exist.
|
833
842
|
# - `:xx => true`: Only set the key if it already exist.
|
834
843
|
# - `:keepttl => true`: Retain the time to live associated with the key.
|
844
|
+
# - `:get => true`: Return the old string stored at key, or nil if key did not exist.
|
835
845
|
# @return [String, Boolean] `"OK"` or true, false if `:nx => true` or `:xx => true`
|
836
|
-
def set(key, value, ex: nil, px: nil, nx: nil, xx: nil, keepttl: nil)
|
846
|
+
def set(key, value, ex: nil, px: nil, exat: nil, pxat: nil, nx: nil, xx: nil, keepttl: nil, get: nil)
|
837
847
|
args = [:set, key, value.to_s]
|
838
848
|
args << "EX" << ex if ex
|
839
849
|
args << "PX" << px if px
|
850
|
+
args << "EXAT" << exat if exat
|
851
|
+
args << "PXAT" << pxat if pxat
|
840
852
|
args << "NX" if nx
|
841
853
|
args << "XX" if xx
|
842
854
|
args << "KEEPTTL" if keepttl
|
855
|
+
args << "GET" if get
|
843
856
|
|
844
857
|
synchronize do |client|
|
845
858
|
if nx || xx
|
@@ -1105,6 +1118,45 @@ class Redis
|
|
1105
1118
|
end
|
1106
1119
|
end
|
1107
1120
|
|
1121
|
+
# Get the value of key and delete the key. This command is similar to GET,
|
1122
|
+
# except for the fact that it also deletes the key on success.
|
1123
|
+
#
|
1124
|
+
# @param [String] key
|
1125
|
+
# @return [String] the old value stored in the key, or `nil` if the key
|
1126
|
+
# did not exist
|
1127
|
+
def getdel(key)
|
1128
|
+
synchronize do |client|
|
1129
|
+
client.call([:getdel, key])
|
1130
|
+
end
|
1131
|
+
end
|
1132
|
+
|
1133
|
+
# Get the value of key and optionally set its expiration. GETEX is similar to
|
1134
|
+
# GET, but is a write command with additional options. When no options are
|
1135
|
+
# provided, GETEX behaves like GET.
|
1136
|
+
#
|
1137
|
+
# @param [String] key
|
1138
|
+
# @param [Hash] options
|
1139
|
+
# - `:ex => Integer`: Set the specified expire time, in seconds.
|
1140
|
+
# - `:px => Integer`: Set the specified expire time, in milliseconds.
|
1141
|
+
# - `:exat => true`: Set the specified Unix time at which the key will
|
1142
|
+
# expire, in seconds.
|
1143
|
+
# - `:pxat => true`: Set the specified Unix time at which the key will
|
1144
|
+
# expire, in milliseconds.
|
1145
|
+
# - `:persist => true`: Remove the time to live associated with the key.
|
1146
|
+
# @return [String] The value of key, or nil when key does not exist.
|
1147
|
+
def getex(key, ex: nil, px: nil, exat: nil, pxat: nil, persist: false)
|
1148
|
+
args = [:getex, key]
|
1149
|
+
args << "EX" << ex if ex
|
1150
|
+
args << "PX" << px if px
|
1151
|
+
args << "EXAT" << exat if exat
|
1152
|
+
args << "PXAT" << pxat if pxat
|
1153
|
+
args << "PERSIST" if persist
|
1154
|
+
|
1155
|
+
synchronize do |client|
|
1156
|
+
client.call(args)
|
1157
|
+
end
|
1158
|
+
end
|
1159
|
+
|
1108
1160
|
# Get the length of the value stored in a key.
|
1109
1161
|
#
|
1110
1162
|
# @param [String] key
|
@@ -1126,6 +1178,59 @@ class Redis
|
|
1126
1178
|
end
|
1127
1179
|
end
|
1128
1180
|
|
1181
|
+
# Remove the first/last element in a list, append/prepend it to another list and return it.
|
1182
|
+
#
|
1183
|
+
# @param [String] source source key
|
1184
|
+
# @param [String] destination destination key
|
1185
|
+
# @param [String, Symbol] where_source from where to remove the element from the source list
|
1186
|
+
# e.g. 'LEFT' - from head, 'RIGHT' - from tail
|
1187
|
+
# @param [String, Symbol] where_destination where to push the element to the source list
|
1188
|
+
# e.g. 'LEFT' - to head, 'RIGHT' - to tail
|
1189
|
+
#
|
1190
|
+
# @return [nil, String] the element, or nil when the source key does not exist
|
1191
|
+
#
|
1192
|
+
# @note This command comes in place of the now deprecated RPOPLPUSH.
|
1193
|
+
# Doing LMOVE RIGHT LEFT is equivalent.
|
1194
|
+
def lmove(source, destination, where_source, where_destination)
|
1195
|
+
where_source, where_destination = _normalize_move_wheres(where_source, where_destination)
|
1196
|
+
|
1197
|
+
synchronize do |client|
|
1198
|
+
client.call([:lmove, source, destination, where_source, where_destination])
|
1199
|
+
end
|
1200
|
+
end
|
1201
|
+
|
1202
|
+
# Remove the first/last element in a list and append/prepend it
|
1203
|
+
# to another list and return it, or block until one is available.
|
1204
|
+
#
|
1205
|
+
# @example With timeout
|
1206
|
+
# element = redis.blmove("foo", "bar", "LEFT", "RIGHT", timeout: 5)
|
1207
|
+
# # => nil on timeout
|
1208
|
+
# # => "element" on success
|
1209
|
+
# @example Without timeout
|
1210
|
+
# element = redis.blmove("foo", "bar", "LEFT", "RIGHT")
|
1211
|
+
# # => "element"
|
1212
|
+
#
|
1213
|
+
# @param [String] source source key
|
1214
|
+
# @param [String] destination destination key
|
1215
|
+
# @param [String, Symbol] where_source from where to remove the element from the source list
|
1216
|
+
# e.g. 'LEFT' - from head, 'RIGHT' - from tail
|
1217
|
+
# @param [String, Symbol] where_destination where to push the element to the source list
|
1218
|
+
# e.g. 'LEFT' - to head, 'RIGHT' - to tail
|
1219
|
+
# @param [Hash] options
|
1220
|
+
# - `:timeout => Numeric`: timeout in seconds, defaults to no timeout
|
1221
|
+
#
|
1222
|
+
# @return [nil, String] the element, or nil when the source key does not exist or the timeout expired
|
1223
|
+
#
|
1224
|
+
def blmove(source, destination, where_source, where_destination, timeout: 0)
|
1225
|
+
where_source, where_destination = _normalize_move_wheres(where_source, where_destination)
|
1226
|
+
|
1227
|
+
synchronize do |client|
|
1228
|
+
command = [:blmove, source, destination, where_source, where_destination, timeout]
|
1229
|
+
timeout += client.timeout if timeout > 0
|
1230
|
+
client.call_with_timeout(command, timeout)
|
1231
|
+
end
|
1232
|
+
end
|
1233
|
+
|
1129
1234
|
# Prepend one or more values to a list, creating the list if it doesn't exist
|
1130
1235
|
#
|
1131
1236
|
# @param [String] key
|
@@ -1170,23 +1275,29 @@ class Redis
|
|
1170
1275
|
end
|
1171
1276
|
end
|
1172
1277
|
|
1173
|
-
# Remove and get the first
|
1278
|
+
# Remove and get the first elements in a list.
|
1174
1279
|
#
|
1175
1280
|
# @param [String] key
|
1176
|
-
# @
|
1177
|
-
|
1281
|
+
# @param [Integer] count number of elements to remove
|
1282
|
+
# @return [String, Array<String>] the values of the first elements
|
1283
|
+
def lpop(key, count = nil)
|
1178
1284
|
synchronize do |client|
|
1179
|
-
|
1285
|
+
command = [:lpop, key]
|
1286
|
+
command << count if count
|
1287
|
+
client.call(command)
|
1180
1288
|
end
|
1181
1289
|
end
|
1182
1290
|
|
1183
|
-
# Remove and get the last
|
1291
|
+
# Remove and get the last elements in a list.
|
1184
1292
|
#
|
1185
1293
|
# @param [String] key
|
1186
|
-
# @
|
1187
|
-
|
1294
|
+
# @param [Integer] count number of elements to remove
|
1295
|
+
# @return [String, Array<String>] the values of the last elements
|
1296
|
+
def rpop(key, count = nil)
|
1188
1297
|
synchronize do |client|
|
1189
|
-
|
1298
|
+
command = [:rpop, key]
|
1299
|
+
command << count if count
|
1300
|
+
client.call(command)
|
1190
1301
|
end
|
1191
1302
|
end
|
1192
1303
|
|
@@ -1468,6 +1579,19 @@ class Redis
|
|
1468
1579
|
end
|
1469
1580
|
end
|
1470
1581
|
|
1582
|
+
# Determine if multiple values are members of a set.
|
1583
|
+
#
|
1584
|
+
# @param [String] key
|
1585
|
+
# @param [String, Array<String>] members
|
1586
|
+
# @return [Array<Boolean>]
|
1587
|
+
def smismember(key, *members)
|
1588
|
+
synchronize do |client|
|
1589
|
+
client.call([:smismember, key, *members]) do |reply|
|
1590
|
+
reply.map(&Boolify)
|
1591
|
+
end
|
1592
|
+
end
|
1593
|
+
end
|
1594
|
+
|
1471
1595
|
# Get all the members in a set.
|
1472
1596
|
#
|
1473
1597
|
# @param [String] key
|
@@ -1572,6 +1696,10 @@ class Redis
|
|
1572
1696
|
# add elements)
|
1573
1697
|
# - `:nx => true`: Don't update already existing elements (always
|
1574
1698
|
# add new elements)
|
1699
|
+
# - `:lt => true`: Only update existing elements if the new score
|
1700
|
+
# is less than the current score
|
1701
|
+
# - `:gt => true`: Only update existing elements if the new score
|
1702
|
+
# is greater than the current score
|
1575
1703
|
# - `:ch => true`: Modify the return value from the number of new
|
1576
1704
|
# elements added, to the total number of elements changed (CH is an
|
1577
1705
|
# abbreviation of changed); changed elements are new elements added
|
@@ -1586,10 +1714,12 @@ class Redis
|
|
1586
1714
|
# pairs that were **added** to the sorted set.
|
1587
1715
|
# - `Float` when option :incr is specified, holding the score of the member
|
1588
1716
|
# after incrementing it.
|
1589
|
-
def zadd(key, *args, nx: nil, xx: nil, ch: nil, incr: nil)
|
1717
|
+
def zadd(key, *args, nx: nil, xx: nil, lt: nil, gt: nil, ch: nil, incr: nil)
|
1590
1718
|
command = [:zadd, key]
|
1591
1719
|
command << "NX" if nx
|
1592
1720
|
command << "XX" if xx
|
1721
|
+
command << "LT" if lt
|
1722
|
+
command << "GT" if gt
|
1593
1723
|
command << "CH" if ch
|
1594
1724
|
command << "INCR" if incr
|
1595
1725
|
|
@@ -1752,6 +1882,63 @@ class Redis
|
|
1752
1882
|
end
|
1753
1883
|
end
|
1754
1884
|
|
1885
|
+
# Get the scores associated with the given members in a sorted set.
|
1886
|
+
#
|
1887
|
+
# @example Get the scores for members "a" and "b"
|
1888
|
+
# redis.zmscore("zset", "a", "b")
|
1889
|
+
# # => [32.0, 48.0]
|
1890
|
+
#
|
1891
|
+
# @param [String] key
|
1892
|
+
# @param [String, Array<String>] members
|
1893
|
+
# @return [Array<Float>] scores of the members
|
1894
|
+
def zmscore(key, *members)
|
1895
|
+
synchronize do |client|
|
1896
|
+
client.call([:zmscore, key, *members]) do |reply|
|
1897
|
+
reply.map(&Floatify)
|
1898
|
+
end
|
1899
|
+
end
|
1900
|
+
end
|
1901
|
+
|
1902
|
+
# Get one or more random members from a sorted set.
|
1903
|
+
#
|
1904
|
+
# @example Get one random member
|
1905
|
+
# redis.zrandmember("zset")
|
1906
|
+
# # => "a"
|
1907
|
+
# @example Get multiple random members
|
1908
|
+
# redis.zrandmember("zset", 2)
|
1909
|
+
# # => ["a", "b"]
|
1910
|
+
# @example Gem multiple random members with scores
|
1911
|
+
# redis.zrandmember("zset", 2, with_scores: true)
|
1912
|
+
# # => [["a", 2.0], ["b", 3.0]]
|
1913
|
+
#
|
1914
|
+
# @param [String] key
|
1915
|
+
# @param [Integer] count
|
1916
|
+
# @param [Hash] options
|
1917
|
+
# - `:with_scores => true`: include scores in output
|
1918
|
+
#
|
1919
|
+
# @return [nil, String, Array<String>, Array<[String, Float]>]
|
1920
|
+
# - when `key` does not exist or set is empty, `nil`
|
1921
|
+
# - when `count` is not specified, a member
|
1922
|
+
# - when `count` is specified and `:with_scores` is not specified, an array of members
|
1923
|
+
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
1924
|
+
def zrandmember(key, count = nil, withscores: false, with_scores: withscores)
|
1925
|
+
if with_scores && count.nil?
|
1926
|
+
raise ArgumentError, "count argument must be specified"
|
1927
|
+
end
|
1928
|
+
|
1929
|
+
args = [:zrandmember, key]
|
1930
|
+
args << count if count
|
1931
|
+
|
1932
|
+
if with_scores
|
1933
|
+
args << "WITHSCORES"
|
1934
|
+
block = FloatifyPairs
|
1935
|
+
end
|
1936
|
+
|
1937
|
+
synchronize do |client|
|
1938
|
+
client.call(args, &block)
|
1939
|
+
end
|
1940
|
+
end
|
1941
|
+
|
1755
1942
|
# Return a range of members in a sorted set, by index.
|
1756
1943
|
#
|
1757
1944
|
# @example Retrieve all members from a sorted set
|
@@ -2054,6 +2241,45 @@ class Redis
|
|
2054
2241
|
end
|
2055
2242
|
end
|
2056
2243
|
|
2244
|
+
# Return the intersection of multiple sorted sets
|
2245
|
+
#
|
2246
|
+
# @example Retrieve the intersection of `2*zsetA` and `1*zsetB`
|
2247
|
+
# redis.zinter("zsetA", "zsetB", :weights => [2.0, 1.0])
|
2248
|
+
# # => ["v1", "v2"]
|
2249
|
+
# @example Retrieve the intersection of `2*zsetA` and `1*zsetB`, and their scores
|
2250
|
+
# redis.zinter("zsetA", "zsetB", :weights => [2.0, 1.0], :with_scores => true)
|
2251
|
+
# # => [["v1", 3.0], ["v2", 6.0]]
|
2252
|
+
#
|
2253
|
+
# @param [String, Array<String>] keys one or more keys to intersect
|
2254
|
+
# @param [Hash] options
|
2255
|
+
# - `:weights => [Float, Float, ...]`: weights to associate with source
|
2256
|
+
# sorted sets
|
2257
|
+
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
2258
|
+
# - `:with_scores => true`: include scores in output
|
2259
|
+
#
|
2260
|
+
# @return [Array<String>, Array<[String, Float]>]
|
2261
|
+
# - when `:with_scores` is not specified, an array of members
|
2262
|
+
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
2263
|
+
def zinter(*keys, weights: nil, aggregate: nil, with_scores: false)
|
2264
|
+
args = [:zinter, keys.size, *keys]
|
2265
|
+
|
2266
|
+
if weights
|
2267
|
+
args << "WEIGHTS"
|
2268
|
+
args.concat(weights)
|
2269
|
+
end
|
2270
|
+
|
2271
|
+
args << "AGGREGATE" << aggregate if aggregate
|
2272
|
+
|
2273
|
+
if with_scores
|
2274
|
+
args << "WITHSCORES"
|
2275
|
+
block = FloatifyPairs
|
2276
|
+
end
|
2277
|
+
|
2278
|
+
synchronize do |client|
|
2279
|
+
client.call(args, &block)
|
2280
|
+
end
|
2281
|
+
end
|
2282
|
+
|
2057
2283
|
# Intersect multiple sorted sets and store the resulting sorted set in a new
|
2058
2284
|
# key.
|
2059
2285
|
#
|
@@ -2636,12 +2862,13 @@ class Redis
|
|
2636
2862
|
_eval(:evalsha, args)
|
2637
2863
|
end
|
2638
2864
|
|
2639
|
-
def _scan(command, cursor, args, match: nil, count: nil, &block)
|
2865
|
+
def _scan(command, cursor, args, match: nil, count: nil, type: nil, &block)
|
2640
2866
|
# SSCAN/ZSCAN/HSCAN already prepend the key to +args+.
|
2641
2867
|
|
2642
2868
|
args << cursor
|
2643
2869
|
args << "MATCH" << match if match
|
2644
2870
|
args << "COUNT" << count if count
|
2871
|
+
args << "TYPE" << type if type
|
2645
2872
|
|
2646
2873
|
synchronize do |client|
|
2647
2874
|
client.call([command] + args, &block)
|
@@ -2656,11 +2883,15 @@ class Redis
|
|
2656
2883
|
# @example Retrieve a batch of keys matching a pattern
|
2657
2884
|
# redis.scan(4, :match => "key:1?")
|
2658
2885
|
# # => ["92", ["key:13", "key:18"]]
|
2886
|
+
# @example Retrieve a batch of keys of a certain type
|
2887
|
+
# redis.scan(92, :type => "zset")
|
2888
|
+
# # => ["173", ["sortedset:14", "sortedset:78"]]
|
2659
2889
|
#
|
2660
2890
|
# @param [String, Integer] cursor the cursor of the iteration
|
2661
2891
|
# @param [Hash] options
|
2662
2892
|
# - `:match => String`: only return keys matching the pattern
|
2663
2893
|
# - `:count => Integer`: return count keys at most per iteration
|
2894
|
+
# - `:type => String`: return keys only of the given type
|
2664
2895
|
#
|
2665
2896
|
# @return [String, Array<String>] the next cursor and all found keys
|
2666
2897
|
def scan(cursor, **options)
|
@@ -2676,10 +2907,15 @@ class Redis
|
|
2676
2907
|
# redis.scan_each(:match => "key:1?") {|key| puts key}
|
2677
2908
|
# # => key:13
|
2678
2909
|
# # => key:18
|
2910
|
+
# @example Execute block for each key of a type
|
2911
|
+
# redis.scan_each(:type => "hash") {|key| puts redis.type(key)}
|
2912
|
+
# # => "hash"
|
2913
|
+
# # => "hash"
|
2679
2914
|
#
|
2680
2915
|
# @param [Hash] options
|
2681
2916
|
# - `:match => String`: only return keys matching the pattern
|
2682
2917
|
# - `:count => Integer`: return count keys at most per iteration
|
2918
|
+
# - `:type => String`: return keys only of the given type
|
2683
2919
|
#
|
2684
2920
|
# @return [Enumerator] an enumerator for all found keys
|
2685
2921
|
def scan_each(**options, &block)
|
@@ -3220,6 +3456,38 @@ class Redis
|
|
3220
3456
|
synchronize { |client| client.call(args, &blk) }
|
3221
3457
|
end
|
3222
3458
|
|
3459
|
+
# Transfers ownership of pending stream entries that match the specified criteria.
|
3460
|
+
#
|
3461
|
+
# @example Claim next pending message stuck > 5 minutes and mark as retry
|
3462
|
+
# redis.xautoclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0')
|
3463
|
+
# @example Claim 50 next pending messages stuck > 5 minutes and mark as retry
|
3464
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0', count: 50)
|
3465
|
+
# @example Claim next pending message stuck > 5 minutes and don't mark as retry
|
3466
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0', justid: true)
|
3467
|
+
# @example Claim next pending message after this id stuck > 5 minutes and mark as retry
|
3468
|
+
# redis.xautoclaim('mystream', 'mygroup', 'consumer1', 3600000, '1641321233-0')
|
3469
|
+
#
|
3470
|
+
# @param key [String] the stream key
|
3471
|
+
# @param group [String] the consumer group name
|
3472
|
+
# @param consumer [String] the consumer name
|
3473
|
+
# @param min_idle_time [Integer] the number of milliseconds
|
3474
|
+
# @param start [String] entry id to start scanning from or 0-0 for everything
|
3475
|
+
# @param count [Integer] number of messages to claim (default 1)
|
3476
|
+
# @param justid [Boolean] whether to fetch just an array of entry ids or not.
|
3477
|
+
# Does not increment retry count when true
|
3478
|
+
#
|
3479
|
+
# @return [Hash{String => Hash}] the entries successfully claimed
|
3480
|
+
# @return [Array<String>] the entry ids successfully claimed if justid option is `true`
|
3481
|
+
def xautoclaim(key, group, consumer, min_idle_time, start, count: nil, justid: false)
|
3482
|
+
args = [:xautoclaim, key, group, consumer, min_idle_time, start]
|
3483
|
+
if count
|
3484
|
+
args << 'COUNT' << count.to_s
|
3485
|
+
end
|
3486
|
+
args << 'JUSTID' if justid
|
3487
|
+
blk = justid ? HashifyStreamAutoclaimJustId : HashifyStreamAutoclaim
|
3488
|
+
synchronize { |client| client.call(args, &blk) }
|
3489
|
+
end
|
3490
|
+
|
3223
3491
|
# Fetches not acknowledging pending entries
|
3224
3492
|
#
|
3225
3493
|
# @example With key and group
|
@@ -3426,10 +3694,24 @@ class Redis
|
|
3426
3694
|
|
3427
3695
|
HashifyStreamEntries = lambda { |reply|
|
3428
3696
|
reply.compact.map do |entry_id, values|
|
3429
|
-
[entry_id, values
|
3697
|
+
[entry_id, values&.each_slice(2)&.to_h]
|
3430
3698
|
end
|
3431
3699
|
}
|
3432
3700
|
|
3701
|
+
HashifyStreamAutoclaim = lambda { |reply|
|
3702
|
+
{
|
3703
|
+
'next' => reply[0],
|
3704
|
+
'entries' => reply[1].map { |entry| [entry[0], entry[1].each_slice(2).to_h] }
|
3705
|
+
}
|
3706
|
+
}
|
3707
|
+
|
3708
|
+
HashifyStreamAutoclaimJustId = lambda { |reply|
|
3709
|
+
{
|
3710
|
+
'next' => reply[0],
|
3711
|
+
'entries' => reply[1]
|
3712
|
+
}
|
3713
|
+
}
|
3714
|
+
|
3433
3715
|
HashifyStreamPendings = lambda { |reply|
|
3434
3716
|
{
|
3435
3717
|
'size' => reply[0],
|
@@ -3529,6 +3811,21 @@ class Redis
|
|
3529
3811
|
end
|
3530
3812
|
end
|
3531
3813
|
end
|
3814
|
+
|
3815
|
+
def _normalize_move_wheres(where_source, where_destination)
|
3816
|
+
where_source = where_source.to_s.upcase
|
3817
|
+
where_destination = where_destination.to_s.upcase
|
3818
|
+
|
3819
|
+
if where_source != "LEFT" && where_source != "RIGHT"
|
3820
|
+
raise ArgumentError, "where_source must be 'LEFT' or 'RIGHT'"
|
3821
|
+
end
|
3822
|
+
|
3823
|
+
if where_destination != "LEFT" && where_destination != "RIGHT"
|
3824
|
+
raise ArgumentError, "where_destination must be 'LEFT' or 'RIGHT'"
|
3825
|
+
end
|
3826
|
+
|
3827
|
+
[where_source, where_destination]
|
3828
|
+
end
|
3532
3829
|
end
|
3533
3830
|
|
3534
3831
|
require_relative "redis/version"
|
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.
|
4
|
+
version: 4.5.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:
|
19
|
+
date: 2021-10-14 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.
|
105
|
+
documentation_uri: https://www.rubydoc.info/gems/redis/4.5.0
|
106
106
|
homepage_uri: https://github.com/redis/redis-rb
|
107
|
-
source_code_uri: https://github.com/redis/redis-rb/tree/v4.
|
107
|
+
source_code_uri: https://github.com/redis/redis-rb/tree/v4.5.0
|
108
108
|
post_install_message:
|
109
109
|
rdoc_options: []
|
110
110
|
require_paths:
|
@@ -113,7 +113,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
113
113
|
requirements:
|
114
114
|
- - ">="
|
115
115
|
- !ruby/object:Gem::Version
|
116
|
-
version: 2.
|
116
|
+
version: 2.4.0
|
117
117
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
118
|
requirements:
|
119
119
|
- - ">="
|