redis 4.3.1 → 4.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/redis.rb CHANGED
@@ -1,32 +1,53 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "monitor"
4
- require_relative "redis/errors"
4
+ require "redis/errors"
5
+ require "redis/commands"
5
6
 
6
7
  class Redis
8
+ BASE_PATH = __dir__
9
+ @exists_returns_integer = true
10
+
11
+ Deprecated = Class.new(StandardError)
12
+
7
13
  class << self
8
14
  attr_reader :exists_returns_integer
15
+ attr_accessor :silence_deprecations, :raise_deprecations
9
16
 
10
17
  def exists_returns_integer=(value)
11
18
  unless value
12
- message = "`Redis#exists(key)` will return an Integer by default in redis-rb 4.3. The option to explicitly " \
19
+ deprecate!(
20
+ "`Redis#exists(key)` will return an Integer by default in redis-rb 4.3. The option to explicitly " \
13
21
  "disable this behaviour via `Redis.exists_returns_integer` will be removed in 5.0. You should use " \
14
22
  "`exists?` instead."
15
-
16
- ::Kernel.warn(message)
23
+ )
17
24
  end
18
25
 
19
26
  @exists_returns_integer = value
20
27
  end
21
28
 
22
- attr_writer :current
23
- end
29
+ def deprecate!(message)
30
+ unless silence_deprecations
31
+ if raise_deprecations
32
+ raise Deprecated, message
33
+ else
34
+ ::Kernel.warn(message)
35
+ end
36
+ end
37
+ end
24
38
 
25
- def self.current
26
- @current ||= Redis.new
39
+ def current
40
+ deprecate!("`Redis.current=` is deprecated and will be removed in 5.0. (called from: #{caller(1, 1).first})")
41
+ @current ||= Redis.new
42
+ end
43
+
44
+ def current=(redis)
45
+ deprecate!("`Redis.current=` is deprecated and will be removed in 5.0. (called from: #{caller(1, 1).first})")
46
+ @current = redis
47
+ end
27
48
  end
28
49
 
29
- include MonitorMixin
50
+ include Commands
30
51
 
31
52
  # Create a new client instance
32
53
  #
@@ -62,12 +83,7 @@ class Redis
62
83
  client = @cluster_mode ? Cluster : Client
63
84
  @original_client = @client = client.new(options)
64
85
  @queue = Hash.new { |h, k| h[k] = [] }
65
-
66
- super() # Monitor#initialize
67
- end
68
-
69
- def synchronize
70
- mon_synchronize { yield(@client) }
86
+ @monitor = Monitor.new
71
87
  end
72
88
 
73
89
  # Run code with the client reconnecting
@@ -93,37 +109,33 @@ class Redis
93
109
  end
94
110
  alias disconnect! close
95
111
 
96
- # Sends a command to Redis and returns its reply.
97
- #
98
- # Replies are converted to Ruby objects according to the RESP protocol, so
99
- # you can expect a Ruby array, integer or nil when Redis sends one. Higher
100
- # level transformations, such as converting an array of pairs into a Ruby
101
- # hash, are up to consumers.
102
- #
103
- # Redis error replies are raised as Ruby exceptions.
104
- def call(*command)
105
- synchronize do |client|
106
- client.call(command)
107
- end
108
- end
109
-
110
- # Queues a command for pipelining.
112
+ # @deprecated Queues a command for pipelining.
111
113
  #
112
114
  # Commands in the queue are executed with the Redis#commit method.
113
115
  #
114
116
  # See http://redis.io/topics/pipelining for more details.
115
117
  #
116
118
  def queue(*command)
119
+ ::Redis.deprecate!(
120
+ "Redis#queue is deprecated and will be removed in Redis 5.0.0. Use Redis#pipelined instead." \
121
+ "(called from: #{caller(1, 1).first})"
122
+ )
123
+
117
124
  synchronize do
118
125
  @queue[Thread.current.object_id] << command
119
126
  end
120
127
  end
121
128
 
122
- # Sends all commands in the queue.
129
+ # @deprecated Sends all commands in the queue.
123
130
  #
124
131
  # See http://redis.io/topics/pipelining for more details.
125
132
  #
126
133
  def commit
134
+ ::Redis.deprecate!(
135
+ "Redis#commit is deprecated and will be removed in Redis 5.0.0. Use Redis#pipelined instead. " \
136
+ "(called from: #{Kernel.caller(1, 1).first})"
137
+ )
138
+
127
139
  synchronize do |client|
128
140
  begin
129
141
  pipeline = Pipeline.new(client)
@@ -142,3416 +154,141 @@ class Redis
142
154
  @client
143
155
  end
144
156
 
145
- # Authenticate to the server.
146
- #
147
- # @param [Array<String>] args includes both username and password
148
- # or only password
149
- # @return [String] `OK`
150
- # @see https://redis.io/commands/auth AUTH command
151
- def auth(*args)
152
- synchronize do |client|
153
- client.call([:auth, *args])
154
- end
155
- end
156
-
157
- # Change the selected database for the current connection.
158
- #
159
- # @param [Integer] db zero-based index of the DB to use (0 to 15)
160
- # @return [String] `OK`
161
- def select(db)
162
- synchronize do |client|
163
- client.db = db
164
- client.call([:select, db])
165
- end
166
- end
167
-
168
- # Ping the server.
169
- #
170
- # @param [optional, String] message
171
- # @return [String] `PONG`
172
- def ping(message = nil)
173
- synchronize do |client|
174
- client.call([:ping, message].compact)
175
- end
176
- end
177
-
178
- # Echo the given string.
179
- #
180
- # @param [String] value
181
- # @return [String]
182
- def echo(value)
183
- synchronize do |client|
184
- client.call([:echo, value])
157
+ def pipelined(&block)
158
+ deprecation_displayed = false
159
+ if block&.arity == 0
160
+ Pipeline.deprecation_warning("pipelined", Kernel.caller_locations(1, 5))
161
+ deprecation_displayed = true
185
162
  end
186
- end
187
163
 
188
- # Close the connection.
189
- #
190
- # @return [String] `OK`
191
- def quit
192
- synchronize do |client|
164
+ synchronize do |prior_client|
193
165
  begin
194
- client.call([:quit])
195
- rescue ConnectionError
166
+ pipeline = Pipeline.new(prior_client)
167
+ @client = deprecation_displayed ? pipeline : DeprecatedPipeline.new(pipeline)
168
+ pipelined_connection = PipelinedConnection.new(pipeline)
169
+ yield pipelined_connection
170
+ prior_client.call_pipeline(pipeline)
196
171
  ensure
197
- client.disconnect
172
+ @client = prior_client
198
173
  end
199
174
  end
200
175
  end
201
176
 
202
- # Asynchronously rewrite the append-only file.
203
- #
204
- # @return [String] `OK`
205
- def bgrewriteaof
206
- synchronize do |client|
207
- client.call([:bgrewriteaof])
208
- end
209
- end
210
-
211
- # Asynchronously save the dataset to disk.
212
- #
213
- # @return [String] `OK`
214
- def bgsave
215
- synchronize do |client|
216
- client.call([:bgsave])
217
- end
218
- end
219
-
220
- # Get or set server configuration parameters.
177
+ # Mark the start of a transaction block.
221
178
  #
222
- # @param [Symbol] action e.g. `:get`, `:set`, `:resetstat`
223
- # @return [String, Hash] string reply, or hash when retrieving more than one
224
- # property with `CONFIG GET`
225
- def config(action, *args)
226
- synchronize do |client|
227
- client.call([:config, action] + args) do |reply|
228
- if reply.is_a?(Array) && action == :get
229
- Hashify.call(reply)
230
- else
231
- reply
232
- end
233
- end
234
- end
235
- end
236
-
237
- # Manage client connections.
179
+ # Passing a block is optional.
238
180
  #
239
- # @param [String, Symbol] subcommand e.g. `kill`, `list`, `getname`, `setname`
240
- # @return [String, Hash] depends on subcommand
241
- def client(subcommand = nil, *args)
242
- synchronize do |client|
243
- client.call([:client, subcommand] + args) do |reply|
244
- if subcommand.to_s == "list"
245
- reply.lines.map do |line|
246
- entries = line.chomp.split(/[ =]/)
247
- Hash[entries.each_slice(2).to_a]
248
- end
249
- else
250
- reply
251
- end
252
- end
253
- end
254
- end
255
-
256
- # Return the number of keys in the selected database.
181
+ # @example With a block
182
+ # redis.multi do |multi|
183
+ # multi.set("key", "value")
184
+ # multi.incr("counter")
185
+ # end # => ["OK", 6]
257
186
  #
258
- # @return [Integer]
259
- def dbsize
260
- synchronize do |client|
261
- client.call([:dbsize])
262
- end
263
- end
264
-
265
- def debug(*args)
266
- synchronize do |client|
267
- client.call([:debug] + args)
268
- end
269
- end
270
-
271
- # Remove all keys from all databases.
187
+ # @example Without a block
188
+ # redis.multi
189
+ # # => "OK"
190
+ # redis.set("key", "value")
191
+ # # => "QUEUED"
192
+ # redis.incr("counter")
193
+ # # => "QUEUED"
194
+ # redis.exec
195
+ # # => ["OK", 6]
272
196
  #
273
- # @param [Hash] options
274
- # - `:async => Boolean`: async flush (default: false)
275
- # @return [String] `OK`
276
- def flushall(options = nil)
277
- synchronize do |client|
278
- if options && options[:async]
279
- client.call(%i[flushall async])
280
- else
281
- client.call([:flushall])
282
- end
283
- end
284
- end
285
-
286
- # Remove all keys from the current database.
197
+ # @yield [multi] the commands that are called inside this block are cached
198
+ # and written to the server upon returning from it
199
+ # @yieldparam [Redis] multi `self`
287
200
  #
288
- # @param [Hash] options
289
- # - `:async => Boolean`: async flush (default: false)
290
- # @return [String] `OK`
291
- def flushdb(options = nil)
292
- synchronize do |client|
293
- if options && options[:async]
294
- client.call(%i[flushdb async])
295
- else
296
- client.call([:flushdb])
297
- end
298
- end
299
- end
300
-
301
- # Get information and statistics about the server.
201
+ # @return [String, Array<...>]
202
+ # - when a block is not given, `OK`
203
+ # - when a block is given, an array with replies
302
204
  #
303
- # @param [String, Symbol] cmd e.g. "commandstats"
304
- # @return [Hash<String, String>]
305
- def info(cmd = nil)
306
- synchronize do |client|
307
- client.call([:info, cmd].compact) do |reply|
308
- if reply.is_a?(String)
309
- reply = HashifyInfo.call(reply)
310
-
311
- if cmd && cmd.to_s == "commandstats"
312
- # Extract nested hashes for INFO COMMANDSTATS
313
- reply = Hash[reply.map do |k, v|
314
- v = v.split(",").map { |e| e.split("=") }
315
- [k[/^cmdstat_(.*)$/, 1], Hash[v]]
316
- end]
317
- end
318
- end
319
-
320
- reply
205
+ # @see #watch
206
+ # @see #unwatch
207
+ def multi(&block)
208
+ if block_given?
209
+ deprecation_displayed = false
210
+ if block&.arity == 0
211
+ Pipeline.deprecation_warning("multi", Kernel.caller_locations(1, 5))
212
+ deprecation_displayed = true
321
213
  end
322
- end
323
- end
324
-
325
- # Get the UNIX time stamp of the last successful save to disk.
326
- #
327
- # @return [Integer]
328
- def lastsave
329
- synchronize do |client|
330
- client.call([:lastsave])
331
- end
332
- end
333
-
334
- # Listen for all requests received by the server in real time.
335
- #
336
- # There is no way to interrupt this command.
337
- #
338
- # @yield a block to be called for every line of output
339
- # @yieldparam [String] line timestamp and command that was executed
340
- def monitor(&block)
341
- synchronize do |client|
342
- client.call_loop([:monitor], &block)
343
- end
344
- end
345
-
346
- # Synchronously save the dataset to disk.
347
- #
348
- # @return [String]
349
- def save
350
- synchronize do |client|
351
- client.call([:save])
352
- end
353
- end
354
214
 
355
- # Synchronously save the dataset to disk and then shut down the server.
356
- def shutdown
357
- synchronize do |client|
358
- client.with_reconnect(false) do
215
+ synchronize do |prior_client|
359
216
  begin
360
- client.call([:shutdown])
361
- rescue ConnectionError
362
- # This means Redis has probably exited.
363
- nil
217
+ pipeline = Pipeline::Multi.new(prior_client)
218
+ @client = deprecation_displayed ? pipeline : DeprecatedMulti.new(pipeline)
219
+ pipelined_connection = PipelinedConnection.new(pipeline)
220
+ yield pipelined_connection
221
+ prior_client.call_pipeline(pipeline)
222
+ ensure
223
+ @client = prior_client
364
224
  end
365
225
  end
226
+ else
227
+ send_command([:multi])
366
228
  end
367
229
  end
368
230
 
369
- # Make the server a slave of another instance, or promote it as master.
370
- def slaveof(host, port)
371
- synchronize do |client|
372
- client.call([:slaveof, host, port])
373
- end
374
- end
375
-
376
- # Interact with the slowlog (get, len, reset)
377
- #
378
- # @param [String] subcommand e.g. `get`, `len`, `reset`
379
- # @param [Integer] length maximum number of entries to return
380
- # @return [Array<String>, Integer, String] depends on subcommand
381
- def slowlog(subcommand, length = nil)
382
- synchronize do |client|
383
- args = [:slowlog, subcommand]
384
- args << length if length
385
- client.call args
386
- end
387
- end
388
-
389
- # Internal command used for replication.
390
- def sync
391
- synchronize do |client|
392
- client.call([:sync])
393
- end
394
- end
395
-
396
- # Return the server time.
397
- #
398
- # @example
399
- # r.time # => [ 1333093196, 606806 ]
400
- #
401
- # @return [Array<Integer>] tuple of seconds since UNIX epoch and
402
- # microseconds in the current second
403
- def time
404
- synchronize do |client|
405
- client.call([:time]) do |reply|
406
- reply&.map(&:to_i)
407
- end
408
- end
409
- end
410
-
411
- # Remove the expiration from a key.
412
- #
413
- # @param [String] key
414
- # @return [Boolean] whether the timeout was removed or not
415
- def persist(key)
416
- synchronize do |client|
417
- client.call([:persist, key], &Boolify)
418
- end
419
- end
420
-
421
- # Set a key's time to live in seconds.
422
- #
423
- # @param [String] key
424
- # @param [Integer] seconds time to live
425
- # @return [Boolean] whether the timeout was set or not
426
- def expire(key, seconds)
427
- synchronize do |client|
428
- client.call([:expire, key, seconds], &Boolify)
429
- end
430
- end
431
-
432
- # Set the expiration for a key as a UNIX timestamp.
433
- #
434
- # @param [String] key
435
- # @param [Integer] unix_time expiry time specified as a UNIX timestamp
436
- # @return [Boolean] whether the timeout was set or not
437
- def expireat(key, unix_time)
438
- synchronize do |client|
439
- client.call([:expireat, key, unix_time], &Boolify)
440
- end
441
- end
442
-
443
- # Get the time to live (in seconds) for a key.
444
- #
445
- # @param [String] key
446
- # @return [Integer] remaining time to live in seconds.
447
- #
448
- # In Redis 2.6 or older the command returns -1 if the key does not exist or if
449
- # the key exist but has no associated expire.
450
- #
451
- # Starting with Redis 2.8 the return value in case of error changed:
452
- #
453
- # - The command returns -2 if the key does not exist.
454
- # - The command returns -1 if the key exists but has no associated expire.
455
- def ttl(key)
456
- synchronize do |client|
457
- client.call([:ttl, key])
458
- end
459
- end
460
-
461
- # Set a key's time to live in milliseconds.
462
- #
463
- # @param [String] key
464
- # @param [Integer] milliseconds time to live
465
- # @return [Boolean] whether the timeout was set or not
466
- def pexpire(key, milliseconds)
467
- synchronize do |client|
468
- client.call([:pexpire, key, milliseconds], &Boolify)
469
- end
470
- end
471
-
472
- # Set the expiration for a key as number of milliseconds from UNIX Epoch.
473
- #
474
- # @param [String] key
475
- # @param [Integer] ms_unix_time expiry time specified as number of milliseconds from UNIX Epoch.
476
- # @return [Boolean] whether the timeout was set or not
477
- def pexpireat(key, ms_unix_time)
478
- synchronize do |client|
479
- client.call([:pexpireat, key, ms_unix_time], &Boolify)
480
- end
481
- end
482
-
483
- # Get the time to live (in milliseconds) for a key.
484
- #
485
- # @param [String] key
486
- # @return [Integer] remaining time to live in milliseconds
487
- # In Redis 2.6 or older the command returns -1 if the key does not exist or if
488
- # the key exist but has no associated expire.
489
- #
490
- # Starting with Redis 2.8 the return value in case of error changed:
491
- #
492
- # - The command returns -2 if the key does not exist.
493
- # - The command returns -1 if the key exists but has no associated expire.
494
- def pttl(key)
495
- synchronize do |client|
496
- client.call([:pttl, key])
497
- end
231
+ def id
232
+ @original_client.id
498
233
  end
499
234
 
500
- # Return a serialized version of the value stored at a key.
501
- #
502
- # @param [String] key
503
- # @return [String] serialized_value
504
- def dump(key)
505
- synchronize do |client|
506
- client.call([:dump, key])
507
- end
235
+ def inspect
236
+ "#<Redis client v#{Redis::VERSION} for #{id}>"
508
237
  end
509
238
 
510
- # Create a key using the serialized value, previously obtained using DUMP.
511
- #
512
- # @param [String] key
513
- # @param [String] ttl
514
- # @param [String] serialized_value
515
- # @param [Hash] options
516
- # - `:replace => Boolean`: if false, raises an error if key already exists
517
- # @raise [Redis::CommandError]
518
- # @return [String] `"OK"`
519
- def restore(key, ttl, serialized_value, replace: nil)
520
- args = [:restore, key, ttl, serialized_value]
521
- args << 'REPLACE' if replace
522
-
523
- synchronize do |client|
524
- client.call(args)
525
- end
239
+ def dup
240
+ self.class.new(@options)
526
241
  end
527
242
 
528
- # Transfer a key from the connected instance to another instance.
529
- #
530
- # @param [String, Array<String>] key
531
- # @param [Hash] options
532
- # - `:host => String`: host of instance to migrate to
533
- # - `:port => Integer`: port of instance to migrate to
534
- # - `:db => Integer`: database to migrate to (default: same as source)
535
- # - `:timeout => Integer`: timeout (default: same as connection timeout)
536
- # - `:copy => Boolean`: Do not remove the key from the local instance.
537
- # - `:replace => Boolean`: Replace existing key on the remote instance.
538
- # @return [String] `"OK"`
539
- def migrate(key, options)
540
- args = [:migrate]
541
- args << (options[:host] || raise(':host not specified'))
542
- args << (options[:port] || raise(':port not specified'))
543
- args << (key.is_a?(String) ? key : '')
544
- args << (options[:db] || @client.db).to_i
545
- args << (options[:timeout] || @client.timeout).to_i
546
- args << 'COPY' if options[:copy]
547
- args << 'REPLACE' if options[:replace]
548
- args += ['KEYS', *key] if key.is_a?(Array)
243
+ def connection
244
+ return @original_client.connection_info if @cluster_mode
549
245
 
550
- synchronize { |client| client.call(args) }
246
+ {
247
+ host: @original_client.host,
248
+ port: @original_client.port,
249
+ db: @original_client.db,
250
+ id: @original_client.id,
251
+ location: @original_client.location
252
+ }
551
253
  end
552
254
 
553
- # Delete one or more keys.
554
- #
555
- # @param [String, Array<String>] keys
556
- # @return [Integer] number of keys that were deleted
557
- def del(*keys)
558
- synchronize do |client|
559
- client.call([:del] + keys)
560
- end
561
- end
255
+ private
562
256
 
563
- # Unlink one or more keys.
564
- #
565
- # @param [String, Array<String>] keys
566
- # @return [Integer] number of keys that were unlinked
567
- def unlink(*keys)
568
- synchronize do |client|
569
- client.call([:unlink] + keys)
570
- end
257
+ def synchronize
258
+ @monitor.synchronize { yield(@client) }
571
259
  end
572
260
 
573
- # Determine how many of the keys exists.
574
- #
575
- # @param [String, Array<String>] keys
576
- # @return [Integer]
577
- def exists(*keys)
578
- if !Redis.exists_returns_integer && keys.size == 1
579
- if Redis.exists_returns_integer.nil?
580
- message = "`Redis#exists(key)` will return an Integer in redis-rb 4.3. `exists?` returns a boolean, you " \
581
- "should use it instead. To opt-in to the new behavior now you can set Redis.exists_returns_integer = " \
582
- "true. To disable this message and keep the current (boolean) behaviour of 'exists' you can set " \
583
- "`Redis.exists_returns_integer = false`, but this option will be removed in 5.0. " \
584
- "(#{::Kernel.caller(1, 1).first})\n"
585
-
586
- ::Kernel.warn(message)
587
- end
588
-
589
- exists?(*keys)
590
- else
591
- _exists(*keys)
261
+ def send_command(command, &block)
262
+ @monitor.synchronize do
263
+ @client.call(command, &block)
592
264
  end
593
265
  end
594
266
 
595
- def _exists(*keys)
596
- synchronize do |client|
597
- client.call([:exists, *keys])
267
+ def send_blocking_command(command, timeout, &block)
268
+ @monitor.synchronize do
269
+ @client.call_with_timeout(command, timeout, &block)
598
270
  end
599
271
  end
600
272
 
601
- # Determine if any of the keys exists.
602
- #
603
- # @param [String, Array<String>] keys
604
- # @return [Boolean]
605
- def exists?(*keys)
606
- synchronize do |client|
607
- client.call([:exists, *keys]) do |value|
608
- value > 0
609
- end
610
- end
611
- end
273
+ def _subscription(method, timeout, channels, block)
274
+ return @client.call([method] + channels) if subscribed?
612
275
 
613
- # Find all keys matching the given pattern.
614
- #
615
- # @param [String] pattern
616
- # @return [Array<String>]
617
- def keys(pattern = "*")
618
- synchronize do |client|
619
- client.call([:keys, pattern]) do |reply|
620
- if reply.is_a?(String)
621
- reply.split(" ")
622
- else
623
- reply
624
- end
625
- end
626
- end
627
- end
628
-
629
- # Move a key to another database.
630
- #
631
- # @example Move a key to another database
632
- # redis.set "foo", "bar"
633
- # # => "OK"
634
- # redis.move "foo", 2
635
- # # => true
636
- # redis.exists "foo"
637
- # # => false
638
- # redis.select 2
639
- # # => "OK"
640
- # redis.exists "foo"
641
- # # => true
642
- # redis.get "foo"
643
- # # => "bar"
644
- #
645
- # @param [String] key
646
- # @param [Integer] db
647
- # @return [Boolean] whether the key was moved or not
648
- def move(key, db)
649
- synchronize do |client|
650
- client.call([:move, key, db], &Boolify)
651
- end
652
- end
653
-
654
- def object(*args)
655
- synchronize do |client|
656
- client.call([:object] + args)
657
- end
658
- end
659
-
660
- # Return a random key from the keyspace.
661
- #
662
- # @return [String]
663
- def randomkey
664
- synchronize do |client|
665
- client.call([:randomkey])
666
- end
667
- end
668
-
669
- # Rename a key. If the new key already exists it is overwritten.
670
- #
671
- # @param [String] old_name
672
- # @param [String] new_name
673
- # @return [String] `OK`
674
- def rename(old_name, new_name)
675
- synchronize do |client|
676
- client.call([:rename, old_name, new_name])
677
- end
678
- end
679
-
680
- # Rename a key, only if the new key does not exist.
681
- #
682
- # @param [String] old_name
683
- # @param [String] new_name
684
- # @return [Boolean] whether the key was renamed or not
685
- def renamenx(old_name, new_name)
686
- synchronize do |client|
687
- client.call([:renamenx, old_name, new_name], &Boolify)
688
- end
689
- end
690
-
691
- # Sort the elements in a list, set or sorted set.
692
- #
693
- # @example Retrieve the first 2 elements from an alphabetically sorted "list"
694
- # redis.sort("list", :order => "alpha", :limit => [0, 2])
695
- # # => ["a", "b"]
696
- # @example Store an alphabetically descending list in "target"
697
- # redis.sort("list", :order => "desc alpha", :store => "target")
698
- # # => 26
699
- #
700
- # @param [String] key
701
- # @param [Hash] options
702
- # - `:by => String`: use external key to sort elements by
703
- # - `:limit => [offset, count]`: skip `offset` elements, return a maximum
704
- # of `count` elements
705
- # - `:get => [String, Array<String>]`: single key or array of keys to
706
- # retrieve per element in the result
707
- # - `:order => String`: combination of `ASC`, `DESC` and optionally `ALPHA`
708
- # - `:store => String`: key to store the result at
709
- #
710
- # @return [Array<String>, Array<Array<String>>, Integer]
711
- # - when `:get` is not specified, or holds a single element, an array of elements
712
- # - when `:get` is specified, and holds more than one element, an array of
713
- # elements where every element is an array with the result for every
714
- # element specified in `:get`
715
- # - when `:store` is specified, the number of elements in the stored result
716
- def sort(key, by: nil, limit: nil, get: nil, order: nil, store: nil)
717
- args = [:sort, key]
718
- args << "BY" << by if by
719
-
720
- if limit
721
- args << "LIMIT"
722
- args.concat(limit)
723
- end
724
-
725
- get = Array(get)
726
- get.each do |item|
727
- args << "GET" << item
728
- end
729
-
730
- args.concat(order.split(" ")) if order
731
- args << "STORE" << store if store
732
-
733
- synchronize do |client|
734
- client.call(args) do |reply|
735
- if get.size > 1 && !store
736
- reply.each_slice(get.size).to_a if reply
737
- else
738
- reply
739
- end
740
- end
741
- end
742
- end
743
-
744
- # Determine the type stored at key.
745
- #
746
- # @param [String] key
747
- # @return [String] `string`, `list`, `set`, `zset`, `hash` or `none`
748
- def type(key)
749
- synchronize do |client|
750
- client.call([:type, key])
751
- end
752
- end
753
-
754
- # Decrement the integer value of a key by one.
755
- #
756
- # @example
757
- # redis.decr("value")
758
- # # => 4
759
- #
760
- # @param [String] key
761
- # @return [Integer] value after decrementing it
762
- def decr(key)
763
- synchronize do |client|
764
- client.call([:decr, key])
765
- end
766
- end
767
-
768
- # Decrement the integer value of a key by the given number.
769
- #
770
- # @example
771
- # redis.decrby("value", 5)
772
- # # => 0
773
- #
774
- # @param [String] key
775
- # @param [Integer] decrement
776
- # @return [Integer] value after decrementing it
777
- def decrby(key, decrement)
778
- synchronize do |client|
779
- client.call([:decrby, key, decrement])
780
- end
781
- end
782
-
783
- # Increment the integer value of a key by one.
784
- #
785
- # @example
786
- # redis.incr("value")
787
- # # => 6
788
- #
789
- # @param [String] key
790
- # @return [Integer] value after incrementing it
791
- def incr(key)
792
- synchronize do |client|
793
- client.call([:incr, key])
794
- end
795
- end
796
-
797
- # Increment the integer value of a key by the given integer number.
798
- #
799
- # @example
800
- # redis.incrby("value", 5)
801
- # # => 10
802
- #
803
- # @param [String] key
804
- # @param [Integer] increment
805
- # @return [Integer] value after incrementing it
806
- def incrby(key, increment)
807
- synchronize do |client|
808
- client.call([:incrby, key, increment])
809
- end
810
- end
811
-
812
- # Increment the numeric value of a key by the given float number.
813
- #
814
- # @example
815
- # redis.incrbyfloat("value", 1.23)
816
- # # => 1.23
817
- #
818
- # @param [String] key
819
- # @param [Float] increment
820
- # @return [Float] value after incrementing it
821
- def incrbyfloat(key, increment)
822
- synchronize do |client|
823
- client.call([:incrbyfloat, key, increment], &Floatify)
824
- end
825
- end
826
-
827
- # Set the string value of a key.
828
- #
829
- # @param [String] key
830
- # @param [String] value
831
- # @param [Hash] options
832
- # - `:ex => Integer`: Set the specified expire time, in seconds.
833
- # - `:px => Integer`: Set the specified expire time, in milliseconds.
834
- # - `:nx => true`: Only set the key if it does not already exist.
835
- # - `:xx => true`: Only set the key if it already exist.
836
- # - `:keepttl => true`: Retain the time to live associated with the key.
837
- # @return [String, Boolean] `"OK"` or true, false if `:nx => true` or `:xx => true`
838
- def set(key, value, ex: nil, px: nil, nx: nil, xx: nil, keepttl: nil)
839
- args = [:set, key, value.to_s]
840
- args << "EX" << ex if ex
841
- args << "PX" << px if px
842
- args << "NX" if nx
843
- args << "XX" if xx
844
- args << "KEEPTTL" if keepttl
845
-
846
- synchronize do |client|
847
- if nx || xx
848
- client.call(args, &BoolifySet)
849
- else
850
- client.call(args)
851
- end
852
- end
853
- end
854
-
855
- # Set the time to live in seconds of a key.
856
- #
857
- # @param [String] key
858
- # @param [Integer] ttl
859
- # @param [String] value
860
- # @return [String] `"OK"`
861
- def setex(key, ttl, value)
862
- synchronize do |client|
863
- client.call([:setex, key, ttl, value.to_s])
864
- end
865
- end
866
-
867
- # Set the time to live in milliseconds of a key.
868
- #
869
- # @param [String] key
870
- # @param [Integer] ttl
871
- # @param [String] value
872
- # @return [String] `"OK"`
873
- def psetex(key, ttl, value)
874
- synchronize do |client|
875
- client.call([:psetex, key, ttl, value.to_s])
876
- end
877
- end
878
-
879
- # Set the value of a key, only if the key does not exist.
880
- #
881
- # @param [String] key
882
- # @param [String] value
883
- # @return [Boolean] whether the key was set or not
884
- def setnx(key, value)
885
- synchronize do |client|
886
- client.call([:setnx, key, value.to_s], &Boolify)
887
- end
888
- end
889
-
890
- # Set one or more values.
891
- #
892
- # @example
893
- # redis.mset("key1", "v1", "key2", "v2")
894
- # # => "OK"
895
- #
896
- # @param [Array<String>] args array of keys and values
897
- # @return [String] `"OK"`
898
- #
899
- # @see #mapped_mset
900
- def mset(*args)
901
- synchronize do |client|
902
- client.call([:mset] + args)
903
- end
904
- end
905
-
906
- # Set one or more values.
907
- #
908
- # @example
909
- # redis.mapped_mset({ "f1" => "v1", "f2" => "v2" })
910
- # # => "OK"
911
- #
912
- # @param [Hash] hash keys mapping to values
913
- # @return [String] `"OK"`
914
- #
915
- # @see #mset
916
- def mapped_mset(hash)
917
- mset(hash.to_a.flatten)
918
- end
919
-
920
- # Set one or more values, only if none of the keys exist.
921
- #
922
- # @example
923
- # redis.msetnx("key1", "v1", "key2", "v2")
924
- # # => true
925
- #
926
- # @param [Array<String>] args array of keys and values
927
- # @return [Boolean] whether or not all values were set
928
- #
929
- # @see #mapped_msetnx
930
- def msetnx(*args)
931
- synchronize do |client|
932
- client.call([:msetnx, *args], &Boolify)
933
- end
934
- end
935
-
936
- # Set one or more values, only if none of the keys exist.
937
- #
938
- # @example
939
- # redis.mapped_msetnx({ "key1" => "v1", "key2" => "v2" })
940
- # # => true
941
- #
942
- # @param [Hash] hash keys mapping to values
943
- # @return [Boolean] whether or not all values were set
944
- #
945
- # @see #msetnx
946
- def mapped_msetnx(hash)
947
- msetnx(hash.to_a.flatten)
948
- end
949
-
950
- # Get the value of a key.
951
- #
952
- # @param [String] key
953
- # @return [String]
954
- def get(key)
955
- synchronize do |client|
956
- client.call([:get, key])
957
- end
958
- end
959
-
960
- # Get the values of all the given keys.
961
- #
962
- # @example
963
- # redis.mget("key1", "key2")
964
- # # => ["v1", "v2"]
965
- #
966
- # @param [Array<String>] keys
967
- # @return [Array<String>] an array of values for the specified keys
968
- #
969
- # @see #mapped_mget
970
- def mget(*keys, &blk)
971
- synchronize do |client|
972
- client.call([:mget, *keys], &blk)
973
- end
974
- end
975
-
976
- # Get the values of all the given keys.
977
- #
978
- # @example
979
- # redis.mapped_mget("key1", "key2")
980
- # # => { "key1" => "v1", "key2" => "v2" }
981
- #
982
- # @param [Array<String>] keys array of keys
983
- # @return [Hash] a hash mapping the specified keys to their values
984
- #
985
- # @see #mget
986
- def mapped_mget(*keys)
987
- mget(*keys) do |reply|
988
- if reply.is_a?(Array)
989
- Hash[keys.zip(reply)]
990
- else
991
- reply
992
- end
993
- end
994
- end
995
-
996
- # Overwrite part of a string at key starting at the specified offset.
997
- #
998
- # @param [String] key
999
- # @param [Integer] offset byte offset
1000
- # @param [String] value
1001
- # @return [Integer] length of the string after it was modified
1002
- def setrange(key, offset, value)
1003
- synchronize do |client|
1004
- client.call([:setrange, key, offset, value.to_s])
1005
- end
1006
- end
1007
-
1008
- # Get a substring of the string stored at a key.
1009
- #
1010
- # @param [String] key
1011
- # @param [Integer] start zero-based start offset
1012
- # @param [Integer] stop zero-based end offset. Use -1 for representing
1013
- # the end of the string
1014
- # @return [Integer] `0` or `1`
1015
- def getrange(key, start, stop)
1016
- synchronize do |client|
1017
- client.call([:getrange, key, start, stop])
1018
- end
1019
- end
1020
-
1021
- # Sets or clears the bit at offset in the string value stored at key.
1022
- #
1023
- # @param [String] key
1024
- # @param [Integer] offset bit offset
1025
- # @param [Integer] value bit value `0` or `1`
1026
- # @return [Integer] the original bit value stored at `offset`
1027
- def setbit(key, offset, value)
1028
- synchronize do |client|
1029
- client.call([:setbit, key, offset, value])
1030
- end
1031
- end
1032
-
1033
- # Returns the bit value at offset in the string value stored at key.
1034
- #
1035
- # @param [String] key
1036
- # @param [Integer] offset bit offset
1037
- # @return [Integer] `0` or `1`
1038
- def getbit(key, offset)
1039
- synchronize do |client|
1040
- client.call([:getbit, key, offset])
1041
- end
1042
- end
1043
-
1044
- # Append a value to a key.
1045
- #
1046
- # @param [String] key
1047
- # @param [String] value value to append
1048
- # @return [Integer] length of the string after appending
1049
- def append(key, value)
1050
- synchronize do |client|
1051
- client.call([:append, key, value])
1052
- end
1053
- end
1054
-
1055
- # Count the number of set bits in a range of the string value stored at key.
1056
- #
1057
- # @param [String] key
1058
- # @param [Integer] start start index
1059
- # @param [Integer] stop stop index
1060
- # @return [Integer] the number of bits set to 1
1061
- def bitcount(key, start = 0, stop = -1)
1062
- synchronize do |client|
1063
- client.call([:bitcount, key, start, stop])
1064
- end
1065
- end
1066
-
1067
- # Perform a bitwise operation between strings and store the resulting string in a key.
1068
- #
1069
- # @param [String] operation e.g. `and`, `or`, `xor`, `not`
1070
- # @param [String] destkey destination key
1071
- # @param [String, Array<String>] keys one or more source keys to perform `operation`
1072
- # @return [Integer] the length of the string stored in `destkey`
1073
- def bitop(operation, destkey, *keys)
1074
- synchronize do |client|
1075
- client.call([:bitop, operation, destkey, *keys])
1076
- end
1077
- end
1078
-
1079
- # Return the position of the first bit set to 1 or 0 in a string.
1080
- #
1081
- # @param [String] key
1082
- # @param [Integer] bit whether to look for the first 1 or 0 bit
1083
- # @param [Integer] start start index
1084
- # @param [Integer] stop stop index
1085
- # @return [Integer] the position of the first 1/0 bit.
1086
- # -1 if looking for 1 and it is not found or start and stop are given.
1087
- def bitpos(key, bit, start = nil, stop = nil)
1088
- raise(ArgumentError, 'stop parameter specified without start parameter') if stop && !start
1089
-
1090
- synchronize do |client|
1091
- command = [:bitpos, key, bit]
1092
- command << start if start
1093
- command << stop if stop
1094
- client.call(command)
1095
- end
1096
- end
1097
-
1098
- # Set the string value of a key and return its old value.
1099
- #
1100
- # @param [String] key
1101
- # @param [String] value value to replace the current value with
1102
- # @return [String] the old value stored in the key, or `nil` if the key
1103
- # did not exist
1104
- def getset(key, value)
1105
- synchronize do |client|
1106
- client.call([:getset, key, value.to_s])
1107
- end
1108
- end
1109
-
1110
- # Get the length of the value stored in a key.
1111
- #
1112
- # @param [String] key
1113
- # @return [Integer] the length of the value stored in the key, or 0
1114
- # if the key does not exist
1115
- def strlen(key)
1116
- synchronize do |client|
1117
- client.call([:strlen, key])
1118
- end
1119
- end
1120
-
1121
- # Get the length of a list.
1122
- #
1123
- # @param [String] key
1124
- # @return [Integer]
1125
- def llen(key)
1126
- synchronize do |client|
1127
- client.call([:llen, key])
1128
- end
1129
- end
1130
-
1131
- # Prepend one or more values to a list, creating the list if it doesn't exist
1132
- #
1133
- # @param [String] key
1134
- # @param [String, Array<String>] value string value, or array of string values to push
1135
- # @return [Integer] the length of the list after the push operation
1136
- def lpush(key, value)
1137
- synchronize do |client|
1138
- client.call([:lpush, key, value])
1139
- end
1140
- end
1141
-
1142
- # Prepend a value to a list, only if the list exists.
1143
- #
1144
- # @param [String] key
1145
- # @param [String] value
1146
- # @return [Integer] the length of the list after the push operation
1147
- def lpushx(key, value)
1148
- synchronize do |client|
1149
- client.call([:lpushx, key, value])
1150
- end
1151
- end
1152
-
1153
- # Append one or more values to a list, creating the list if it doesn't exist
1154
- #
1155
- # @param [String] key
1156
- # @param [String, Array<String>] value string value, or array of string values to push
1157
- # @return [Integer] the length of the list after the push operation
1158
- def rpush(key, value)
1159
- synchronize do |client|
1160
- client.call([:rpush, key, value])
1161
- end
1162
- end
1163
-
1164
- # Append a value to a list, only if the list exists.
1165
- #
1166
- # @param [String] key
1167
- # @param [String] value
1168
- # @return [Integer] the length of the list after the push operation
1169
- def rpushx(key, value)
1170
- synchronize do |client|
1171
- client.call([:rpushx, key, value])
1172
- end
1173
- end
1174
-
1175
- # Remove and get the first elements in a list.
1176
- #
1177
- # @param [String] key
1178
- # @param [Integer] count number of elements to remove
1179
- # @return [String, Array<String>] the values of the first elements
1180
- def lpop(key, count = nil)
1181
- synchronize do |client|
1182
- command = [:lpop, key]
1183
- command << count if count
1184
- client.call(command)
1185
- end
1186
- end
1187
-
1188
- # Remove and get the last elements in a list.
1189
- #
1190
- # @param [String] key
1191
- # @param [Integer] count number of elements to remove
1192
- # @return [String, Array<String>] the values of the last elements
1193
- def rpop(key, count = nil)
1194
- synchronize do |client|
1195
- command = [:rpop, key]
1196
- command << count if count
1197
- client.call(command)
1198
- end
1199
- end
1200
-
1201
- # Remove the last element in a list, append it to another list and return it.
1202
- #
1203
- # @param [String] source source key
1204
- # @param [String] destination destination key
1205
- # @return [nil, String] the element, or nil when the source key does not exist
1206
- def rpoplpush(source, destination)
1207
- synchronize do |client|
1208
- client.call([:rpoplpush, source, destination])
1209
- end
1210
- end
1211
-
1212
- def _bpop(cmd, args, &blk)
1213
- timeout = if args.last.is_a?(Hash)
1214
- options = args.pop
1215
- options[:timeout]
1216
- elsif args.last.respond_to?(:to_int)
1217
- # Issue deprecation notice in obnoxious mode...
1218
- args.pop.to_int
1219
- end
1220
-
1221
- timeout ||= 0
1222
-
1223
- if args.size > 1
1224
- # Issue deprecation notice in obnoxious mode...
1225
- end
1226
-
1227
- keys = args.flatten
1228
-
1229
- synchronize do |client|
1230
- command = [cmd, keys, timeout]
1231
- timeout += client.timeout if timeout > 0
1232
- client.call_with_timeout(command, timeout, &blk)
1233
- end
1234
- end
1235
-
1236
- # Remove and get the first element in a list, or block until one is available.
1237
- #
1238
- # @example With timeout
1239
- # list, element = redis.blpop("list", :timeout => 5)
1240
- # # => nil on timeout
1241
- # # => ["list", "element"] on success
1242
- # @example Without timeout
1243
- # list, element = redis.blpop("list")
1244
- # # => ["list", "element"]
1245
- # @example Blocking pop on multiple lists
1246
- # list, element = redis.blpop(["list", "another_list"])
1247
- # # => ["list", "element"]
1248
- #
1249
- # @param [String, Array<String>] keys one or more keys to perform the
1250
- # blocking pop on
1251
- # @param [Hash] options
1252
- # - `:timeout => Integer`: timeout in seconds, defaults to no timeout
1253
- #
1254
- # @return [nil, [String, String]]
1255
- # - `nil` when the operation timed out
1256
- # - tuple of the list that was popped from and element was popped otherwise
1257
- def blpop(*args)
1258
- _bpop(:blpop, args)
1259
- end
1260
-
1261
- # Remove and get the last element in a list, or block until one is available.
1262
- #
1263
- # @param [String, Array<String>] keys one or more keys to perform the
1264
- # blocking pop on
1265
- # @param [Hash] options
1266
- # - `:timeout => Integer`: timeout in seconds, defaults to no timeout
1267
- #
1268
- # @return [nil, [String, String]]
1269
- # - `nil` when the operation timed out
1270
- # - tuple of the list that was popped from and element was popped otherwise
1271
- #
1272
- # @see #blpop
1273
- def brpop(*args)
1274
- _bpop(:brpop, args)
1275
- end
1276
-
1277
- # Pop a value from a list, push it to another list and return it; or block
1278
- # until one is available.
1279
- #
1280
- # @param [String] source source key
1281
- # @param [String] destination destination key
1282
- # @param [Hash] options
1283
- # - `:timeout => Integer`: timeout in seconds, defaults to no timeout
1284
- #
1285
- # @return [nil, String]
1286
- # - `nil` when the operation timed out
1287
- # - the element was popped and pushed otherwise
1288
- def brpoplpush(source, destination, deprecated_timeout = 0, timeout: deprecated_timeout)
1289
- synchronize do |client|
1290
- command = [:brpoplpush, source, destination, timeout]
1291
- timeout += client.timeout if timeout > 0
1292
- client.call_with_timeout(command, timeout)
1293
- end
1294
- end
1295
-
1296
- # Get an element from a list by its index.
1297
- #
1298
- # @param [String] key
1299
- # @param [Integer] index
1300
- # @return [String]
1301
- def lindex(key, index)
1302
- synchronize do |client|
1303
- client.call([:lindex, key, index])
1304
- end
1305
- end
1306
-
1307
- # Insert an element before or after another element in a list.
1308
- #
1309
- # @param [String] key
1310
- # @param [String, Symbol] where `BEFORE` or `AFTER`
1311
- # @param [String] pivot reference element
1312
- # @param [String] value
1313
- # @return [Integer] length of the list after the insert operation, or `-1`
1314
- # when the element `pivot` was not found
1315
- def linsert(key, where, pivot, value)
1316
- synchronize do |client|
1317
- client.call([:linsert, key, where, pivot, value])
1318
- end
1319
- end
1320
-
1321
- # Get a range of elements from a list.
1322
- #
1323
- # @param [String] key
1324
- # @param [Integer] start start index
1325
- # @param [Integer] stop stop index
1326
- # @return [Array<String>]
1327
- def lrange(key, start, stop)
1328
- synchronize do |client|
1329
- client.call([:lrange, key, start, stop])
1330
- end
1331
- end
1332
-
1333
- # Remove elements from a list.
1334
- #
1335
- # @param [String] key
1336
- # @param [Integer] count number of elements to remove. Use a positive
1337
- # value to remove the first `count` occurrences of `value`. A negative
1338
- # value to remove the last `count` occurrences of `value`. Or zero, to
1339
- # remove all occurrences of `value` from the list.
1340
- # @param [String] value
1341
- # @return [Integer] the number of removed elements
1342
- def lrem(key, count, value)
1343
- synchronize do |client|
1344
- client.call([:lrem, key, count, value])
1345
- end
1346
- end
1347
-
1348
- # Set the value of an element in a list by its index.
1349
- #
1350
- # @param [String] key
1351
- # @param [Integer] index
1352
- # @param [String] value
1353
- # @return [String] `OK`
1354
- def lset(key, index, value)
1355
- synchronize do |client|
1356
- client.call([:lset, key, index, value])
1357
- end
1358
- end
1359
-
1360
- # Trim a list to the specified range.
1361
- #
1362
- # @param [String] key
1363
- # @param [Integer] start start index
1364
- # @param [Integer] stop stop index
1365
- # @return [String] `OK`
1366
- def ltrim(key, start, stop)
1367
- synchronize do |client|
1368
- client.call([:ltrim, key, start, stop])
1369
- end
1370
- end
1371
-
1372
- # Get the number of members in a set.
1373
- #
1374
- # @param [String] key
1375
- # @return [Integer]
1376
- def scard(key)
1377
- synchronize do |client|
1378
- client.call([:scard, key])
1379
- end
1380
- end
1381
-
1382
- # Add one or more members to a set.
1383
- #
1384
- # @param [String] key
1385
- # @param [String, Array<String>] member one member, or array of members
1386
- # @return [Boolean, Integer] `Boolean` when a single member is specified,
1387
- # holding whether or not adding the member succeeded, or `Integer` when an
1388
- # array of members is specified, holding the number of members that were
1389
- # successfully added
1390
- def sadd(key, member)
1391
- synchronize do |client|
1392
- client.call([:sadd, key, member]) do |reply|
1393
- if member.is_a? Array
1394
- # Variadic: return integer
1395
- reply
1396
- else
1397
- # Single argument: return boolean
1398
- Boolify.call(reply)
1399
- end
1400
- end
1401
- end
1402
- end
1403
-
1404
- # Remove one or more members from a set.
1405
- #
1406
- # @param [String] key
1407
- # @param [String, Array<String>] member one member, or array of members
1408
- # @return [Boolean, Integer] `Boolean` when a single member is specified,
1409
- # holding whether or not removing the member succeeded, or `Integer` when an
1410
- # array of members is specified, holding the number of members that were
1411
- # successfully removed
1412
- def srem(key, member)
1413
- synchronize do |client|
1414
- client.call([:srem, key, member]) do |reply|
1415
- if member.is_a? Array
1416
- # Variadic: return integer
1417
- reply
1418
- else
1419
- # Single argument: return boolean
1420
- Boolify.call(reply)
1421
- end
1422
- end
1423
- end
1424
- end
1425
-
1426
- # Remove and return one or more random member from a set.
1427
- #
1428
- # @param [String] key
1429
- # @return [String]
1430
- # @param [Integer] count
1431
- def spop(key, count = nil)
1432
- synchronize do |client|
1433
- if count.nil?
1434
- client.call([:spop, key])
1435
- else
1436
- client.call([:spop, key, count])
1437
- end
1438
- end
1439
- end
1440
-
1441
- # Get one or more random members from a set.
1442
- #
1443
- # @param [String] key
1444
- # @param [Integer] count
1445
- # @return [String]
1446
- def srandmember(key, count = nil)
1447
- synchronize do |client|
1448
- if count.nil?
1449
- client.call([:srandmember, key])
1450
- else
1451
- client.call([:srandmember, key, count])
1452
- end
1453
- end
1454
- end
1455
-
1456
- # Move a member from one set to another.
1457
- #
1458
- # @param [String] source source key
1459
- # @param [String] destination destination key
1460
- # @param [String] member member to move from `source` to `destination`
1461
- # @return [Boolean]
1462
- def smove(source, destination, member)
1463
- synchronize do |client|
1464
- client.call([:smove, source, destination, member], &Boolify)
1465
- end
1466
- end
1467
-
1468
- # Determine if a given value is a member of a set.
1469
- #
1470
- # @param [String] key
1471
- # @param [String] member
1472
- # @return [Boolean]
1473
- def sismember(key, member)
1474
- synchronize do |client|
1475
- client.call([:sismember, key, member], &Boolify)
1476
- end
1477
- end
1478
-
1479
- # Get all the members in a set.
1480
- #
1481
- # @param [String] key
1482
- # @return [Array<String>]
1483
- def smembers(key)
1484
- synchronize do |client|
1485
- client.call([:smembers, key])
1486
- end
1487
- end
1488
-
1489
- # Subtract multiple sets.
1490
- #
1491
- # @param [String, Array<String>] keys keys pointing to sets to subtract
1492
- # @return [Array<String>] members in the difference
1493
- def sdiff(*keys)
1494
- synchronize do |client|
1495
- client.call([:sdiff, *keys])
1496
- end
1497
- end
1498
-
1499
- # Subtract multiple sets and store the resulting set in a key.
1500
- #
1501
- # @param [String] destination destination key
1502
- # @param [String, Array<String>] keys keys pointing to sets to subtract
1503
- # @return [Integer] number of elements in the resulting set
1504
- def sdiffstore(destination, *keys)
1505
- synchronize do |client|
1506
- client.call([:sdiffstore, destination, *keys])
1507
- end
1508
- end
1509
-
1510
- # Intersect multiple sets.
1511
- #
1512
- # @param [String, Array<String>] keys keys pointing to sets to intersect
1513
- # @return [Array<String>] members in the intersection
1514
- def sinter(*keys)
1515
- synchronize do |client|
1516
- client.call([:sinter, *keys])
1517
- end
1518
- end
1519
-
1520
- # Intersect multiple sets and store the resulting set in a key.
1521
- #
1522
- # @param [String] destination destination key
1523
- # @param [String, Array<String>] keys keys pointing to sets to intersect
1524
- # @return [Integer] number of elements in the resulting set
1525
- def sinterstore(destination, *keys)
1526
- synchronize do |client|
1527
- client.call([:sinterstore, destination, *keys])
1528
- end
1529
- end
1530
-
1531
- # Add multiple sets.
1532
- #
1533
- # @param [String, Array<String>] keys keys pointing to sets to unify
1534
- # @return [Array<String>] members in the union
1535
- def sunion(*keys)
1536
- synchronize do |client|
1537
- client.call([:sunion, *keys])
1538
- end
1539
- end
1540
-
1541
- # Add multiple sets and store the resulting set in a key.
1542
- #
1543
- # @param [String] destination destination key
1544
- # @param [String, Array<String>] keys keys pointing to sets to unify
1545
- # @return [Integer] number of elements in the resulting set
1546
- def sunionstore(destination, *keys)
1547
- synchronize do |client|
1548
- client.call([:sunionstore, destination, *keys])
1549
- end
1550
- end
1551
-
1552
- # Get the number of members in a sorted set.
1553
- #
1554
- # @example
1555
- # redis.zcard("zset")
1556
- # # => 4
1557
- #
1558
- # @param [String] key
1559
- # @return [Integer]
1560
- def zcard(key)
1561
- synchronize do |client|
1562
- client.call([:zcard, key])
1563
- end
1564
- end
1565
-
1566
- # Add one or more members to a sorted set, or update the score for members
1567
- # that already exist.
1568
- #
1569
- # @example Add a single `[score, member]` pair to a sorted set
1570
- # redis.zadd("zset", 32.0, "member")
1571
- # @example Add an array of `[score, member]` pairs to a sorted set
1572
- # redis.zadd("zset", [[32.0, "a"], [64.0, "b"]])
1573
- #
1574
- # @param [String] key
1575
- # @param [[Float, String], Array<[Float, String]>] args
1576
- # - a single `[score, member]` pair
1577
- # - an array of `[score, member]` pairs
1578
- # @param [Hash] options
1579
- # - `:xx => true`: Only update elements that already exist (never
1580
- # add elements)
1581
- # - `:nx => true`: Don't update already existing elements (always
1582
- # add new elements)
1583
- # - `:ch => true`: Modify the return value from the number of new
1584
- # elements added, to the total number of elements changed (CH is an
1585
- # abbreviation of changed); changed elements are new elements added
1586
- # and elements already existing for which the score was updated
1587
- # - `:incr => true`: When this option is specified ZADD acts like
1588
- # ZINCRBY; only one score-element pair can be specified in this mode
1589
- #
1590
- # @return [Boolean, Integer, Float]
1591
- # - `Boolean` when a single pair is specified, holding whether or not it was
1592
- # **added** to the sorted set.
1593
- # - `Integer` when an array of pairs is specified, holding the number of
1594
- # pairs that were **added** to the sorted set.
1595
- # - `Float` when option :incr is specified, holding the score of the member
1596
- # after incrementing it.
1597
- def zadd(key, *args, nx: nil, xx: nil, ch: nil, incr: nil)
1598
- command = [:zadd, key]
1599
- command << "NX" if nx
1600
- command << "XX" if xx
1601
- command << "CH" if ch
1602
- command << "INCR" if incr
1603
-
1604
- synchronize do |client|
1605
- if args.size == 1 && args[0].is_a?(Array)
1606
- # Variadic: return float if INCR, integer if !INCR
1607
- client.call(command + args[0], &(incr ? Floatify : nil))
1608
- elsif args.size == 2
1609
- # Single pair: return float if INCR, boolean if !INCR
1610
- client.call(command + args, &(incr ? Floatify : Boolify))
1611
- else
1612
- raise ArgumentError, "wrong number of arguments"
1613
- end
1614
- end
1615
- end
1616
-
1617
- # Increment the score of a member in a sorted set.
1618
- #
1619
- # @example
1620
- # redis.zincrby("zset", 32.0, "a")
1621
- # # => 64.0
1622
- #
1623
- # @param [String] key
1624
- # @param [Float] increment
1625
- # @param [String] member
1626
- # @return [Float] score of the member after incrementing it
1627
- def zincrby(key, increment, member)
1628
- synchronize do |client|
1629
- client.call([:zincrby, key, increment, member], &Floatify)
1630
- end
1631
- end
1632
-
1633
- # Remove one or more members from a sorted set.
1634
- #
1635
- # @example Remove a single member from a sorted set
1636
- # redis.zrem("zset", "a")
1637
- # @example Remove an array of members from a sorted set
1638
- # redis.zrem("zset", ["a", "b"])
1639
- #
1640
- # @param [String] key
1641
- # @param [String, Array<String>] member
1642
- # - a single member
1643
- # - an array of members
1644
- #
1645
- # @return [Boolean, Integer]
1646
- # - `Boolean` when a single member is specified, holding whether or not it
1647
- # was removed from the sorted set
1648
- # - `Integer` when an array of pairs is specified, holding the number of
1649
- # members that were removed to the sorted set
1650
- def zrem(key, member)
1651
- synchronize do |client|
1652
- client.call([:zrem, key, member]) do |reply|
1653
- if member.is_a? Array
1654
- # Variadic: return integer
1655
- reply
1656
- else
1657
- # Single argument: return boolean
1658
- Boolify.call(reply)
1659
- end
1660
- end
1661
- end
1662
- end
1663
-
1664
- # Removes and returns up to count members with the highest scores in the sorted set stored at key.
1665
- #
1666
- # @example Popping a member
1667
- # redis.zpopmax('zset')
1668
- # #=> ['b', 2.0]
1669
- # @example With count option
1670
- # redis.zpopmax('zset', 2)
1671
- # #=> [['b', 2.0], ['a', 1.0]]
1672
- #
1673
- # @params key [String] a key of the sorted set
1674
- # @params count [Integer] a number of members
1675
- #
1676
- # @return [Array<String, Float>] element and score pair if count is not specified
1677
- # @return [Array<Array<String, Float>>] list of popped elements and scores
1678
- def zpopmax(key, count = nil)
1679
- synchronize do |client|
1680
- members = client.call([:zpopmax, key, count].compact, &FloatifyPairs)
1681
- count.to_i > 1 ? members : members.first
1682
- end
1683
- end
1684
-
1685
- # Removes and returns up to count members with the lowest scores in the sorted set stored at key.
1686
- #
1687
- # @example Popping a member
1688
- # redis.zpopmin('zset')
1689
- # #=> ['a', 1.0]
1690
- # @example With count option
1691
- # redis.zpopmin('zset', 2)
1692
- # #=> [['a', 1.0], ['b', 2.0]]
1693
- #
1694
- # @params key [String] a key of the sorted set
1695
- # @params count [Integer] a number of members
1696
- #
1697
- # @return [Array<String, Float>] element and score pair if count is not specified
1698
- # @return [Array<Array<String, Float>>] list of popped elements and scores
1699
- def zpopmin(key, count = nil)
1700
- synchronize do |client|
1701
- members = client.call([:zpopmin, key, count].compact, &FloatifyPairs)
1702
- count.to_i > 1 ? members : members.first
1703
- end
1704
- end
1705
-
1706
- # Removes and returns up to count members with the highest scores in the sorted set stored at keys,
1707
- # or block until one is available.
1708
- #
1709
- # @example Popping a member from a sorted set
1710
- # redis.bzpopmax('zset', 1)
1711
- # #=> ['zset', 'b', 2.0]
1712
- # @example Popping a member from multiple sorted sets
1713
- # redis.bzpopmax('zset1', 'zset2', 1)
1714
- # #=> ['zset1', 'b', 2.0]
1715
- #
1716
- # @params keys [Array<String>] one or multiple keys of the sorted sets
1717
- # @params timeout [Integer] the maximum number of seconds to block
1718
- #
1719
- # @return [Array<String, String, Float>] a touple of key, member and score
1720
- # @return [nil] when no element could be popped and the timeout expired
1721
- def bzpopmax(*args)
1722
- _bpop(:bzpopmax, args) do |reply|
1723
- reply.is_a?(Array) ? [reply[0], reply[1], Floatify.call(reply[2])] : reply
1724
- end
1725
- end
1726
-
1727
- # Removes and returns up to count members with the lowest scores in the sorted set stored at keys,
1728
- # or block until one is available.
1729
- #
1730
- # @example Popping a member from a sorted set
1731
- # redis.bzpopmin('zset', 1)
1732
- # #=> ['zset', 'a', 1.0]
1733
- # @example Popping a member from multiple sorted sets
1734
- # redis.bzpopmin('zset1', 'zset2', 1)
1735
- # #=> ['zset1', 'a', 1.0]
1736
- #
1737
- # @params keys [Array<String>] one or multiple keys of the sorted sets
1738
- # @params timeout [Integer] the maximum number of seconds to block
1739
- #
1740
- # @return [Array<String, String, Float>] a touple of key, member and score
1741
- # @return [nil] when no element could be popped and the timeout expired
1742
- def bzpopmin(*args)
1743
- _bpop(:bzpopmin, args) do |reply|
1744
- reply.is_a?(Array) ? [reply[0], reply[1], Floatify.call(reply[2])] : reply
1745
- end
1746
- end
1747
-
1748
- # Get the score associated with the given member in a sorted set.
1749
- #
1750
- # @example Get the score for member "a"
1751
- # redis.zscore("zset", "a")
1752
- # # => 32.0
1753
- #
1754
- # @param [String] key
1755
- # @param [String] member
1756
- # @return [Float] score of the member
1757
- def zscore(key, member)
1758
- synchronize do |client|
1759
- client.call([:zscore, key, member], &Floatify)
1760
- end
1761
- end
1762
-
1763
- # Return a range of members in a sorted set, by index.
1764
- #
1765
- # @example Retrieve all members from a sorted set
1766
- # redis.zrange("zset", 0, -1)
1767
- # # => ["a", "b"]
1768
- # @example Retrieve all members and their scores from a sorted set
1769
- # redis.zrange("zset", 0, -1, :with_scores => true)
1770
- # # => [["a", 32.0], ["b", 64.0]]
1771
- #
1772
- # @param [String] key
1773
- # @param [Integer] start start index
1774
- # @param [Integer] stop stop index
1775
- # @param [Hash] options
1776
- # - `:with_scores => true`: include scores in output
1777
- #
1778
- # @return [Array<String>, Array<[String, Float]>]
1779
- # - when `:with_scores` is not specified, an array of members
1780
- # - when `:with_scores` is specified, an array with `[member, score]` pairs
1781
- def zrange(key, start, stop, withscores: false, with_scores: withscores)
1782
- args = [:zrange, key, start, stop]
1783
-
1784
- if with_scores
1785
- args << "WITHSCORES"
1786
- block = FloatifyPairs
1787
- end
1788
-
1789
- synchronize do |client|
1790
- client.call(args, &block)
1791
- end
1792
- end
1793
-
1794
- # Return a range of members in a sorted set, by index, with scores ordered
1795
- # from high to low.
1796
- #
1797
- # @example Retrieve all members from a sorted set
1798
- # redis.zrevrange("zset", 0, -1)
1799
- # # => ["b", "a"]
1800
- # @example Retrieve all members and their scores from a sorted set
1801
- # redis.zrevrange("zset", 0, -1, :with_scores => true)
1802
- # # => [["b", 64.0], ["a", 32.0]]
1803
- #
1804
- # @see #zrange
1805
- def zrevrange(key, start, stop, withscores: false, with_scores: withscores)
1806
- args = [:zrevrange, key, start, stop]
1807
-
1808
- if with_scores
1809
- args << "WITHSCORES"
1810
- block = FloatifyPairs
1811
- end
1812
-
1813
- synchronize do |client|
1814
- client.call(args, &block)
1815
- end
1816
- end
1817
-
1818
- # Determine the index of a member in a sorted set.
1819
- #
1820
- # @param [String] key
1821
- # @param [String] member
1822
- # @return [Integer]
1823
- def zrank(key, member)
1824
- synchronize do |client|
1825
- client.call([:zrank, key, member])
1826
- end
1827
- end
1828
-
1829
- # Determine the index of a member in a sorted set, with scores ordered from
1830
- # high to low.
1831
- #
1832
- # @param [String] key
1833
- # @param [String] member
1834
- # @return [Integer]
1835
- def zrevrank(key, member)
1836
- synchronize do |client|
1837
- client.call([:zrevrank, key, member])
1838
- end
1839
- end
1840
-
1841
- # Remove all members in a sorted set within the given indexes.
1842
- #
1843
- # @example Remove first 5 members
1844
- # redis.zremrangebyrank("zset", 0, 4)
1845
- # # => 5
1846
- # @example Remove last 5 members
1847
- # redis.zremrangebyrank("zset", -5, -1)
1848
- # # => 5
1849
- #
1850
- # @param [String] key
1851
- # @param [Integer] start start index
1852
- # @param [Integer] stop stop index
1853
- # @return [Integer] number of members that were removed
1854
- def zremrangebyrank(key, start, stop)
1855
- synchronize do |client|
1856
- client.call([:zremrangebyrank, key, start, stop])
1857
- end
1858
- end
1859
-
1860
- # Count the members, with the same score in a sorted set, within the given lexicographical range.
1861
- #
1862
- # @example Count members matching a
1863
- # redis.zlexcount("zset", "[a", "[a\xff")
1864
- # # => 1
1865
- # @example Count members matching a-z
1866
- # redis.zlexcount("zset", "[a", "[z\xff")
1867
- # # => 26
1868
- #
1869
- # @param [String] key
1870
- # @param [String] min
1871
- # - inclusive minimum is specified by prefixing `(`
1872
- # - exclusive minimum is specified by prefixing `[`
1873
- # @param [String] max
1874
- # - inclusive maximum is specified by prefixing `(`
1875
- # - exclusive maximum is specified by prefixing `[`
1876
- #
1877
- # @return [Integer] number of members within the specified lexicographical range
1878
- def zlexcount(key, min, max)
1879
- synchronize do |client|
1880
- client.call([:zlexcount, key, min, max])
1881
- end
1882
- end
1883
-
1884
- # Return a range of members with the same score in a sorted set, by lexicographical ordering
1885
- #
1886
- # @example Retrieve members matching a
1887
- # redis.zrangebylex("zset", "[a", "[a\xff")
1888
- # # => ["aaren", "aarika", "abagael", "abby"]
1889
- # @example Retrieve the first 2 members matching a
1890
- # redis.zrangebylex("zset", "[a", "[a\xff", :limit => [0, 2])
1891
- # # => ["aaren", "aarika"]
1892
- #
1893
- # @param [String] key
1894
- # @param [String] min
1895
- # - inclusive minimum is specified by prefixing `(`
1896
- # - exclusive minimum is specified by prefixing `[`
1897
- # @param [String] max
1898
- # - inclusive maximum is specified by prefixing `(`
1899
- # - exclusive maximum is specified by prefixing `[`
1900
- # @param [Hash] options
1901
- # - `:limit => [offset, count]`: skip `offset` members, return a maximum of
1902
- # `count` members
1903
- #
1904
- # @return [Array<String>, Array<[String, Float]>]
1905
- def zrangebylex(key, min, max, limit: nil)
1906
- args = [:zrangebylex, key, min, max]
1907
-
1908
- if limit
1909
- args << "LIMIT"
1910
- args.concat(limit)
1911
- end
1912
-
1913
- synchronize do |client|
1914
- client.call(args)
1915
- end
1916
- end
1917
-
1918
- # Return a range of members with the same score in a sorted set, by reversed lexicographical ordering.
1919
- # Apart from the reversed ordering, #zrevrangebylex is similar to #zrangebylex.
1920
- #
1921
- # @example Retrieve members matching a
1922
- # redis.zrevrangebylex("zset", "[a", "[a\xff")
1923
- # # => ["abbygail", "abby", "abagael", "aaren"]
1924
- # @example Retrieve the last 2 members matching a
1925
- # redis.zrevrangebylex("zset", "[a", "[a\xff", :limit => [0, 2])
1926
- # # => ["abbygail", "abby"]
1927
- #
1928
- # @see #zrangebylex
1929
- def zrevrangebylex(key, max, min, limit: nil)
1930
- args = [:zrevrangebylex, key, max, min]
1931
-
1932
- if limit
1933
- args << "LIMIT"
1934
- args.concat(limit)
1935
- end
1936
-
1937
- synchronize do |client|
1938
- client.call(args)
1939
- end
1940
- end
1941
-
1942
- # Return a range of members in a sorted set, by score.
1943
- #
1944
- # @example Retrieve members with score `>= 5` and `< 100`
1945
- # redis.zrangebyscore("zset", "5", "(100")
1946
- # # => ["a", "b"]
1947
- # @example Retrieve the first 2 members with score `>= 0`
1948
- # redis.zrangebyscore("zset", "0", "+inf", :limit => [0, 2])
1949
- # # => ["a", "b"]
1950
- # @example Retrieve members and their scores with scores `> 5`
1951
- # redis.zrangebyscore("zset", "(5", "+inf", :with_scores => true)
1952
- # # => [["a", 32.0], ["b", 64.0]]
1953
- #
1954
- # @param [String] key
1955
- # @param [String] min
1956
- # - inclusive minimum score is specified verbatim
1957
- # - exclusive minimum score is specified by prefixing `(`
1958
- # @param [String] max
1959
- # - inclusive maximum score is specified verbatim
1960
- # - exclusive maximum score is specified by prefixing `(`
1961
- # @param [Hash] options
1962
- # - `:with_scores => true`: include scores in output
1963
- # - `:limit => [offset, count]`: skip `offset` members, return a maximum of
1964
- # `count` members
1965
- #
1966
- # @return [Array<String>, Array<[String, Float]>]
1967
- # - when `:with_scores` is not specified, an array of members
1968
- # - when `:with_scores` is specified, an array with `[member, score]` pairs
1969
- def zrangebyscore(key, min, max, withscores: false, with_scores: withscores, limit: nil)
1970
- args = [:zrangebyscore, key, min, max]
1971
-
1972
- if with_scores
1973
- args << "WITHSCORES"
1974
- block = FloatifyPairs
1975
- end
1976
-
1977
- if limit
1978
- args << "LIMIT"
1979
- args.concat(limit)
1980
- end
1981
-
1982
- synchronize do |client|
1983
- client.call(args, &block)
1984
- end
1985
- end
1986
-
1987
- # Return a range of members in a sorted set, by score, with scores ordered
1988
- # from high to low.
1989
- #
1990
- # @example Retrieve members with score `< 100` and `>= 5`
1991
- # redis.zrevrangebyscore("zset", "(100", "5")
1992
- # # => ["b", "a"]
1993
- # @example Retrieve the first 2 members with score `<= 0`
1994
- # redis.zrevrangebyscore("zset", "0", "-inf", :limit => [0, 2])
1995
- # # => ["b", "a"]
1996
- # @example Retrieve members and their scores with scores `> 5`
1997
- # redis.zrevrangebyscore("zset", "+inf", "(5", :with_scores => true)
1998
- # # => [["b", 64.0], ["a", 32.0]]
1999
- #
2000
- # @see #zrangebyscore
2001
- def zrevrangebyscore(key, max, min, withscores: false, with_scores: withscores, limit: nil)
2002
- args = [:zrevrangebyscore, key, max, min]
2003
-
2004
- if with_scores
2005
- args << "WITHSCORES"
2006
- block = FloatifyPairs
2007
- end
2008
-
2009
- if limit
2010
- args << "LIMIT"
2011
- args.concat(limit)
2012
- end
2013
-
2014
- synchronize do |client|
2015
- client.call(args, &block)
2016
- end
2017
- end
2018
-
2019
- # Remove all members in a sorted set within the given scores.
2020
- #
2021
- # @example Remove members with score `>= 5` and `< 100`
2022
- # redis.zremrangebyscore("zset", "5", "(100")
2023
- # # => 2
2024
- # @example Remove members with scores `> 5`
2025
- # redis.zremrangebyscore("zset", "(5", "+inf")
2026
- # # => 2
2027
- #
2028
- # @param [String] key
2029
- # @param [String] min
2030
- # - inclusive minimum score is specified verbatim
2031
- # - exclusive minimum score is specified by prefixing `(`
2032
- # @param [String] max
2033
- # - inclusive maximum score is specified verbatim
2034
- # - exclusive maximum score is specified by prefixing `(`
2035
- # @return [Integer] number of members that were removed
2036
- def zremrangebyscore(key, min, max)
2037
- synchronize do |client|
2038
- client.call([:zremrangebyscore, key, min, max])
2039
- end
2040
- end
2041
-
2042
- # Count the members in a sorted set with scores within the given values.
2043
- #
2044
- # @example Count members with score `>= 5` and `< 100`
2045
- # redis.zcount("zset", "5", "(100")
2046
- # # => 2
2047
- # @example Count members with scores `> 5`
2048
- # redis.zcount("zset", "(5", "+inf")
2049
- # # => 2
2050
- #
2051
- # @param [String] key
2052
- # @param [String] min
2053
- # - inclusive minimum score is specified verbatim
2054
- # - exclusive minimum score is specified by prefixing `(`
2055
- # @param [String] max
2056
- # - inclusive maximum score is specified verbatim
2057
- # - exclusive maximum score is specified by prefixing `(`
2058
- # @return [Integer] number of members in within the specified range
2059
- def zcount(key, min, max)
2060
- synchronize do |client|
2061
- client.call([:zcount, key, min, max])
2062
- end
2063
- end
2064
-
2065
- # Intersect multiple sorted sets and store the resulting sorted set in a new
2066
- # key.
2067
- #
2068
- # @example Compute the intersection of `2*zsetA` with `1*zsetB`, summing their scores
2069
- # redis.zinterstore("zsetC", ["zsetA", "zsetB"], :weights => [2.0, 1.0], :aggregate => "sum")
2070
- # # => 4
2071
- #
2072
- # @param [String] destination destination key
2073
- # @param [Array<String>] keys source keys
2074
- # @param [Hash] options
2075
- # - `:weights => [Float, Float, ...]`: weights to associate with source
2076
- # sorted sets
2077
- # - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
2078
- # @return [Integer] number of elements in the resulting sorted set
2079
- def zinterstore(destination, keys, weights: nil, aggregate: nil)
2080
- args = [:zinterstore, destination, keys.size, *keys]
2081
-
2082
- if weights
2083
- args << "WEIGHTS"
2084
- args.concat(weights)
2085
- end
2086
-
2087
- args << "AGGREGATE" << aggregate if aggregate
2088
-
2089
- synchronize do |client|
2090
- client.call(args)
2091
- end
2092
- end
2093
-
2094
- # Add multiple sorted sets and store the resulting sorted set in a new key.
2095
- #
2096
- # @example Compute the union of `2*zsetA` with `1*zsetB`, summing their scores
2097
- # redis.zunionstore("zsetC", ["zsetA", "zsetB"], :weights => [2.0, 1.0], :aggregate => "sum")
2098
- # # => 8
2099
- #
2100
- # @param [String] destination destination key
2101
- # @param [Array<String>] keys source keys
2102
- # @param [Hash] options
2103
- # - `:weights => [Float, Float, ...]`: weights to associate with source
2104
- # sorted sets
2105
- # - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
2106
- # @return [Integer] number of elements in the resulting sorted set
2107
- def zunionstore(destination, keys, weights: nil, aggregate: nil)
2108
- args = [:zunionstore, destination, keys.size, *keys]
2109
-
2110
- if weights
2111
- args << "WEIGHTS"
2112
- args.concat(weights)
2113
- end
2114
-
2115
- args << "AGGREGATE" << aggregate if aggregate
2116
-
2117
- synchronize do |client|
2118
- client.call(args)
2119
- end
2120
- end
2121
-
2122
- # Get the number of fields in a hash.
2123
- #
2124
- # @param [String] key
2125
- # @return [Integer] number of fields in the hash
2126
- def hlen(key)
2127
- synchronize do |client|
2128
- client.call([:hlen, key])
2129
- end
2130
- end
2131
-
2132
- # Set one or more hash values.
2133
- #
2134
- # @example
2135
- # redis.hset("hash", "f1", "v1", "f2", "v2") # => 2
2136
- # redis.hset("hash", { "f1" => "v1", "f2" => "v2" }) # => 2
2137
- #
2138
- # @param [String] key
2139
- # @param [Array<String> | Hash<String, String>] attrs array or hash of fields and values
2140
- # @return [Integer] The number of fields that were added to the hash
2141
- def hset(key, *attrs)
2142
- attrs = attrs.first.flatten if attrs.size == 1 && attrs.first.is_a?(Hash)
2143
-
2144
- synchronize do |client|
2145
- client.call([:hset, key, *attrs])
2146
- end
2147
- end
2148
-
2149
- # Set the value of a hash field, only if the field does not exist.
2150
- #
2151
- # @param [String] key
2152
- # @param [String] field
2153
- # @param [String] value
2154
- # @return [Boolean] whether or not the field was **added** to the hash
2155
- def hsetnx(key, field, value)
2156
- synchronize do |client|
2157
- client.call([:hsetnx, key, field, value], &Boolify)
2158
- end
2159
- end
2160
-
2161
- # Set one or more hash values.
2162
- #
2163
- # @example
2164
- # redis.hmset("hash", "f1", "v1", "f2", "v2")
2165
- # # => "OK"
2166
- #
2167
- # @param [String] key
2168
- # @param [Array<String>] attrs array of fields and values
2169
- # @return [String] `"OK"`
2170
- #
2171
- # @see #mapped_hmset
2172
- def hmset(key, *attrs)
2173
- synchronize do |client|
2174
- client.call([:hmset, key] + attrs)
2175
- end
2176
- end
2177
-
2178
- # Set one or more hash values.
2179
- #
2180
- # @example
2181
- # redis.mapped_hmset("hash", { "f1" => "v1", "f2" => "v2" })
2182
- # # => "OK"
2183
- #
2184
- # @param [String] key
2185
- # @param [Hash] hash a non-empty hash with fields mapping to values
2186
- # @return [String] `"OK"`
2187
- #
2188
- # @see #hmset
2189
- def mapped_hmset(key, hash)
2190
- hmset(key, hash.to_a.flatten)
2191
- end
2192
-
2193
- # Get the value of a hash field.
2194
- #
2195
- # @param [String] key
2196
- # @param [String] field
2197
- # @return [String]
2198
- def hget(key, field)
2199
- synchronize do |client|
2200
- client.call([:hget, key, field])
2201
- end
2202
- end
2203
-
2204
- # Get the values of all the given hash fields.
2205
- #
2206
- # @example
2207
- # redis.hmget("hash", "f1", "f2")
2208
- # # => ["v1", "v2"]
2209
- #
2210
- # @param [String] key
2211
- # @param [Array<String>] fields array of fields
2212
- # @return [Array<String>] an array of values for the specified fields
2213
- #
2214
- # @see #mapped_hmget
2215
- def hmget(key, *fields, &blk)
2216
- synchronize do |client|
2217
- client.call([:hmget, key] + fields, &blk)
2218
- end
2219
- end
2220
-
2221
- # Get the values of all the given hash fields.
2222
- #
2223
- # @example
2224
- # redis.mapped_hmget("hash", "f1", "f2")
2225
- # # => { "f1" => "v1", "f2" => "v2" }
2226
- #
2227
- # @param [String] key
2228
- # @param [Array<String>] fields array of fields
2229
- # @return [Hash] a hash mapping the specified fields to their values
2230
- #
2231
- # @see #hmget
2232
- def mapped_hmget(key, *fields)
2233
- hmget(key, *fields) do |reply|
2234
- if reply.is_a?(Array)
2235
- Hash[fields.zip(reply)]
2236
- else
2237
- reply
2238
- end
2239
- end
2240
- end
2241
-
2242
- # Delete one or more hash fields.
2243
- #
2244
- # @param [String] key
2245
- # @param [String, Array<String>] field
2246
- # @return [Integer] the number of fields that were removed from the hash
2247
- def hdel(key, *fields)
2248
- synchronize do |client|
2249
- client.call([:hdel, key, *fields])
2250
- end
2251
- end
2252
-
2253
- # Determine if a hash field exists.
2254
- #
2255
- # @param [String] key
2256
- # @param [String] field
2257
- # @return [Boolean] whether or not the field exists in the hash
2258
- def hexists(key, field)
2259
- synchronize do |client|
2260
- client.call([:hexists, key, field], &Boolify)
2261
- end
2262
- end
2263
-
2264
- # Increment the integer value of a hash field by the given integer number.
2265
- #
2266
- # @param [String] key
2267
- # @param [String] field
2268
- # @param [Integer] increment
2269
- # @return [Integer] value of the field after incrementing it
2270
- def hincrby(key, field, increment)
2271
- synchronize do |client|
2272
- client.call([:hincrby, key, field, increment])
2273
- end
2274
- end
2275
-
2276
- # Increment the numeric value of a hash field by the given float number.
2277
- #
2278
- # @param [String] key
2279
- # @param [String] field
2280
- # @param [Float] increment
2281
- # @return [Float] value of the field after incrementing it
2282
- def hincrbyfloat(key, field, increment)
2283
- synchronize do |client|
2284
- client.call([:hincrbyfloat, key, field, increment], &Floatify)
2285
- end
2286
- end
2287
-
2288
- # Get all the fields in a hash.
2289
- #
2290
- # @param [String] key
2291
- # @return [Array<String>]
2292
- def hkeys(key)
2293
- synchronize do |client|
2294
- client.call([:hkeys, key])
2295
- end
2296
- end
2297
-
2298
- # Get all the values in a hash.
2299
- #
2300
- # @param [String] key
2301
- # @return [Array<String>]
2302
- def hvals(key)
2303
- synchronize do |client|
2304
- client.call([:hvals, key])
2305
- end
2306
- end
2307
-
2308
- # Get all the fields and values in a hash.
2309
- #
2310
- # @param [String] key
2311
- # @return [Hash<String, String>]
2312
- def hgetall(key)
2313
- synchronize do |client|
2314
- client.call([:hgetall, key], &Hashify)
2315
- end
2316
- end
2317
-
2318
- # Post a message to a channel.
2319
- def publish(channel, message)
2320
- synchronize do |client|
2321
- client.call([:publish, channel, message])
2322
- end
2323
- end
2324
-
2325
- def subscribed?
2326
- synchronize do |client|
2327
- client.is_a? SubscribedClient
2328
- end
2329
- end
2330
-
2331
- # Listen for messages published to the given channels.
2332
- def subscribe(*channels, &block)
2333
- synchronize do |_client|
2334
- _subscription(:subscribe, 0, channels, block)
2335
- end
2336
- end
2337
-
2338
- # Listen for messages published to the given channels. Throw a timeout error
2339
- # if there is no messages for a timeout period.
2340
- def subscribe_with_timeout(timeout, *channels, &block)
2341
- synchronize do |_client|
2342
- _subscription(:subscribe_with_timeout, timeout, channels, block)
2343
- end
2344
- end
2345
-
2346
- # Stop listening for messages posted to the given channels.
2347
- def unsubscribe(*channels)
2348
- synchronize do |client|
2349
- raise "Can't unsubscribe if not subscribed." unless subscribed?
2350
-
2351
- client.unsubscribe(*channels)
2352
- end
2353
- end
2354
-
2355
- # Listen for messages published to channels matching the given patterns.
2356
- def psubscribe(*channels, &block)
2357
- synchronize do |_client|
2358
- _subscription(:psubscribe, 0, channels, block)
2359
- end
2360
- end
2361
-
2362
- # Listen for messages published to channels matching the given patterns.
2363
- # Throw a timeout error if there is no messages for a timeout period.
2364
- def psubscribe_with_timeout(timeout, *channels, &block)
2365
- synchronize do |_client|
2366
- _subscription(:psubscribe_with_timeout, timeout, channels, block)
2367
- end
2368
- end
2369
-
2370
- # Stop listening for messages posted to channels matching the given patterns.
2371
- def punsubscribe(*channels)
2372
- synchronize do |client|
2373
- raise "Can't unsubscribe if not subscribed." unless subscribed?
2374
-
2375
- client.punsubscribe(*channels)
2376
- end
2377
- end
2378
-
2379
- # Inspect the state of the Pub/Sub subsystem.
2380
- # Possible subcommands: channels, numsub, numpat.
2381
- def pubsub(subcommand, *args)
2382
- synchronize do |client|
2383
- client.call([:pubsub, subcommand] + args)
2384
- end
2385
- end
2386
-
2387
- # Watch the given keys to determine execution of the MULTI/EXEC block.
2388
- #
2389
- # Using a block is optional, but is necessary for thread-safety.
2390
- #
2391
- # An `#unwatch` is automatically issued if an exception is raised within the
2392
- # block that is a subclass of StandardError and is not a ConnectionError.
2393
- #
2394
- # @example With a block
2395
- # redis.watch("key") do
2396
- # if redis.get("key") == "some value"
2397
- # redis.multi do |multi|
2398
- # multi.set("key", "other value")
2399
- # multi.incr("counter")
2400
- # end
2401
- # else
2402
- # redis.unwatch
2403
- # end
2404
- # end
2405
- # # => ["OK", 6]
2406
- #
2407
- # @example Without a block
2408
- # redis.watch("key")
2409
- # # => "OK"
2410
- #
2411
- # @param [String, Array<String>] keys one or more keys to watch
2412
- # @return [Object] if using a block, returns the return value of the block
2413
- # @return [String] if not using a block, returns `OK`
2414
- #
2415
- # @see #unwatch
2416
- # @see #multi
2417
- def watch(*keys)
2418
- synchronize do |client|
2419
- res = client.call([:watch, *keys])
2420
-
2421
- if block_given?
2422
- begin
2423
- yield(self)
2424
- rescue ConnectionError
2425
- raise
2426
- rescue StandardError
2427
- unwatch
2428
- raise
2429
- end
2430
- else
2431
- res
2432
- end
2433
- end
2434
- end
2435
-
2436
- # Forget about all watched keys.
2437
- #
2438
- # @return [String] `OK`
2439
- #
2440
- # @see #watch
2441
- # @see #multi
2442
- def unwatch
2443
- synchronize do |client|
2444
- client.call([:unwatch])
2445
- end
2446
- end
2447
-
2448
- def pipelined
2449
- synchronize do |prior_client|
2450
- begin
2451
- @client = Pipeline.new(prior_client)
2452
- yield(self)
2453
- prior_client.call_pipeline(@client)
2454
- ensure
2455
- @client = prior_client
2456
- end
2457
- end
2458
- end
2459
-
2460
- # Mark the start of a transaction block.
2461
- #
2462
- # Passing a block is optional.
2463
- #
2464
- # @example With a block
2465
- # redis.multi do |multi|
2466
- # multi.set("key", "value")
2467
- # multi.incr("counter")
2468
- # end # => ["OK", 6]
2469
- #
2470
- # @example Without a block
2471
- # redis.multi
2472
- # # => "OK"
2473
- # redis.set("key", "value")
2474
- # # => "QUEUED"
2475
- # redis.incr("counter")
2476
- # # => "QUEUED"
2477
- # redis.exec
2478
- # # => ["OK", 6]
2479
- #
2480
- # @yield [multi] the commands that are called inside this block are cached
2481
- # and written to the server upon returning from it
2482
- # @yieldparam [Redis] multi `self`
2483
- #
2484
- # @return [String, Array<...>]
2485
- # - when a block is not given, `OK`
2486
- # - when a block is given, an array with replies
2487
- #
2488
- # @see #watch
2489
- # @see #unwatch
2490
- def multi
2491
- synchronize do |prior_client|
2492
- if !block_given?
2493
- prior_client.call([:multi])
2494
- else
2495
- begin
2496
- @client = Pipeline::Multi.new(prior_client)
2497
- yield(self)
2498
- prior_client.call_pipeline(@client)
2499
- ensure
2500
- @client = prior_client
2501
- end
2502
- end
2503
- end
2504
- end
2505
-
2506
- # Execute all commands issued after MULTI.
2507
- #
2508
- # Only call this method when `#multi` was called **without** a block.
2509
- #
2510
- # @return [nil, Array<...>]
2511
- # - when commands were not executed, `nil`
2512
- # - when commands were executed, an array with their replies
2513
- #
2514
- # @see #multi
2515
- # @see #discard
2516
- def exec
2517
- synchronize do |client|
2518
- client.call([:exec])
2519
- end
2520
- end
2521
-
2522
- # Discard all commands issued after MULTI.
2523
- #
2524
- # Only call this method when `#multi` was called **without** a block.
2525
- #
2526
- # @return [String] `"OK"`
2527
- #
2528
- # @see #multi
2529
- # @see #exec
2530
- def discard
2531
- synchronize do |client|
2532
- client.call([:discard])
2533
- end
2534
- end
2535
-
2536
- # Control remote script registry.
2537
- #
2538
- # @example Load a script
2539
- # sha = redis.script(:load, "return 1")
2540
- # # => <sha of this script>
2541
- # @example Check if a script exists
2542
- # redis.script(:exists, sha)
2543
- # # => true
2544
- # @example Check if multiple scripts exist
2545
- # redis.script(:exists, [sha, other_sha])
2546
- # # => [true, false]
2547
- # @example Flush the script registry
2548
- # redis.script(:flush)
2549
- # # => "OK"
2550
- # @example Kill a running script
2551
- # redis.script(:kill)
2552
- # # => "OK"
2553
- #
2554
- # @param [String] subcommand e.g. `exists`, `flush`, `load`, `kill`
2555
- # @param [Array<String>] args depends on subcommand
2556
- # @return [String, Boolean, Array<Boolean>, ...] depends on subcommand
2557
- #
2558
- # @see #eval
2559
- # @see #evalsha
2560
- def script(subcommand, *args)
2561
- subcommand = subcommand.to_s.downcase
2562
-
2563
- if subcommand == "exists"
2564
- synchronize do |client|
2565
- arg = args.first
2566
-
2567
- client.call([:script, :exists, arg]) do |reply|
2568
- reply = reply.map { |r| Boolify.call(r) }
2569
-
2570
- if arg.is_a?(Array)
2571
- reply
2572
- else
2573
- reply.first
2574
- end
2575
- end
2576
- end
2577
- else
2578
- synchronize do |client|
2579
- client.call([:script, subcommand] + args)
2580
- end
2581
- end
2582
- end
2583
-
2584
- def _eval(cmd, args)
2585
- script = args.shift
2586
- options = args.pop if args.last.is_a?(Hash)
2587
- options ||= {}
2588
-
2589
- keys = args.shift || options[:keys] || []
2590
- argv = args.shift || options[:argv] || []
2591
-
2592
- synchronize do |client|
2593
- client.call([cmd, script, keys.length] + keys + argv)
2594
- end
2595
- end
2596
-
2597
- # Evaluate Lua script.
2598
- #
2599
- # @example EVAL without KEYS nor ARGV
2600
- # redis.eval("return 1")
2601
- # # => 1
2602
- # @example EVAL with KEYS and ARGV as array arguments
2603
- # redis.eval("return { KEYS, ARGV }", ["k1", "k2"], ["a1", "a2"])
2604
- # # => [["k1", "k2"], ["a1", "a2"]]
2605
- # @example EVAL with KEYS and ARGV in a hash argument
2606
- # redis.eval("return { KEYS, ARGV }", :keys => ["k1", "k2"], :argv => ["a1", "a2"])
2607
- # # => [["k1", "k2"], ["a1", "a2"]]
2608
- #
2609
- # @param [Array<String>] keys optional array with keys to pass to the script
2610
- # @param [Array<String>] argv optional array with arguments to pass to the script
2611
- # @param [Hash] options
2612
- # - `:keys => Array<String>`: optional array with keys to pass to the script
2613
- # - `:argv => Array<String>`: optional array with arguments to pass to the script
2614
- # @return depends on the script
2615
- #
2616
- # @see #script
2617
- # @see #evalsha
2618
- def eval(*args)
2619
- _eval(:eval, args)
2620
- end
2621
-
2622
- # Evaluate Lua script by its SHA.
2623
- #
2624
- # @example EVALSHA without KEYS nor ARGV
2625
- # redis.evalsha(sha)
2626
- # # => <depends on script>
2627
- # @example EVALSHA with KEYS and ARGV as array arguments
2628
- # redis.evalsha(sha, ["k1", "k2"], ["a1", "a2"])
2629
- # # => <depends on script>
2630
- # @example EVALSHA with KEYS and ARGV in a hash argument
2631
- # redis.evalsha(sha, :keys => ["k1", "k2"], :argv => ["a1", "a2"])
2632
- # # => <depends on script>
2633
- #
2634
- # @param [Array<String>] keys optional array with keys to pass to the script
2635
- # @param [Array<String>] argv optional array with arguments to pass to the script
2636
- # @param [Hash] options
2637
- # - `:keys => Array<String>`: optional array with keys to pass to the script
2638
- # - `:argv => Array<String>`: optional array with arguments to pass to the script
2639
- # @return depends on the script
2640
- #
2641
- # @see #script
2642
- # @see #eval
2643
- def evalsha(*args)
2644
- _eval(:evalsha, args)
2645
- end
2646
-
2647
- def _scan(command, cursor, args, match: nil, count: nil, type: nil, &block)
2648
- # SSCAN/ZSCAN/HSCAN already prepend the key to +args+.
2649
-
2650
- args << cursor
2651
- args << "MATCH" << match if match
2652
- args << "COUNT" << count if count
2653
- args << "TYPE" << type if type
2654
-
2655
- synchronize do |client|
2656
- client.call([command] + args, &block)
2657
- end
2658
- end
2659
-
2660
- # Scan the keyspace
2661
- #
2662
- # @example Retrieve the first batch of keys
2663
- # redis.scan(0)
2664
- # # => ["4", ["key:21", "key:47", "key:42"]]
2665
- # @example Retrieve a batch of keys matching a pattern
2666
- # redis.scan(4, :match => "key:1?")
2667
- # # => ["92", ["key:13", "key:18"]]
2668
- # @example Retrieve a batch of keys of a certain type
2669
- # redis.scan(92, :type => "zset")
2670
- # # => ["173", ["sortedset:14", "sortedset:78"]]
2671
- #
2672
- # @param [String, Integer] cursor the cursor of the iteration
2673
- # @param [Hash] options
2674
- # - `:match => String`: only return keys matching the pattern
2675
- # - `:count => Integer`: return count keys at most per iteration
2676
- # - `:type => String`: return keys only of the given type
2677
- #
2678
- # @return [String, Array<String>] the next cursor and all found keys
2679
- def scan(cursor, **options)
2680
- _scan(:scan, cursor, [], **options)
2681
- end
2682
-
2683
- # Scan the keyspace
2684
- #
2685
- # @example Retrieve all of the keys (with possible duplicates)
2686
- # redis.scan_each.to_a
2687
- # # => ["key:21", "key:47", "key:42"]
2688
- # @example Execute block for each key matching a pattern
2689
- # redis.scan_each(:match => "key:1?") {|key| puts key}
2690
- # # => key:13
2691
- # # => key:18
2692
- # @example Execute block for each key of a type
2693
- # redis.scan_each(:type => "hash") {|key| puts redis.type(key)}
2694
- # # => "hash"
2695
- # # => "hash"
2696
- #
2697
- # @param [Hash] options
2698
- # - `:match => String`: only return keys matching the pattern
2699
- # - `:count => Integer`: return count keys at most per iteration
2700
- # - `:type => String`: return keys only of the given type
2701
- #
2702
- # @return [Enumerator] an enumerator for all found keys
2703
- def scan_each(**options, &block)
2704
- return to_enum(:scan_each, **options) unless block_given?
2705
-
2706
- cursor = 0
2707
- loop do
2708
- cursor, keys = scan(cursor, **options)
2709
- keys.each(&block)
2710
- break if cursor == "0"
2711
- end
2712
- end
2713
-
2714
- # Scan a hash
2715
- #
2716
- # @example Retrieve the first batch of key/value pairs in a hash
2717
- # redis.hscan("hash", 0)
2718
- #
2719
- # @param [String, Integer] cursor the cursor of the iteration
2720
- # @param [Hash] options
2721
- # - `:match => String`: only return keys matching the pattern
2722
- # - `:count => Integer`: return count keys at most per iteration
2723
- #
2724
- # @return [String, Array<[String, String]>] the next cursor and all found keys
2725
- def hscan(key, cursor, **options)
2726
- _scan(:hscan, cursor, [key], **options) do |reply|
2727
- [reply[0], reply[1].each_slice(2).to_a]
2728
- end
2729
- end
2730
-
2731
- # Scan a hash
2732
- #
2733
- # @example Retrieve all of the key/value pairs in a hash
2734
- # redis.hscan_each("hash").to_a
2735
- # # => [["key70", "70"], ["key80", "80"]]
2736
- #
2737
- # @param [Hash] options
2738
- # - `:match => String`: only return keys matching the pattern
2739
- # - `:count => Integer`: return count keys at most per iteration
2740
- #
2741
- # @return [Enumerator] an enumerator for all found keys
2742
- def hscan_each(key, **options, &block)
2743
- return to_enum(:hscan_each, key, **options) unless block_given?
2744
-
2745
- cursor = 0
2746
- loop do
2747
- cursor, values = hscan(key, cursor, **options)
2748
- values.each(&block)
2749
- break if cursor == "0"
2750
- end
2751
- end
2752
-
2753
- # Scan a sorted set
2754
- #
2755
- # @example Retrieve the first batch of key/value pairs in a hash
2756
- # redis.zscan("zset", 0)
2757
- #
2758
- # @param [String, Integer] cursor the cursor of the iteration
2759
- # @param [Hash] options
2760
- # - `:match => String`: only return keys matching the pattern
2761
- # - `:count => Integer`: return count keys at most per iteration
2762
- #
2763
- # @return [String, Array<[String, Float]>] the next cursor and all found
2764
- # members and scores
2765
- def zscan(key, cursor, **options)
2766
- _scan(:zscan, cursor, [key], **options) do |reply|
2767
- [reply[0], FloatifyPairs.call(reply[1])]
2768
- end
2769
- end
2770
-
2771
- # Scan a sorted set
2772
- #
2773
- # @example Retrieve all of the members/scores in a sorted set
2774
- # redis.zscan_each("zset").to_a
2775
- # # => [["key70", "70"], ["key80", "80"]]
2776
- #
2777
- # @param [Hash] options
2778
- # - `:match => String`: only return keys matching the pattern
2779
- # - `:count => Integer`: return count keys at most per iteration
2780
- #
2781
- # @return [Enumerator] an enumerator for all found scores and members
2782
- def zscan_each(key, **options, &block)
2783
- return to_enum(:zscan_each, key, **options) unless block_given?
2784
-
2785
- cursor = 0
2786
- loop do
2787
- cursor, values = zscan(key, cursor, **options)
2788
- values.each(&block)
2789
- break if cursor == "0"
2790
- end
2791
- end
2792
-
2793
- # Scan a set
2794
- #
2795
- # @example Retrieve the first batch of keys in a set
2796
- # redis.sscan("set", 0)
2797
- #
2798
- # @param [String, Integer] cursor the cursor of the iteration
2799
- # @param [Hash] options
2800
- # - `:match => String`: only return keys matching the pattern
2801
- # - `:count => Integer`: return count keys at most per iteration
2802
- #
2803
- # @return [String, Array<String>] the next cursor and all found members
2804
- def sscan(key, cursor, **options)
2805
- _scan(:sscan, cursor, [key], **options)
2806
- end
2807
-
2808
- # Scan a set
2809
- #
2810
- # @example Retrieve all of the keys in a set
2811
- # redis.sscan_each("set").to_a
2812
- # # => ["key1", "key2", "key3"]
2813
- #
2814
- # @param [Hash] options
2815
- # - `:match => String`: only return keys matching the pattern
2816
- # - `:count => Integer`: return count keys at most per iteration
2817
- #
2818
- # @return [Enumerator] an enumerator for all keys in the set
2819
- def sscan_each(key, **options, &block)
2820
- return to_enum(:sscan_each, key, **options) unless block_given?
2821
-
2822
- cursor = 0
2823
- loop do
2824
- cursor, keys = sscan(key, cursor, **options)
2825
- keys.each(&block)
2826
- break if cursor == "0"
2827
- end
2828
- end
2829
-
2830
- # Add one or more members to a HyperLogLog structure.
2831
- #
2832
- # @param [String] key
2833
- # @param [String, Array<String>] member one member, or array of members
2834
- # @return [Boolean] true if at least 1 HyperLogLog internal register was altered. false otherwise.
2835
- def pfadd(key, member)
2836
- synchronize do |client|
2837
- client.call([:pfadd, key, member], &Boolify)
2838
- end
2839
- end
2840
-
2841
- # Get the approximate cardinality of members added to HyperLogLog structure.
2842
- #
2843
- # If called with multiple keys, returns the approximate cardinality of the
2844
- # union of the HyperLogLogs contained in the keys.
2845
- #
2846
- # @param [String, Array<String>] keys
2847
- # @return [Integer]
2848
- def pfcount(*keys)
2849
- synchronize do |client|
2850
- client.call([:pfcount] + keys)
2851
- end
2852
- end
2853
-
2854
- # Merge multiple HyperLogLog values into an unique value that will approximate the cardinality of the union of
2855
- # the observed Sets of the source HyperLogLog structures.
2856
- #
2857
- # @param [String] dest_key destination key
2858
- # @param [String, Array<String>] source_key source key, or array of keys
2859
- # @return [Boolean]
2860
- def pfmerge(dest_key, *source_key)
2861
- synchronize do |client|
2862
- client.call([:pfmerge, dest_key, *source_key], &BoolifySet)
2863
- end
2864
- end
2865
-
2866
- # Adds the specified geospatial items (latitude, longitude, name) to the specified key
2867
- #
2868
- # @param [String] key
2869
- # @param [Array] member arguemnts for member or members: longitude, latitude, name
2870
- # @return [Integer] number of elements added to the sorted set
2871
- def geoadd(key, *member)
2872
- synchronize do |client|
2873
- client.call([:geoadd, key, *member])
2874
- end
2875
- end
2876
-
2877
- # Returns geohash string representing position for specified members of the specified key.
2878
- #
2879
- # @param [String] key
2880
- # @param [String, Array<String>] member one member or array of members
2881
- # @return [Array<String, nil>] returns array containg geohash string if member is present, nil otherwise
2882
- def geohash(key, member)
2883
- synchronize do |client|
2884
- client.call([:geohash, key, member])
2885
- end
2886
- end
2887
-
2888
- # Query a sorted set representing a geospatial index to fetch members matching a
2889
- # given maximum distance from a point
2890
- #
2891
- # @param [Array] args key, longitude, latitude, radius, unit(m|km|ft|mi)
2892
- # @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest
2893
- # or the farthest to the nearest relative to the center
2894
- # @param [Integer] count limit the results to the first N matching items
2895
- # @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
2896
- # @return [Array<String>] may be changed with `options`
2897
-
2898
- def georadius(*args, **geoptions)
2899
- geoarguments = _geoarguments(*args, **geoptions)
2900
-
2901
- synchronize do |client|
2902
- client.call([:georadius, *geoarguments])
2903
- end
2904
- end
2905
-
2906
- # Query a sorted set representing a geospatial index to fetch members matching a
2907
- # given maximum distance from an already existing member
2908
- #
2909
- # @param [Array] args key, member, radius, unit(m|km|ft|mi)
2910
- # @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest or the farthest
2911
- # to the nearest relative to the center
2912
- # @param [Integer] count limit the results to the first N matching items
2913
- # @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
2914
- # @return [Array<String>] may be changed with `options`
2915
-
2916
- def georadiusbymember(*args, **geoptions)
2917
- geoarguments = _geoarguments(*args, **geoptions)
2918
-
2919
- synchronize do |client|
2920
- client.call([:georadiusbymember, *geoarguments])
2921
- end
2922
- end
2923
-
2924
- # Returns longitude and latitude of members of a geospatial index
2925
- #
2926
- # @param [String] key
2927
- # @param [String, Array<String>] member one member or array of members
2928
- # @return [Array<Array<String>, nil>] returns array of elements, where each
2929
- # element is either array of longitude and latitude or nil
2930
- def geopos(key, member)
2931
- synchronize do |client|
2932
- client.call([:geopos, key, member])
2933
- end
2934
- end
2935
-
2936
- # Returns the distance between two members of a geospatial index
2937
- #
2938
- # @param [String ]key
2939
- # @param [Array<String>] members
2940
- # @param ['m', 'km', 'mi', 'ft'] unit
2941
- # @return [String, nil] returns distance in spefied unit if both members present, nil otherwise.
2942
- def geodist(key, member1, member2, unit = 'm')
2943
- synchronize do |client|
2944
- client.call([:geodist, key, member1, member2, unit])
2945
- end
2946
- end
2947
-
2948
- # Returns the stream information each subcommand.
2949
- #
2950
- # @example stream
2951
- # redis.xinfo(:stream, 'mystream')
2952
- # @example groups
2953
- # redis.xinfo(:groups, 'mystream')
2954
- # @example consumers
2955
- # redis.xinfo(:consumers, 'mystream', 'mygroup')
2956
- #
2957
- # @param subcommand [String] e.g. `stream` `groups` `consumers`
2958
- # @param key [String] the stream key
2959
- # @param group [String] the consumer group name, required if subcommand is `consumers`
2960
- #
2961
- # @return [Hash] information of the stream if subcommand is `stream`
2962
- # @return [Array<Hash>] information of the consumer groups if subcommand is `groups`
2963
- # @return [Array<Hash>] information of the consumers if subcommand is `consumers`
2964
- def xinfo(subcommand, key, group = nil)
2965
- args = [:xinfo, subcommand, key, group].compact
2966
- synchronize do |client|
2967
- client.call(args) do |reply|
2968
- case subcommand.to_s.downcase
2969
- when 'stream' then Hashify.call(reply)
2970
- when 'groups', 'consumers' then reply.map { |arr| Hashify.call(arr) }
2971
- else reply
2972
- end
2973
- end
2974
- end
2975
- end
2976
-
2977
- # Add new entry to the stream.
2978
- #
2979
- # @example Without options
2980
- # redis.xadd('mystream', f1: 'v1', f2: 'v2')
2981
- # @example With options
2982
- # redis.xadd('mystream', { f1: 'v1', f2: 'v2' }, id: '0-0', maxlen: 1000, approximate: true)
2983
- #
2984
- # @param key [String] the stream key
2985
- # @param entry [Hash] one or multiple field-value pairs
2986
- # @param opts [Hash] several options for `XADD` command
2987
- #
2988
- # @option opts [String] :id the entry id, default value is `*`, it means auto generation
2989
- # @option opts [Integer] :maxlen max length of entries
2990
- # @option opts [Boolean] :approximate whether to add `~` modifier of maxlen or not
2991
- #
2992
- # @return [String] the entry id
2993
- def xadd(key, entry, approximate: nil, maxlen: nil, id: '*')
2994
- args = [:xadd, key]
2995
- if maxlen
2996
- args << "MAXLEN"
2997
- args << "~" if approximate
2998
- args << maxlen
2999
- end
3000
- args << id
3001
- args.concat(entry.to_a.flatten)
3002
- synchronize { |client| client.call(args) }
3003
- end
3004
-
3005
- # Trims older entries of the stream if needed.
3006
- #
3007
- # @example Without options
3008
- # redis.xtrim('mystream', 1000)
3009
- # @example With options
3010
- # redis.xtrim('mystream', 1000, approximate: true)
3011
- #
3012
- # @param key [String] the stream key
3013
- # @param mexlen [Integer] max length of entries
3014
- # @param approximate [Boolean] whether to add `~` modifier of maxlen or not
3015
- #
3016
- # @return [Integer] the number of entries actually deleted
3017
- def xtrim(key, maxlen, approximate: false)
3018
- args = [:xtrim, key, 'MAXLEN', (approximate ? '~' : nil), maxlen].compact
3019
- synchronize { |client| client.call(args) }
3020
- end
3021
-
3022
- # Delete entries by entry ids.
3023
- #
3024
- # @example With splatted entry ids
3025
- # redis.xdel('mystream', '0-1', '0-2')
3026
- # @example With arrayed entry ids
3027
- # redis.xdel('mystream', ['0-1', '0-2'])
3028
- #
3029
- # @param key [String] the stream key
3030
- # @param ids [Array<String>] one or multiple entry ids
3031
- #
3032
- # @return [Integer] the number of entries actually deleted
3033
- def xdel(key, *ids)
3034
- args = [:xdel, key].concat(ids.flatten)
3035
- synchronize { |client| client.call(args) }
3036
- end
3037
-
3038
- # Fetches entries of the stream in ascending order.
3039
- #
3040
- # @example Without options
3041
- # redis.xrange('mystream')
3042
- # @example With a specific start
3043
- # redis.xrange('mystream', '0-1')
3044
- # @example With a specific start and end
3045
- # redis.xrange('mystream', '0-1', '0-3')
3046
- # @example With count options
3047
- # redis.xrange('mystream', count: 10)
3048
- #
3049
- # @param key [String] the stream key
3050
- # @param start [String] first entry id of range, default value is `-`
3051
- # @param end [String] last entry id of range, default value is `+`
3052
- # @param count [Integer] the number of entries as limit
3053
- #
3054
- # @return [Array<Array<String, Hash>>] the ids and entries pairs
3055
- def xrange(key, start = '-', range_end = '+', count: nil)
3056
- args = [:xrange, key, start, range_end]
3057
- args.concat(['COUNT', count]) if count
3058
- synchronize { |client| client.call(args, &HashifyStreamEntries) }
3059
- end
3060
-
3061
- # Fetches entries of the stream in descending order.
3062
- #
3063
- # @example Without options
3064
- # redis.xrevrange('mystream')
3065
- # @example With a specific end
3066
- # redis.xrevrange('mystream', '0-3')
3067
- # @example With a specific end and start
3068
- # redis.xrevrange('mystream', '0-3', '0-1')
3069
- # @example With count options
3070
- # redis.xrevrange('mystream', count: 10)
3071
- #
3072
- # @param key [String] the stream key
3073
- # @param end [String] first entry id of range, default value is `+`
3074
- # @param start [String] last entry id of range, default value is `-`
3075
- # @params count [Integer] the number of entries as limit
3076
- #
3077
- # @return [Array<Array<String, Hash>>] the ids and entries pairs
3078
- def xrevrange(key, range_end = '+', start = '-', count: nil)
3079
- args = [:xrevrange, key, range_end, start]
3080
- args.concat(['COUNT', count]) if count
3081
- synchronize { |client| client.call(args, &HashifyStreamEntries) }
3082
- end
3083
-
3084
- # Returns the number of entries inside a stream.
3085
- #
3086
- # @example With key
3087
- # redis.xlen('mystream')
3088
- #
3089
- # @param key [String] the stream key
3090
- #
3091
- # @return [Integer] the number of entries
3092
- def xlen(key)
3093
- synchronize { |client| client.call([:xlen, key]) }
3094
- end
3095
-
3096
- # Fetches entries from one or multiple streams. Optionally blocking.
3097
- #
3098
- # @example With a key
3099
- # redis.xread('mystream', '0-0')
3100
- # @example With multiple keys
3101
- # redis.xread(%w[mystream1 mystream2], %w[0-0 0-0])
3102
- # @example With count option
3103
- # redis.xread('mystream', '0-0', count: 2)
3104
- # @example With block option
3105
- # redis.xread('mystream', '$', block: 1000)
3106
- #
3107
- # @param keys [Array<String>] one or multiple stream keys
3108
- # @param ids [Array<String>] one or multiple entry ids
3109
- # @param count [Integer] the number of entries as limit per stream
3110
- # @param block [Integer] the number of milliseconds as blocking timeout
3111
- #
3112
- # @return [Hash{String => Hash{String => Hash}}] the entries
3113
- def xread(keys, ids, count: nil, block: nil)
3114
- args = [:xread]
3115
- args << 'COUNT' << count if count
3116
- args << 'BLOCK' << block.to_i if block
3117
- _xread(args, keys, ids, block)
3118
- end
3119
-
3120
- # Manages the consumer group of the stream.
3121
- #
3122
- # @example With `create` subcommand
3123
- # redis.xgroup(:create, 'mystream', 'mygroup', '$')
3124
- # @example With `setid` subcommand
3125
- # redis.xgroup(:setid, 'mystream', 'mygroup', '$')
3126
- # @example With `destroy` subcommand
3127
- # redis.xgroup(:destroy, 'mystream', 'mygroup')
3128
- # @example With `delconsumer` subcommand
3129
- # redis.xgroup(:delconsumer, 'mystream', 'mygroup', 'consumer1')
3130
- #
3131
- # @param subcommand [String] `create` `setid` `destroy` `delconsumer`
3132
- # @param key [String] the stream key
3133
- # @param group [String] the consumer group name
3134
- # @param id_or_consumer [String]
3135
- # * the entry id or `$`, required if subcommand is `create` or `setid`
3136
- # * the consumer name, required if subcommand is `delconsumer`
3137
- # @param mkstream [Boolean] whether to create an empty stream automatically or not
3138
- #
3139
- # @return [String] `OK` if subcommand is `create` or `setid`
3140
- # @return [Integer] effected count if subcommand is `destroy` or `delconsumer`
3141
- def xgroup(subcommand, key, group, id_or_consumer = nil, mkstream: false)
3142
- args = [:xgroup, subcommand, key, group, id_or_consumer, (mkstream ? 'MKSTREAM' : nil)].compact
3143
- synchronize { |client| client.call(args) }
3144
- end
3145
-
3146
- # Fetches a subset of the entries from one or multiple streams related with the consumer group.
3147
- # Optionally blocking.
3148
- #
3149
- # @example With a key
3150
- # redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>')
3151
- # @example With multiple keys
3152
- # redis.xreadgroup('mygroup', 'consumer1', %w[mystream1 mystream2], %w[> >])
3153
- # @example With count option
3154
- # redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', count: 2)
3155
- # @example With block option
3156
- # redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', block: 1000)
3157
- # @example With noack option
3158
- # redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', noack: true)
3159
- #
3160
- # @param group [String] the consumer group name
3161
- # @param consumer [String] the consumer name
3162
- # @param keys [Array<String>] one or multiple stream keys
3163
- # @param ids [Array<String>] one or multiple entry ids
3164
- # @param opts [Hash] several options for `XREADGROUP` command
3165
- #
3166
- # @option opts [Integer] :count the number of entries as limit
3167
- # @option opts [Integer] :block the number of milliseconds as blocking timeout
3168
- # @option opts [Boolean] :noack whether message loss is acceptable or not
3169
- #
3170
- # @return [Hash{String => Hash{String => Hash}}] the entries
3171
- def xreadgroup(group, consumer, keys, ids, count: nil, block: nil, noack: nil)
3172
- args = [:xreadgroup, 'GROUP', group, consumer]
3173
- args << 'COUNT' << count if count
3174
- args << 'BLOCK' << block.to_i if block
3175
- args << 'NOACK' if noack
3176
- _xread(args, keys, ids, block)
3177
- end
3178
-
3179
- # Removes one or multiple entries from the pending entries list of a stream consumer group.
3180
- #
3181
- # @example With a entry id
3182
- # redis.xack('mystream', 'mygroup', '1526569495631-0')
3183
- # @example With splatted entry ids
3184
- # redis.xack('mystream', 'mygroup', '0-1', '0-2')
3185
- # @example With arrayed entry ids
3186
- # redis.xack('mystream', 'mygroup', %w[0-1 0-2])
3187
- #
3188
- # @param key [String] the stream key
3189
- # @param group [String] the consumer group name
3190
- # @param ids [Array<String>] one or multiple entry ids
3191
- #
3192
- # @return [Integer] the number of entries successfully acknowledged
3193
- def xack(key, group, *ids)
3194
- args = [:xack, key, group].concat(ids.flatten)
3195
- synchronize { |client| client.call(args) }
3196
- end
3197
-
3198
- # Changes the ownership of a pending entry
3199
- #
3200
- # @example With splatted entry ids
3201
- # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-1', '0-2')
3202
- # @example With arrayed entry ids
3203
- # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2])
3204
- # @example With idle option
3205
- # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], idle: 1000)
3206
- # @example With time option
3207
- # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], time: 1542866959000)
3208
- # @example With retrycount option
3209
- # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], retrycount: 10)
3210
- # @example With force option
3211
- # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], force: true)
3212
- # @example With justid option
3213
- # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], justid: true)
3214
- #
3215
- # @param key [String] the stream key
3216
- # @param group [String] the consumer group name
3217
- # @param consumer [String] the consumer name
3218
- # @param min_idle_time [Integer] the number of milliseconds
3219
- # @param ids [Array<String>] one or multiple entry ids
3220
- # @param opts [Hash] several options for `XCLAIM` command
3221
- #
3222
- # @option opts [Integer] :idle the number of milliseconds as last time it was delivered of the entry
3223
- # @option opts [Integer] :time the number of milliseconds as a specific Unix Epoch time
3224
- # @option opts [Integer] :retrycount the number of retry counter
3225
- # @option opts [Boolean] :force whether to create the pending entry to the pending entries list or not
3226
- # @option opts [Boolean] :justid whether to fetch just an array of entry ids or not
3227
- #
3228
- # @return [Hash{String => Hash}] the entries successfully claimed
3229
- # @return [Array<String>] the entry ids successfully claimed if justid option is `true`
3230
- def xclaim(key, group, consumer, min_idle_time, *ids, **opts)
3231
- args = [:xclaim, key, group, consumer, min_idle_time].concat(ids.flatten)
3232
- args.concat(['IDLE', opts[:idle].to_i]) if opts[:idle]
3233
- args.concat(['TIME', opts[:time].to_i]) if opts[:time]
3234
- args.concat(['RETRYCOUNT', opts[:retrycount]]) if opts[:retrycount]
3235
- args << 'FORCE' if opts[:force]
3236
- args << 'JUSTID' if opts[:justid]
3237
- blk = opts[:justid] ? Noop : HashifyStreamEntries
3238
- synchronize { |client| client.call(args, &blk) }
3239
- end
3240
-
3241
- # Fetches not acknowledging pending entries
3242
- #
3243
- # @example With key and group
3244
- # redis.xpending('mystream', 'mygroup')
3245
- # @example With range options
3246
- # redis.xpending('mystream', 'mygroup', '-', '+', 10)
3247
- # @example With range and consumer options
3248
- # redis.xpending('mystream', 'mygroup', '-', '+', 10, 'consumer1')
3249
- #
3250
- # @param key [String] the stream key
3251
- # @param group [String] the consumer group name
3252
- # @param start [String] start first entry id of range
3253
- # @param end [String] end last entry id of range
3254
- # @param count [Integer] count the number of entries as limit
3255
- # @param consumer [String] the consumer name
3256
- #
3257
- # @return [Hash] the summary of pending entries
3258
- # @return [Array<Hash>] the pending entries details if options were specified
3259
- def xpending(key, group, *args)
3260
- command_args = [:xpending, key, group]
3261
- case args.size
3262
- when 0, 3, 4
3263
- command_args.concat(args)
3264
- else
3265
- raise ArgumentError, "wrong number of arguments (given #{args.size + 2}, expected 2, 5 or 6)"
3266
- end
3267
-
3268
- summary_needed = args.empty?
3269
- blk = summary_needed ? HashifyStreamPendings : HashifyStreamPendingDetails
3270
- synchronize { |client| client.call(command_args, &blk) }
3271
- end
3272
-
3273
- # Interact with the sentinel command (masters, master, slaves, failover)
3274
- #
3275
- # @param [String] subcommand e.g. `masters`, `master`, `slaves`
3276
- # @param [Array<String>] args depends on subcommand
3277
- # @return [Array<String>, Hash<String, String>, String] depends on subcommand
3278
- def sentinel(subcommand, *args)
3279
- subcommand = subcommand.to_s.downcase
3280
- synchronize do |client|
3281
- client.call([:sentinel, subcommand] + args) do |reply|
3282
- case subcommand
3283
- when "get-master-addr-by-name"
3284
- reply
3285
- else
3286
- if reply.is_a?(Array)
3287
- if reply[0].is_a?(Array)
3288
- reply.map(&Hashify)
3289
- else
3290
- Hashify.call(reply)
3291
- end
3292
- else
3293
- reply
3294
- end
3295
- end
3296
- end
3297
- end
3298
- end
3299
-
3300
- # Sends `CLUSTER *` command to random node and returns its reply.
3301
- #
3302
- # @see https://redis.io/commands#cluster Reference of cluster command
3303
- #
3304
- # @param subcommand [String, Symbol] the subcommand of cluster command
3305
- # e.g. `:slots`, `:nodes`, `:slaves`, `:info`
3306
- #
3307
- # @return [Object] depends on the subcommand
3308
- def cluster(subcommand, *args)
3309
- subcommand = subcommand.to_s.downcase
3310
- block = case subcommand
3311
- when 'slots'
3312
- HashifyClusterSlots
3313
- when 'nodes'
3314
- HashifyClusterNodes
3315
- when 'slaves'
3316
- HashifyClusterSlaves
3317
- when 'info'
3318
- HashifyInfo
3319
- else
3320
- Noop
3321
- end
3322
-
3323
- # @see https://github.com/antirez/redis/blob/unstable/src/redis-trib.rb#L127 raw reply expected
3324
- block = Noop unless @cluster_mode
3325
-
3326
- synchronize do |client|
3327
- client.call([:cluster, subcommand] + args, &block)
3328
- end
3329
- end
3330
-
3331
- # Sends `ASKING` command to random node and returns its reply.
3332
- #
3333
- # @see https://redis.io/topics/cluster-spec#ask-redirection ASK redirection
3334
- #
3335
- # @return [String] `'OK'`
3336
- def asking
3337
- synchronize { |client| client.call(%i[asking]) }
3338
- end
3339
-
3340
- def id
3341
- @original_client.id
3342
- end
3343
-
3344
- def inspect
3345
- "#<Redis client v#{Redis::VERSION} for #{id}>"
3346
- end
3347
-
3348
- def dup
3349
- self.class.new(@options)
3350
- end
3351
-
3352
- def connection
3353
- return @original_client.connection_info if @cluster_mode
3354
-
3355
- {
3356
- host: @original_client.host,
3357
- port: @original_client.port,
3358
- db: @original_client.db,
3359
- id: @original_client.id,
3360
- location: @original_client.location
3361
- }
3362
- end
3363
-
3364
- def method_missing(command, *args) # rubocop:disable Style/MissingRespondToMissing
3365
- synchronize do |client|
3366
- client.call([command] + args)
3367
- end
3368
- end
3369
-
3370
- private
3371
-
3372
- # Commands returning 1 for true and 0 for false may be executed in a pipeline
3373
- # where the method call will return nil. Propagate the nil instead of falsely
3374
- # returning false.
3375
- Boolify = lambda { |value|
3376
- case value
3377
- when 1
3378
- true
3379
- when 0
3380
- false
3381
- else
3382
- value
3383
- end
3384
- }
3385
-
3386
- BoolifySet = lambda { |value|
3387
- case value
3388
- when "OK"
3389
- true
3390
- when nil
3391
- false
3392
- else
3393
- value
3394
- end
3395
- }
3396
-
3397
- Hashify = lambda { |value|
3398
- if value.respond_to?(:each_slice)
3399
- value.each_slice(2).to_h
3400
- else
3401
- value
3402
- end
3403
- }
3404
-
3405
- Floatify = lambda { |value|
3406
- case value
3407
- when "inf"
3408
- Float::INFINITY
3409
- when "-inf"
3410
- -Float::INFINITY
3411
- when String
3412
- Float(value)
3413
- else
3414
- value
3415
- end
3416
- }
3417
-
3418
- FloatifyPairs = lambda { |value|
3419
- return value unless value.respond_to?(:each_slice)
3420
-
3421
- value.each_slice(2).map do |member, score|
3422
- [member, Floatify.call(score)]
3423
- end
3424
- }
3425
-
3426
- HashifyInfo = lambda { |reply|
3427
- lines = reply.split("\r\n").grep_v(/^(#|$)/)
3428
- lines.map! { |line| line.split(':', 2) }
3429
- lines.compact!
3430
- lines.to_h
3431
- }
3432
-
3433
- HashifyStreams = lambda { |reply|
3434
- case reply
3435
- when nil
3436
- {}
3437
- else
3438
- reply.map { |key, entries| [key, HashifyStreamEntries.call(entries)] }.to_h
3439
- end
3440
- }
3441
-
3442
- EMPTY_STREAM_RESPONSE = [nil].freeze
3443
- private_constant :EMPTY_STREAM_RESPONSE
3444
-
3445
- HashifyStreamEntries = lambda { |reply|
3446
- reply.compact.map do |entry_id, values|
3447
- [entry_id, values.each_slice(2).to_h]
3448
- end
3449
- }
3450
-
3451
- HashifyStreamPendings = lambda { |reply|
3452
- {
3453
- 'size' => reply[0],
3454
- 'min_entry_id' => reply[1],
3455
- 'max_entry_id' => reply[2],
3456
- 'consumers' => reply[3].nil? ? {} : reply[3].to_h
3457
- }
3458
- }
3459
-
3460
- HashifyStreamPendingDetails = lambda { |reply|
3461
- reply.map do |arr|
3462
- {
3463
- 'entry_id' => arr[0],
3464
- 'consumer' => arr[1],
3465
- 'elapsed' => arr[2],
3466
- 'count' => arr[3]
3467
- }
3468
- end
3469
- }
3470
-
3471
- HashifyClusterNodeInfo = lambda { |str|
3472
- arr = str.split(' ')
3473
- {
3474
- 'node_id' => arr[0],
3475
- 'ip_port' => arr[1],
3476
- 'flags' => arr[2].split(','),
3477
- 'master_node_id' => arr[3],
3478
- 'ping_sent' => arr[4],
3479
- 'pong_recv' => arr[5],
3480
- 'config_epoch' => arr[6],
3481
- 'link_state' => arr[7],
3482
- 'slots' => arr[8].nil? ? nil : Range.new(*arr[8].split('-'))
3483
- }
3484
- }
3485
-
3486
- HashifyClusterSlots = lambda { |reply|
3487
- reply.map do |arr|
3488
- first_slot, last_slot = arr[0..1]
3489
- master = { 'ip' => arr[2][0], 'port' => arr[2][1], 'node_id' => arr[2][2] }
3490
- replicas = arr[3..-1].map { |r| { 'ip' => r[0], 'port' => r[1], 'node_id' => r[2] } }
3491
- {
3492
- 'start_slot' => first_slot,
3493
- 'end_slot' => last_slot,
3494
- 'master' => master,
3495
- 'replicas' => replicas
3496
- }
3497
- end
3498
- }
3499
-
3500
- HashifyClusterNodes = lambda { |reply|
3501
- reply.split(/[\r\n]+/).map { |str| HashifyClusterNodeInfo.call(str) }
3502
- }
3503
-
3504
- HashifyClusterSlaves = lambda { |reply|
3505
- reply.map { |str| HashifyClusterNodeInfo.call(str) }
3506
- }
3507
-
3508
- Noop = ->(reply) { reply }
3509
-
3510
- def _geoarguments(*args, options: nil, sort: nil, count: nil)
3511
- args.push sort if sort
3512
- args.push 'count', count if count
3513
- args.push options if options
3514
- args
3515
- end
3516
-
3517
- def _subscription(method, timeout, channels, block)
3518
- return @client.call([method] + channels) if subscribed?
3519
-
3520
- begin
3521
- original, @client = @client, SubscribedClient.new(@client)
3522
- if timeout > 0
3523
- @client.send(method, timeout, *channels, &block)
3524
- else
3525
- @client.send(method, *channels, &block)
276
+ begin
277
+ original, @client = @client, SubscribedClient.new(@client)
278
+ if timeout > 0
279
+ @client.send(method, timeout, *channels, &block)
280
+ else
281
+ @client.send(method, *channels, &block)
3526
282
  end
3527
283
  ensure
3528
284
  @client = original
3529
285
  end
3530
286
  end
3531
-
3532
- def _xread(args, keys, ids, blocking_timeout_msec)
3533
- keys = keys.is_a?(Array) ? keys : [keys]
3534
- ids = ids.is_a?(Array) ? ids : [ids]
3535
- args << 'STREAMS'
3536
- args.concat(keys)
3537
- args.concat(ids)
3538
-
3539
- synchronize do |client|
3540
- if blocking_timeout_msec.nil?
3541
- client.call(args, &HashifyStreams)
3542
- elsif blocking_timeout_msec.to_f.zero?
3543
- client.call_without_timeout(args, &HashifyStreams)
3544
- else
3545
- timeout = client.timeout.to_f + blocking_timeout_msec.to_f / 1000.0
3546
- client.call_with_timeout(args, timeout, &HashifyStreams)
3547
- end
3548
- end
3549
- end
3550
287
  end
3551
288
 
3552
- require_relative "redis/version"
3553
- require_relative "redis/connection"
3554
- require_relative "redis/client"
3555
- require_relative "redis/cluster"
3556
- require_relative "redis/pipeline"
3557
- require_relative "redis/subscribe"
289
+ require "redis/version"
290
+ require "redis/connection"
291
+ require "redis/client"
292
+ require "redis/cluster"
293
+ require "redis/pipeline"
294
+ require "redis/subscribe"