redis 4.2.5 → 4.5.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 +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
|
- - ">="
|