redis 4.1.4 → 4.3.1
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 +48 -0
- data/README.md +27 -17
- data/lib/redis.rb +317 -260
- data/lib/redis/client.rb +92 -74
- data/lib/redis/cluster.rb +5 -1
- data/lib/redis/cluster/node.rb +3 -0
- data/lib/redis/cluster/option.rb +9 -3
- data/lib/redis/cluster/slot.rb +28 -14
- data/lib/redis/cluster/slot_loader.rb +2 -3
- data/lib/redis/connection.rb +1 -0
- data/lib/redis/connection/command_helper.rb +2 -2
- data/lib/redis/connection/hiredis.rb +3 -3
- data/lib/redis/connection/registry.rb +1 -1
- data/lib/redis/connection/ruby.rb +89 -107
- data/lib/redis/connection/synchrony.rb +8 -4
- data/lib/redis/distributed.rb +114 -63
- data/lib/redis/errors.rb +1 -0
- data/lib/redis/hash_ring.rb +14 -14
- data/lib/redis/pipeline.rb +6 -8
- data/lib/redis/subscribe.rb +10 -12
- data/lib/redis/version.rb +2 -1
- metadata +14 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 416a2f007042c19453c13361aa4440a507e47fb32c28adc68e7c574c6651f5b4
|
4
|
+
data.tar.gz: 1a845f2af649d64f8b274962c9d5d10e6eb5d046474b6e44288676432fe8a98b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3766992242ae284ca474bc8564c6760de88e635a8c3bc3c80da08062d698cc891bf00455b5d98768709ecc766f8ad305fe03cc5806f03fda3ebb93049e0a1cce
|
7
|
+
data.tar.gz: f440c984ec58ff091a6a696952239cb04cf145752b485543e5da7215a327b40be4391b3fe6ca67753f84ec43913b9d90ec0b6f812e1696890a7c17cbf3aa3630
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,52 @@
|
|
1
1
|
# Unreleased
|
2
2
|
|
3
|
+
# 4.3.1
|
4
|
+
|
5
|
+
* Fix password authentication against redis server 5 and older.
|
6
|
+
|
7
|
+
# 4.3.0
|
8
|
+
|
9
|
+
* Add the TYPE argument to scan and scan_each. See #985.
|
10
|
+
* Support AUTH command for ACL. See #967.
|
11
|
+
|
12
|
+
# 4.2.5
|
13
|
+
|
14
|
+
* Optimize the ruby connector write buffering. See #964.
|
15
|
+
|
16
|
+
# 4.2.4
|
17
|
+
|
18
|
+
* Fix bytesize calculations in the ruby connector, and work on a copy of the buffer. Fix #961, #962.
|
19
|
+
|
20
|
+
# 4.2.3
|
21
|
+
|
22
|
+
* Use io/wait instead of IO.select in the ruby connector. See #960.
|
23
|
+
* Use exception free non blocking IOs in the ruby connector. See #926.
|
24
|
+
* Prevent corruption of the client when an interrupt happen during inside a pipeline block. See #945.
|
25
|
+
|
26
|
+
# 4.2.2
|
27
|
+
|
28
|
+
* Fix `WATCH` support for `Redis::Distributed`. See #941.
|
29
|
+
* Fix handling of empty stream responses. See #905, #929.
|
30
|
+
|
31
|
+
# 4.2.1
|
32
|
+
|
33
|
+
* Fix `exists?` returning an actual boolean when called with multiple keys. See #918.
|
34
|
+
* Setting `Redis.exists_returns_integer = false` disables warning message about new behaviour. See #920.
|
35
|
+
|
36
|
+
# 4.2.0
|
37
|
+
|
38
|
+
* Convert commands to accept keyword arguments rather than option hashes. This both help catching typos, and reduce needless allocations.
|
39
|
+
* Deprecate the synchrony driver. It will be removed in 5.0 and hopefully maintained as a separate gem. See #915.
|
40
|
+
* Make `Redis#exists` variadic, will return an Integer if called with multiple keys.
|
41
|
+
* Add `Redis#exists?` to get a Boolean if any of the keys exists.
|
42
|
+
* `Redis#exists` when called with a single key will warn that future versions will return an Integer.
|
43
|
+
Set `Redis.exists_returns_integer = true` to opt-in to the new behavior.
|
44
|
+
* Support `keepttl` ooption in `set`. See #913.
|
45
|
+
* Optimized initialization of Redis::Cluster. See #912.
|
46
|
+
* Accept sentinel options even with string key. See #599.
|
47
|
+
* Verify TLS connections by default. See #900.
|
48
|
+
* Make `Redis#hset` variadic. It now returns an integer, not a boolean. See #910.
|
49
|
+
|
3
50
|
# 4.1.4
|
4
51
|
|
5
52
|
* Alias `Redis#disconnect` as `#close`. See #901.
|
@@ -9,6 +56,7 @@
|
|
9
56
|
* Increase buffer size in the ruby connector. See #880.
|
10
57
|
* Fix thread safety of `Redis.queue`. See #878.
|
11
58
|
* Deprecate `Redis::Future#==` as it's likely to be a mistake. See #876.
|
59
|
+
* Support `KEEPTTL` option for SET command. See #913.
|
12
60
|
|
13
61
|
# 4.1.3
|
14
62
|
|
data/README.md
CHANGED
@@ -1,8 +1,9 @@
|
|
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.
|
5
5
|
|
6
|
+
See [RubyDoc.info][rubydoc] for the API docs of the latest published gem.
|
6
7
|
|
7
8
|
## Getting started
|
8
9
|
|
@@ -34,6 +35,9 @@ You can also specify connection options as a [`redis://` URL][redis-url]:
|
|
34
35
|
redis = Redis.new(url: "redis://:p4ssw0rd@10.0.1.1:6380/15")
|
35
36
|
```
|
36
37
|
|
38
|
+
The client expects passwords with special chracters to be URL-encoded (i.e.
|
39
|
+
`CGI.escape(password)`).
|
40
|
+
|
37
41
|
By default, the client will try to read the `REDIS_URL` environment variable
|
38
42
|
and use that as URL to connect to. The above statement is therefore equivalent
|
39
43
|
to setting this environment variable and calling `Redis.new` without arguments.
|
@@ -50,6 +54,12 @@ To connect to a password protected Redis instance, use:
|
|
50
54
|
redis = Redis.new(password: "mysecret")
|
51
55
|
```
|
52
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
|
+
|
53
63
|
The Redis class exports methods that are named identical to the commands
|
54
64
|
they execute. The arguments these methods accept are often identical to
|
55
65
|
the arguments specified on the [Redis website][redis-commands]. For
|
@@ -147,8 +157,8 @@ redis.mget('{key}1', '{key}2')
|
|
147
157
|
|
148
158
|
## Storing objects
|
149
159
|
|
150
|
-
Redis
|
151
|
-
|
160
|
+
Redis "string" types can be used to store serialized Ruby objects, for
|
161
|
+
example with JSON:
|
152
162
|
|
153
163
|
```ruby
|
154
164
|
require "json"
|
@@ -261,6 +271,7 @@ All timeout values are specified in seconds.
|
|
261
271
|
When using pub/sub, you can subscribe to a channel using a timeout as well:
|
262
272
|
|
263
273
|
```ruby
|
274
|
+
redis = Redis.new(reconnect_attempts: 0)
|
264
275
|
redis.subscribe_with_timeout(5, "news") do |on|
|
265
276
|
on.message do |channel, message|
|
266
277
|
# ...
|
@@ -322,7 +333,7 @@ This library supports natively terminating client side SSL/TLS connections
|
|
322
333
|
when talking to Redis via a server-side proxy such as [stunnel], [hitch],
|
323
334
|
or [ghostunnel].
|
324
335
|
|
325
|
-
To enable SSL support, pass the `:ssl =>
|
336
|
+
To enable SSL support, pass the `:ssl => true` option when configuring the
|
326
337
|
Redis client, or pass in `:url => "rediss://..."` (like HTTPS for Redis).
|
327
338
|
You will also need to pass in an `:ssl_params => { ... }` hash used to
|
328
339
|
configure the `OpenSSL::SSL::SSLContext` object used for the connection:
|
@@ -435,7 +446,7 @@ redis = Redis.new(:driver => :synchrony)
|
|
435
446
|
## Testing
|
436
447
|
|
437
448
|
This library is tested against recent Ruby and Redis versions.
|
438
|
-
Check [
|
449
|
+
Check [Github Actions][gh-actions-link] for the exact versions supported.
|
439
450
|
|
440
451
|
## See Also
|
441
452
|
|
@@ -451,15 +462,14 @@ client and evangelized Redis in Rubyland. Thank you, Ezra.
|
|
451
462
|
## Contributing
|
452
463
|
|
453
464
|
[Fork the project](https://github.com/redis/redis-rb) and send pull
|
454
|
-
requests.
|
455
|
-
|
456
|
-
|
457
|
-
[inchpages-image]:
|
458
|
-
[inchpages-link]:
|
459
|
-
[redis-commands]:
|
460
|
-
[redis-home]:
|
461
|
-
[redis-url]:
|
462
|
-
[
|
463
|
-
[
|
464
|
-
[
|
465
|
-
[rubydoc]: http://www.rubydoc.info/gems/redis
|
465
|
+
requests.
|
466
|
+
|
467
|
+
|
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.rb
CHANGED
@@ -4,12 +4,26 @@ require "monitor"
|
|
4
4
|
require_relative "redis/errors"
|
5
5
|
|
6
6
|
class Redis
|
7
|
-
|
8
|
-
|
7
|
+
class << self
|
8
|
+
attr_reader :exists_returns_integer
|
9
|
+
|
10
|
+
def exists_returns_integer=(value)
|
11
|
+
unless value
|
12
|
+
message = "`Redis#exists(key)` will return an Integer by default in redis-rb 4.3. The option to explicitly " \
|
13
|
+
"disable this behaviour via `Redis.exists_returns_integer` will be removed in 5.0. You should use " \
|
14
|
+
"`exists?` instead."
|
15
|
+
|
16
|
+
::Kernel.warn(message)
|
17
|
+
end
|
18
|
+
|
19
|
+
@exists_returns_integer = value
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_writer :current
|
9
23
|
end
|
10
24
|
|
11
|
-
def self.current
|
12
|
-
@current
|
25
|
+
def self.current
|
26
|
+
@current ||= Redis.new
|
13
27
|
end
|
14
28
|
|
15
29
|
include MonitorMixin
|
@@ -17,17 +31,22 @@ class Redis
|
|
17
31
|
# Create a new client instance
|
18
32
|
#
|
19
33
|
# @param [Hash] options
|
20
|
-
# @option options [String] :url (value of the environment variable REDIS_URL) a Redis URL, for a TCP connection:
|
34
|
+
# @option options [String] :url (value of the environment variable REDIS_URL) a Redis URL, for a TCP connection:
|
35
|
+
# `redis://:[password]@[hostname]:[port]/[db]` (password, port and database are optional), for a unix socket
|
36
|
+
# connection: `unix://[path to Redis socket]`. This overrides all other options.
|
21
37
|
# @option options [String] :host ("127.0.0.1") server hostname
|
22
38
|
# @option options [Integer] :port (6379) server port
|
23
39
|
# @option options [String] :path path to server socket (overrides host and port)
|
24
40
|
# @option options [Float] :timeout (5.0) timeout in seconds
|
25
41
|
# @option options [Float] :connect_timeout (same as timeout) timeout for initial connect in seconds
|
42
|
+
# @option options [String] :username Username to authenticate against server
|
26
43
|
# @option options [String] :password Password to authenticate against server
|
27
44
|
# @option options [Integer] :db (0) Database to select after initial connect
|
28
45
|
# @option options [Symbol] :driver Driver to use, currently supported: `:ruby`, `:hiredis`, `:synchrony`
|
29
|
-
# @option options [String] :id ID for the client connection, assigns name to current connection by sending
|
30
|
-
#
|
46
|
+
# @option options [String] :id ID for the client connection, assigns name to current connection by sending
|
47
|
+
# `CLIENT SETNAME`
|
48
|
+
# @option options [Hash, Integer] :tcp_keepalive Keepalive values, if Integer `intvl` and `probe` are calculated
|
49
|
+
# based on the value, if Hash `time`, `intvl` and `probes` can be specified as a Integer
|
31
50
|
# @option options [Integer] :reconnect_attempts Number of attempts trying to connect
|
32
51
|
# @option options [Boolean] :inherit_socket (false) Whether to use socket in forked process or not
|
33
52
|
# @option options [Array] :sentinels List of sentinels to contact
|
@@ -52,7 +71,7 @@ class Redis
|
|
52
71
|
end
|
53
72
|
|
54
73
|
# Run code with the client reconnecting
|
55
|
-
def with_reconnect(val=true, &blk)
|
74
|
+
def with_reconnect(val = true, &blk)
|
56
75
|
synchronize do |client|
|
57
76
|
client.with_reconnect(val, &blk)
|
58
77
|
end
|
@@ -125,12 +144,13 @@ class Redis
|
|
125
144
|
|
126
145
|
# Authenticate to the server.
|
127
146
|
#
|
128
|
-
# @param [String]
|
129
|
-
#
|
147
|
+
# @param [Array<String>] args includes both username and password
|
148
|
+
# or only password
|
130
149
|
# @return [String] `OK`
|
131
|
-
|
150
|
+
# @see https://redis.io/commands/auth AUTH command
|
151
|
+
def auth(*args)
|
132
152
|
synchronize do |client|
|
133
|
-
client.call([:auth,
|
153
|
+
client.call([:auth, *args])
|
134
154
|
end
|
135
155
|
end
|
136
156
|
|
@@ -205,7 +225,7 @@ class Redis
|
|
205
225
|
def config(action, *args)
|
206
226
|
synchronize do |client|
|
207
227
|
client.call([:config, action] + args) do |reply|
|
208
|
-
if reply.
|
228
|
+
if reply.is_a?(Array) && action == :get
|
209
229
|
Hashify.call(reply)
|
210
230
|
else
|
211
231
|
reply
|
@@ -256,7 +276,7 @@ class Redis
|
|
256
276
|
def flushall(options = nil)
|
257
277
|
synchronize do |client|
|
258
278
|
if options && options[:async]
|
259
|
-
client.call([
|
279
|
+
client.call(%i[flushall async])
|
260
280
|
else
|
261
281
|
client.call([:flushall])
|
262
282
|
end
|
@@ -271,7 +291,7 @@ class Redis
|
|
271
291
|
def flushdb(options = nil)
|
272
292
|
synchronize do |client|
|
273
293
|
if options && options[:async]
|
274
|
-
client.call([
|
294
|
+
client.call(%i[flushdb async])
|
275
295
|
else
|
276
296
|
client.call([:flushdb])
|
277
297
|
end
|
@@ -285,7 +305,7 @@ class Redis
|
|
285
305
|
def info(cmd = nil)
|
286
306
|
synchronize do |client|
|
287
307
|
client.call([:info, cmd].compact) do |reply|
|
288
|
-
if reply.
|
308
|
+
if reply.is_a?(String)
|
289
309
|
reply = HashifyInfo.call(reply)
|
290
310
|
|
291
311
|
if cmd && cmd.to_s == "commandstats"
|
@@ -358,7 +378,7 @@ class Redis
|
|
358
378
|
# @param [String] subcommand e.g. `get`, `len`, `reset`
|
359
379
|
# @param [Integer] length maximum number of entries to return
|
360
380
|
# @return [Array<String>, Integer, String] depends on subcommand
|
361
|
-
def slowlog(subcommand, length=nil)
|
381
|
+
def slowlog(subcommand, length = nil)
|
362
382
|
synchronize do |client|
|
363
383
|
args = [:slowlog, subcommand]
|
364
384
|
args << length if length
|
@@ -383,7 +403,7 @@ class Redis
|
|
383
403
|
def time
|
384
404
|
synchronize do |client|
|
385
405
|
client.call([:time]) do |reply|
|
386
|
-
reply
|
406
|
+
reply&.map(&:to_i)
|
387
407
|
end
|
388
408
|
end
|
389
409
|
end
|
@@ -496,9 +516,9 @@ class Redis
|
|
496
516
|
# - `:replace => Boolean`: if false, raises an error if key already exists
|
497
517
|
# @raise [Redis::CommandError]
|
498
518
|
# @return [String] `"OK"`
|
499
|
-
def restore(key, ttl, serialized_value,
|
519
|
+
def restore(key, ttl, serialized_value, replace: nil)
|
500
520
|
args = [:restore, key, ttl, serialized_value]
|
501
|
-
args << 'REPLACE' if
|
521
|
+
args << 'REPLACE' if replace
|
502
522
|
|
503
523
|
synchronize do |client|
|
504
524
|
client.call(args)
|
@@ -550,13 +570,43 @@ class Redis
|
|
550
570
|
end
|
551
571
|
end
|
552
572
|
|
553
|
-
# Determine
|
573
|
+
# Determine how many of the keys exists.
|
554
574
|
#
|
555
|
-
# @param [String]
|
575
|
+
# @param [String, Array<String>] keys
|
576
|
+
# @return [Integer]
|
577
|
+
def exists(*keys)
|
578
|
+
if !Redis.exists_returns_integer && keys.size == 1
|
579
|
+
if Redis.exists_returns_integer.nil?
|
580
|
+
message = "`Redis#exists(key)` will return an Integer in redis-rb 4.3. `exists?` returns a boolean, you " \
|
581
|
+
"should use it instead. To opt-in to the new behavior now you can set Redis.exists_returns_integer = " \
|
582
|
+
"true. To disable this message and keep the current (boolean) behaviour of 'exists' you can set " \
|
583
|
+
"`Redis.exists_returns_integer = false`, but this option will be removed in 5.0. " \
|
584
|
+
"(#{::Kernel.caller(1, 1).first})\n"
|
585
|
+
|
586
|
+
::Kernel.warn(message)
|
587
|
+
end
|
588
|
+
|
589
|
+
exists?(*keys)
|
590
|
+
else
|
591
|
+
_exists(*keys)
|
592
|
+
end
|
593
|
+
end
|
594
|
+
|
595
|
+
def _exists(*keys)
|
596
|
+
synchronize do |client|
|
597
|
+
client.call([:exists, *keys])
|
598
|
+
end
|
599
|
+
end
|
600
|
+
|
601
|
+
# Determine if any of the keys exists.
|
602
|
+
#
|
603
|
+
# @param [String, Array<String>] keys
|
556
604
|
# @return [Boolean]
|
557
|
-
def exists(
|
605
|
+
def exists?(*keys)
|
558
606
|
synchronize do |client|
|
559
|
-
client.call([:exists,
|
607
|
+
client.call([:exists, *keys]) do |value|
|
608
|
+
value > 0
|
609
|
+
end
|
560
610
|
end
|
561
611
|
end
|
562
612
|
|
@@ -567,7 +617,7 @@ class Redis
|
|
567
617
|
def keys(pattern = "*")
|
568
618
|
synchronize do |client|
|
569
619
|
client.call([:keys, pattern]) do |reply|
|
570
|
-
if reply.
|
620
|
+
if reply.is_a?(String)
|
571
621
|
reply.split(" ")
|
572
622
|
else
|
573
623
|
reply
|
@@ -663,30 +713,27 @@ class Redis
|
|
663
713
|
# elements where every element is an array with the result for every
|
664
714
|
# element specified in `:get`
|
665
715
|
# - when `:store` is specified, the number of elements in the stored result
|
666
|
-
def sort(key,
|
667
|
-
args = []
|
716
|
+
def sort(key, by: nil, limit: nil, get: nil, order: nil, store: nil)
|
717
|
+
args = [:sort, key]
|
718
|
+
args << "BY" << by if by
|
668
719
|
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
args.concat(["LIMIT"] + limit) if limit
|
720
|
+
if limit
|
721
|
+
args << "LIMIT"
|
722
|
+
args.concat(limit)
|
723
|
+
end
|
674
724
|
|
675
|
-
get = Array(
|
676
|
-
|
725
|
+
get = Array(get)
|
726
|
+
get.each do |item|
|
727
|
+
args << "GET" << item
|
728
|
+
end
|
677
729
|
|
678
|
-
order = options[:order]
|
679
730
|
args.concat(order.split(" ")) if order
|
680
|
-
|
681
|
-
store = options[:store]
|
682
|
-
args.concat(["STORE", store]) if store
|
731
|
+
args << "STORE" << store if store
|
683
732
|
|
684
733
|
synchronize do |client|
|
685
|
-
client.call(
|
734
|
+
client.call(args) do |reply|
|
686
735
|
if get.size > 1 && !store
|
687
|
-
if reply
|
688
|
-
reply.each_slice(get.size).to_a
|
689
|
-
end
|
736
|
+
reply.each_slice(get.size).to_a if reply
|
690
737
|
else
|
691
738
|
reply
|
692
739
|
end
|
@@ -786,27 +833,21 @@ class Redis
|
|
786
833
|
# - `:px => Integer`: Set the specified expire time, in milliseconds.
|
787
834
|
# - `:nx => true`: Only set the key if it does not already exist.
|
788
835
|
# - `:xx => true`: Only set the key if it already exist.
|
836
|
+
# - `:keepttl => true`: Retain the time to live associated with the key.
|
789
837
|
# @return [String, Boolean] `"OK"` or true, false if `:nx => true` or `:xx => true`
|
790
|
-
def set(key, value,
|
791
|
-
args = []
|
792
|
-
|
793
|
-
|
794
|
-
args
|
795
|
-
|
796
|
-
|
797
|
-
args.concat(["PX", px]) if px
|
798
|
-
|
799
|
-
nx = options[:nx]
|
800
|
-
args.concat(["NX"]) if nx
|
801
|
-
|
802
|
-
xx = options[:xx]
|
803
|
-
args.concat(["XX"]) if xx
|
838
|
+
def set(key, value, ex: nil, px: nil, nx: nil, xx: nil, keepttl: nil)
|
839
|
+
args = [:set, key, value.to_s]
|
840
|
+
args << "EX" << ex if ex
|
841
|
+
args << "PX" << px if px
|
842
|
+
args << "NX" if nx
|
843
|
+
args << "XX" if xx
|
844
|
+
args << "KEEPTTL" if keepttl
|
804
845
|
|
805
846
|
synchronize do |client|
|
806
847
|
if nx || xx
|
807
|
-
client.call(
|
848
|
+
client.call(args, &BoolifySet)
|
808
849
|
else
|
809
|
-
client.call(
|
850
|
+
client.call(args)
|
810
851
|
end
|
811
852
|
end
|
812
853
|
end
|
@@ -888,7 +929,7 @@ class Redis
|
|
888
929
|
# @see #mapped_msetnx
|
889
930
|
def msetnx(*args)
|
890
931
|
synchronize do |client|
|
891
|
-
client.call([:msetnx
|
932
|
+
client.call([:msetnx, *args], &Boolify)
|
892
933
|
end
|
893
934
|
end
|
894
935
|
|
@@ -928,7 +969,7 @@ class Redis
|
|
928
969
|
# @see #mapped_mget
|
929
970
|
def mget(*keys, &blk)
|
930
971
|
synchronize do |client|
|
931
|
-
client.call([:mget
|
972
|
+
client.call([:mget, *keys], &blk)
|
932
973
|
end
|
933
974
|
end
|
934
975
|
|
@@ -944,7 +985,7 @@ class Redis
|
|
944
985
|
# @see #mget
|
945
986
|
def mapped_mget(*keys)
|
946
987
|
mget(*keys) do |reply|
|
947
|
-
if reply.
|
988
|
+
if reply.is_a?(Array)
|
948
989
|
Hash[keys.zip(reply)]
|
949
990
|
else
|
950
991
|
reply
|
@@ -1031,7 +1072,7 @@ class Redis
|
|
1031
1072
|
# @return [Integer] the length of the string stored in `destkey`
|
1032
1073
|
def bitop(operation, destkey, *keys)
|
1033
1074
|
synchronize do |client|
|
1034
|
-
client.call([:bitop, operation, destkey
|
1075
|
+
client.call([:bitop, operation, destkey, *keys])
|
1035
1076
|
end
|
1036
1077
|
end
|
1037
1078
|
|
@@ -1043,10 +1084,8 @@ class Redis
|
|
1043
1084
|
# @param [Integer] stop stop index
|
1044
1085
|
# @return [Integer] the position of the first 1/0 bit.
|
1045
1086
|
# -1 if looking for 1 and it is not found or start and stop are given.
|
1046
|
-
def bitpos(key, bit, start=nil, stop=nil)
|
1047
|
-
|
1048
|
-
raise(ArgumentError, 'stop parameter specified without start parameter')
|
1049
|
-
end
|
1087
|
+
def bitpos(key, bit, start = nil, stop = nil)
|
1088
|
+
raise(ArgumentError, 'stop parameter specified without start parameter') if stop && !start
|
1050
1089
|
|
1051
1090
|
synchronize do |client|
|
1052
1091
|
command = [:bitpos, key, bit]
|
@@ -1133,23 +1172,29 @@ class Redis
|
|
1133
1172
|
end
|
1134
1173
|
end
|
1135
1174
|
|
1136
|
-
# Remove and get the first
|
1175
|
+
# Remove and get the first elements in a list.
|
1137
1176
|
#
|
1138
1177
|
# @param [String] key
|
1139
|
-
# @
|
1140
|
-
|
1178
|
+
# @param [Integer] count number of elements to remove
|
1179
|
+
# @return [String, Array<String>] the values of the first elements
|
1180
|
+
def lpop(key, count = nil)
|
1141
1181
|
synchronize do |client|
|
1142
|
-
|
1182
|
+
command = [:lpop, key]
|
1183
|
+
command << count if count
|
1184
|
+
client.call(command)
|
1143
1185
|
end
|
1144
1186
|
end
|
1145
1187
|
|
1146
|
-
# Remove and get the last
|
1188
|
+
# Remove and get the last elements in a list.
|
1147
1189
|
#
|
1148
1190
|
# @param [String] key
|
1149
|
-
# @
|
1150
|
-
|
1191
|
+
# @param [Integer] count number of elements to remove
|
1192
|
+
# @return [String, Array<String>] the values of the last elements
|
1193
|
+
def rpop(key, count = nil)
|
1151
1194
|
synchronize do |client|
|
1152
|
-
|
1195
|
+
command = [:rpop, key]
|
1196
|
+
command << count if count
|
1197
|
+
client.call(command)
|
1153
1198
|
end
|
1154
1199
|
end
|
1155
1200
|
|
@@ -1240,15 +1285,7 @@ class Redis
|
|
1240
1285
|
# @return [nil, String]
|
1241
1286
|
# - `nil` when the operation timed out
|
1242
1287
|
# - the element was popped and pushed otherwise
|
1243
|
-
def brpoplpush(source, destination,
|
1244
|
-
case options
|
1245
|
-
when Integer
|
1246
|
-
# Issue deprecation notice in obnoxious mode...
|
1247
|
-
options = { :timeout => options }
|
1248
|
-
end
|
1249
|
-
|
1250
|
-
timeout = options[:timeout] || 0
|
1251
|
-
|
1288
|
+
def brpoplpush(source, destination, deprecated_timeout = 0, timeout: deprecated_timeout)
|
1252
1289
|
synchronize do |client|
|
1253
1290
|
command = [:brpoplpush, source, destination, timeout]
|
1254
1291
|
timeout += client.timeout if timeout > 0
|
@@ -1455,7 +1492,7 @@ class Redis
|
|
1455
1492
|
# @return [Array<String>] members in the difference
|
1456
1493
|
def sdiff(*keys)
|
1457
1494
|
synchronize do |client|
|
1458
|
-
client.call([:sdiff
|
1495
|
+
client.call([:sdiff, *keys])
|
1459
1496
|
end
|
1460
1497
|
end
|
1461
1498
|
|
@@ -1466,7 +1503,7 @@ class Redis
|
|
1466
1503
|
# @return [Integer] number of elements in the resulting set
|
1467
1504
|
def sdiffstore(destination, *keys)
|
1468
1505
|
synchronize do |client|
|
1469
|
-
client.call([:sdiffstore, destination
|
1506
|
+
client.call([:sdiffstore, destination, *keys])
|
1470
1507
|
end
|
1471
1508
|
end
|
1472
1509
|
|
@@ -1476,7 +1513,7 @@ class Redis
|
|
1476
1513
|
# @return [Array<String>] members in the intersection
|
1477
1514
|
def sinter(*keys)
|
1478
1515
|
synchronize do |client|
|
1479
|
-
client.call([:sinter
|
1516
|
+
client.call([:sinter, *keys])
|
1480
1517
|
end
|
1481
1518
|
end
|
1482
1519
|
|
@@ -1487,7 +1524,7 @@ class Redis
|
|
1487
1524
|
# @return [Integer] number of elements in the resulting set
|
1488
1525
|
def sinterstore(destination, *keys)
|
1489
1526
|
synchronize do |client|
|
1490
|
-
client.call([:sinterstore, destination
|
1527
|
+
client.call([:sinterstore, destination, *keys])
|
1491
1528
|
end
|
1492
1529
|
end
|
1493
1530
|
|
@@ -1497,7 +1534,7 @@ class Redis
|
|
1497
1534
|
# @return [Array<String>] members in the union
|
1498
1535
|
def sunion(*keys)
|
1499
1536
|
synchronize do |client|
|
1500
|
-
client.call([:sunion
|
1537
|
+
client.call([:sunion, *keys])
|
1501
1538
|
end
|
1502
1539
|
end
|
1503
1540
|
|
@@ -1508,7 +1545,7 @@ class Redis
|
|
1508
1545
|
# @return [Integer] number of elements in the resulting set
|
1509
1546
|
def sunionstore(destination, *keys)
|
1510
1547
|
synchronize do |client|
|
1511
|
-
client.call([:sunionstore, destination
|
1548
|
+
client.call([:sunionstore, destination, *keys])
|
1512
1549
|
end
|
1513
1550
|
end
|
1514
1551
|
|
@@ -1557,31 +1594,20 @@ class Redis
|
|
1557
1594
|
# pairs that were **added** to the sorted set.
|
1558
1595
|
# - `Float` when option :incr is specified, holding the score of the member
|
1559
1596
|
# after incrementing it.
|
1560
|
-
def zadd(key, *args
|
1561
|
-
|
1562
|
-
if
|
1563
|
-
|
1564
|
-
|
1565
|
-
|
1566
|
-
zadd_options << "NX" if nx
|
1567
|
-
|
1568
|
-
xx = options[:xx]
|
1569
|
-
zadd_options << "XX" if xx
|
1570
|
-
|
1571
|
-
ch = options[:ch]
|
1572
|
-
zadd_options << "CH" if ch
|
1573
|
-
|
1574
|
-
incr = options[:incr]
|
1575
|
-
zadd_options << "INCR" if incr
|
1576
|
-
end
|
1597
|
+
def zadd(key, *args, nx: nil, xx: nil, ch: nil, incr: nil)
|
1598
|
+
command = [:zadd, key]
|
1599
|
+
command << "NX" if nx
|
1600
|
+
command << "XX" if xx
|
1601
|
+
command << "CH" if ch
|
1602
|
+
command << "INCR" if incr
|
1577
1603
|
|
1578
1604
|
synchronize do |client|
|
1579
1605
|
if args.size == 1 && args[0].is_a?(Array)
|
1580
1606
|
# Variadic: return float if INCR, integer if !INCR
|
1581
|
-
client.call(
|
1607
|
+
client.call(command + args[0], &(incr ? Floatify : nil))
|
1582
1608
|
elsif args.size == 2
|
1583
1609
|
# Single pair: return float if INCR, boolean if !INCR
|
1584
|
-
client.call(
|
1610
|
+
client.call(command + args, &(incr ? Floatify : Boolify))
|
1585
1611
|
else
|
1586
1612
|
raise ArgumentError, "wrong number of arguments"
|
1587
1613
|
end
|
@@ -1752,10 +1778,8 @@ class Redis
|
|
1752
1778
|
# @return [Array<String>, Array<[String, Float]>]
|
1753
1779
|
# - when `:with_scores` is not specified, an array of members
|
1754
1780
|
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
1755
|
-
def zrange(key, start, stop,
|
1756
|
-
args = []
|
1757
|
-
|
1758
|
-
with_scores = options[:with_scores] || options[:withscores]
|
1781
|
+
def zrange(key, start, stop, withscores: false, with_scores: withscores)
|
1782
|
+
args = [:zrange, key, start, stop]
|
1759
1783
|
|
1760
1784
|
if with_scores
|
1761
1785
|
args << "WITHSCORES"
|
@@ -1763,7 +1787,7 @@ class Redis
|
|
1763
1787
|
end
|
1764
1788
|
|
1765
1789
|
synchronize do |client|
|
1766
|
-
client.call(
|
1790
|
+
client.call(args, &block)
|
1767
1791
|
end
|
1768
1792
|
end
|
1769
1793
|
|
@@ -1778,10 +1802,8 @@ class Redis
|
|
1778
1802
|
# # => [["b", 64.0], ["a", 32.0]]
|
1779
1803
|
#
|
1780
1804
|
# @see #zrange
|
1781
|
-
def zrevrange(key, start, stop,
|
1782
|
-
args = []
|
1783
|
-
|
1784
|
-
with_scores = options[:with_scores] || options[:withscores]
|
1805
|
+
def zrevrange(key, start, stop, withscores: false, with_scores: withscores)
|
1806
|
+
args = [:zrevrange, key, start, stop]
|
1785
1807
|
|
1786
1808
|
if with_scores
|
1787
1809
|
args << "WITHSCORES"
|
@@ -1789,7 +1811,7 @@ class Redis
|
|
1789
1811
|
end
|
1790
1812
|
|
1791
1813
|
synchronize do |client|
|
1792
|
-
client.call(
|
1814
|
+
client.call(args, &block)
|
1793
1815
|
end
|
1794
1816
|
end
|
1795
1817
|
|
@@ -1880,14 +1902,16 @@ class Redis
|
|
1880
1902
|
# `count` members
|
1881
1903
|
#
|
1882
1904
|
# @return [Array<String>, Array<[String, Float]>]
|
1883
|
-
def zrangebylex(key, min, max,
|
1884
|
-
args = []
|
1905
|
+
def zrangebylex(key, min, max, limit: nil)
|
1906
|
+
args = [:zrangebylex, key, min, max]
|
1885
1907
|
|
1886
|
-
|
1887
|
-
|
1908
|
+
if limit
|
1909
|
+
args << "LIMIT"
|
1910
|
+
args.concat(limit)
|
1911
|
+
end
|
1888
1912
|
|
1889
1913
|
synchronize do |client|
|
1890
|
-
client.call(
|
1914
|
+
client.call(args)
|
1891
1915
|
end
|
1892
1916
|
end
|
1893
1917
|
|
@@ -1902,14 +1926,16 @@ class Redis
|
|
1902
1926
|
# # => ["abbygail", "abby"]
|
1903
1927
|
#
|
1904
1928
|
# @see #zrangebylex
|
1905
|
-
def zrevrangebylex(key, max, min,
|
1906
|
-
args = []
|
1929
|
+
def zrevrangebylex(key, max, min, limit: nil)
|
1930
|
+
args = [:zrevrangebylex, key, max, min]
|
1907
1931
|
|
1908
|
-
|
1909
|
-
|
1932
|
+
if limit
|
1933
|
+
args << "LIMIT"
|
1934
|
+
args.concat(limit)
|
1935
|
+
end
|
1910
1936
|
|
1911
1937
|
synchronize do |client|
|
1912
|
-
client.call(
|
1938
|
+
client.call(args)
|
1913
1939
|
end
|
1914
1940
|
end
|
1915
1941
|
|
@@ -1940,21 +1966,21 @@ class Redis
|
|
1940
1966
|
# @return [Array<String>, Array<[String, Float]>]
|
1941
1967
|
# - when `:with_scores` is not specified, an array of members
|
1942
1968
|
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
1943
|
-
def zrangebyscore(key, min, max,
|
1944
|
-
args = []
|
1945
|
-
|
1946
|
-
with_scores = options[:with_scores] || options[:withscores]
|
1969
|
+
def zrangebyscore(key, min, max, withscores: false, with_scores: withscores, limit: nil)
|
1970
|
+
args = [:zrangebyscore, key, min, max]
|
1947
1971
|
|
1948
1972
|
if with_scores
|
1949
1973
|
args << "WITHSCORES"
|
1950
1974
|
block = FloatifyPairs
|
1951
1975
|
end
|
1952
1976
|
|
1953
|
-
|
1954
|
-
|
1977
|
+
if limit
|
1978
|
+
args << "LIMIT"
|
1979
|
+
args.concat(limit)
|
1980
|
+
end
|
1955
1981
|
|
1956
1982
|
synchronize do |client|
|
1957
|
-
client.call(
|
1983
|
+
client.call(args, &block)
|
1958
1984
|
end
|
1959
1985
|
end
|
1960
1986
|
|
@@ -1972,21 +1998,21 @@ class Redis
|
|
1972
1998
|
# # => [["b", 64.0], ["a", 32.0]]
|
1973
1999
|
#
|
1974
2000
|
# @see #zrangebyscore
|
1975
|
-
def zrevrangebyscore(key, max, min,
|
1976
|
-
args = []
|
1977
|
-
|
1978
|
-
with_scores = options[:with_scores] || options[:withscores]
|
2001
|
+
def zrevrangebyscore(key, max, min, withscores: false, with_scores: withscores, limit: nil)
|
2002
|
+
args = [:zrevrangebyscore, key, max, min]
|
1979
2003
|
|
1980
2004
|
if with_scores
|
1981
|
-
args <<
|
2005
|
+
args << "WITHSCORES"
|
1982
2006
|
block = FloatifyPairs
|
1983
2007
|
end
|
1984
2008
|
|
1985
|
-
|
1986
|
-
|
2009
|
+
if limit
|
2010
|
+
args << "LIMIT"
|
2011
|
+
args.concat(limit)
|
2012
|
+
end
|
1987
2013
|
|
1988
2014
|
synchronize do |client|
|
1989
|
-
client.call(
|
2015
|
+
client.call(args, &block)
|
1990
2016
|
end
|
1991
2017
|
end
|
1992
2018
|
|
@@ -2050,17 +2076,18 @@ class Redis
|
|
2050
2076
|
# sorted sets
|
2051
2077
|
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
2052
2078
|
# @return [Integer] number of elements in the resulting sorted set
|
2053
|
-
def zinterstore(destination, keys,
|
2054
|
-
args = []
|
2079
|
+
def zinterstore(destination, keys, weights: nil, aggregate: nil)
|
2080
|
+
args = [:zinterstore, destination, keys.size, *keys]
|
2055
2081
|
|
2056
|
-
|
2057
|
-
|
2082
|
+
if weights
|
2083
|
+
args << "WEIGHTS"
|
2084
|
+
args.concat(weights)
|
2085
|
+
end
|
2058
2086
|
|
2059
|
-
aggregate
|
2060
|
-
args.concat(["AGGREGATE", aggregate]) if aggregate
|
2087
|
+
args << "AGGREGATE" << aggregate if aggregate
|
2061
2088
|
|
2062
2089
|
synchronize do |client|
|
2063
|
-
client.call(
|
2090
|
+
client.call(args)
|
2064
2091
|
end
|
2065
2092
|
end
|
2066
2093
|
|
@@ -2077,17 +2104,18 @@ class Redis
|
|
2077
2104
|
# sorted sets
|
2078
2105
|
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
2079
2106
|
# @return [Integer] number of elements in the resulting sorted set
|
2080
|
-
def zunionstore(destination, keys,
|
2081
|
-
args = []
|
2107
|
+
def zunionstore(destination, keys, weights: nil, aggregate: nil)
|
2108
|
+
args = [:zunionstore, destination, keys.size, *keys]
|
2082
2109
|
|
2083
|
-
|
2084
|
-
|
2110
|
+
if weights
|
2111
|
+
args << "WEIGHTS"
|
2112
|
+
args.concat(weights)
|
2113
|
+
end
|
2085
2114
|
|
2086
|
-
aggregate
|
2087
|
-
args.concat(["AGGREGATE", aggregate]) if aggregate
|
2115
|
+
args << "AGGREGATE" << aggregate if aggregate
|
2088
2116
|
|
2089
2117
|
synchronize do |client|
|
2090
|
-
client.call(
|
2118
|
+
client.call(args)
|
2091
2119
|
end
|
2092
2120
|
end
|
2093
2121
|
|
@@ -2101,15 +2129,20 @@ class Redis
|
|
2101
2129
|
end
|
2102
2130
|
end
|
2103
2131
|
|
2104
|
-
# Set
|
2132
|
+
# Set one or more hash values.
|
2133
|
+
#
|
2134
|
+
# @example
|
2135
|
+
# redis.hset("hash", "f1", "v1", "f2", "v2") # => 2
|
2136
|
+
# redis.hset("hash", { "f1" => "v1", "f2" => "v2" }) # => 2
|
2105
2137
|
#
|
2106
2138
|
# @param [String] key
|
2107
|
-
# @param [String]
|
2108
|
-
# @
|
2109
|
-
|
2110
|
-
|
2139
|
+
# @param [Array<String> | Hash<String, String>] attrs array or hash of fields and values
|
2140
|
+
# @return [Integer] The number of fields that were added to the hash
|
2141
|
+
def hset(key, *attrs)
|
2142
|
+
attrs = attrs.first.flatten if attrs.size == 1 && attrs.first.is_a?(Hash)
|
2143
|
+
|
2111
2144
|
synchronize do |client|
|
2112
|
-
client.call([:hset, key,
|
2145
|
+
client.call([:hset, key, *attrs])
|
2113
2146
|
end
|
2114
2147
|
end
|
2115
2148
|
|
@@ -2198,7 +2231,7 @@ class Redis
|
|
2198
2231
|
# @see #hmget
|
2199
2232
|
def mapped_hmget(key, *fields)
|
2200
2233
|
hmget(key, *fields) do |reply|
|
2201
|
-
if reply.
|
2234
|
+
if reply.is_a?(Array)
|
2202
2235
|
Hash[fields.zip(reply)]
|
2203
2236
|
else
|
2204
2237
|
reply
|
@@ -2291,20 +2324,21 @@ class Redis
|
|
2291
2324
|
|
2292
2325
|
def subscribed?
|
2293
2326
|
synchronize do |client|
|
2294
|
-
client.
|
2327
|
+
client.is_a? SubscribedClient
|
2295
2328
|
end
|
2296
2329
|
end
|
2297
2330
|
|
2298
2331
|
# Listen for messages published to the given channels.
|
2299
2332
|
def subscribe(*channels, &block)
|
2300
|
-
synchronize do |
|
2333
|
+
synchronize do |_client|
|
2301
2334
|
_subscription(:subscribe, 0, channels, block)
|
2302
2335
|
end
|
2303
2336
|
end
|
2304
2337
|
|
2305
|
-
# Listen for messages published to the given channels. Throw a timeout error
|
2338
|
+
# Listen for messages published to the given channels. Throw a timeout error
|
2339
|
+
# if there is no messages for a timeout period.
|
2306
2340
|
def subscribe_with_timeout(timeout, *channels, &block)
|
2307
|
-
synchronize do |
|
2341
|
+
synchronize do |_client|
|
2308
2342
|
_subscription(:subscribe_with_timeout, timeout, channels, block)
|
2309
2343
|
end
|
2310
2344
|
end
|
@@ -2312,21 +2346,23 @@ class Redis
|
|
2312
2346
|
# Stop listening for messages posted to the given channels.
|
2313
2347
|
def unsubscribe(*channels)
|
2314
2348
|
synchronize do |client|
|
2315
|
-
raise
|
2349
|
+
raise "Can't unsubscribe if not subscribed." unless subscribed?
|
2350
|
+
|
2316
2351
|
client.unsubscribe(*channels)
|
2317
2352
|
end
|
2318
2353
|
end
|
2319
2354
|
|
2320
2355
|
# Listen for messages published to channels matching the given patterns.
|
2321
2356
|
def psubscribe(*channels, &block)
|
2322
|
-
synchronize do |
|
2357
|
+
synchronize do |_client|
|
2323
2358
|
_subscription(:psubscribe, 0, channels, block)
|
2324
2359
|
end
|
2325
2360
|
end
|
2326
2361
|
|
2327
|
-
# Listen for messages published to channels matching the given patterns.
|
2362
|
+
# Listen for messages published to channels matching the given patterns.
|
2363
|
+
# Throw a timeout error if there is no messages for a timeout period.
|
2328
2364
|
def psubscribe_with_timeout(timeout, *channels, &block)
|
2329
|
-
synchronize do |
|
2365
|
+
synchronize do |_client|
|
2330
2366
|
_subscription(:psubscribe_with_timeout, timeout, channels, block)
|
2331
2367
|
end
|
2332
2368
|
end
|
@@ -2334,7 +2370,8 @@ class Redis
|
|
2334
2370
|
# Stop listening for messages posted to channels matching the given patterns.
|
2335
2371
|
def punsubscribe(*channels)
|
2336
2372
|
synchronize do |client|
|
2337
|
-
raise
|
2373
|
+
raise "Can't unsubscribe if not subscribed." unless subscribed?
|
2374
|
+
|
2338
2375
|
client.punsubscribe(*channels)
|
2339
2376
|
end
|
2340
2377
|
end
|
@@ -2379,7 +2416,7 @@ class Redis
|
|
2379
2416
|
# @see #multi
|
2380
2417
|
def watch(*keys)
|
2381
2418
|
synchronize do |client|
|
2382
|
-
res = client.call([:watch
|
2419
|
+
res = client.call([:watch, *keys])
|
2383
2420
|
|
2384
2421
|
if block_given?
|
2385
2422
|
begin
|
@@ -2409,14 +2446,13 @@ class Redis
|
|
2409
2446
|
end
|
2410
2447
|
|
2411
2448
|
def pipelined
|
2412
|
-
synchronize do |
|
2449
|
+
synchronize do |prior_client|
|
2413
2450
|
begin
|
2414
|
-
|
2415
|
-
original, @client = @client, pipeline
|
2451
|
+
@client = Pipeline.new(prior_client)
|
2416
2452
|
yield(self)
|
2417
|
-
|
2453
|
+
prior_client.call_pipeline(@client)
|
2418
2454
|
ensure
|
2419
|
-
@client =
|
2455
|
+
@client = prior_client
|
2420
2456
|
end
|
2421
2457
|
end
|
2422
2458
|
end
|
@@ -2452,17 +2488,16 @@ class Redis
|
|
2452
2488
|
# @see #watch
|
2453
2489
|
# @see #unwatch
|
2454
2490
|
def multi
|
2455
|
-
synchronize do |
|
2491
|
+
synchronize do |prior_client|
|
2456
2492
|
if !block_given?
|
2457
|
-
|
2493
|
+
prior_client.call([:multi])
|
2458
2494
|
else
|
2459
2495
|
begin
|
2460
|
-
|
2461
|
-
original, @client = @client, pipeline
|
2496
|
+
@client = Pipeline::Multi.new(prior_client)
|
2462
2497
|
yield(self)
|
2463
|
-
|
2498
|
+
prior_client.call_pipeline(@client)
|
2464
2499
|
ensure
|
2465
|
-
@client =
|
2500
|
+
@client = prior_client
|
2466
2501
|
end
|
2467
2502
|
end
|
2468
2503
|
end
|
@@ -2609,18 +2644,13 @@ class Redis
|
|
2609
2644
|
_eval(:evalsha, args)
|
2610
2645
|
end
|
2611
2646
|
|
2612
|
-
def _scan(command, cursor, args,
|
2647
|
+
def _scan(command, cursor, args, match: nil, count: nil, type: nil, &block)
|
2613
2648
|
# SSCAN/ZSCAN/HSCAN already prepend the key to +args+.
|
2614
2649
|
|
2615
2650
|
args << cursor
|
2616
|
-
|
2617
|
-
|
2618
|
-
|
2619
|
-
end
|
2620
|
-
|
2621
|
-
if count = options[:count]
|
2622
|
-
args.concat(["COUNT", count])
|
2623
|
-
end
|
2651
|
+
args << "MATCH" << match if match
|
2652
|
+
args << "COUNT" << count if count
|
2653
|
+
args << "TYPE" << type if type
|
2624
2654
|
|
2625
2655
|
synchronize do |client|
|
2626
2656
|
client.call([command] + args, &block)
|
@@ -2635,15 +2665,19 @@ class Redis
|
|
2635
2665
|
# @example Retrieve a batch of keys matching a pattern
|
2636
2666
|
# redis.scan(4, :match => "key:1?")
|
2637
2667
|
# # => ["92", ["key:13", "key:18"]]
|
2668
|
+
# @example Retrieve a batch of keys of a certain type
|
2669
|
+
# redis.scan(92, :type => "zset")
|
2670
|
+
# # => ["173", ["sortedset:14", "sortedset:78"]]
|
2638
2671
|
#
|
2639
2672
|
# @param [String, Integer] cursor the cursor of the iteration
|
2640
2673
|
# @param [Hash] options
|
2641
2674
|
# - `:match => String`: only return keys matching the pattern
|
2642
2675
|
# - `:count => Integer`: return count keys at most per iteration
|
2676
|
+
# - `:type => String`: return keys only of the given type
|
2643
2677
|
#
|
2644
2678
|
# @return [String, Array<String>] the next cursor and all found keys
|
2645
|
-
def scan(cursor, options
|
2646
|
-
_scan(:scan, cursor, [], options)
|
2679
|
+
def scan(cursor, **options)
|
2680
|
+
_scan(:scan, cursor, [], **options)
|
2647
2681
|
end
|
2648
2682
|
|
2649
2683
|
# Scan the keyspace
|
@@ -2655,17 +2689,23 @@ class Redis
|
|
2655
2689
|
# redis.scan_each(:match => "key:1?") {|key| puts key}
|
2656
2690
|
# # => key:13
|
2657
2691
|
# # => key:18
|
2692
|
+
# @example Execute block for each key of a type
|
2693
|
+
# redis.scan_each(:type => "hash") {|key| puts redis.type(key)}
|
2694
|
+
# # => "hash"
|
2695
|
+
# # => "hash"
|
2658
2696
|
#
|
2659
2697
|
# @param [Hash] options
|
2660
2698
|
# - `:match => String`: only return keys matching the pattern
|
2661
2699
|
# - `:count => Integer`: return count keys at most per iteration
|
2700
|
+
# - `:type => String`: return keys only of the given type
|
2662
2701
|
#
|
2663
2702
|
# @return [Enumerator] an enumerator for all found keys
|
2664
|
-
def scan_each(options
|
2665
|
-
return to_enum(:scan_each, options) unless block_given?
|
2703
|
+
def scan_each(**options, &block)
|
2704
|
+
return to_enum(:scan_each, **options) unless block_given?
|
2705
|
+
|
2666
2706
|
cursor = 0
|
2667
2707
|
loop do
|
2668
|
-
cursor, keys = scan(cursor, options)
|
2708
|
+
cursor, keys = scan(cursor, **options)
|
2669
2709
|
keys.each(&block)
|
2670
2710
|
break if cursor == "0"
|
2671
2711
|
end
|
@@ -2682,8 +2722,8 @@ class Redis
|
|
2682
2722
|
# - `:count => Integer`: return count keys at most per iteration
|
2683
2723
|
#
|
2684
2724
|
# @return [String, Array<[String, String]>] the next cursor and all found keys
|
2685
|
-
def hscan(key, cursor, options
|
2686
|
-
_scan(:hscan, cursor, [key], options) do |reply|
|
2725
|
+
def hscan(key, cursor, **options)
|
2726
|
+
_scan(:hscan, cursor, [key], **options) do |reply|
|
2687
2727
|
[reply[0], reply[1].each_slice(2).to_a]
|
2688
2728
|
end
|
2689
2729
|
end
|
@@ -2699,11 +2739,12 @@ class Redis
|
|
2699
2739
|
# - `:count => Integer`: return count keys at most per iteration
|
2700
2740
|
#
|
2701
2741
|
# @return [Enumerator] an enumerator for all found keys
|
2702
|
-
def hscan_each(key, options
|
2703
|
-
return to_enum(:hscan_each, key, options) unless block_given?
|
2742
|
+
def hscan_each(key, **options, &block)
|
2743
|
+
return to_enum(:hscan_each, key, **options) unless block_given?
|
2744
|
+
|
2704
2745
|
cursor = 0
|
2705
2746
|
loop do
|
2706
|
-
cursor, values = hscan(key, cursor, options)
|
2747
|
+
cursor, values = hscan(key, cursor, **options)
|
2707
2748
|
values.each(&block)
|
2708
2749
|
break if cursor == "0"
|
2709
2750
|
end
|
@@ -2721,8 +2762,8 @@ class Redis
|
|
2721
2762
|
#
|
2722
2763
|
# @return [String, Array<[String, Float]>] the next cursor and all found
|
2723
2764
|
# members and scores
|
2724
|
-
def zscan(key, cursor, options
|
2725
|
-
_scan(:zscan, cursor, [key], options) do |reply|
|
2765
|
+
def zscan(key, cursor, **options)
|
2766
|
+
_scan(:zscan, cursor, [key], **options) do |reply|
|
2726
2767
|
[reply[0], FloatifyPairs.call(reply[1])]
|
2727
2768
|
end
|
2728
2769
|
end
|
@@ -2738,11 +2779,12 @@ class Redis
|
|
2738
2779
|
# - `:count => Integer`: return count keys at most per iteration
|
2739
2780
|
#
|
2740
2781
|
# @return [Enumerator] an enumerator for all found scores and members
|
2741
|
-
def zscan_each(key, options
|
2742
|
-
return to_enum(:zscan_each, key, options) unless block_given?
|
2782
|
+
def zscan_each(key, **options, &block)
|
2783
|
+
return to_enum(:zscan_each, key, **options) unless block_given?
|
2784
|
+
|
2743
2785
|
cursor = 0
|
2744
2786
|
loop do
|
2745
|
-
cursor, values = zscan(key, cursor, options)
|
2787
|
+
cursor, values = zscan(key, cursor, **options)
|
2746
2788
|
values.each(&block)
|
2747
2789
|
break if cursor == "0"
|
2748
2790
|
end
|
@@ -2759,8 +2801,8 @@ class Redis
|
|
2759
2801
|
# - `:count => Integer`: return count keys at most per iteration
|
2760
2802
|
#
|
2761
2803
|
# @return [String, Array<String>] the next cursor and all found members
|
2762
|
-
def sscan(key, cursor, options
|
2763
|
-
_scan(:sscan, cursor, [key], options)
|
2804
|
+
def sscan(key, cursor, **options)
|
2805
|
+
_scan(:sscan, cursor, [key], **options)
|
2764
2806
|
end
|
2765
2807
|
|
2766
2808
|
# Scan a set
|
@@ -2774,11 +2816,12 @@ class Redis
|
|
2774
2816
|
# - `:count => Integer`: return count keys at most per iteration
|
2775
2817
|
#
|
2776
2818
|
# @return [Enumerator] an enumerator for all keys in the set
|
2777
|
-
def sscan_each(key, options
|
2778
|
-
return to_enum(:sscan_each, key, options) unless block_given?
|
2819
|
+
def sscan_each(key, **options, &block)
|
2820
|
+
return to_enum(:sscan_each, key, **options) unless block_given?
|
2821
|
+
|
2779
2822
|
cursor = 0
|
2780
2823
|
loop do
|
2781
|
-
cursor, keys = sscan(key, cursor, options)
|
2824
|
+
cursor, keys = sscan(key, cursor, **options)
|
2782
2825
|
keys.each(&block)
|
2783
2826
|
break if cursor == "0"
|
2784
2827
|
end
|
@@ -2842,12 +2885,12 @@ class Redis
|
|
2842
2885
|
end
|
2843
2886
|
end
|
2844
2887
|
|
2845
|
-
|
2846
2888
|
# Query a sorted set representing a geospatial index to fetch members matching a
|
2847
2889
|
# given maximum distance from a point
|
2848
2890
|
#
|
2849
2891
|
# @param [Array] args key, longitude, latitude, radius, unit(m|km|ft|mi)
|
2850
|
-
# @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest
|
2892
|
+
# @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest
|
2893
|
+
# or the farthest to the nearest relative to the center
|
2851
2894
|
# @param [Integer] count limit the results to the first N matching items
|
2852
2895
|
# @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
|
2853
2896
|
# @return [Array<String>] may be changed with `options`
|
@@ -2864,7 +2907,8 @@ class Redis
|
|
2864
2907
|
# given maximum distance from an already existing member
|
2865
2908
|
#
|
2866
2909
|
# @param [Array] args key, member, radius, unit(m|km|ft|mi)
|
2867
|
-
# @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest or the farthest
|
2910
|
+
# @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest or the farthest
|
2911
|
+
# to the nearest relative to the center
|
2868
2912
|
# @param [Integer] count limit the results to the first N matching items
|
2869
2913
|
# @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
|
2870
2914
|
# @return [Array<String>] may be changed with `options`
|
@@ -2881,7 +2925,8 @@ class Redis
|
|
2881
2925
|
#
|
2882
2926
|
# @param [String] key
|
2883
2927
|
# @param [String, Array<String>] member one member or array of members
|
2884
|
-
# @return [Array<Array<String>, nil>] returns array of elements, where each
|
2928
|
+
# @return [Array<Array<String>, nil>] returns array of elements, where each
|
2929
|
+
# element is either array of longitude and latitude or nil
|
2885
2930
|
def geopos(key, member)
|
2886
2931
|
synchronize do |client|
|
2887
2932
|
client.call([:geopos, key, member])
|
@@ -2945,10 +2990,14 @@ class Redis
|
|
2945
2990
|
# @option opts [Boolean] :approximate whether to add `~` modifier of maxlen or not
|
2946
2991
|
#
|
2947
2992
|
# @return [String] the entry id
|
2948
|
-
def xadd(key, entry,
|
2993
|
+
def xadd(key, entry, approximate: nil, maxlen: nil, id: '*')
|
2949
2994
|
args = [:xadd, key]
|
2950
|
-
|
2951
|
-
|
2995
|
+
if maxlen
|
2996
|
+
args << "MAXLEN"
|
2997
|
+
args << "~" if approximate
|
2998
|
+
args << maxlen
|
2999
|
+
end
|
3000
|
+
args << id
|
2952
3001
|
args.concat(entry.to_a.flatten)
|
2953
3002
|
synchronize { |client| client.call(args) }
|
2954
3003
|
end
|
@@ -3003,8 +3052,8 @@ class Redis
|
|
3003
3052
|
# @param count [Integer] the number of entries as limit
|
3004
3053
|
#
|
3005
3054
|
# @return [Array<Array<String, Hash>>] the ids and entries pairs
|
3006
|
-
def xrange(key, start = '-',
|
3007
|
-
args = [:xrange, key, start,
|
3055
|
+
def xrange(key, start = '-', range_end = '+', count: nil)
|
3056
|
+
args = [:xrange, key, start, range_end]
|
3008
3057
|
args.concat(['COUNT', count]) if count
|
3009
3058
|
synchronize { |client| client.call(args, &HashifyStreamEntries) }
|
3010
3059
|
end
|
@@ -3026,8 +3075,8 @@ class Redis
|
|
3026
3075
|
# @params count [Integer] the number of entries as limit
|
3027
3076
|
#
|
3028
3077
|
# @return [Array<Array<String, Hash>>] the ids and entries pairs
|
3029
|
-
def xrevrange(key,
|
3030
|
-
args = [:xrevrange, key,
|
3078
|
+
def xrevrange(key, range_end = '+', start = '-', count: nil)
|
3079
|
+
args = [:xrevrange, key, range_end, start]
|
3031
3080
|
args.concat(['COUNT', count]) if count
|
3032
3081
|
synchronize { |client| client.call(args, &HashifyStreamEntries) }
|
3033
3082
|
end
|
@@ -3119,12 +3168,12 @@ class Redis
|
|
3119
3168
|
# @option opts [Boolean] :noack whether message loss is acceptable or not
|
3120
3169
|
#
|
3121
3170
|
# @return [Hash{String => Hash{String => Hash}}] the entries
|
3122
|
-
def xreadgroup(group, consumer, keys, ids,
|
3171
|
+
def xreadgroup(group, consumer, keys, ids, count: nil, block: nil, noack: nil)
|
3123
3172
|
args = [:xreadgroup, 'GROUP', group, consumer]
|
3124
|
-
args << 'COUNT' <<
|
3125
|
-
args << 'BLOCK' <<
|
3126
|
-
args << 'NOACK' if
|
3127
|
-
_xread(args, keys, ids,
|
3173
|
+
args << 'COUNT' << count if count
|
3174
|
+
args << 'BLOCK' << block.to_i if block
|
3175
|
+
args << 'NOACK' if noack
|
3176
|
+
_xread(args, keys, ids, block)
|
3128
3177
|
end
|
3129
3178
|
|
3130
3179
|
# Removes one or multiple entries from the pending entries list of a stream consumer group.
|
@@ -3234,8 +3283,8 @@ class Redis
|
|
3234
3283
|
when "get-master-addr-by-name"
|
3235
3284
|
reply
|
3236
3285
|
else
|
3237
|
-
if reply.
|
3238
|
-
if reply[0].
|
3286
|
+
if reply.is_a?(Array)
|
3287
|
+
if reply[0].is_a?(Array)
|
3239
3288
|
reply.map(&Hashify)
|
3240
3289
|
else
|
3241
3290
|
Hashify.call(reply)
|
@@ -3259,12 +3308,17 @@ class Redis
|
|
3259
3308
|
def cluster(subcommand, *args)
|
3260
3309
|
subcommand = subcommand.to_s.downcase
|
3261
3310
|
block = case subcommand
|
3262
|
-
|
3263
|
-
|
3264
|
-
|
3265
|
-
|
3266
|
-
|
3267
|
-
|
3311
|
+
when 'slots'
|
3312
|
+
HashifyClusterSlots
|
3313
|
+
when 'nodes'
|
3314
|
+
HashifyClusterNodes
|
3315
|
+
when 'slaves'
|
3316
|
+
HashifyClusterSlaves
|
3317
|
+
when 'info'
|
3318
|
+
HashifyInfo
|
3319
|
+
else
|
3320
|
+
Noop
|
3321
|
+
end
|
3268
3322
|
|
3269
3323
|
# @see https://github.com/antirez/redis/blob/unstable/src/redis-trib.rb#L127 raw reply expected
|
3270
3324
|
block = Noop unless @cluster_mode
|
@@ -3299,21 +3353,21 @@ class Redis
|
|
3299
3353
|
return @original_client.connection_info if @cluster_mode
|
3300
3354
|
|
3301
3355
|
{
|
3302
|
-
host:
|
3303
|
-
port:
|
3304
|
-
db:
|
3305
|
-
id:
|
3356
|
+
host: @original_client.host,
|
3357
|
+
port: @original_client.port,
|
3358
|
+
db: @original_client.db,
|
3359
|
+
id: @original_client.id,
|
3306
3360
|
location: @original_client.location
|
3307
3361
|
}
|
3308
3362
|
end
|
3309
3363
|
|
3310
|
-
def method_missing(command, *args)
|
3364
|
+
def method_missing(command, *args) # rubocop:disable Style/MissingRespondToMissing
|
3311
3365
|
synchronize do |client|
|
3312
3366
|
client.call([command] + args)
|
3313
3367
|
end
|
3314
3368
|
end
|
3315
3369
|
|
3316
|
-
private
|
3370
|
+
private
|
3317
3371
|
|
3318
3372
|
# Commands returning 1 for true and 0 for false may be executed in a pipeline
|
3319
3373
|
# where the method call will return nil. Propagate the nil instead of falsely
|
@@ -3385,18 +3439,21 @@ private
|
|
3385
3439
|
end
|
3386
3440
|
}
|
3387
3441
|
|
3442
|
+
EMPTY_STREAM_RESPONSE = [nil].freeze
|
3443
|
+
private_constant :EMPTY_STREAM_RESPONSE
|
3444
|
+
|
3388
3445
|
HashifyStreamEntries = lambda { |reply|
|
3389
|
-
reply.map do |entry_id, values|
|
3446
|
+
reply.compact.map do |entry_id, values|
|
3390
3447
|
[entry_id, values.each_slice(2).to_h]
|
3391
3448
|
end
|
3392
3449
|
}
|
3393
3450
|
|
3394
3451
|
HashifyStreamPendings = lambda { |reply|
|
3395
3452
|
{
|
3396
|
-
'size'
|
3453
|
+
'size' => reply[0],
|
3397
3454
|
'min_entry_id' => reply[1],
|
3398
3455
|
'max_entry_id' => reply[2],
|
3399
|
-
'consumers'
|
3456
|
+
'consumers' => reply[3].nil? ? {} : reply[3].to_h
|
3400
3457
|
}
|
3401
3458
|
}
|
3402
3459
|
|
@@ -3405,8 +3462,8 @@ private
|
|
3405
3462
|
{
|
3406
3463
|
'entry_id' => arr[0],
|
3407
3464
|
'consumer' => arr[1],
|
3408
|
-
'elapsed'
|
3409
|
-
'count'
|
3465
|
+
'elapsed' => arr[2],
|
3466
|
+
'count' => arr[3]
|
3410
3467
|
}
|
3411
3468
|
end
|
3412
3469
|
}
|
@@ -3414,15 +3471,15 @@ private
|
|
3414
3471
|
HashifyClusterNodeInfo = lambda { |str|
|
3415
3472
|
arr = str.split(' ')
|
3416
3473
|
{
|
3417
|
-
'node_id'
|
3418
|
-
'ip_port'
|
3419
|
-
'flags'
|
3474
|
+
'node_id' => arr[0],
|
3475
|
+
'ip_port' => arr[1],
|
3476
|
+
'flags' => arr[2].split(','),
|
3420
3477
|
'master_node_id' => arr[3],
|
3421
|
-
'ping_sent'
|
3422
|
-
'pong_recv'
|
3423
|
-
'config_epoch'
|
3424
|
-
'link_state'
|
3425
|
-
'slots'
|
3478
|
+
'ping_sent' => arr[4],
|
3479
|
+
'pong_recv' => arr[5],
|
3480
|
+
'config_epoch' => arr[6],
|
3481
|
+
'link_state' => arr[7],
|
3482
|
+
'slots' => arr[8].nil? ? nil : Range.new(*arr[8].split('-'))
|
3426
3483
|
}
|
3427
3484
|
}
|
3428
3485
|
|
@@ -3433,9 +3490,9 @@ private
|
|
3433
3490
|
replicas = arr[3..-1].map { |r| { 'ip' => r[0], 'port' => r[1], 'node_id' => r[2] } }
|
3434
3491
|
{
|
3435
3492
|
'start_slot' => first_slot,
|
3436
|
-
'end_slot'
|
3437
|
-
'master'
|
3438
|
-
'replicas'
|
3493
|
+
'end_slot' => last_slot,
|
3494
|
+
'master' => master,
|
3495
|
+
'replicas' => replicas
|
3439
3496
|
}
|
3440
3497
|
end
|
3441
3498
|
}
|