redis 4.0.0.rc1 → 4.5.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 +5 -5
- data/CHANGELOG.md +164 -3
- data/README.md +127 -18
- data/lib/redis/client.rb +164 -93
- data/lib/redis/cluster/command.rb +81 -0
- data/lib/redis/cluster/command_loader.rb +33 -0
- data/lib/redis/cluster/key_slot_converter.rb +72 -0
- data/lib/redis/cluster/node.rb +108 -0
- data/lib/redis/cluster/node_key.rb +31 -0
- data/lib/redis/cluster/node_loader.rb +37 -0
- data/lib/redis/cluster/option.rb +93 -0
- data/lib/redis/cluster/slot.rb +86 -0
- data/lib/redis/cluster/slot_loader.rb +49 -0
- data/lib/redis/cluster.rb +291 -0
- data/lib/redis/connection/command_helper.rb +5 -2
- data/lib/redis/connection/hiredis.rb +4 -3
- data/lib/redis/connection/registry.rb +2 -1
- data/lib/redis/connection/ruby.rb +125 -106
- data/lib/redis/connection/synchrony.rb +18 -5
- data/lib/redis/connection.rb +2 -0
- data/lib/redis/distributed.rb +995 -0
- data/lib/redis/errors.rb +48 -0
- data/lib/redis/hash_ring.rb +89 -0
- data/lib/redis/pipeline.rb +55 -9
- data/lib/redis/subscribe.rb +11 -12
- data/lib/redis/version.rb +3 -1
- data/lib/redis.rb +1433 -381
- metadata +34 -141
- data/.gitignore +0 -16
- data/.travis/Gemfile +0 -11
- data/.travis.yml +0 -71
- data/.yardopts +0 -3
- data/Gemfile +0 -3
- data/benchmarking/logging.rb +0 -71
- data/benchmarking/pipeline.rb +0 -51
- data/benchmarking/speed.rb +0 -21
- data/benchmarking/suite.rb +0 -24
- data/benchmarking/worker.rb +0 -71
- data/examples/basic.rb +0 -15
- data/examples/consistency.rb +0 -114
- data/examples/incr-decr.rb +0 -17
- data/examples/list.rb +0 -26
- data/examples/pubsub.rb +0 -37
- data/examples/sentinel/sentinel.conf +0 -9
- data/examples/sentinel/start +0 -49
- data/examples/sentinel.rb +0 -41
- data/examples/sets.rb +0 -36
- data/examples/unicorn/config.ru +0 -3
- data/examples/unicorn/unicorn.rb +0 -20
- data/makefile +0 -42
- data/redis.gemspec +0 -40
- data/test/bitpos_test.rb +0 -63
- data/test/blocking_commands_test.rb +0 -183
- data/test/client_test.rb +0 -59
- data/test/command_map_test.rb +0 -28
- data/test/commands_on_hashes_test.rb +0 -174
- data/test/commands_on_hyper_log_log_test.rb +0 -70
- data/test/commands_on_lists_test.rb +0 -154
- data/test/commands_on_sets_test.rb +0 -208
- data/test/commands_on_sorted_sets_test.rb +0 -444
- data/test/commands_on_strings_test.rb +0 -338
- data/test/commands_on_value_types_test.rb +0 -246
- data/test/connection_handling_test.rb +0 -275
- data/test/db/.gitkeep +0 -0
- data/test/encoding_test.rb +0 -14
- data/test/error_replies_test.rb +0 -57
- data/test/fork_safety_test.rb +0 -60
- data/test/helper.rb +0 -179
- data/test/helper_test.rb +0 -22
- data/test/internals_test.rb +0 -435
- data/test/persistence_control_commands_test.rb +0 -24
- data/test/pipelining_commands_test.rb +0 -238
- data/test/publish_subscribe_test.rb +0 -280
- data/test/remote_server_control_commands_test.rb +0 -175
- data/test/scanning_test.rb +0 -407
- data/test/scripting_test.rb +0 -76
- data/test/sentinel_command_test.rb +0 -78
- data/test/sentinel_test.rb +0 -253
- data/test/sorting_test.rb +0 -57
- data/test/ssl_test.rb +0 -69
- data/test/support/connection/hiredis.rb +0 -1
- data/test/support/connection/ruby.rb +0 -1
- data/test/support/connection/synchrony.rb +0 -17
- data/test/support/redis_mock.rb +0 -130
- data/test/support/ssl/gen_certs.sh +0 -31
- data/test/support/ssl/trusted-ca.crt +0 -25
- data/test/support/ssl/trusted-ca.key +0 -27
- data/test/support/ssl/trusted-cert.crt +0 -81
- data/test/support/ssl/trusted-cert.key +0 -28
- data/test/support/ssl/untrusted-ca.crt +0 -26
- data/test/support/ssl/untrusted-ca.key +0 -27
- data/test/support/ssl/untrusted-cert.crt +0 -82
- data/test/support/ssl/untrusted-cert.key +0 -28
- data/test/support/wire/synchrony.rb +0 -24
- data/test/support/wire/thread.rb +0 -5
- data/test/synchrony_driver.rb +0 -85
- data/test/test.conf.erb +0 -9
- data/test/thread_safety_test.rb +0 -60
- data/test/transactions_test.rb +0 -262
- data/test/unknown_commands_test.rb +0 -12
- data/test/url_param_test.rb +0 -136
data/lib/redis.rb
CHANGED
@@ -1,14 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "monitor"
|
2
4
|
require_relative "redis/errors"
|
3
5
|
|
4
6
|
class Redis
|
7
|
+
@exists_returns_integer = true
|
5
8
|
|
6
|
-
|
7
|
-
|
9
|
+
class << self
|
10
|
+
attr_reader :exists_returns_integer
|
11
|
+
|
12
|
+
def exists_returns_integer=(value)
|
13
|
+
unless value
|
14
|
+
message = "`Redis#exists(key)` will return an Integer by default in redis-rb 4.3. The option to explicitly " \
|
15
|
+
"disable this behaviour via `Redis.exists_returns_integer` will be removed in 5.0. You should use " \
|
16
|
+
"`exists?` instead."
|
17
|
+
|
18
|
+
::Kernel.warn(message)
|
19
|
+
end
|
20
|
+
|
21
|
+
@exists_returns_integer = value
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_writer :current
|
8
25
|
end
|
9
26
|
|
10
|
-
def self.current
|
11
|
-
@current
|
27
|
+
def self.current
|
28
|
+
@current ||= Redis.new
|
12
29
|
end
|
13
30
|
|
14
31
|
include MonitorMixin
|
@@ -16,26 +33,36 @@ class Redis
|
|
16
33
|
# Create a new client instance
|
17
34
|
#
|
18
35
|
# @param [Hash] options
|
19
|
-
# @option options [String] :url (value of the environment variable REDIS_URL) a Redis URL, for a TCP connection:
|
36
|
+
# @option options [String] :url (value of the environment variable REDIS_URL) a Redis URL, for a TCP connection:
|
37
|
+
# `redis://:[password]@[hostname]:[port]/[db]` (password, port and database are optional), for a unix socket
|
38
|
+
# connection: `unix://[path to Redis socket]`. This overrides all other options.
|
20
39
|
# @option options [String] :host ("127.0.0.1") server hostname
|
21
|
-
# @option options [
|
40
|
+
# @option options [Integer] :port (6379) server port
|
22
41
|
# @option options [String] :path path to server socket (overrides host and port)
|
23
42
|
# @option options [Float] :timeout (5.0) timeout in seconds
|
24
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
|
25
45
|
# @option options [String] :password Password to authenticate against server
|
26
|
-
# @option options [
|
46
|
+
# @option options [Integer] :db (0) Database to select after initial connect
|
27
47
|
# @option options [Symbol] :driver Driver to use, currently supported: `:ruby`, `:hiredis`, `:synchrony`
|
28
|
-
# @option options [String] :id ID for the client connection, assigns name to current connection by sending
|
29
|
-
#
|
30
|
-
# @option options [
|
48
|
+
# @option options [String] :id ID for the client connection, assigns name to current connection by sending
|
49
|
+
# `CLIENT SETNAME`
|
50
|
+
# @option options [Hash, Integer] :tcp_keepalive Keepalive values, if Integer `intvl` and `probe` are calculated
|
51
|
+
# based on the value, if Hash `time`, `intvl` and `probes` can be specified as a Integer
|
52
|
+
# @option options [Integer] :reconnect_attempts Number of attempts trying to connect
|
31
53
|
# @option options [Boolean] :inherit_socket (false) Whether to use socket in forked process or not
|
32
54
|
# @option options [Array] :sentinels List of sentinels to contact
|
33
55
|
# @option options [Symbol] :role (:master) Role to fetch via Sentinel, either `:master` or `:slave`
|
56
|
+
# @option options [Array<String, Hash{Symbol => String, Integer}>] :cluster List of cluster nodes to contact
|
57
|
+
# @option options [Boolean] :replica Whether to use readonly replica nodes in Redis Cluster or not
|
58
|
+
# @option options [Class] :connector Class of custom connector
|
34
59
|
#
|
35
60
|
# @return [Redis] a new client instance
|
36
61
|
def initialize(options = {})
|
37
62
|
@options = options.dup
|
38
|
-
@
|
63
|
+
@cluster_mode = options.key?(:cluster)
|
64
|
+
client = @cluster_mode ? Cluster : Client
|
65
|
+
@original_client = @client = client.new(options)
|
39
66
|
@queue = Hash.new { |h, k| h[k] = [] }
|
40
67
|
|
41
68
|
super() # Monitor#initialize
|
@@ -46,7 +73,7 @@ class Redis
|
|
46
73
|
end
|
47
74
|
|
48
75
|
# Run code with the client reconnecting
|
49
|
-
def with_reconnect(val=true, &blk)
|
76
|
+
def with_reconnect(val = true, &blk)
|
50
77
|
synchronize do |client|
|
51
78
|
client.with_reconnect(val, &blk)
|
52
79
|
end
|
@@ -89,7 +116,9 @@ class Redis
|
|
89
116
|
# See http://redis.io/topics/pipelining for more details.
|
90
117
|
#
|
91
118
|
def queue(*command)
|
92
|
-
|
119
|
+
synchronize do
|
120
|
+
@queue[Thread.current.object_id] << command
|
121
|
+
end
|
93
122
|
end
|
94
123
|
|
95
124
|
# Sends all commands in the queue.
|
@@ -99,7 +128,12 @@ class Redis
|
|
99
128
|
def commit
|
100
129
|
synchronize do |client|
|
101
130
|
begin
|
102
|
-
|
131
|
+
pipeline = Pipeline.new(client)
|
132
|
+
@queue[Thread.current.object_id].each do |command|
|
133
|
+
pipeline.call(command)
|
134
|
+
end
|
135
|
+
|
136
|
+
client.call_pipelined(pipeline)
|
103
137
|
ensure
|
104
138
|
@queue.delete(Thread.current.object_id)
|
105
139
|
end
|
@@ -112,18 +146,19 @@ class Redis
|
|
112
146
|
|
113
147
|
# Authenticate to the server.
|
114
148
|
#
|
115
|
-
# @param [String]
|
116
|
-
#
|
149
|
+
# @param [Array<String>] args includes both username and password
|
150
|
+
# or only password
|
117
151
|
# @return [String] `OK`
|
118
|
-
|
152
|
+
# @see https://redis.io/commands/auth AUTH command
|
153
|
+
def auth(*args)
|
119
154
|
synchronize do |client|
|
120
|
-
client.call([:auth,
|
155
|
+
client.call([:auth, *args])
|
121
156
|
end
|
122
157
|
end
|
123
158
|
|
124
159
|
# Change the selected database for the current connection.
|
125
160
|
#
|
126
|
-
# @param [
|
161
|
+
# @param [Integer] db zero-based index of the DB to use (0 to 15)
|
127
162
|
# @return [String] `OK`
|
128
163
|
def select(db)
|
129
164
|
synchronize do |client|
|
@@ -134,10 +169,11 @@ class Redis
|
|
134
169
|
|
135
170
|
# Ping the server.
|
136
171
|
#
|
172
|
+
# @param [optional, String] message
|
137
173
|
# @return [String] `PONG`
|
138
|
-
def ping
|
174
|
+
def ping(message = nil)
|
139
175
|
synchronize do |client|
|
140
|
-
client.call([:ping])
|
176
|
+
client.call([:ping, message].compact)
|
141
177
|
end
|
142
178
|
end
|
143
179
|
|
@@ -191,7 +227,7 @@ class Redis
|
|
191
227
|
def config(action, *args)
|
192
228
|
synchronize do |client|
|
193
229
|
client.call([:config, action] + args) do |reply|
|
194
|
-
if reply.
|
230
|
+
if reply.is_a?(Array) && action == :get
|
195
231
|
Hashify.call(reply)
|
196
232
|
else
|
197
233
|
reply
|
@@ -221,7 +257,7 @@ class Redis
|
|
221
257
|
|
222
258
|
# Return the number of keys in the selected database.
|
223
259
|
#
|
224
|
-
# @return [
|
260
|
+
# @return [Integer]
|
225
261
|
def dbsize
|
226
262
|
synchronize do |client|
|
227
263
|
client.call([:dbsize])
|
@@ -236,19 +272,31 @@ class Redis
|
|
236
272
|
|
237
273
|
# Remove all keys from all databases.
|
238
274
|
#
|
275
|
+
# @param [Hash] options
|
276
|
+
# - `:async => Boolean`: async flush (default: false)
|
239
277
|
# @return [String] `OK`
|
240
|
-
def flushall
|
278
|
+
def flushall(options = nil)
|
241
279
|
synchronize do |client|
|
242
|
-
|
280
|
+
if options && options[:async]
|
281
|
+
client.call(%i[flushall async])
|
282
|
+
else
|
283
|
+
client.call([:flushall])
|
284
|
+
end
|
243
285
|
end
|
244
286
|
end
|
245
287
|
|
246
288
|
# Remove all keys from the current database.
|
247
289
|
#
|
290
|
+
# @param [Hash] options
|
291
|
+
# - `:async => Boolean`: async flush (default: false)
|
248
292
|
# @return [String] `OK`
|
249
|
-
def flushdb
|
293
|
+
def flushdb(options = nil)
|
250
294
|
synchronize do |client|
|
251
|
-
|
295
|
+
if options && options[:async]
|
296
|
+
client.call(%i[flushdb async])
|
297
|
+
else
|
298
|
+
client.call([:flushdb])
|
299
|
+
end
|
252
300
|
end
|
253
301
|
end
|
254
302
|
|
@@ -259,10 +307,8 @@ class Redis
|
|
259
307
|
def info(cmd = nil)
|
260
308
|
synchronize do |client|
|
261
309
|
client.call([:info, cmd].compact) do |reply|
|
262
|
-
if reply.
|
263
|
-
reply =
|
264
|
-
line.split(":", 2) unless line =~ /^(#|$)/
|
265
|
-
end.compact]
|
310
|
+
if reply.is_a?(String)
|
311
|
+
reply = HashifyInfo.call(reply)
|
266
312
|
|
267
313
|
if cmd && cmd.to_s == "commandstats"
|
268
314
|
# Extract nested hashes for INFO COMMANDSTATS
|
@@ -280,7 +326,7 @@ class Redis
|
|
280
326
|
|
281
327
|
# Get the UNIX time stamp of the last successful save to disk.
|
282
328
|
#
|
283
|
-
# @return [
|
329
|
+
# @return [Integer]
|
284
330
|
def lastsave
|
285
331
|
synchronize do |client|
|
286
332
|
client.call([:lastsave])
|
@@ -332,9 +378,9 @@ class Redis
|
|
332
378
|
# Interact with the slowlog (get, len, reset)
|
333
379
|
#
|
334
380
|
# @param [String] subcommand e.g. `get`, `len`, `reset`
|
335
|
-
# @param [
|
336
|
-
# @return [Array<String>,
|
337
|
-
def slowlog(subcommand, length=nil)
|
381
|
+
# @param [Integer] length maximum number of entries to return
|
382
|
+
# @return [Array<String>, Integer, String] depends on subcommand
|
383
|
+
def slowlog(subcommand, length = nil)
|
338
384
|
synchronize do |client|
|
339
385
|
args = [:slowlog, subcommand]
|
340
386
|
args << length if length
|
@@ -354,12 +400,12 @@ class Redis
|
|
354
400
|
# @example
|
355
401
|
# r.time # => [ 1333093196, 606806 ]
|
356
402
|
#
|
357
|
-
# @return [Array<
|
403
|
+
# @return [Array<Integer>] tuple of seconds since UNIX epoch and
|
358
404
|
# microseconds in the current second
|
359
405
|
def time
|
360
406
|
synchronize do |client|
|
361
407
|
client.call([:time]) do |reply|
|
362
|
-
reply
|
408
|
+
reply&.map(&:to_i)
|
363
409
|
end
|
364
410
|
end
|
365
411
|
end
|
@@ -377,7 +423,7 @@ class Redis
|
|
377
423
|
# Set a key's time to live in seconds.
|
378
424
|
#
|
379
425
|
# @param [String] key
|
380
|
-
# @param [
|
426
|
+
# @param [Integer] seconds time to live
|
381
427
|
# @return [Boolean] whether the timeout was set or not
|
382
428
|
def expire(key, seconds)
|
383
429
|
synchronize do |client|
|
@@ -388,7 +434,7 @@ class Redis
|
|
388
434
|
# Set the expiration for a key as a UNIX timestamp.
|
389
435
|
#
|
390
436
|
# @param [String] key
|
391
|
-
# @param [
|
437
|
+
# @param [Integer] unix_time expiry time specified as a UNIX timestamp
|
392
438
|
# @return [Boolean] whether the timeout was set or not
|
393
439
|
def expireat(key, unix_time)
|
394
440
|
synchronize do |client|
|
@@ -399,7 +445,7 @@ class Redis
|
|
399
445
|
# Get the time to live (in seconds) for a key.
|
400
446
|
#
|
401
447
|
# @param [String] key
|
402
|
-
# @return [
|
448
|
+
# @return [Integer] remaining time to live in seconds.
|
403
449
|
#
|
404
450
|
# In Redis 2.6 or older the command returns -1 if the key does not exist or if
|
405
451
|
# the key exist but has no associated expire.
|
@@ -417,7 +463,7 @@ class Redis
|
|
417
463
|
# Set a key's time to live in milliseconds.
|
418
464
|
#
|
419
465
|
# @param [String] key
|
420
|
-
# @param [
|
466
|
+
# @param [Integer] milliseconds time to live
|
421
467
|
# @return [Boolean] whether the timeout was set or not
|
422
468
|
def pexpire(key, milliseconds)
|
423
469
|
synchronize do |client|
|
@@ -428,7 +474,7 @@ class Redis
|
|
428
474
|
# Set the expiration for a key as number of milliseconds from UNIX Epoch.
|
429
475
|
#
|
430
476
|
# @param [String] key
|
431
|
-
# @param [
|
477
|
+
# @param [Integer] ms_unix_time expiry time specified as number of milliseconds from UNIX Epoch.
|
432
478
|
# @return [Boolean] whether the timeout was set or not
|
433
479
|
def pexpireat(key, ms_unix_time)
|
434
480
|
synchronize do |client|
|
@@ -439,7 +485,7 @@ class Redis
|
|
439
485
|
# Get the time to live (in milliseconds) for a key.
|
440
486
|
#
|
441
487
|
# @param [String] key
|
442
|
-
# @return [
|
488
|
+
# @return [Integer] remaining time to live in milliseconds
|
443
489
|
# In Redis 2.6 or older the command returns -1 if the key does not exist or if
|
444
490
|
# the key exist but has no associated expire.
|
445
491
|
#
|
@@ -468,50 +514,104 @@ class Redis
|
|
468
514
|
# @param [String] key
|
469
515
|
# @param [String] ttl
|
470
516
|
# @param [String] serialized_value
|
517
|
+
# @param [Hash] options
|
518
|
+
# - `:replace => Boolean`: if false, raises an error if key already exists
|
519
|
+
# @raise [Redis::CommandError]
|
471
520
|
# @return [String] `"OK"`
|
472
|
-
def restore(key, ttl, serialized_value)
|
521
|
+
def restore(key, ttl, serialized_value, replace: nil)
|
522
|
+
args = [:restore, key, ttl, serialized_value]
|
523
|
+
args << 'REPLACE' if replace
|
524
|
+
|
473
525
|
synchronize do |client|
|
474
|
-
client.call(
|
526
|
+
client.call(args)
|
475
527
|
end
|
476
528
|
end
|
477
529
|
|
478
530
|
# Transfer a key from the connected instance to another instance.
|
479
531
|
#
|
480
|
-
# @param [String] key
|
532
|
+
# @param [String, Array<String>] key
|
481
533
|
# @param [Hash] options
|
482
534
|
# - `:host => String`: host of instance to migrate to
|
483
535
|
# - `:port => Integer`: port of instance to migrate to
|
484
536
|
# - `:db => Integer`: database to migrate to (default: same as source)
|
485
537
|
# - `:timeout => Integer`: timeout (default: same as connection timeout)
|
538
|
+
# - `:copy => Boolean`: Do not remove the key from the local instance.
|
539
|
+
# - `:replace => Boolean`: Replace existing key on the remote instance.
|
486
540
|
# @return [String] `"OK"`
|
487
541
|
def migrate(key, options)
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
542
|
+
args = [:migrate]
|
543
|
+
args << (options[:host] || raise(':host not specified'))
|
544
|
+
args << (options[:port] || raise(':port not specified'))
|
545
|
+
args << (key.is_a?(String) ? key : '')
|
546
|
+
args << (options[:db] || @client.db).to_i
|
547
|
+
args << (options[:timeout] || @client.timeout).to_i
|
548
|
+
args << 'COPY' if options[:copy]
|
549
|
+
args << 'REPLACE' if options[:replace]
|
550
|
+
args += ['KEYS', *key] if key.is_a?(Array)
|
492
551
|
|
493
|
-
synchronize
|
494
|
-
client.call([:migrate, host, port, key, db, timeout])
|
495
|
-
end
|
552
|
+
synchronize { |client| client.call(args) }
|
496
553
|
end
|
497
554
|
|
498
555
|
# Delete one or more keys.
|
499
556
|
#
|
500
557
|
# @param [String, Array<String>] keys
|
501
|
-
# @return [
|
558
|
+
# @return [Integer] number of keys that were deleted
|
502
559
|
def del(*keys)
|
560
|
+
keys.flatten!(1)
|
561
|
+
return 0 if keys.empty?
|
562
|
+
|
503
563
|
synchronize do |client|
|
504
564
|
client.call([:del] + keys)
|
505
565
|
end
|
506
566
|
end
|
507
567
|
|
508
|
-
#
|
568
|
+
# Unlink one or more keys.
|
509
569
|
#
|
510
|
-
# @param [String]
|
570
|
+
# @param [String, Array<String>] keys
|
571
|
+
# @return [Integer] number of keys that were unlinked
|
572
|
+
def unlink(*keys)
|
573
|
+
synchronize do |client|
|
574
|
+
client.call([:unlink] + keys)
|
575
|
+
end
|
576
|
+
end
|
577
|
+
|
578
|
+
# Determine how many of the keys exists.
|
579
|
+
#
|
580
|
+
# @param [String, Array<String>] keys
|
581
|
+
# @return [Integer]
|
582
|
+
def exists(*keys)
|
583
|
+
if !Redis.exists_returns_integer && keys.size == 1
|
584
|
+
if Redis.exists_returns_integer.nil?
|
585
|
+
message = "`Redis#exists(key)` will return an Integer in redis-rb 4.3. `exists?` returns a boolean, you " \
|
586
|
+
"should use it instead. To opt-in to the new behavior now you can set Redis.exists_returns_integer = " \
|
587
|
+
"true. To disable this message and keep the current (boolean) behaviour of 'exists' you can set " \
|
588
|
+
"`Redis.exists_returns_integer = false`, but this option will be removed in 5.0. " \
|
589
|
+
"(#{::Kernel.caller(1, 1).first})\n"
|
590
|
+
|
591
|
+
::Kernel.warn(message)
|
592
|
+
end
|
593
|
+
|
594
|
+
exists?(*keys)
|
595
|
+
else
|
596
|
+
_exists(*keys)
|
597
|
+
end
|
598
|
+
end
|
599
|
+
|
600
|
+
def _exists(*keys)
|
601
|
+
synchronize do |client|
|
602
|
+
client.call([:exists, *keys])
|
603
|
+
end
|
604
|
+
end
|
605
|
+
|
606
|
+
# Determine if any of the keys exists.
|
607
|
+
#
|
608
|
+
# @param [String, Array<String>] keys
|
511
609
|
# @return [Boolean]
|
512
|
-
def exists(
|
610
|
+
def exists?(*keys)
|
513
611
|
synchronize do |client|
|
514
|
-
client.call([:exists,
|
612
|
+
client.call([:exists, *keys]) do |value|
|
613
|
+
value > 0
|
614
|
+
end
|
515
615
|
end
|
516
616
|
end
|
517
617
|
|
@@ -522,7 +622,7 @@ class Redis
|
|
522
622
|
def keys(pattern = "*")
|
523
623
|
synchronize do |client|
|
524
624
|
client.call([:keys, pattern]) do |reply|
|
525
|
-
if reply.
|
625
|
+
if reply.is_a?(String)
|
526
626
|
reply.split(" ")
|
527
627
|
else
|
528
628
|
reply
|
@@ -548,7 +648,7 @@ class Redis
|
|
548
648
|
# # => "bar"
|
549
649
|
#
|
550
650
|
# @param [String] key
|
551
|
-
# @param [
|
651
|
+
# @param [Integer] db
|
552
652
|
# @return [Boolean] whether the key was moved or not
|
553
653
|
def move(key, db)
|
554
654
|
synchronize do |client|
|
@@ -612,36 +712,33 @@ class Redis
|
|
612
712
|
# - `:order => String`: combination of `ASC`, `DESC` and optionally `ALPHA`
|
613
713
|
# - `:store => String`: key to store the result at
|
614
714
|
#
|
615
|
-
# @return [Array<String>, Array<Array<String>>,
|
715
|
+
# @return [Array<String>, Array<Array<String>>, Integer]
|
616
716
|
# - when `:get` is not specified, or holds a single element, an array of elements
|
617
717
|
# - when `:get` is specified, and holds more than one element, an array of
|
618
718
|
# elements where every element is an array with the result for every
|
619
719
|
# element specified in `:get`
|
620
720
|
# - when `:store` is specified, the number of elements in the stored result
|
621
|
-
def sort(key,
|
622
|
-
args = []
|
623
|
-
|
624
|
-
by = options[:by]
|
625
|
-
args.concat(["BY", by]) if by
|
721
|
+
def sort(key, by: nil, limit: nil, get: nil, order: nil, store: nil)
|
722
|
+
args = [:sort, key]
|
723
|
+
args << "BY" << by if by
|
626
724
|
|
627
|
-
|
628
|
-
|
725
|
+
if limit
|
726
|
+
args << "LIMIT"
|
727
|
+
args.concat(limit)
|
728
|
+
end
|
629
729
|
|
630
|
-
get = Array(
|
631
|
-
|
730
|
+
get = Array(get)
|
731
|
+
get.each do |item|
|
732
|
+
args << "GET" << item
|
733
|
+
end
|
632
734
|
|
633
|
-
order = options[:order]
|
634
735
|
args.concat(order.split(" ")) if order
|
635
|
-
|
636
|
-
store = options[:store]
|
637
|
-
args.concat(["STORE", store]) if store
|
736
|
+
args << "STORE" << store if store
|
638
737
|
|
639
738
|
synchronize do |client|
|
640
|
-
client.call(
|
739
|
+
client.call(args) do |reply|
|
641
740
|
if get.size > 1 && !store
|
642
|
-
if reply
|
643
|
-
reply.each_slice(get.size).to_a
|
644
|
-
end
|
741
|
+
reply.each_slice(get.size).to_a if reply
|
645
742
|
else
|
646
743
|
reply
|
647
744
|
end
|
@@ -666,7 +763,7 @@ class Redis
|
|
666
763
|
# # => 4
|
667
764
|
#
|
668
765
|
# @param [String] key
|
669
|
-
# @return [
|
766
|
+
# @return [Integer] value after decrementing it
|
670
767
|
def decr(key)
|
671
768
|
synchronize do |client|
|
672
769
|
client.call([:decr, key])
|
@@ -680,8 +777,8 @@ class Redis
|
|
680
777
|
# # => 0
|
681
778
|
#
|
682
779
|
# @param [String] key
|
683
|
-
# @param [
|
684
|
-
# @return [
|
780
|
+
# @param [Integer] decrement
|
781
|
+
# @return [Integer] value after decrementing it
|
685
782
|
def decrby(key, decrement)
|
686
783
|
synchronize do |client|
|
687
784
|
client.call([:decrby, key, decrement])
|
@@ -695,7 +792,7 @@ class Redis
|
|
695
792
|
# # => 6
|
696
793
|
#
|
697
794
|
# @param [String] key
|
698
|
-
# @return [
|
795
|
+
# @return [Integer] value after incrementing it
|
699
796
|
def incr(key)
|
700
797
|
synchronize do |client|
|
701
798
|
client.call([:incr, key])
|
@@ -709,8 +806,8 @@ class Redis
|
|
709
806
|
# # => 10
|
710
807
|
#
|
711
808
|
# @param [String] key
|
712
|
-
# @param [
|
713
|
-
# @return [
|
809
|
+
# @param [Integer] increment
|
810
|
+
# @return [Integer] value after incrementing it
|
714
811
|
def incrby(key, increment)
|
715
812
|
synchronize do |client|
|
716
813
|
client.call([:incrby, key, increment])
|
@@ -737,31 +834,31 @@ class Redis
|
|
737
834
|
# @param [String] key
|
738
835
|
# @param [String] value
|
739
836
|
# @param [Hash] options
|
740
|
-
# - `:ex =>
|
741
|
-
# - `:px =>
|
837
|
+
# - `:ex => Integer`: Set the specified expire time, in seconds.
|
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.
|
742
841
|
# - `:nx => true`: Only set the key if it does not already exist.
|
743
842
|
# - `:xx => true`: Only set the key if it already exist.
|
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.
|
744
845
|
# @return [String, Boolean] `"OK"` or true, false if `:nx => true` or `:xx => true`
|
745
|
-
def set(key, value,
|
746
|
-
args = []
|
747
|
-
|
748
|
-
|
749
|
-
args
|
750
|
-
|
751
|
-
|
752
|
-
args
|
753
|
-
|
754
|
-
|
755
|
-
args.concat(["NX"]) if nx
|
756
|
-
|
757
|
-
xx = options[:xx]
|
758
|
-
args.concat(["XX"]) if xx
|
846
|
+
def set(key, value, ex: nil, px: nil, exat: nil, pxat: nil, nx: nil, xx: nil, keepttl: nil, get: nil)
|
847
|
+
args = [:set, key, value.to_s]
|
848
|
+
args << "EX" << ex if ex
|
849
|
+
args << "PX" << px if px
|
850
|
+
args << "EXAT" << exat if exat
|
851
|
+
args << "PXAT" << pxat if pxat
|
852
|
+
args << "NX" if nx
|
853
|
+
args << "XX" if xx
|
854
|
+
args << "KEEPTTL" if keepttl
|
855
|
+
args << "GET" if get
|
759
856
|
|
760
857
|
synchronize do |client|
|
761
858
|
if nx || xx
|
762
|
-
client.call(
|
859
|
+
client.call(args, &BoolifySet)
|
763
860
|
else
|
764
|
-
client.call(
|
861
|
+
client.call(args)
|
765
862
|
end
|
766
863
|
end
|
767
864
|
end
|
@@ -769,7 +866,7 @@ class Redis
|
|
769
866
|
# Set the time to live in seconds of a key.
|
770
867
|
#
|
771
868
|
# @param [String] key
|
772
|
-
# @param [
|
869
|
+
# @param [Integer] ttl
|
773
870
|
# @param [String] value
|
774
871
|
# @return [String] `"OK"`
|
775
872
|
def setex(key, ttl, value)
|
@@ -781,7 +878,7 @@ class Redis
|
|
781
878
|
# Set the time to live in milliseconds of a key.
|
782
879
|
#
|
783
880
|
# @param [String] key
|
784
|
-
# @param [
|
881
|
+
# @param [Integer] ttl
|
785
882
|
# @param [String] value
|
786
883
|
# @return [String] `"OK"`
|
787
884
|
def psetex(key, ttl, value)
|
@@ -843,7 +940,7 @@ class Redis
|
|
843
940
|
# @see #mapped_msetnx
|
844
941
|
def msetnx(*args)
|
845
942
|
synchronize do |client|
|
846
|
-
client.call([:msetnx
|
943
|
+
client.call([:msetnx, *args], &Boolify)
|
847
944
|
end
|
848
945
|
end
|
849
946
|
|
@@ -874,7 +971,7 @@ class Redis
|
|
874
971
|
# Get the values of all the given keys.
|
875
972
|
#
|
876
973
|
# @example
|
877
|
-
# redis.mget("key1", "
|
974
|
+
# redis.mget("key1", "key2")
|
878
975
|
# # => ["v1", "v2"]
|
879
976
|
#
|
880
977
|
# @param [Array<String>] keys
|
@@ -883,7 +980,7 @@ class Redis
|
|
883
980
|
# @see #mapped_mget
|
884
981
|
def mget(*keys, &blk)
|
885
982
|
synchronize do |client|
|
886
|
-
client.call([:mget
|
983
|
+
client.call([:mget, *keys], &blk)
|
887
984
|
end
|
888
985
|
end
|
889
986
|
|
@@ -899,7 +996,7 @@ class Redis
|
|
899
996
|
# @see #mget
|
900
997
|
def mapped_mget(*keys)
|
901
998
|
mget(*keys) do |reply|
|
902
|
-
if reply.
|
999
|
+
if reply.is_a?(Array)
|
903
1000
|
Hash[keys.zip(reply)]
|
904
1001
|
else
|
905
1002
|
reply
|
@@ -910,9 +1007,9 @@ class Redis
|
|
910
1007
|
# Overwrite part of a string at key starting at the specified offset.
|
911
1008
|
#
|
912
1009
|
# @param [String] key
|
913
|
-
# @param [
|
1010
|
+
# @param [Integer] offset byte offset
|
914
1011
|
# @param [String] value
|
915
|
-
# @return [
|
1012
|
+
# @return [Integer] length of the string after it was modified
|
916
1013
|
def setrange(key, offset, value)
|
917
1014
|
synchronize do |client|
|
918
1015
|
client.call([:setrange, key, offset, value.to_s])
|
@@ -922,10 +1019,10 @@ class Redis
|
|
922
1019
|
# Get a substring of the string stored at a key.
|
923
1020
|
#
|
924
1021
|
# @param [String] key
|
925
|
-
# @param [
|
926
|
-
# @param [
|
1022
|
+
# @param [Integer] start zero-based start offset
|
1023
|
+
# @param [Integer] stop zero-based end offset. Use -1 for representing
|
927
1024
|
# the end of the string
|
928
|
-
# @return [
|
1025
|
+
# @return [Integer] `0` or `1`
|
929
1026
|
def getrange(key, start, stop)
|
930
1027
|
synchronize do |client|
|
931
1028
|
client.call([:getrange, key, start, stop])
|
@@ -935,9 +1032,9 @@ class Redis
|
|
935
1032
|
# Sets or clears the bit at offset in the string value stored at key.
|
936
1033
|
#
|
937
1034
|
# @param [String] key
|
938
|
-
# @param [
|
939
|
-
# @param [
|
940
|
-
# @return [
|
1035
|
+
# @param [Integer] offset bit offset
|
1036
|
+
# @param [Integer] value bit value `0` or `1`
|
1037
|
+
# @return [Integer] the original bit value stored at `offset`
|
941
1038
|
def setbit(key, offset, value)
|
942
1039
|
synchronize do |client|
|
943
1040
|
client.call([:setbit, key, offset, value])
|
@@ -947,8 +1044,8 @@ class Redis
|
|
947
1044
|
# Returns the bit value at offset in the string value stored at key.
|
948
1045
|
#
|
949
1046
|
# @param [String] key
|
950
|
-
# @param [
|
951
|
-
# @return [
|
1047
|
+
# @param [Integer] offset bit offset
|
1048
|
+
# @return [Integer] `0` or `1`
|
952
1049
|
def getbit(key, offset)
|
953
1050
|
synchronize do |client|
|
954
1051
|
client.call([:getbit, key, offset])
|
@@ -959,7 +1056,7 @@ class Redis
|
|
959
1056
|
#
|
960
1057
|
# @param [String] key
|
961
1058
|
# @param [String] value value to append
|
962
|
-
# @return [
|
1059
|
+
# @return [Integer] length of the string after appending
|
963
1060
|
def append(key, value)
|
964
1061
|
synchronize do |client|
|
965
1062
|
client.call([:append, key, value])
|
@@ -969,9 +1066,9 @@ class Redis
|
|
969
1066
|
# Count the number of set bits in a range of the string value stored at key.
|
970
1067
|
#
|
971
1068
|
# @param [String] key
|
972
|
-
# @param [
|
973
|
-
# @param [
|
974
|
-
# @return [
|
1069
|
+
# @param [Integer] start start index
|
1070
|
+
# @param [Integer] stop stop index
|
1071
|
+
# @return [Integer] the number of bits set to 1
|
975
1072
|
def bitcount(key, start = 0, stop = -1)
|
976
1073
|
synchronize do |client|
|
977
1074
|
client.call([:bitcount, key, start, stop])
|
@@ -983,25 +1080,23 @@ class Redis
|
|
983
1080
|
# @param [String] operation e.g. `and`, `or`, `xor`, `not`
|
984
1081
|
# @param [String] destkey destination key
|
985
1082
|
# @param [String, Array<String>] keys one or more source keys to perform `operation`
|
986
|
-
# @return [
|
1083
|
+
# @return [Integer] the length of the string stored in `destkey`
|
987
1084
|
def bitop(operation, destkey, *keys)
|
988
1085
|
synchronize do |client|
|
989
|
-
client.call([:bitop, operation, destkey
|
1086
|
+
client.call([:bitop, operation, destkey, *keys])
|
990
1087
|
end
|
991
1088
|
end
|
992
1089
|
|
993
1090
|
# Return the position of the first bit set to 1 or 0 in a string.
|
994
1091
|
#
|
995
1092
|
# @param [String] key
|
996
|
-
# @param [
|
997
|
-
# @param [
|
998
|
-
# @param [
|
999
|
-
# @return [
|
1093
|
+
# @param [Integer] bit whether to look for the first 1 or 0 bit
|
1094
|
+
# @param [Integer] start start index
|
1095
|
+
# @param [Integer] stop stop index
|
1096
|
+
# @return [Integer] the position of the first 1/0 bit.
|
1000
1097
|
# -1 if looking for 1 and it is not found or start and stop are given.
|
1001
|
-
def bitpos(key, bit, start=nil, stop=nil)
|
1002
|
-
|
1003
|
-
raise(ArgumentError, 'stop parameter specified without start parameter')
|
1004
|
-
end
|
1098
|
+
def bitpos(key, bit, start = nil, stop = nil)
|
1099
|
+
raise(ArgumentError, 'stop parameter specified without start parameter') if stop && !start
|
1005
1100
|
|
1006
1101
|
synchronize do |client|
|
1007
1102
|
command = [:bitpos, key, bit]
|
@@ -1023,10 +1118,49 @@ class Redis
|
|
1023
1118
|
end
|
1024
1119
|
end
|
1025
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
|
+
|
1026
1160
|
# Get the length of the value stored in a key.
|
1027
1161
|
#
|
1028
1162
|
# @param [String] key
|
1029
|
-
# @return [
|
1163
|
+
# @return [Integer] the length of the value stored in the key, or 0
|
1030
1164
|
# if the key does not exist
|
1031
1165
|
def strlen(key)
|
1032
1166
|
synchronize do |client|
|
@@ -1037,18 +1171,71 @@ class Redis
|
|
1037
1171
|
# Get the length of a list.
|
1038
1172
|
#
|
1039
1173
|
# @param [String] key
|
1040
|
-
# @return [
|
1174
|
+
# @return [Integer]
|
1041
1175
|
def llen(key)
|
1042
1176
|
synchronize do |client|
|
1043
1177
|
client.call([:llen, key])
|
1044
1178
|
end
|
1045
1179
|
end
|
1046
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
|
+
|
1047
1234
|
# Prepend one or more values to a list, creating the list if it doesn't exist
|
1048
1235
|
#
|
1049
1236
|
# @param [String] key
|
1050
|
-
# @param [String, Array] value string value, or array of string values to push
|
1051
|
-
# @return [
|
1237
|
+
# @param [String, Array<String>] value string value, or array of string values to push
|
1238
|
+
# @return [Integer] the length of the list after the push operation
|
1052
1239
|
def lpush(key, value)
|
1053
1240
|
synchronize do |client|
|
1054
1241
|
client.call([:lpush, key, value])
|
@@ -1059,7 +1246,7 @@ class Redis
|
|
1059
1246
|
#
|
1060
1247
|
# @param [String] key
|
1061
1248
|
# @param [String] value
|
1062
|
-
# @return [
|
1249
|
+
# @return [Integer] the length of the list after the push operation
|
1063
1250
|
def lpushx(key, value)
|
1064
1251
|
synchronize do |client|
|
1065
1252
|
client.call([:lpushx, key, value])
|
@@ -1069,8 +1256,8 @@ class Redis
|
|
1069
1256
|
# Append one or more values to a list, creating the list if it doesn't exist
|
1070
1257
|
#
|
1071
1258
|
# @param [String] key
|
1072
|
-
# @param [String] value
|
1073
|
-
# @return [
|
1259
|
+
# @param [String, Array<String>] value string value, or array of string values to push
|
1260
|
+
# @return [Integer] the length of the list after the push operation
|
1074
1261
|
def rpush(key, value)
|
1075
1262
|
synchronize do |client|
|
1076
1263
|
client.call([:rpush, key, value])
|
@@ -1081,30 +1268,36 @@ class Redis
|
|
1081
1268
|
#
|
1082
1269
|
# @param [String] key
|
1083
1270
|
# @param [String] value
|
1084
|
-
# @return [
|
1271
|
+
# @return [Integer] the length of the list after the push operation
|
1085
1272
|
def rpushx(key, value)
|
1086
1273
|
synchronize do |client|
|
1087
1274
|
client.call([:rpushx, key, value])
|
1088
1275
|
end
|
1089
1276
|
end
|
1090
1277
|
|
1091
|
-
# Remove and get the first
|
1278
|
+
# Remove and get the first elements in a list.
|
1092
1279
|
#
|
1093
1280
|
# @param [String] key
|
1094
|
-
# @
|
1095
|
-
|
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)
|
1096
1284
|
synchronize do |client|
|
1097
|
-
|
1285
|
+
command = [:lpop, key]
|
1286
|
+
command << count if count
|
1287
|
+
client.call(command)
|
1098
1288
|
end
|
1099
1289
|
end
|
1100
1290
|
|
1101
|
-
# Remove and get the last
|
1291
|
+
# Remove and get the last elements in a list.
|
1102
1292
|
#
|
1103
1293
|
# @param [String] key
|
1104
|
-
# @
|
1105
|
-
|
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)
|
1106
1297
|
synchronize do |client|
|
1107
|
-
|
1298
|
+
command = [:rpop, key]
|
1299
|
+
command << count if count
|
1300
|
+
client.call(command)
|
1108
1301
|
end
|
1109
1302
|
end
|
1110
1303
|
|
@@ -1119,28 +1312,27 @@ class Redis
|
|
1119
1312
|
end
|
1120
1313
|
end
|
1121
1314
|
|
1122
|
-
def _bpop(cmd, args)
|
1123
|
-
|
1124
|
-
|
1125
|
-
case args.last
|
1126
|
-
when Hash
|
1315
|
+
def _bpop(cmd, args, &blk)
|
1316
|
+
timeout = if args.last.is_a?(Hash)
|
1127
1317
|
options = args.pop
|
1128
|
-
|
1318
|
+
options[:timeout]
|
1319
|
+
elsif args.last.respond_to?(:to_int)
|
1129
1320
|
# Issue deprecation notice in obnoxious mode...
|
1130
|
-
|
1321
|
+
args.pop.to_int
|
1131
1322
|
end
|
1132
1323
|
|
1324
|
+
timeout ||= 0
|
1325
|
+
|
1133
1326
|
if args.size > 1
|
1134
1327
|
# Issue deprecation notice in obnoxious mode...
|
1135
1328
|
end
|
1136
1329
|
|
1137
1330
|
keys = args.flatten
|
1138
|
-
timeout = options[:timeout] || 0
|
1139
1331
|
|
1140
1332
|
synchronize do |client|
|
1141
1333
|
command = [cmd, keys, timeout]
|
1142
1334
|
timeout += client.timeout if timeout > 0
|
1143
|
-
client.call_with_timeout(command, timeout)
|
1335
|
+
client.call_with_timeout(command, timeout, &blk)
|
1144
1336
|
end
|
1145
1337
|
end
|
1146
1338
|
|
@@ -1160,7 +1352,7 @@ class Redis
|
|
1160
1352
|
# @param [String, Array<String>] keys one or more keys to perform the
|
1161
1353
|
# blocking pop on
|
1162
1354
|
# @param [Hash] options
|
1163
|
-
# - `:timeout =>
|
1355
|
+
# - `:timeout => Integer`: timeout in seconds, defaults to no timeout
|
1164
1356
|
#
|
1165
1357
|
# @return [nil, [String, String]]
|
1166
1358
|
# - `nil` when the operation timed out
|
@@ -1174,7 +1366,7 @@ class Redis
|
|
1174
1366
|
# @param [String, Array<String>] keys one or more keys to perform the
|
1175
1367
|
# blocking pop on
|
1176
1368
|
# @param [Hash] options
|
1177
|
-
# - `:timeout =>
|
1369
|
+
# - `:timeout => Integer`: timeout in seconds, defaults to no timeout
|
1178
1370
|
#
|
1179
1371
|
# @return [nil, [String, String]]
|
1180
1372
|
# - `nil` when the operation timed out
|
@@ -1191,20 +1383,12 @@ class Redis
|
|
1191
1383
|
# @param [String] source source key
|
1192
1384
|
# @param [String] destination destination key
|
1193
1385
|
# @param [Hash] options
|
1194
|
-
# - `:timeout =>
|
1386
|
+
# - `:timeout => Integer`: timeout in seconds, defaults to no timeout
|
1195
1387
|
#
|
1196
1388
|
# @return [nil, String]
|
1197
1389
|
# - `nil` when the operation timed out
|
1198
1390
|
# - the element was popped and pushed otherwise
|
1199
|
-
def brpoplpush(source, destination,
|
1200
|
-
case options
|
1201
|
-
when Integer
|
1202
|
-
# Issue deprecation notice in obnoxious mode...
|
1203
|
-
options = { :timeout => options }
|
1204
|
-
end
|
1205
|
-
|
1206
|
-
timeout = options[:timeout] || 0
|
1207
|
-
|
1391
|
+
def brpoplpush(source, destination, deprecated_timeout = 0, timeout: deprecated_timeout)
|
1208
1392
|
synchronize do |client|
|
1209
1393
|
command = [:brpoplpush, source, destination, timeout]
|
1210
1394
|
timeout += client.timeout if timeout > 0
|
@@ -1215,7 +1399,7 @@ class Redis
|
|
1215
1399
|
# Get an element from a list by its index.
|
1216
1400
|
#
|
1217
1401
|
# @param [String] key
|
1218
|
-
# @param [
|
1402
|
+
# @param [Integer] index
|
1219
1403
|
# @return [String]
|
1220
1404
|
def lindex(key, index)
|
1221
1405
|
synchronize do |client|
|
@@ -1229,7 +1413,7 @@ class Redis
|
|
1229
1413
|
# @param [String, Symbol] where `BEFORE` or `AFTER`
|
1230
1414
|
# @param [String] pivot reference element
|
1231
1415
|
# @param [String] value
|
1232
|
-
# @return [
|
1416
|
+
# @return [Integer] length of the list after the insert operation, or `-1`
|
1233
1417
|
# when the element `pivot` was not found
|
1234
1418
|
def linsert(key, where, pivot, value)
|
1235
1419
|
synchronize do |client|
|
@@ -1240,8 +1424,8 @@ class Redis
|
|
1240
1424
|
# Get a range of elements from a list.
|
1241
1425
|
#
|
1242
1426
|
# @param [String] key
|
1243
|
-
# @param [
|
1244
|
-
# @param [
|
1427
|
+
# @param [Integer] start start index
|
1428
|
+
# @param [Integer] stop stop index
|
1245
1429
|
# @return [Array<String>]
|
1246
1430
|
def lrange(key, start, stop)
|
1247
1431
|
synchronize do |client|
|
@@ -1252,12 +1436,12 @@ class Redis
|
|
1252
1436
|
# Remove elements from a list.
|
1253
1437
|
#
|
1254
1438
|
# @param [String] key
|
1255
|
-
# @param [
|
1439
|
+
# @param [Integer] count number of elements to remove. Use a positive
|
1256
1440
|
# value to remove the first `count` occurrences of `value`. A negative
|
1257
1441
|
# value to remove the last `count` occurrences of `value`. Or zero, to
|
1258
1442
|
# remove all occurrences of `value` from the list.
|
1259
1443
|
# @param [String] value
|
1260
|
-
# @return [
|
1444
|
+
# @return [Integer] the number of removed elements
|
1261
1445
|
def lrem(key, count, value)
|
1262
1446
|
synchronize do |client|
|
1263
1447
|
client.call([:lrem, key, count, value])
|
@@ -1267,7 +1451,7 @@ class Redis
|
|
1267
1451
|
# Set the value of an element in a list by its index.
|
1268
1452
|
#
|
1269
1453
|
# @param [String] key
|
1270
|
-
# @param [
|
1454
|
+
# @param [Integer] index
|
1271
1455
|
# @param [String] value
|
1272
1456
|
# @return [String] `OK`
|
1273
1457
|
def lset(key, index, value)
|
@@ -1279,8 +1463,8 @@ class Redis
|
|
1279
1463
|
# Trim a list to the specified range.
|
1280
1464
|
#
|
1281
1465
|
# @param [String] key
|
1282
|
-
# @param [
|
1283
|
-
# @param [
|
1466
|
+
# @param [Integer] start start index
|
1467
|
+
# @param [Integer] stop stop index
|
1284
1468
|
# @return [String] `OK`
|
1285
1469
|
def ltrim(key, start, stop)
|
1286
1470
|
synchronize do |client|
|
@@ -1291,7 +1475,7 @@ class Redis
|
|
1291
1475
|
# Get the number of members in a set.
|
1292
1476
|
#
|
1293
1477
|
# @param [String] key
|
1294
|
-
# @return [
|
1478
|
+
# @return [Integer]
|
1295
1479
|
def scard(key)
|
1296
1480
|
synchronize do |client|
|
1297
1481
|
client.call([:scard, key])
|
@@ -1302,8 +1486,8 @@ class Redis
|
|
1302
1486
|
#
|
1303
1487
|
# @param [String] key
|
1304
1488
|
# @param [String, Array<String>] member one member, or array of members
|
1305
|
-
# @return [Boolean,
|
1306
|
-
# holding whether or not adding the member succeeded, or `
|
1489
|
+
# @return [Boolean, Integer] `Boolean` when a single member is specified,
|
1490
|
+
# holding whether or not adding the member succeeded, or `Integer` when an
|
1307
1491
|
# array of members is specified, holding the number of members that were
|
1308
1492
|
# successfully added
|
1309
1493
|
def sadd(key, member)
|
@@ -1324,8 +1508,8 @@ class Redis
|
|
1324
1508
|
#
|
1325
1509
|
# @param [String] key
|
1326
1510
|
# @param [String, Array<String>] member one member, or array of members
|
1327
|
-
# @return [Boolean,
|
1328
|
-
# holding whether or not removing the member succeeded, or `
|
1511
|
+
# @return [Boolean, Integer] `Boolean` when a single member is specified,
|
1512
|
+
# holding whether or not removing the member succeeded, or `Integer` when an
|
1329
1513
|
# array of members is specified, holding the number of members that were
|
1330
1514
|
# successfully removed
|
1331
1515
|
def srem(key, member)
|
@@ -1346,7 +1530,7 @@ class Redis
|
|
1346
1530
|
#
|
1347
1531
|
# @param [String] key
|
1348
1532
|
# @return [String]
|
1349
|
-
# @param [
|
1533
|
+
# @param [Integer] count
|
1350
1534
|
def spop(key, count = nil)
|
1351
1535
|
synchronize do |client|
|
1352
1536
|
if count.nil?
|
@@ -1360,7 +1544,7 @@ class Redis
|
|
1360
1544
|
# Get one or more random members from a set.
|
1361
1545
|
#
|
1362
1546
|
# @param [String] key
|
1363
|
-
# @param [
|
1547
|
+
# @param [Integer] count
|
1364
1548
|
# @return [String]
|
1365
1549
|
def srandmember(key, count = nil)
|
1366
1550
|
synchronize do |client|
|
@@ -1395,6 +1579,19 @@ class Redis
|
|
1395
1579
|
end
|
1396
1580
|
end
|
1397
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
|
+
|
1398
1595
|
# Get all the members in a set.
|
1399
1596
|
#
|
1400
1597
|
# @param [String] key
|
@@ -1411,7 +1608,7 @@ class Redis
|
|
1411
1608
|
# @return [Array<String>] members in the difference
|
1412
1609
|
def sdiff(*keys)
|
1413
1610
|
synchronize do |client|
|
1414
|
-
client.call([:sdiff
|
1611
|
+
client.call([:sdiff, *keys])
|
1415
1612
|
end
|
1416
1613
|
end
|
1417
1614
|
|
@@ -1419,10 +1616,10 @@ class Redis
|
|
1419
1616
|
#
|
1420
1617
|
# @param [String] destination destination key
|
1421
1618
|
# @param [String, Array<String>] keys keys pointing to sets to subtract
|
1422
|
-
# @return [
|
1619
|
+
# @return [Integer] number of elements in the resulting set
|
1423
1620
|
def sdiffstore(destination, *keys)
|
1424
1621
|
synchronize do |client|
|
1425
|
-
client.call([:sdiffstore, destination
|
1622
|
+
client.call([:sdiffstore, destination, *keys])
|
1426
1623
|
end
|
1427
1624
|
end
|
1428
1625
|
|
@@ -1432,7 +1629,7 @@ class Redis
|
|
1432
1629
|
# @return [Array<String>] members in the intersection
|
1433
1630
|
def sinter(*keys)
|
1434
1631
|
synchronize do |client|
|
1435
|
-
client.call([:sinter
|
1632
|
+
client.call([:sinter, *keys])
|
1436
1633
|
end
|
1437
1634
|
end
|
1438
1635
|
|
@@ -1440,10 +1637,10 @@ class Redis
|
|
1440
1637
|
#
|
1441
1638
|
# @param [String] destination destination key
|
1442
1639
|
# @param [String, Array<String>] keys keys pointing to sets to intersect
|
1443
|
-
# @return [
|
1640
|
+
# @return [Integer] number of elements in the resulting set
|
1444
1641
|
def sinterstore(destination, *keys)
|
1445
1642
|
synchronize do |client|
|
1446
|
-
client.call([:sinterstore, destination
|
1643
|
+
client.call([:sinterstore, destination, *keys])
|
1447
1644
|
end
|
1448
1645
|
end
|
1449
1646
|
|
@@ -1453,7 +1650,7 @@ class Redis
|
|
1453
1650
|
# @return [Array<String>] members in the union
|
1454
1651
|
def sunion(*keys)
|
1455
1652
|
synchronize do |client|
|
1456
|
-
client.call([:sunion
|
1653
|
+
client.call([:sunion, *keys])
|
1457
1654
|
end
|
1458
1655
|
end
|
1459
1656
|
|
@@ -1461,10 +1658,10 @@ class Redis
|
|
1461
1658
|
#
|
1462
1659
|
# @param [String] destination destination key
|
1463
1660
|
# @param [String, Array<String>] keys keys pointing to sets to unify
|
1464
|
-
# @return [
|
1661
|
+
# @return [Integer] number of elements in the resulting set
|
1465
1662
|
def sunionstore(destination, *keys)
|
1466
1663
|
synchronize do |client|
|
1467
|
-
client.call([:sunionstore, destination
|
1664
|
+
client.call([:sunionstore, destination, *keys])
|
1468
1665
|
end
|
1469
1666
|
end
|
1470
1667
|
|
@@ -1475,7 +1672,7 @@ class Redis
|
|
1475
1672
|
# # => 4
|
1476
1673
|
#
|
1477
1674
|
# @param [String] key
|
1478
|
-
# @return [
|
1675
|
+
# @return [Integer]
|
1479
1676
|
def zcard(key)
|
1480
1677
|
synchronize do |client|
|
1481
1678
|
client.call([:zcard, key])
|
@@ -1499,6 +1696,10 @@ class Redis
|
|
1499
1696
|
# add elements)
|
1500
1697
|
# - `:nx => true`: Don't update already existing elements (always
|
1501
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
|
1502
1703
|
# - `:ch => true`: Modify the return value from the number of new
|
1503
1704
|
# elements added, to the total number of elements changed (CH is an
|
1504
1705
|
# abbreviation of changed); changed elements are new elements added
|
@@ -1506,38 +1707,29 @@ class Redis
|
|
1506
1707
|
# - `:incr => true`: When this option is specified ZADD acts like
|
1507
1708
|
# ZINCRBY; only one score-element pair can be specified in this mode
|
1508
1709
|
#
|
1509
|
-
# @return [Boolean,
|
1710
|
+
# @return [Boolean, Integer, Float]
|
1510
1711
|
# - `Boolean` when a single pair is specified, holding whether or not it was
|
1511
1712
|
# **added** to the sorted set.
|
1512
|
-
# - `
|
1713
|
+
# - `Integer` when an array of pairs is specified, holding the number of
|
1513
1714
|
# pairs that were **added** to the sorted set.
|
1514
1715
|
# - `Float` when option :incr is specified, holding the score of the member
|
1515
1716
|
# after incrementing it.
|
1516
|
-
def zadd(key, *args
|
1517
|
-
|
1518
|
-
if
|
1519
|
-
|
1520
|
-
|
1521
|
-
|
1522
|
-
|
1523
|
-
|
1524
|
-
xx = options[:xx]
|
1525
|
-
zadd_options << "XX" if xx
|
1526
|
-
|
1527
|
-
ch = options[:ch]
|
1528
|
-
zadd_options << "CH" if ch
|
1529
|
-
|
1530
|
-
incr = options[:incr]
|
1531
|
-
zadd_options << "INCR" if incr
|
1532
|
-
end
|
1717
|
+
def zadd(key, *args, nx: nil, xx: nil, lt: nil, gt: nil, ch: nil, incr: nil)
|
1718
|
+
command = [:zadd, key]
|
1719
|
+
command << "NX" if nx
|
1720
|
+
command << "XX" if xx
|
1721
|
+
command << "LT" if lt
|
1722
|
+
command << "GT" if gt
|
1723
|
+
command << "CH" if ch
|
1724
|
+
command << "INCR" if incr
|
1533
1725
|
|
1534
1726
|
synchronize do |client|
|
1535
1727
|
if args.size == 1 && args[0].is_a?(Array)
|
1536
1728
|
# Variadic: return float if INCR, integer if !INCR
|
1537
|
-
client.call(
|
1729
|
+
client.call(command + args[0], &(incr ? Floatify : nil))
|
1538
1730
|
elsif args.size == 2
|
1539
1731
|
# Single pair: return float if INCR, boolean if !INCR
|
1540
|
-
client.call(
|
1732
|
+
client.call(command + args, &(incr ? Floatify : Boolify))
|
1541
1733
|
else
|
1542
1734
|
raise ArgumentError, "wrong number of arguments"
|
1543
1735
|
end
|
@@ -1572,10 +1764,10 @@ class Redis
|
|
1572
1764
|
# - a single member
|
1573
1765
|
# - an array of members
|
1574
1766
|
#
|
1575
|
-
# @return [Boolean,
|
1767
|
+
# @return [Boolean, Integer]
|
1576
1768
|
# - `Boolean` when a single member is specified, holding whether or not it
|
1577
1769
|
# was removed from the sorted set
|
1578
|
-
# - `
|
1770
|
+
# - `Integer` when an array of pairs is specified, holding the number of
|
1579
1771
|
# members that were removed to the sorted set
|
1580
1772
|
def zrem(key, member)
|
1581
1773
|
synchronize do |client|
|
@@ -1591,6 +1783,90 @@ class Redis
|
|
1591
1783
|
end
|
1592
1784
|
end
|
1593
1785
|
|
1786
|
+
# Removes and returns up to count members with the highest scores in the sorted set stored at key.
|
1787
|
+
#
|
1788
|
+
# @example Popping a member
|
1789
|
+
# redis.zpopmax('zset')
|
1790
|
+
# #=> ['b', 2.0]
|
1791
|
+
# @example With count option
|
1792
|
+
# redis.zpopmax('zset', 2)
|
1793
|
+
# #=> [['b', 2.0], ['a', 1.0]]
|
1794
|
+
#
|
1795
|
+
# @params key [String] a key of the sorted set
|
1796
|
+
# @params count [Integer] a number of members
|
1797
|
+
#
|
1798
|
+
# @return [Array<String, Float>] element and score pair if count is not specified
|
1799
|
+
# @return [Array<Array<String, Float>>] list of popped elements and scores
|
1800
|
+
def zpopmax(key, count = nil)
|
1801
|
+
synchronize do |client|
|
1802
|
+
members = client.call([:zpopmax, key, count].compact, &FloatifyPairs)
|
1803
|
+
count.to_i > 1 ? members : members.first
|
1804
|
+
end
|
1805
|
+
end
|
1806
|
+
|
1807
|
+
# Removes and returns up to count members with the lowest scores in the sorted set stored at key.
|
1808
|
+
#
|
1809
|
+
# @example Popping a member
|
1810
|
+
# redis.zpopmin('zset')
|
1811
|
+
# #=> ['a', 1.0]
|
1812
|
+
# @example With count option
|
1813
|
+
# redis.zpopmin('zset', 2)
|
1814
|
+
# #=> [['a', 1.0], ['b', 2.0]]
|
1815
|
+
#
|
1816
|
+
# @params key [String] a key of the sorted set
|
1817
|
+
# @params count [Integer] a number of members
|
1818
|
+
#
|
1819
|
+
# @return [Array<String, Float>] element and score pair if count is not specified
|
1820
|
+
# @return [Array<Array<String, Float>>] list of popped elements and scores
|
1821
|
+
def zpopmin(key, count = nil)
|
1822
|
+
synchronize do |client|
|
1823
|
+
members = client.call([:zpopmin, key, count].compact, &FloatifyPairs)
|
1824
|
+
count.to_i > 1 ? members : members.first
|
1825
|
+
end
|
1826
|
+
end
|
1827
|
+
|
1828
|
+
# Removes and returns up to count members with the highest scores in the sorted set stored at keys,
|
1829
|
+
# or block until one is available.
|
1830
|
+
#
|
1831
|
+
# @example Popping a member from a sorted set
|
1832
|
+
# redis.bzpopmax('zset', 1)
|
1833
|
+
# #=> ['zset', 'b', 2.0]
|
1834
|
+
# @example Popping a member from multiple sorted sets
|
1835
|
+
# redis.bzpopmax('zset1', 'zset2', 1)
|
1836
|
+
# #=> ['zset1', 'b', 2.0]
|
1837
|
+
#
|
1838
|
+
# @params keys [Array<String>] one or multiple keys of the sorted sets
|
1839
|
+
# @params timeout [Integer] the maximum number of seconds to block
|
1840
|
+
#
|
1841
|
+
# @return [Array<String, String, Float>] a touple of key, member and score
|
1842
|
+
# @return [nil] when no element could be popped and the timeout expired
|
1843
|
+
def bzpopmax(*args)
|
1844
|
+
_bpop(:bzpopmax, args) do |reply|
|
1845
|
+
reply.is_a?(Array) ? [reply[0], reply[1], Floatify.call(reply[2])] : reply
|
1846
|
+
end
|
1847
|
+
end
|
1848
|
+
|
1849
|
+
# Removes and returns up to count members with the lowest scores in the sorted set stored at keys,
|
1850
|
+
# or block until one is available.
|
1851
|
+
#
|
1852
|
+
# @example Popping a member from a sorted set
|
1853
|
+
# redis.bzpopmin('zset', 1)
|
1854
|
+
# #=> ['zset', 'a', 1.0]
|
1855
|
+
# @example Popping a member from multiple sorted sets
|
1856
|
+
# redis.bzpopmin('zset1', 'zset2', 1)
|
1857
|
+
# #=> ['zset1', 'a', 1.0]
|
1858
|
+
#
|
1859
|
+
# @params keys [Array<String>] one or multiple keys of the sorted sets
|
1860
|
+
# @params timeout [Integer] the maximum number of seconds to block
|
1861
|
+
#
|
1862
|
+
# @return [Array<String, String, Float>] a touple of key, member and score
|
1863
|
+
# @return [nil] when no element could be popped and the timeout expired
|
1864
|
+
def bzpopmin(*args)
|
1865
|
+
_bpop(:bzpopmin, args) do |reply|
|
1866
|
+
reply.is_a?(Array) ? [reply[0], reply[1], Floatify.call(reply[2])] : reply
|
1867
|
+
end
|
1868
|
+
end
|
1869
|
+
|
1594
1870
|
# Get the score associated with the given member in a sorted set.
|
1595
1871
|
#
|
1596
1872
|
# @example Get the score for member "a"
|
@@ -1606,6 +1882,63 @@ class Redis
|
|
1606
1882
|
end
|
1607
1883
|
end
|
1608
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
|
+
|
1609
1942
|
# Return a range of members in a sorted set, by index.
|
1610
1943
|
#
|
1611
1944
|
# @example Retrieve all members from a sorted set
|
@@ -1616,18 +1949,16 @@ class Redis
|
|
1616
1949
|
# # => [["a", 32.0], ["b", 64.0]]
|
1617
1950
|
#
|
1618
1951
|
# @param [String] key
|
1619
|
-
# @param [
|
1620
|
-
# @param [
|
1952
|
+
# @param [Integer] start start index
|
1953
|
+
# @param [Integer] stop stop index
|
1621
1954
|
# @param [Hash] options
|
1622
1955
|
# - `:with_scores => true`: include scores in output
|
1623
1956
|
#
|
1624
1957
|
# @return [Array<String>, Array<[String, Float]>]
|
1625
1958
|
# - when `:with_scores` is not specified, an array of members
|
1626
1959
|
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
1627
|
-
def zrange(key, start, stop,
|
1628
|
-
args = []
|
1629
|
-
|
1630
|
-
with_scores = options[:with_scores] || options[:withscores]
|
1960
|
+
def zrange(key, start, stop, withscores: false, with_scores: withscores)
|
1961
|
+
args = [:zrange, key, start, stop]
|
1631
1962
|
|
1632
1963
|
if with_scores
|
1633
1964
|
args << "WITHSCORES"
|
@@ -1635,7 +1966,7 @@ class Redis
|
|
1635
1966
|
end
|
1636
1967
|
|
1637
1968
|
synchronize do |client|
|
1638
|
-
client.call(
|
1969
|
+
client.call(args, &block)
|
1639
1970
|
end
|
1640
1971
|
end
|
1641
1972
|
|
@@ -1650,10 +1981,8 @@ class Redis
|
|
1650
1981
|
# # => [["b", 64.0], ["a", 32.0]]
|
1651
1982
|
#
|
1652
1983
|
# @see #zrange
|
1653
|
-
def zrevrange(key, start, stop,
|
1654
|
-
args = []
|
1655
|
-
|
1656
|
-
with_scores = options[:with_scores] || options[:withscores]
|
1984
|
+
def zrevrange(key, start, stop, withscores: false, with_scores: withscores)
|
1985
|
+
args = [:zrevrange, key, start, stop]
|
1657
1986
|
|
1658
1987
|
if with_scores
|
1659
1988
|
args << "WITHSCORES"
|
@@ -1661,7 +1990,7 @@ class Redis
|
|
1661
1990
|
end
|
1662
1991
|
|
1663
1992
|
synchronize do |client|
|
1664
|
-
client.call(
|
1993
|
+
client.call(args, &block)
|
1665
1994
|
end
|
1666
1995
|
end
|
1667
1996
|
|
@@ -1669,7 +1998,7 @@ class Redis
|
|
1669
1998
|
#
|
1670
1999
|
# @param [String] key
|
1671
2000
|
# @param [String] member
|
1672
|
-
# @return [
|
2001
|
+
# @return [Integer]
|
1673
2002
|
def zrank(key, member)
|
1674
2003
|
synchronize do |client|
|
1675
2004
|
client.call([:zrank, key, member])
|
@@ -1681,7 +2010,7 @@ class Redis
|
|
1681
2010
|
#
|
1682
2011
|
# @param [String] key
|
1683
2012
|
# @param [String] member
|
1684
|
-
# @return [
|
2013
|
+
# @return [Integer]
|
1685
2014
|
def zrevrank(key, member)
|
1686
2015
|
synchronize do |client|
|
1687
2016
|
client.call([:zrevrank, key, member])
|
@@ -1698,15 +2027,39 @@ class Redis
|
|
1698
2027
|
# # => 5
|
1699
2028
|
#
|
1700
2029
|
# @param [String] key
|
1701
|
-
# @param [
|
1702
|
-
# @param [
|
1703
|
-
# @return [
|
2030
|
+
# @param [Integer] start start index
|
2031
|
+
# @param [Integer] stop stop index
|
2032
|
+
# @return [Integer] number of members that were removed
|
1704
2033
|
def zremrangebyrank(key, start, stop)
|
1705
2034
|
synchronize do |client|
|
1706
2035
|
client.call([:zremrangebyrank, key, start, stop])
|
1707
2036
|
end
|
1708
2037
|
end
|
1709
2038
|
|
2039
|
+
# Count the members, with the same score in a sorted set, within the given lexicographical range.
|
2040
|
+
#
|
2041
|
+
# @example Count members matching a
|
2042
|
+
# redis.zlexcount("zset", "[a", "[a\xff")
|
2043
|
+
# # => 1
|
2044
|
+
# @example Count members matching a-z
|
2045
|
+
# redis.zlexcount("zset", "[a", "[z\xff")
|
2046
|
+
# # => 26
|
2047
|
+
#
|
2048
|
+
# @param [String] key
|
2049
|
+
# @param [String] min
|
2050
|
+
# - inclusive minimum is specified by prefixing `(`
|
2051
|
+
# - exclusive minimum is specified by prefixing `[`
|
2052
|
+
# @param [String] max
|
2053
|
+
# - inclusive maximum is specified by prefixing `(`
|
2054
|
+
# - exclusive maximum is specified by prefixing `[`
|
2055
|
+
#
|
2056
|
+
# @return [Integer] number of members within the specified lexicographical range
|
2057
|
+
def zlexcount(key, min, max)
|
2058
|
+
synchronize do |client|
|
2059
|
+
client.call([:zlexcount, key, min, max])
|
2060
|
+
end
|
2061
|
+
end
|
2062
|
+
|
1710
2063
|
# Return a range of members with the same score in a sorted set, by lexicographical ordering
|
1711
2064
|
#
|
1712
2065
|
# @example Retrieve members matching a
|
@@ -1728,14 +2081,16 @@ class Redis
|
|
1728
2081
|
# `count` members
|
1729
2082
|
#
|
1730
2083
|
# @return [Array<String>, Array<[String, Float]>]
|
1731
|
-
def zrangebylex(key, min, max,
|
1732
|
-
args = []
|
2084
|
+
def zrangebylex(key, min, max, limit: nil)
|
2085
|
+
args = [:zrangebylex, key, min, max]
|
1733
2086
|
|
1734
|
-
|
1735
|
-
|
2087
|
+
if limit
|
2088
|
+
args << "LIMIT"
|
2089
|
+
args.concat(limit)
|
2090
|
+
end
|
1736
2091
|
|
1737
2092
|
synchronize do |client|
|
1738
|
-
client.call(
|
2093
|
+
client.call(args)
|
1739
2094
|
end
|
1740
2095
|
end
|
1741
2096
|
|
@@ -1750,14 +2105,16 @@ class Redis
|
|
1750
2105
|
# # => ["abbygail", "abby"]
|
1751
2106
|
#
|
1752
2107
|
# @see #zrangebylex
|
1753
|
-
def zrevrangebylex(key, max, min,
|
1754
|
-
args = []
|
2108
|
+
def zrevrangebylex(key, max, min, limit: nil)
|
2109
|
+
args = [:zrevrangebylex, key, max, min]
|
1755
2110
|
|
1756
|
-
|
1757
|
-
|
2111
|
+
if limit
|
2112
|
+
args << "LIMIT"
|
2113
|
+
args.concat(limit)
|
2114
|
+
end
|
1758
2115
|
|
1759
2116
|
synchronize do |client|
|
1760
|
-
client.call(
|
2117
|
+
client.call(args)
|
1761
2118
|
end
|
1762
2119
|
end
|
1763
2120
|
|
@@ -1788,21 +2145,21 @@ class Redis
|
|
1788
2145
|
# @return [Array<String>, Array<[String, Float]>]
|
1789
2146
|
# - when `:with_scores` is not specified, an array of members
|
1790
2147
|
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
1791
|
-
def zrangebyscore(key, min, max,
|
1792
|
-
args = []
|
1793
|
-
|
1794
|
-
with_scores = options[:with_scores] || options[:withscores]
|
2148
|
+
def zrangebyscore(key, min, max, withscores: false, with_scores: withscores, limit: nil)
|
2149
|
+
args = [:zrangebyscore, key, min, max]
|
1795
2150
|
|
1796
2151
|
if with_scores
|
1797
2152
|
args << "WITHSCORES"
|
1798
2153
|
block = FloatifyPairs
|
1799
2154
|
end
|
1800
2155
|
|
1801
|
-
|
1802
|
-
|
2156
|
+
if limit
|
2157
|
+
args << "LIMIT"
|
2158
|
+
args.concat(limit)
|
2159
|
+
end
|
1803
2160
|
|
1804
2161
|
synchronize do |client|
|
1805
|
-
client.call(
|
2162
|
+
client.call(args, &block)
|
1806
2163
|
end
|
1807
2164
|
end
|
1808
2165
|
|
@@ -1820,21 +2177,21 @@ class Redis
|
|
1820
2177
|
# # => [["b", 64.0], ["a", 32.0]]
|
1821
2178
|
#
|
1822
2179
|
# @see #zrangebyscore
|
1823
|
-
def zrevrangebyscore(key, max, min,
|
1824
|
-
args = []
|
1825
|
-
|
1826
|
-
with_scores = options[:with_scores] || options[:withscores]
|
2180
|
+
def zrevrangebyscore(key, max, min, withscores: false, with_scores: withscores, limit: nil)
|
2181
|
+
args = [:zrevrangebyscore, key, max, min]
|
1827
2182
|
|
1828
2183
|
if with_scores
|
1829
|
-
args <<
|
2184
|
+
args << "WITHSCORES"
|
1830
2185
|
block = FloatifyPairs
|
1831
2186
|
end
|
1832
2187
|
|
1833
|
-
|
1834
|
-
|
2188
|
+
if limit
|
2189
|
+
args << "LIMIT"
|
2190
|
+
args.concat(limit)
|
2191
|
+
end
|
1835
2192
|
|
1836
2193
|
synchronize do |client|
|
1837
|
-
client.call(
|
2194
|
+
client.call(args, &block)
|
1838
2195
|
end
|
1839
2196
|
end
|
1840
2197
|
|
@@ -1854,7 +2211,7 @@ class Redis
|
|
1854
2211
|
# @param [String] max
|
1855
2212
|
# - inclusive maximum score is specified verbatim
|
1856
2213
|
# - exclusive maximum score is specified by prefixing `(`
|
1857
|
-
# @return [
|
2214
|
+
# @return [Integer] number of members that were removed
|
1858
2215
|
def zremrangebyscore(key, min, max)
|
1859
2216
|
synchronize do |client|
|
1860
2217
|
client.call([:zremrangebyscore, key, min, max])
|
@@ -1877,13 +2234,52 @@ class Redis
|
|
1877
2234
|
# @param [String] max
|
1878
2235
|
# - inclusive maximum score is specified verbatim
|
1879
2236
|
# - exclusive maximum score is specified by prefixing `(`
|
1880
|
-
# @return [
|
2237
|
+
# @return [Integer] number of members in within the specified range
|
1881
2238
|
def zcount(key, min, max)
|
1882
2239
|
synchronize do |client|
|
1883
2240
|
client.call([:zcount, key, min, max])
|
1884
2241
|
end
|
1885
2242
|
end
|
1886
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
|
+
|
1887
2283
|
# Intersect multiple sorted sets and store the resulting sorted set in a new
|
1888
2284
|
# key.
|
1889
2285
|
#
|
@@ -1897,18 +2293,19 @@ class Redis
|
|
1897
2293
|
# - `:weights => [Float, Float, ...]`: weights to associate with source
|
1898
2294
|
# sorted sets
|
1899
2295
|
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
1900
|
-
# @return [
|
1901
|
-
def zinterstore(destination, keys,
|
1902
|
-
args = []
|
2296
|
+
# @return [Integer] number of elements in the resulting sorted set
|
2297
|
+
def zinterstore(destination, keys, weights: nil, aggregate: nil)
|
2298
|
+
args = [:zinterstore, destination, keys.size, *keys]
|
1903
2299
|
|
1904
|
-
|
1905
|
-
|
2300
|
+
if weights
|
2301
|
+
args << "WEIGHTS"
|
2302
|
+
args.concat(weights)
|
2303
|
+
end
|
1906
2304
|
|
1907
|
-
aggregate
|
1908
|
-
args.concat(["AGGREGATE", aggregate]) if aggregate
|
2305
|
+
args << "AGGREGATE" << aggregate if aggregate
|
1909
2306
|
|
1910
2307
|
synchronize do |client|
|
1911
|
-
client.call(
|
2308
|
+
client.call(args)
|
1912
2309
|
end
|
1913
2310
|
end
|
1914
2311
|
|
@@ -1924,40 +2321,46 @@ class Redis
|
|
1924
2321
|
# - `:weights => [Float, Float, ...]`: weights to associate with source
|
1925
2322
|
# sorted sets
|
1926
2323
|
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
1927
|
-
# @return [
|
1928
|
-
def zunionstore(destination, keys,
|
1929
|
-
args = []
|
2324
|
+
# @return [Integer] number of elements in the resulting sorted set
|
2325
|
+
def zunionstore(destination, keys, weights: nil, aggregate: nil)
|
2326
|
+
args = [:zunionstore, destination, keys.size, *keys]
|
1930
2327
|
|
1931
|
-
|
1932
|
-
|
2328
|
+
if weights
|
2329
|
+
args << "WEIGHTS"
|
2330
|
+
args.concat(weights)
|
2331
|
+
end
|
1933
2332
|
|
1934
|
-
aggregate
|
1935
|
-
args.concat(["AGGREGATE", aggregate]) if aggregate
|
2333
|
+
args << "AGGREGATE" << aggregate if aggregate
|
1936
2334
|
|
1937
2335
|
synchronize do |client|
|
1938
|
-
client.call(
|
2336
|
+
client.call(args)
|
1939
2337
|
end
|
1940
2338
|
end
|
1941
2339
|
|
1942
2340
|
# Get the number of fields in a hash.
|
1943
2341
|
#
|
1944
2342
|
# @param [String] key
|
1945
|
-
# @return [
|
2343
|
+
# @return [Integer] number of fields in the hash
|
1946
2344
|
def hlen(key)
|
1947
2345
|
synchronize do |client|
|
1948
2346
|
client.call([:hlen, key])
|
1949
2347
|
end
|
1950
2348
|
end
|
1951
2349
|
|
1952
|
-
# Set
|
2350
|
+
# Set one or more hash values.
|
2351
|
+
#
|
2352
|
+
# @example
|
2353
|
+
# redis.hset("hash", "f1", "v1", "f2", "v2") # => 2
|
2354
|
+
# redis.hset("hash", { "f1" => "v1", "f2" => "v2" }) # => 2
|
1953
2355
|
#
|
1954
2356
|
# @param [String] key
|
1955
|
-
# @param [String]
|
1956
|
-
# @
|
1957
|
-
|
1958
|
-
|
2357
|
+
# @param [Array<String> | Hash<String, String>] attrs array or hash of fields and values
|
2358
|
+
# @return [Integer] The number of fields that were added to the hash
|
2359
|
+
def hset(key, *attrs)
|
2360
|
+
attrs = attrs.first.flatten if attrs.size == 1 && attrs.first.is_a?(Hash)
|
2361
|
+
|
1959
2362
|
synchronize do |client|
|
1960
|
-
client.call([:hset, key,
|
2363
|
+
client.call([:hset, key, *attrs])
|
1961
2364
|
end
|
1962
2365
|
end
|
1963
2366
|
|
@@ -2046,7 +2449,7 @@ class Redis
|
|
2046
2449
|
# @see #hmget
|
2047
2450
|
def mapped_hmget(key, *fields)
|
2048
2451
|
hmget(key, *fields) do |reply|
|
2049
|
-
if reply.
|
2452
|
+
if reply.is_a?(Array)
|
2050
2453
|
Hash[fields.zip(reply)]
|
2051
2454
|
else
|
2052
2455
|
reply
|
@@ -2058,10 +2461,10 @@ class Redis
|
|
2058
2461
|
#
|
2059
2462
|
# @param [String] key
|
2060
2463
|
# @param [String, Array<String>] field
|
2061
|
-
# @return [
|
2062
|
-
def hdel(key,
|
2464
|
+
# @return [Integer] the number of fields that were removed from the hash
|
2465
|
+
def hdel(key, *fields)
|
2063
2466
|
synchronize do |client|
|
2064
|
-
client.call([:hdel, key,
|
2467
|
+
client.call([:hdel, key, *fields])
|
2065
2468
|
end
|
2066
2469
|
end
|
2067
2470
|
|
@@ -2080,8 +2483,8 @@ class Redis
|
|
2080
2483
|
#
|
2081
2484
|
# @param [String] key
|
2082
2485
|
# @param [String] field
|
2083
|
-
# @param [
|
2084
|
-
# @return [
|
2486
|
+
# @param [Integer] increment
|
2487
|
+
# @return [Integer] value of the field after incrementing it
|
2085
2488
|
def hincrby(key, field, increment)
|
2086
2489
|
synchronize do |client|
|
2087
2490
|
client.call([:hincrby, key, field, increment])
|
@@ -2139,20 +2542,21 @@ class Redis
|
|
2139
2542
|
|
2140
2543
|
def subscribed?
|
2141
2544
|
synchronize do |client|
|
2142
|
-
client.
|
2545
|
+
client.is_a? SubscribedClient
|
2143
2546
|
end
|
2144
2547
|
end
|
2145
2548
|
|
2146
2549
|
# Listen for messages published to the given channels.
|
2147
2550
|
def subscribe(*channels, &block)
|
2148
|
-
synchronize do |
|
2551
|
+
synchronize do |_client|
|
2149
2552
|
_subscription(:subscribe, 0, channels, block)
|
2150
2553
|
end
|
2151
2554
|
end
|
2152
2555
|
|
2153
|
-
# Listen for messages published to the given channels. Throw a timeout error
|
2556
|
+
# Listen for messages published to the given channels. Throw a timeout error
|
2557
|
+
# if there is no messages for a timeout period.
|
2154
2558
|
def subscribe_with_timeout(timeout, *channels, &block)
|
2155
|
-
synchronize do |
|
2559
|
+
synchronize do |_client|
|
2156
2560
|
_subscription(:subscribe_with_timeout, timeout, channels, block)
|
2157
2561
|
end
|
2158
2562
|
end
|
@@ -2160,21 +2564,23 @@ class Redis
|
|
2160
2564
|
# Stop listening for messages posted to the given channels.
|
2161
2565
|
def unsubscribe(*channels)
|
2162
2566
|
synchronize do |client|
|
2163
|
-
raise
|
2567
|
+
raise "Can't unsubscribe if not subscribed." unless subscribed?
|
2568
|
+
|
2164
2569
|
client.unsubscribe(*channels)
|
2165
2570
|
end
|
2166
2571
|
end
|
2167
2572
|
|
2168
2573
|
# Listen for messages published to channels matching the given patterns.
|
2169
2574
|
def psubscribe(*channels, &block)
|
2170
|
-
synchronize do |
|
2575
|
+
synchronize do |_client|
|
2171
2576
|
_subscription(:psubscribe, 0, channels, block)
|
2172
2577
|
end
|
2173
2578
|
end
|
2174
2579
|
|
2175
|
-
# Listen for messages published to channels matching the given patterns.
|
2580
|
+
# Listen for messages published to channels matching the given patterns.
|
2581
|
+
# Throw a timeout error if there is no messages for a timeout period.
|
2176
2582
|
def psubscribe_with_timeout(timeout, *channels, &block)
|
2177
|
-
synchronize do |
|
2583
|
+
synchronize do |_client|
|
2178
2584
|
_subscription(:psubscribe_with_timeout, timeout, channels, block)
|
2179
2585
|
end
|
2180
2586
|
end
|
@@ -2182,7 +2588,8 @@ class Redis
|
|
2182
2588
|
# Stop listening for messages posted to channels matching the given patterns.
|
2183
2589
|
def punsubscribe(*channels)
|
2184
2590
|
synchronize do |client|
|
2185
|
-
raise
|
2591
|
+
raise "Can't unsubscribe if not subscribed." unless subscribed?
|
2592
|
+
|
2186
2593
|
client.punsubscribe(*channels)
|
2187
2594
|
end
|
2188
2595
|
end
|
@@ -2227,7 +2634,7 @@ class Redis
|
|
2227
2634
|
# @see #multi
|
2228
2635
|
def watch(*keys)
|
2229
2636
|
synchronize do |client|
|
2230
|
-
res = client.call([:watch
|
2637
|
+
res = client.call([:watch, *keys])
|
2231
2638
|
|
2232
2639
|
if block_given?
|
2233
2640
|
begin
|
@@ -2257,13 +2664,13 @@ class Redis
|
|
2257
2664
|
end
|
2258
2665
|
|
2259
2666
|
def pipelined
|
2260
|
-
synchronize do |
|
2667
|
+
synchronize do |prior_client|
|
2261
2668
|
begin
|
2262
|
-
|
2669
|
+
@client = Pipeline.new(prior_client)
|
2263
2670
|
yield(self)
|
2264
|
-
|
2671
|
+
prior_client.call_pipeline(@client)
|
2265
2672
|
ensure
|
2266
|
-
@client =
|
2673
|
+
@client = prior_client
|
2267
2674
|
end
|
2268
2675
|
end
|
2269
2676
|
end
|
@@ -2299,17 +2706,16 @@ class Redis
|
|
2299
2706
|
# @see #watch
|
2300
2707
|
# @see #unwatch
|
2301
2708
|
def multi
|
2302
|
-
synchronize do |
|
2709
|
+
synchronize do |prior_client|
|
2303
2710
|
if !block_given?
|
2304
|
-
|
2711
|
+
prior_client.call([:multi])
|
2305
2712
|
else
|
2306
2713
|
begin
|
2307
|
-
|
2308
|
-
original, @client = @client, pipeline
|
2714
|
+
@client = Pipeline::Multi.new(prior_client)
|
2309
2715
|
yield(self)
|
2310
|
-
|
2716
|
+
prior_client.call_pipeline(@client)
|
2311
2717
|
ensure
|
2312
|
-
@client =
|
2718
|
+
@client = prior_client
|
2313
2719
|
end
|
2314
2720
|
end
|
2315
2721
|
end
|
@@ -2456,18 +2862,13 @@ class Redis
|
|
2456
2862
|
_eval(:evalsha, args)
|
2457
2863
|
end
|
2458
2864
|
|
2459
|
-
def _scan(command, cursor, args,
|
2865
|
+
def _scan(command, cursor, args, match: nil, count: nil, type: nil, &block)
|
2460
2866
|
# SSCAN/ZSCAN/HSCAN already prepend the key to +args+.
|
2461
2867
|
|
2462
2868
|
args << cursor
|
2463
|
-
|
2464
|
-
|
2465
|
-
|
2466
|
-
end
|
2467
|
-
|
2468
|
-
if count = options[:count]
|
2469
|
-
args.concat(["COUNT", count])
|
2470
|
-
end
|
2869
|
+
args << "MATCH" << match if match
|
2870
|
+
args << "COUNT" << count if count
|
2871
|
+
args << "TYPE" << type if type
|
2471
2872
|
|
2472
2873
|
synchronize do |client|
|
2473
2874
|
client.call([command] + args, &block)
|
@@ -2482,15 +2883,19 @@ class Redis
|
|
2482
2883
|
# @example Retrieve a batch of keys matching a pattern
|
2483
2884
|
# redis.scan(4, :match => "key:1?")
|
2484
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"]]
|
2485
2889
|
#
|
2486
2890
|
# @param [String, Integer] cursor the cursor of the iteration
|
2487
2891
|
# @param [Hash] options
|
2488
2892
|
# - `:match => String`: only return keys matching the pattern
|
2489
2893
|
# - `:count => Integer`: return count keys at most per iteration
|
2894
|
+
# - `:type => String`: return keys only of the given type
|
2490
2895
|
#
|
2491
2896
|
# @return [String, Array<String>] the next cursor and all found keys
|
2492
|
-
def scan(cursor, options
|
2493
|
-
_scan(:scan, cursor, [], options)
|
2897
|
+
def scan(cursor, **options)
|
2898
|
+
_scan(:scan, cursor, [], **options)
|
2494
2899
|
end
|
2495
2900
|
|
2496
2901
|
# Scan the keyspace
|
@@ -2502,17 +2907,23 @@ class Redis
|
|
2502
2907
|
# redis.scan_each(:match => "key:1?") {|key| puts key}
|
2503
2908
|
# # => key:13
|
2504
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"
|
2505
2914
|
#
|
2506
2915
|
# @param [Hash] options
|
2507
2916
|
# - `:match => String`: only return keys matching the pattern
|
2508
2917
|
# - `:count => Integer`: return count keys at most per iteration
|
2918
|
+
# - `:type => String`: return keys only of the given type
|
2509
2919
|
#
|
2510
2920
|
# @return [Enumerator] an enumerator for all found keys
|
2511
|
-
def scan_each(options
|
2512
|
-
return to_enum(:scan_each, options) unless block_given?
|
2921
|
+
def scan_each(**options, &block)
|
2922
|
+
return to_enum(:scan_each, **options) unless block_given?
|
2923
|
+
|
2513
2924
|
cursor = 0
|
2514
2925
|
loop do
|
2515
|
-
cursor, keys = scan(cursor, options)
|
2926
|
+
cursor, keys = scan(cursor, **options)
|
2516
2927
|
keys.each(&block)
|
2517
2928
|
break if cursor == "0"
|
2518
2929
|
end
|
@@ -2529,8 +2940,8 @@ class Redis
|
|
2529
2940
|
# - `:count => Integer`: return count keys at most per iteration
|
2530
2941
|
#
|
2531
2942
|
# @return [String, Array<[String, String]>] the next cursor and all found keys
|
2532
|
-
def hscan(key, cursor, options
|
2533
|
-
_scan(:hscan, cursor, [key], options) do |reply|
|
2943
|
+
def hscan(key, cursor, **options)
|
2944
|
+
_scan(:hscan, cursor, [key], **options) do |reply|
|
2534
2945
|
[reply[0], reply[1].each_slice(2).to_a]
|
2535
2946
|
end
|
2536
2947
|
end
|
@@ -2546,11 +2957,12 @@ class Redis
|
|
2546
2957
|
# - `:count => Integer`: return count keys at most per iteration
|
2547
2958
|
#
|
2548
2959
|
# @return [Enumerator] an enumerator for all found keys
|
2549
|
-
def hscan_each(key, options
|
2550
|
-
return to_enum(:hscan_each, key, options) unless block_given?
|
2960
|
+
def hscan_each(key, **options, &block)
|
2961
|
+
return to_enum(:hscan_each, key, **options) unless block_given?
|
2962
|
+
|
2551
2963
|
cursor = 0
|
2552
2964
|
loop do
|
2553
|
-
cursor, values = hscan(key, cursor, options)
|
2965
|
+
cursor, values = hscan(key, cursor, **options)
|
2554
2966
|
values.each(&block)
|
2555
2967
|
break if cursor == "0"
|
2556
2968
|
end
|
@@ -2568,8 +2980,8 @@ class Redis
|
|
2568
2980
|
#
|
2569
2981
|
# @return [String, Array<[String, Float]>] the next cursor and all found
|
2570
2982
|
# members and scores
|
2571
|
-
def zscan(key, cursor, options
|
2572
|
-
_scan(:zscan, cursor, [key], options) do |reply|
|
2983
|
+
def zscan(key, cursor, **options)
|
2984
|
+
_scan(:zscan, cursor, [key], **options) do |reply|
|
2573
2985
|
[reply[0], FloatifyPairs.call(reply[1])]
|
2574
2986
|
end
|
2575
2987
|
end
|
@@ -2585,11 +2997,12 @@ class Redis
|
|
2585
2997
|
# - `:count => Integer`: return count keys at most per iteration
|
2586
2998
|
#
|
2587
2999
|
# @return [Enumerator] an enumerator for all found scores and members
|
2588
|
-
def zscan_each(key, options
|
2589
|
-
return to_enum(:zscan_each, key, options) unless block_given?
|
3000
|
+
def zscan_each(key, **options, &block)
|
3001
|
+
return to_enum(:zscan_each, key, **options) unless block_given?
|
3002
|
+
|
2590
3003
|
cursor = 0
|
2591
3004
|
loop do
|
2592
|
-
cursor, values = zscan(key, cursor, options)
|
3005
|
+
cursor, values = zscan(key, cursor, **options)
|
2593
3006
|
values.each(&block)
|
2594
3007
|
break if cursor == "0"
|
2595
3008
|
end
|
@@ -2606,8 +3019,8 @@ class Redis
|
|
2606
3019
|
# - `:count => Integer`: return count keys at most per iteration
|
2607
3020
|
#
|
2608
3021
|
# @return [String, Array<String>] the next cursor and all found members
|
2609
|
-
def sscan(key, cursor, options
|
2610
|
-
_scan(:sscan, cursor, [key], options)
|
3022
|
+
def sscan(key, cursor, **options)
|
3023
|
+
_scan(:sscan, cursor, [key], **options)
|
2611
3024
|
end
|
2612
3025
|
|
2613
3026
|
# Scan a set
|
@@ -2621,11 +3034,12 @@ class Redis
|
|
2621
3034
|
# - `:count => Integer`: return count keys at most per iteration
|
2622
3035
|
#
|
2623
3036
|
# @return [Enumerator] an enumerator for all keys in the set
|
2624
|
-
def sscan_each(key, options
|
2625
|
-
return to_enum(:sscan_each, key, options) unless block_given?
|
3037
|
+
def sscan_each(key, **options, &block)
|
3038
|
+
return to_enum(:sscan_each, key, **options) unless block_given?
|
3039
|
+
|
2626
3040
|
cursor = 0
|
2627
3041
|
loop do
|
2628
|
-
cursor, keys = sscan(key, cursor, options)
|
3042
|
+
cursor, keys = sscan(key, cursor, **options)
|
2629
3043
|
keys.each(&block)
|
2630
3044
|
break if cursor == "0"
|
2631
3045
|
end
|
@@ -2648,7 +3062,7 @@ class Redis
|
|
2648
3062
|
# union of the HyperLogLogs contained in the keys.
|
2649
3063
|
#
|
2650
3064
|
# @param [String, Array<String>] keys
|
2651
|
-
# @return [
|
3065
|
+
# @return [Integer]
|
2652
3066
|
def pfcount(*keys)
|
2653
3067
|
synchronize do |client|
|
2654
3068
|
client.call([:pfcount] + keys)
|
@@ -2667,6 +3081,445 @@ class Redis
|
|
2667
3081
|
end
|
2668
3082
|
end
|
2669
3083
|
|
3084
|
+
# Adds the specified geospatial items (latitude, longitude, name) to the specified key
|
3085
|
+
#
|
3086
|
+
# @param [String] key
|
3087
|
+
# @param [Array] member arguemnts for member or members: longitude, latitude, name
|
3088
|
+
# @return [Integer] number of elements added to the sorted set
|
3089
|
+
def geoadd(key, *member)
|
3090
|
+
synchronize do |client|
|
3091
|
+
client.call([:geoadd, key, *member])
|
3092
|
+
end
|
3093
|
+
end
|
3094
|
+
|
3095
|
+
# Returns geohash string representing position for specified members of the specified key.
|
3096
|
+
#
|
3097
|
+
# @param [String] key
|
3098
|
+
# @param [String, Array<String>] member one member or array of members
|
3099
|
+
# @return [Array<String, nil>] returns array containg geohash string if member is present, nil otherwise
|
3100
|
+
def geohash(key, member)
|
3101
|
+
synchronize do |client|
|
3102
|
+
client.call([:geohash, key, member])
|
3103
|
+
end
|
3104
|
+
end
|
3105
|
+
|
3106
|
+
# Query a sorted set representing a geospatial index to fetch members matching a
|
3107
|
+
# given maximum distance from a point
|
3108
|
+
#
|
3109
|
+
# @param [Array] args key, longitude, latitude, radius, unit(m|km|ft|mi)
|
3110
|
+
# @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest
|
3111
|
+
# or the farthest to the nearest relative to the center
|
3112
|
+
# @param [Integer] count limit the results to the first N matching items
|
3113
|
+
# @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
|
3114
|
+
# @return [Array<String>] may be changed with `options`
|
3115
|
+
|
3116
|
+
def georadius(*args, **geoptions)
|
3117
|
+
geoarguments = _geoarguments(*args, **geoptions)
|
3118
|
+
|
3119
|
+
synchronize do |client|
|
3120
|
+
client.call([:georadius, *geoarguments])
|
3121
|
+
end
|
3122
|
+
end
|
3123
|
+
|
3124
|
+
# Query a sorted set representing a geospatial index to fetch members matching a
|
3125
|
+
# given maximum distance from an already existing member
|
3126
|
+
#
|
3127
|
+
# @param [Array] args key, member, radius, unit(m|km|ft|mi)
|
3128
|
+
# @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest or the farthest
|
3129
|
+
# to the nearest relative to the center
|
3130
|
+
# @param [Integer] count limit the results to the first N matching items
|
3131
|
+
# @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
|
3132
|
+
# @return [Array<String>] may be changed with `options`
|
3133
|
+
|
3134
|
+
def georadiusbymember(*args, **geoptions)
|
3135
|
+
geoarguments = _geoarguments(*args, **geoptions)
|
3136
|
+
|
3137
|
+
synchronize do |client|
|
3138
|
+
client.call([:georadiusbymember, *geoarguments])
|
3139
|
+
end
|
3140
|
+
end
|
3141
|
+
|
3142
|
+
# Returns longitude and latitude of members of a geospatial index
|
3143
|
+
#
|
3144
|
+
# @param [String] key
|
3145
|
+
# @param [String, Array<String>] member one member or array of members
|
3146
|
+
# @return [Array<Array<String>, nil>] returns array of elements, where each
|
3147
|
+
# element is either array of longitude and latitude or nil
|
3148
|
+
def geopos(key, member)
|
3149
|
+
synchronize do |client|
|
3150
|
+
client.call([:geopos, key, member])
|
3151
|
+
end
|
3152
|
+
end
|
3153
|
+
|
3154
|
+
# Returns the distance between two members of a geospatial index
|
3155
|
+
#
|
3156
|
+
# @param [String ]key
|
3157
|
+
# @param [Array<String>] members
|
3158
|
+
# @param ['m', 'km', 'mi', 'ft'] unit
|
3159
|
+
# @return [String, nil] returns distance in spefied unit if both members present, nil otherwise.
|
3160
|
+
def geodist(key, member1, member2, unit = 'm')
|
3161
|
+
synchronize do |client|
|
3162
|
+
client.call([:geodist, key, member1, member2, unit])
|
3163
|
+
end
|
3164
|
+
end
|
3165
|
+
|
3166
|
+
# Returns the stream information each subcommand.
|
3167
|
+
#
|
3168
|
+
# @example stream
|
3169
|
+
# redis.xinfo(:stream, 'mystream')
|
3170
|
+
# @example groups
|
3171
|
+
# redis.xinfo(:groups, 'mystream')
|
3172
|
+
# @example consumers
|
3173
|
+
# redis.xinfo(:consumers, 'mystream', 'mygroup')
|
3174
|
+
#
|
3175
|
+
# @param subcommand [String] e.g. `stream` `groups` `consumers`
|
3176
|
+
# @param key [String] the stream key
|
3177
|
+
# @param group [String] the consumer group name, required if subcommand is `consumers`
|
3178
|
+
#
|
3179
|
+
# @return [Hash] information of the stream if subcommand is `stream`
|
3180
|
+
# @return [Array<Hash>] information of the consumer groups if subcommand is `groups`
|
3181
|
+
# @return [Array<Hash>] information of the consumers if subcommand is `consumers`
|
3182
|
+
def xinfo(subcommand, key, group = nil)
|
3183
|
+
args = [:xinfo, subcommand, key, group].compact
|
3184
|
+
synchronize do |client|
|
3185
|
+
client.call(args) do |reply|
|
3186
|
+
case subcommand.to_s.downcase
|
3187
|
+
when 'stream' then Hashify.call(reply)
|
3188
|
+
when 'groups', 'consumers' then reply.map { |arr| Hashify.call(arr) }
|
3189
|
+
else reply
|
3190
|
+
end
|
3191
|
+
end
|
3192
|
+
end
|
3193
|
+
end
|
3194
|
+
|
3195
|
+
# Add new entry to the stream.
|
3196
|
+
#
|
3197
|
+
# @example Without options
|
3198
|
+
# redis.xadd('mystream', f1: 'v1', f2: 'v2')
|
3199
|
+
# @example With options
|
3200
|
+
# redis.xadd('mystream', { f1: 'v1', f2: 'v2' }, id: '0-0', maxlen: 1000, approximate: true)
|
3201
|
+
#
|
3202
|
+
# @param key [String] the stream key
|
3203
|
+
# @param entry [Hash] one or multiple field-value pairs
|
3204
|
+
# @param opts [Hash] several options for `XADD` command
|
3205
|
+
#
|
3206
|
+
# @option opts [String] :id the entry id, default value is `*`, it means auto generation
|
3207
|
+
# @option opts [Integer] :maxlen max length of entries
|
3208
|
+
# @option opts [Boolean] :approximate whether to add `~` modifier of maxlen or not
|
3209
|
+
#
|
3210
|
+
# @return [String] the entry id
|
3211
|
+
def xadd(key, entry, approximate: nil, maxlen: nil, id: '*')
|
3212
|
+
args = [:xadd, key]
|
3213
|
+
if maxlen
|
3214
|
+
args << "MAXLEN"
|
3215
|
+
args << "~" if approximate
|
3216
|
+
args << maxlen
|
3217
|
+
end
|
3218
|
+
args << id
|
3219
|
+
args.concat(entry.to_a.flatten)
|
3220
|
+
synchronize { |client| client.call(args) }
|
3221
|
+
end
|
3222
|
+
|
3223
|
+
# Trims older entries of the stream if needed.
|
3224
|
+
#
|
3225
|
+
# @example Without options
|
3226
|
+
# redis.xtrim('mystream', 1000)
|
3227
|
+
# @example With options
|
3228
|
+
# redis.xtrim('mystream', 1000, approximate: true)
|
3229
|
+
#
|
3230
|
+
# @param key [String] the stream key
|
3231
|
+
# @param mexlen [Integer] max length of entries
|
3232
|
+
# @param approximate [Boolean] whether to add `~` modifier of maxlen or not
|
3233
|
+
#
|
3234
|
+
# @return [Integer] the number of entries actually deleted
|
3235
|
+
def xtrim(key, maxlen, approximate: false)
|
3236
|
+
args = [:xtrim, key, 'MAXLEN', (approximate ? '~' : nil), maxlen].compact
|
3237
|
+
synchronize { |client| client.call(args) }
|
3238
|
+
end
|
3239
|
+
|
3240
|
+
# Delete entries by entry ids.
|
3241
|
+
#
|
3242
|
+
# @example With splatted entry ids
|
3243
|
+
# redis.xdel('mystream', '0-1', '0-2')
|
3244
|
+
# @example With arrayed entry ids
|
3245
|
+
# redis.xdel('mystream', ['0-1', '0-2'])
|
3246
|
+
#
|
3247
|
+
# @param key [String] the stream key
|
3248
|
+
# @param ids [Array<String>] one or multiple entry ids
|
3249
|
+
#
|
3250
|
+
# @return [Integer] the number of entries actually deleted
|
3251
|
+
def xdel(key, *ids)
|
3252
|
+
args = [:xdel, key].concat(ids.flatten)
|
3253
|
+
synchronize { |client| client.call(args) }
|
3254
|
+
end
|
3255
|
+
|
3256
|
+
# Fetches entries of the stream in ascending order.
|
3257
|
+
#
|
3258
|
+
# @example Without options
|
3259
|
+
# redis.xrange('mystream')
|
3260
|
+
# @example With a specific start
|
3261
|
+
# redis.xrange('mystream', '0-1')
|
3262
|
+
# @example With a specific start and end
|
3263
|
+
# redis.xrange('mystream', '0-1', '0-3')
|
3264
|
+
# @example With count options
|
3265
|
+
# redis.xrange('mystream', count: 10)
|
3266
|
+
#
|
3267
|
+
# @param key [String] the stream key
|
3268
|
+
# @param start [String] first entry id of range, default value is `-`
|
3269
|
+
# @param end [String] last entry id of range, default value is `+`
|
3270
|
+
# @param count [Integer] the number of entries as limit
|
3271
|
+
#
|
3272
|
+
# @return [Array<Array<String, Hash>>] the ids and entries pairs
|
3273
|
+
def xrange(key, start = '-', range_end = '+', count: nil)
|
3274
|
+
args = [:xrange, key, start, range_end]
|
3275
|
+
args.concat(['COUNT', count]) if count
|
3276
|
+
synchronize { |client| client.call(args, &HashifyStreamEntries) }
|
3277
|
+
end
|
3278
|
+
|
3279
|
+
# Fetches entries of the stream in descending order.
|
3280
|
+
#
|
3281
|
+
# @example Without options
|
3282
|
+
# redis.xrevrange('mystream')
|
3283
|
+
# @example With a specific end
|
3284
|
+
# redis.xrevrange('mystream', '0-3')
|
3285
|
+
# @example With a specific end and start
|
3286
|
+
# redis.xrevrange('mystream', '0-3', '0-1')
|
3287
|
+
# @example With count options
|
3288
|
+
# redis.xrevrange('mystream', count: 10)
|
3289
|
+
#
|
3290
|
+
# @param key [String] the stream key
|
3291
|
+
# @param end [String] first entry id of range, default value is `+`
|
3292
|
+
# @param start [String] last entry id of range, default value is `-`
|
3293
|
+
# @params count [Integer] the number of entries as limit
|
3294
|
+
#
|
3295
|
+
# @return [Array<Array<String, Hash>>] the ids and entries pairs
|
3296
|
+
def xrevrange(key, range_end = '+', start = '-', count: nil)
|
3297
|
+
args = [:xrevrange, key, range_end, start]
|
3298
|
+
args.concat(['COUNT', count]) if count
|
3299
|
+
synchronize { |client| client.call(args, &HashifyStreamEntries) }
|
3300
|
+
end
|
3301
|
+
|
3302
|
+
# Returns the number of entries inside a stream.
|
3303
|
+
#
|
3304
|
+
# @example With key
|
3305
|
+
# redis.xlen('mystream')
|
3306
|
+
#
|
3307
|
+
# @param key [String] the stream key
|
3308
|
+
#
|
3309
|
+
# @return [Integer] the number of entries
|
3310
|
+
def xlen(key)
|
3311
|
+
synchronize { |client| client.call([:xlen, key]) }
|
3312
|
+
end
|
3313
|
+
|
3314
|
+
# Fetches entries from one or multiple streams. Optionally blocking.
|
3315
|
+
#
|
3316
|
+
# @example With a key
|
3317
|
+
# redis.xread('mystream', '0-0')
|
3318
|
+
# @example With multiple keys
|
3319
|
+
# redis.xread(%w[mystream1 mystream2], %w[0-0 0-0])
|
3320
|
+
# @example With count option
|
3321
|
+
# redis.xread('mystream', '0-0', count: 2)
|
3322
|
+
# @example With block option
|
3323
|
+
# redis.xread('mystream', '$', block: 1000)
|
3324
|
+
#
|
3325
|
+
# @param keys [Array<String>] one or multiple stream keys
|
3326
|
+
# @param ids [Array<String>] one or multiple entry ids
|
3327
|
+
# @param count [Integer] the number of entries as limit per stream
|
3328
|
+
# @param block [Integer] the number of milliseconds as blocking timeout
|
3329
|
+
#
|
3330
|
+
# @return [Hash{String => Hash{String => Hash}}] the entries
|
3331
|
+
def xread(keys, ids, count: nil, block: nil)
|
3332
|
+
args = [:xread]
|
3333
|
+
args << 'COUNT' << count if count
|
3334
|
+
args << 'BLOCK' << block.to_i if block
|
3335
|
+
_xread(args, keys, ids, block)
|
3336
|
+
end
|
3337
|
+
|
3338
|
+
# Manages the consumer group of the stream.
|
3339
|
+
#
|
3340
|
+
# @example With `create` subcommand
|
3341
|
+
# redis.xgroup(:create, 'mystream', 'mygroup', '$')
|
3342
|
+
# @example With `setid` subcommand
|
3343
|
+
# redis.xgroup(:setid, 'mystream', 'mygroup', '$')
|
3344
|
+
# @example With `destroy` subcommand
|
3345
|
+
# redis.xgroup(:destroy, 'mystream', 'mygroup')
|
3346
|
+
# @example With `delconsumer` subcommand
|
3347
|
+
# redis.xgroup(:delconsumer, 'mystream', 'mygroup', 'consumer1')
|
3348
|
+
#
|
3349
|
+
# @param subcommand [String] `create` `setid` `destroy` `delconsumer`
|
3350
|
+
# @param key [String] the stream key
|
3351
|
+
# @param group [String] the consumer group name
|
3352
|
+
# @param id_or_consumer [String]
|
3353
|
+
# * the entry id or `$`, required if subcommand is `create` or `setid`
|
3354
|
+
# * the consumer name, required if subcommand is `delconsumer`
|
3355
|
+
# @param mkstream [Boolean] whether to create an empty stream automatically or not
|
3356
|
+
#
|
3357
|
+
# @return [String] `OK` if subcommand is `create` or `setid`
|
3358
|
+
# @return [Integer] effected count if subcommand is `destroy` or `delconsumer`
|
3359
|
+
def xgroup(subcommand, key, group, id_or_consumer = nil, mkstream: false)
|
3360
|
+
args = [:xgroup, subcommand, key, group, id_or_consumer, (mkstream ? 'MKSTREAM' : nil)].compact
|
3361
|
+
synchronize { |client| client.call(args) }
|
3362
|
+
end
|
3363
|
+
|
3364
|
+
# Fetches a subset of the entries from one or multiple streams related with the consumer group.
|
3365
|
+
# Optionally blocking.
|
3366
|
+
#
|
3367
|
+
# @example With a key
|
3368
|
+
# redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>')
|
3369
|
+
# @example With multiple keys
|
3370
|
+
# redis.xreadgroup('mygroup', 'consumer1', %w[mystream1 mystream2], %w[> >])
|
3371
|
+
# @example With count option
|
3372
|
+
# redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', count: 2)
|
3373
|
+
# @example With block option
|
3374
|
+
# redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', block: 1000)
|
3375
|
+
# @example With noack option
|
3376
|
+
# redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', noack: true)
|
3377
|
+
#
|
3378
|
+
# @param group [String] the consumer group name
|
3379
|
+
# @param consumer [String] the consumer name
|
3380
|
+
# @param keys [Array<String>] one or multiple stream keys
|
3381
|
+
# @param ids [Array<String>] one or multiple entry ids
|
3382
|
+
# @param opts [Hash] several options for `XREADGROUP` command
|
3383
|
+
#
|
3384
|
+
# @option opts [Integer] :count the number of entries as limit
|
3385
|
+
# @option opts [Integer] :block the number of milliseconds as blocking timeout
|
3386
|
+
# @option opts [Boolean] :noack whether message loss is acceptable or not
|
3387
|
+
#
|
3388
|
+
# @return [Hash{String => Hash{String => Hash}}] the entries
|
3389
|
+
def xreadgroup(group, consumer, keys, ids, count: nil, block: nil, noack: nil)
|
3390
|
+
args = [:xreadgroup, 'GROUP', group, consumer]
|
3391
|
+
args << 'COUNT' << count if count
|
3392
|
+
args << 'BLOCK' << block.to_i if block
|
3393
|
+
args << 'NOACK' if noack
|
3394
|
+
_xread(args, keys, ids, block)
|
3395
|
+
end
|
3396
|
+
|
3397
|
+
# Removes one or multiple entries from the pending entries list of a stream consumer group.
|
3398
|
+
#
|
3399
|
+
# @example With a entry id
|
3400
|
+
# redis.xack('mystream', 'mygroup', '1526569495631-0')
|
3401
|
+
# @example With splatted entry ids
|
3402
|
+
# redis.xack('mystream', 'mygroup', '0-1', '0-2')
|
3403
|
+
# @example With arrayed entry ids
|
3404
|
+
# redis.xack('mystream', 'mygroup', %w[0-1 0-2])
|
3405
|
+
#
|
3406
|
+
# @param key [String] the stream key
|
3407
|
+
# @param group [String] the consumer group name
|
3408
|
+
# @param ids [Array<String>] one or multiple entry ids
|
3409
|
+
#
|
3410
|
+
# @return [Integer] the number of entries successfully acknowledged
|
3411
|
+
def xack(key, group, *ids)
|
3412
|
+
args = [:xack, key, group].concat(ids.flatten)
|
3413
|
+
synchronize { |client| client.call(args) }
|
3414
|
+
end
|
3415
|
+
|
3416
|
+
# Changes the ownership of a pending entry
|
3417
|
+
#
|
3418
|
+
# @example With splatted entry ids
|
3419
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-1', '0-2')
|
3420
|
+
# @example With arrayed entry ids
|
3421
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2])
|
3422
|
+
# @example With idle option
|
3423
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], idle: 1000)
|
3424
|
+
# @example With time option
|
3425
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], time: 1542866959000)
|
3426
|
+
# @example With retrycount option
|
3427
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], retrycount: 10)
|
3428
|
+
# @example With force option
|
3429
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], force: true)
|
3430
|
+
# @example With justid option
|
3431
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], justid: true)
|
3432
|
+
#
|
3433
|
+
# @param key [String] the stream key
|
3434
|
+
# @param group [String] the consumer group name
|
3435
|
+
# @param consumer [String] the consumer name
|
3436
|
+
# @param min_idle_time [Integer] the number of milliseconds
|
3437
|
+
# @param ids [Array<String>] one or multiple entry ids
|
3438
|
+
# @param opts [Hash] several options for `XCLAIM` command
|
3439
|
+
#
|
3440
|
+
# @option opts [Integer] :idle the number of milliseconds as last time it was delivered of the entry
|
3441
|
+
# @option opts [Integer] :time the number of milliseconds as a specific Unix Epoch time
|
3442
|
+
# @option opts [Integer] :retrycount the number of retry counter
|
3443
|
+
# @option opts [Boolean] :force whether to create the pending entry to the pending entries list or not
|
3444
|
+
# @option opts [Boolean] :justid whether to fetch just an array of entry ids or not
|
3445
|
+
#
|
3446
|
+
# @return [Hash{String => Hash}] the entries successfully claimed
|
3447
|
+
# @return [Array<String>] the entry ids successfully claimed if justid option is `true`
|
3448
|
+
def xclaim(key, group, consumer, min_idle_time, *ids, **opts)
|
3449
|
+
args = [:xclaim, key, group, consumer, min_idle_time].concat(ids.flatten)
|
3450
|
+
args.concat(['IDLE', opts[:idle].to_i]) if opts[:idle]
|
3451
|
+
args.concat(['TIME', opts[:time].to_i]) if opts[:time]
|
3452
|
+
args.concat(['RETRYCOUNT', opts[:retrycount]]) if opts[:retrycount]
|
3453
|
+
args << 'FORCE' if opts[:force]
|
3454
|
+
args << 'JUSTID' if opts[:justid]
|
3455
|
+
blk = opts[:justid] ? Noop : HashifyStreamEntries
|
3456
|
+
synchronize { |client| client.call(args, &blk) }
|
3457
|
+
end
|
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
|
+
|
3491
|
+
# Fetches not acknowledging pending entries
|
3492
|
+
#
|
3493
|
+
# @example With key and group
|
3494
|
+
# redis.xpending('mystream', 'mygroup')
|
3495
|
+
# @example With range options
|
3496
|
+
# redis.xpending('mystream', 'mygroup', '-', '+', 10)
|
3497
|
+
# @example With range and consumer options
|
3498
|
+
# redis.xpending('mystream', 'mygroup', '-', '+', 10, 'consumer1')
|
3499
|
+
#
|
3500
|
+
# @param key [String] the stream key
|
3501
|
+
# @param group [String] the consumer group name
|
3502
|
+
# @param start [String] start first entry id of range
|
3503
|
+
# @param end [String] end last entry id of range
|
3504
|
+
# @param count [Integer] count the number of entries as limit
|
3505
|
+
# @param consumer [String] the consumer name
|
3506
|
+
#
|
3507
|
+
# @return [Hash] the summary of pending entries
|
3508
|
+
# @return [Array<Hash>] the pending entries details if options were specified
|
3509
|
+
def xpending(key, group, *args)
|
3510
|
+
command_args = [:xpending, key, group]
|
3511
|
+
case args.size
|
3512
|
+
when 0, 3, 4
|
3513
|
+
command_args.concat(args)
|
3514
|
+
else
|
3515
|
+
raise ArgumentError, "wrong number of arguments (given #{args.size + 2}, expected 2, 5 or 6)"
|
3516
|
+
end
|
3517
|
+
|
3518
|
+
summary_needed = args.empty?
|
3519
|
+
blk = summary_needed ? HashifyStreamPendings : HashifyStreamPendingDetails
|
3520
|
+
synchronize { |client| client.call(command_args, &blk) }
|
3521
|
+
end
|
3522
|
+
|
2670
3523
|
# Interact with the sentinel command (masters, master, slaves, failover)
|
2671
3524
|
#
|
2672
3525
|
# @param [String] subcommand e.g. `masters`, `master`, `slaves`
|
@@ -2680,8 +3533,8 @@ class Redis
|
|
2680
3533
|
when "get-master-addr-by-name"
|
2681
3534
|
reply
|
2682
3535
|
else
|
2683
|
-
if reply.
|
2684
|
-
if reply[0].
|
3536
|
+
if reply.is_a?(Array)
|
3537
|
+
if reply[0].is_a?(Array)
|
2685
3538
|
reply.map(&Hashify)
|
2686
3539
|
else
|
2687
3540
|
Hashify.call(reply)
|
@@ -2694,6 +3547,46 @@ class Redis
|
|
2694
3547
|
end
|
2695
3548
|
end
|
2696
3549
|
|
3550
|
+
# Sends `CLUSTER *` command to random node and returns its reply.
|
3551
|
+
#
|
3552
|
+
# @see https://redis.io/commands#cluster Reference of cluster command
|
3553
|
+
#
|
3554
|
+
# @param subcommand [String, Symbol] the subcommand of cluster command
|
3555
|
+
# e.g. `:slots`, `:nodes`, `:slaves`, `:info`
|
3556
|
+
#
|
3557
|
+
# @return [Object] depends on the subcommand
|
3558
|
+
def cluster(subcommand, *args)
|
3559
|
+
subcommand = subcommand.to_s.downcase
|
3560
|
+
block = case subcommand
|
3561
|
+
when 'slots'
|
3562
|
+
HashifyClusterSlots
|
3563
|
+
when 'nodes'
|
3564
|
+
HashifyClusterNodes
|
3565
|
+
when 'slaves'
|
3566
|
+
HashifyClusterSlaves
|
3567
|
+
when 'info'
|
3568
|
+
HashifyInfo
|
3569
|
+
else
|
3570
|
+
Noop
|
3571
|
+
end
|
3572
|
+
|
3573
|
+
# @see https://github.com/antirez/redis/blob/unstable/src/redis-trib.rb#L127 raw reply expected
|
3574
|
+
block = Noop unless @cluster_mode
|
3575
|
+
|
3576
|
+
synchronize do |client|
|
3577
|
+
client.call([:cluster, subcommand] + args, &block)
|
3578
|
+
end
|
3579
|
+
end
|
3580
|
+
|
3581
|
+
# Sends `ASKING` command to random node and returns its reply.
|
3582
|
+
#
|
3583
|
+
# @see https://redis.io/topics/cluster-spec#ask-redirection ASK redirection
|
3584
|
+
#
|
3585
|
+
# @return [String] `'OK'`
|
3586
|
+
def asking
|
3587
|
+
synchronize { |client| client.call(%i[asking]) }
|
3588
|
+
end
|
3589
|
+
|
2697
3590
|
def id
|
2698
3591
|
@original_client.id
|
2699
3592
|
end
|
@@ -2706,59 +3599,184 @@ class Redis
|
|
2706
3599
|
self.class.new(@options)
|
2707
3600
|
end
|
2708
3601
|
|
2709
|
-
def
|
3602
|
+
def connection
|
3603
|
+
return @original_client.connection_info if @cluster_mode
|
3604
|
+
|
3605
|
+
{
|
3606
|
+
host: @original_client.host,
|
3607
|
+
port: @original_client.port,
|
3608
|
+
db: @original_client.db,
|
3609
|
+
id: @original_client.id,
|
3610
|
+
location: @original_client.location
|
3611
|
+
}
|
3612
|
+
end
|
3613
|
+
|
3614
|
+
def method_missing(command, *args) # rubocop:disable Style/MissingRespondToMissing
|
2710
3615
|
synchronize do |client|
|
2711
3616
|
client.call([command] + args)
|
2712
3617
|
end
|
2713
3618
|
end
|
2714
3619
|
|
2715
|
-
private
|
3620
|
+
private
|
2716
3621
|
|
2717
3622
|
# Commands returning 1 for true and 0 for false may be executed in a pipeline
|
2718
3623
|
# where the method call will return nil. Propagate the nil instead of falsely
|
2719
3624
|
# returning false.
|
2720
|
-
Boolify =
|
2721
|
-
|
2722
|
-
|
2723
|
-
|
3625
|
+
Boolify = lambda { |value|
|
3626
|
+
case value
|
3627
|
+
when 1
|
3628
|
+
true
|
3629
|
+
when 0
|
3630
|
+
false
|
3631
|
+
else
|
3632
|
+
value
|
3633
|
+
end
|
3634
|
+
}
|
2724
3635
|
|
2725
|
-
BoolifySet =
|
2726
|
-
|
2727
|
-
|
2728
|
-
|
2729
|
-
|
2730
|
-
|
2731
|
-
|
2732
|
-
|
3636
|
+
BoolifySet = lambda { |value|
|
3637
|
+
case value
|
3638
|
+
when "OK"
|
3639
|
+
true
|
3640
|
+
when nil
|
3641
|
+
false
|
3642
|
+
else
|
3643
|
+
value
|
3644
|
+
end
|
3645
|
+
}
|
2733
3646
|
|
2734
|
-
Hashify =
|
2735
|
-
|
2736
|
-
|
2737
|
-
|
2738
|
-
|
2739
|
-
|
2740
|
-
|
2741
|
-
|
3647
|
+
Hashify = lambda { |value|
|
3648
|
+
if value.respond_to?(:each_slice)
|
3649
|
+
value.each_slice(2).to_h
|
3650
|
+
else
|
3651
|
+
value
|
3652
|
+
end
|
3653
|
+
}
|
3654
|
+
|
3655
|
+
Floatify = lambda { |value|
|
3656
|
+
case value
|
3657
|
+
when "inf"
|
3658
|
+
Float::INFINITY
|
3659
|
+
when "-inf"
|
3660
|
+
-Float::INFINITY
|
3661
|
+
when String
|
3662
|
+
Float(value)
|
3663
|
+
else
|
3664
|
+
value
|
3665
|
+
end
|
3666
|
+
}
|
2742
3667
|
|
2743
|
-
|
2744
|
-
|
2745
|
-
|
2746
|
-
|
2747
|
-
|
2748
|
-
|
2749
|
-
|
2750
|
-
|
2751
|
-
|
3668
|
+
FloatifyPairs = lambda { |value|
|
3669
|
+
return value unless value.respond_to?(:each_slice)
|
3670
|
+
|
3671
|
+
value.each_slice(2).map do |member, score|
|
3672
|
+
[member, Floatify.call(score)]
|
3673
|
+
end
|
3674
|
+
}
|
3675
|
+
|
3676
|
+
HashifyInfo = lambda { |reply|
|
3677
|
+
lines = reply.split("\r\n").grep_v(/^(#|$)/)
|
3678
|
+
lines.map! { |line| line.split(':', 2) }
|
3679
|
+
lines.compact!
|
3680
|
+
lines.to_h
|
3681
|
+
}
|
3682
|
+
|
3683
|
+
HashifyStreams = lambda { |reply|
|
3684
|
+
case reply
|
3685
|
+
when nil
|
3686
|
+
{}
|
3687
|
+
else
|
3688
|
+
reply.map { |key, entries| [key, HashifyStreamEntries.call(entries)] }.to_h
|
3689
|
+
end
|
3690
|
+
}
|
3691
|
+
|
3692
|
+
EMPTY_STREAM_RESPONSE = [nil].freeze
|
3693
|
+
private_constant :EMPTY_STREAM_RESPONSE
|
3694
|
+
|
3695
|
+
HashifyStreamEntries = lambda { |reply|
|
3696
|
+
reply.compact.map do |entry_id, values|
|
3697
|
+
[entry_id, values&.each_slice(2)&.to_h]
|
3698
|
+
end
|
3699
|
+
}
|
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] }
|
2752
3705
|
}
|
3706
|
+
}
|
2753
3707
|
|
2754
|
-
|
2755
|
-
|
2756
|
-
|
2757
|
-
|
2758
|
-
|
2759
|
-
|
2760
|
-
|
3708
|
+
HashifyStreamAutoclaimJustId = lambda { |reply|
|
3709
|
+
{
|
3710
|
+
'next' => reply[0],
|
3711
|
+
'entries' => reply[1]
|
3712
|
+
}
|
3713
|
+
}
|
3714
|
+
|
3715
|
+
HashifyStreamPendings = lambda { |reply|
|
3716
|
+
{
|
3717
|
+
'size' => reply[0],
|
3718
|
+
'min_entry_id' => reply[1],
|
3719
|
+
'max_entry_id' => reply[2],
|
3720
|
+
'consumers' => reply[3].nil? ? {} : reply[3].to_h
|
2761
3721
|
}
|
3722
|
+
}
|
3723
|
+
|
3724
|
+
HashifyStreamPendingDetails = lambda { |reply|
|
3725
|
+
reply.map do |arr|
|
3726
|
+
{
|
3727
|
+
'entry_id' => arr[0],
|
3728
|
+
'consumer' => arr[1],
|
3729
|
+
'elapsed' => arr[2],
|
3730
|
+
'count' => arr[3]
|
3731
|
+
}
|
3732
|
+
end
|
3733
|
+
}
|
3734
|
+
|
3735
|
+
HashifyClusterNodeInfo = lambda { |str|
|
3736
|
+
arr = str.split(' ')
|
3737
|
+
{
|
3738
|
+
'node_id' => arr[0],
|
3739
|
+
'ip_port' => arr[1],
|
3740
|
+
'flags' => arr[2].split(','),
|
3741
|
+
'master_node_id' => arr[3],
|
3742
|
+
'ping_sent' => arr[4],
|
3743
|
+
'pong_recv' => arr[5],
|
3744
|
+
'config_epoch' => arr[6],
|
3745
|
+
'link_state' => arr[7],
|
3746
|
+
'slots' => arr[8].nil? ? nil : Range.new(*arr[8].split('-'))
|
3747
|
+
}
|
3748
|
+
}
|
3749
|
+
|
3750
|
+
HashifyClusterSlots = lambda { |reply|
|
3751
|
+
reply.map do |arr|
|
3752
|
+
first_slot, last_slot = arr[0..1]
|
3753
|
+
master = { 'ip' => arr[2][0], 'port' => arr[2][1], 'node_id' => arr[2][2] }
|
3754
|
+
replicas = arr[3..-1].map { |r| { 'ip' => r[0], 'port' => r[1], 'node_id' => r[2] } }
|
3755
|
+
{
|
3756
|
+
'start_slot' => first_slot,
|
3757
|
+
'end_slot' => last_slot,
|
3758
|
+
'master' => master,
|
3759
|
+
'replicas' => replicas
|
3760
|
+
}
|
3761
|
+
end
|
3762
|
+
}
|
3763
|
+
|
3764
|
+
HashifyClusterNodes = lambda { |reply|
|
3765
|
+
reply.split(/[\r\n]+/).map { |str| HashifyClusterNodeInfo.call(str) }
|
3766
|
+
}
|
3767
|
+
|
3768
|
+
HashifyClusterSlaves = lambda { |reply|
|
3769
|
+
reply.map { |str| HashifyClusterNodeInfo.call(str) }
|
3770
|
+
}
|
3771
|
+
|
3772
|
+
Noop = ->(reply) { reply }
|
3773
|
+
|
3774
|
+
def _geoarguments(*args, options: nil, sort: nil, count: nil)
|
3775
|
+
args.push sort if sort
|
3776
|
+
args.push 'count', count if count
|
3777
|
+
args.push options if options
|
3778
|
+
args
|
3779
|
+
end
|
2762
3780
|
|
2763
3781
|
def _subscription(method, timeout, channels, block)
|
2764
3782
|
return @client.call([method] + channels) if subscribed?
|
@@ -2775,10 +3793,44 @@ private
|
|
2775
3793
|
end
|
2776
3794
|
end
|
2777
3795
|
|
3796
|
+
def _xread(args, keys, ids, blocking_timeout_msec)
|
3797
|
+
keys = keys.is_a?(Array) ? keys : [keys]
|
3798
|
+
ids = ids.is_a?(Array) ? ids : [ids]
|
3799
|
+
args << 'STREAMS'
|
3800
|
+
args.concat(keys)
|
3801
|
+
args.concat(ids)
|
3802
|
+
|
3803
|
+
synchronize do |client|
|
3804
|
+
if blocking_timeout_msec.nil?
|
3805
|
+
client.call(args, &HashifyStreams)
|
3806
|
+
elsif blocking_timeout_msec.to_f.zero?
|
3807
|
+
client.call_without_timeout(args, &HashifyStreams)
|
3808
|
+
else
|
3809
|
+
timeout = client.timeout.to_f + blocking_timeout_msec.to_f / 1000.0
|
3810
|
+
client.call_with_timeout(args, timeout, &HashifyStreams)
|
3811
|
+
end
|
3812
|
+
end
|
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
|
2778
3829
|
end
|
2779
3830
|
|
2780
3831
|
require_relative "redis/version"
|
2781
3832
|
require_relative "redis/connection"
|
2782
3833
|
require_relative "redis/client"
|
3834
|
+
require_relative "redis/cluster"
|
2783
3835
|
require_relative "redis/pipeline"
|
2784
3836
|
require_relative "redis/subscribe"
|