redis 4.8.1 → 5.4.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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +82 -0
  3. data/README.md +125 -162
  4. data/lib/redis/client.rb +82 -616
  5. data/lib/redis/commands/bitmaps.rb +14 -4
  6. data/lib/redis/commands/cluster.rb +1 -18
  7. data/lib/redis/commands/connection.rb +5 -10
  8. data/lib/redis/commands/geo.rb +3 -3
  9. data/lib/redis/commands/hashes.rb +13 -6
  10. data/lib/redis/commands/hyper_log_log.rb +1 -1
  11. data/lib/redis/commands/keys.rb +27 -23
  12. data/lib/redis/commands/lists.rb +74 -25
  13. data/lib/redis/commands/pubsub.rb +34 -25
  14. data/lib/redis/commands/server.rb +15 -15
  15. data/lib/redis/commands/sets.rb +35 -40
  16. data/lib/redis/commands/sorted_sets.rb +128 -18
  17. data/lib/redis/commands/streams.rb +48 -21
  18. data/lib/redis/commands/strings.rb +18 -17
  19. data/lib/redis/commands/transactions.rb +7 -31
  20. data/lib/redis/commands.rb +11 -12
  21. data/lib/redis/distributed.rb +136 -72
  22. data/lib/redis/errors.rb +15 -50
  23. data/lib/redis/hash_ring.rb +26 -26
  24. data/lib/redis/pipeline.rb +47 -222
  25. data/lib/redis/subscribe.rb +50 -14
  26. data/lib/redis/version.rb +1 -1
  27. data/lib/redis.rb +77 -184
  28. metadata +10 -57
  29. data/lib/redis/cluster/command.rb +0 -79
  30. data/lib/redis/cluster/command_loader.rb +0 -33
  31. data/lib/redis/cluster/key_slot_converter.rb +0 -72
  32. data/lib/redis/cluster/node.rb +0 -120
  33. data/lib/redis/cluster/node_key.rb +0 -31
  34. data/lib/redis/cluster/node_loader.rb +0 -34
  35. data/lib/redis/cluster/option.rb +0 -100
  36. data/lib/redis/cluster/slot.rb +0 -86
  37. data/lib/redis/cluster/slot_loader.rb +0 -46
  38. data/lib/redis/cluster.rb +0 -315
  39. data/lib/redis/connection/command_helper.rb +0 -41
  40. data/lib/redis/connection/hiredis.rb +0 -68
  41. data/lib/redis/connection/registry.rb +0 -13
  42. data/lib/redis/connection/ruby.rb +0 -437
  43. data/lib/redis/connection/synchrony.rb +0 -148
  44. data/lib/redis/connection.rb +0 -11
@@ -15,56 +15,40 @@ class Redis
15
15
  #
16
16
  # @param [String] key
17
17
  # @param [String, Array<String>] member one member, or array of members
18
- # @return [Boolean, Integer] `Boolean` when a single member is specified,
19
- # holding whether or not adding the member succeeded, or `Integer` when an
20
- # array of members is specified, holding the number of members that were
21
- # successfully added
22
- def sadd(key, member)
23
- block = if Redis.sadd_returns_boolean && !member.is_a?(Array)
24
- ::Redis.deprecate!(
25
- "Redis#sadd will always return an Integer in Redis 5.0.0. Use Redis#sadd? instead." \
26
- "(called from: #{caller(1, 1).first})"
27
- )
28
- Boolify
29
- end
30
- send_command([:sadd, key, member], &block)
18
+ # @return [Integer] The number of members that were successfully added
19
+ def sadd(key, *members)
20
+ members.flatten!(1)
21
+ send_command([:sadd, key].concat(members))
31
22
  end
32
23
 
33
24
  # Add one or more members to a set.
34
25
  #
35
26
  # @param [String] key
36
27
  # @param [String, Array<String>] member one member, or array of members
37
- # @return [Boolean] Whether or not at least one member was added.
38
- def sadd?(key, member)
39
- send_command([:sadd, key, member], &Boolify)
28
+ # @return [Boolean] Wether at least one member was successfully added.
29
+ def sadd?(key, *members)
30
+ members.flatten!(1)
31
+ send_command([:sadd, key].concat(members), &Boolify)
40
32
  end
41
33
 
42
34
  # Remove one or more members from a set.
43
35
  #
44
36
  # @param [String] key
45
37
  # @param [String, Array<String>] member one member, or array of members
46
- # @return [Boolean, Integer] `Boolean` when a single member is specified,
47
- # holding whether or not removing the member succeeded, or `Integer` when an
48
- # array of members is specified, holding the number of members that were
49
- # successfully removed
50
- def srem(key, member)
51
- block = if Redis.sadd_returns_boolean && !member.is_a?(Array)
52
- ::Redis.deprecate!(
53
- "Redis#srem will always return an Integer in Redis 5.0.0. Use Redis#srem? instead." \
54
- "(called from: #{caller(1, 1).first})"
55
- )
56
- Boolify
57
- end
58
- send_command([:srem, key, member], &block)
38
+ # @return [Integer] The number of members that were successfully removed
39
+ def srem(key, *members)
40
+ members.flatten!(1)
41
+ send_command([:srem, key].concat(members))
59
42
  end
60
43
 
61
44
  # Remove one or more members from a set.
62
45
  #
63
46
  # @param [String] key
64
47
  # @param [String, Array<String>] member one member, or array of members
65
- # @return [Boolean] `Boolean` Whether or not a member was removed.
66
- def srem?(key, member)
67
- send_command([:srem, key, member], &Boolify)
48
+ # @return [Boolean] Wether at least one member was successfully removed.
49
+ def srem?(key, *members)
50
+ members.flatten!(1)
51
+ send_command([:srem, key].concat(members), &Boolify)
68
52
  end
69
53
 
70
54
  # Remove and return one or more random member from a set.
@@ -76,7 +60,7 @@ class Redis
76
60
  if count.nil?
77
61
  send_command([:spop, key])
78
62
  else
79
- send_command([:spop, key, count])
63
+ send_command([:spop, key, Integer(count)])
80
64
  end
81
65
  end
82
66
 
@@ -118,7 +102,8 @@ class Redis
118
102
  # @param [String, Array<String>] members
119
103
  # @return [Array<Boolean>]
120
104
  def smismember(key, *members)
121
- send_command([:smismember, key, *members]) do |reply|
105
+ members.flatten!(1)
106
+ send_command([:smismember, key].concat(members)) do |reply|
122
107
  reply.map(&Boolify)
123
108
  end
124
109
  end
@@ -136,7 +121,8 @@ class Redis
136
121
  # @param [String, Array<String>] keys keys pointing to sets to subtract
137
122
  # @return [Array<String>] members in the difference
138
123
  def sdiff(*keys)
139
- send_command([:sdiff, *keys])
124
+ keys.flatten!(1)
125
+ send_command([:sdiff].concat(keys))
140
126
  end
141
127
 
142
128
  # Subtract multiple sets and store the resulting set in a key.
@@ -145,7 +131,8 @@ class Redis
145
131
  # @param [String, Array<String>] keys keys pointing to sets to subtract
146
132
  # @return [Integer] number of elements in the resulting set
147
133
  def sdiffstore(destination, *keys)
148
- send_command([:sdiffstore, destination, *keys])
134
+ keys.flatten!(1)
135
+ send_command([:sdiffstore, destination].concat(keys))
149
136
  end
150
137
 
151
138
  # Intersect multiple sets.
@@ -153,7 +140,8 @@ class Redis
153
140
  # @param [String, Array<String>] keys keys pointing to sets to intersect
154
141
  # @return [Array<String>] members in the intersection
155
142
  def sinter(*keys)
156
- send_command([:sinter, *keys])
143
+ keys.flatten!(1)
144
+ send_command([:sinter].concat(keys))
157
145
  end
158
146
 
159
147
  # Intersect multiple sets and store the resulting set in a key.
@@ -162,7 +150,8 @@ class Redis
162
150
  # @param [String, Array<String>] keys keys pointing to sets to intersect
163
151
  # @return [Integer] number of elements in the resulting set
164
152
  def sinterstore(destination, *keys)
165
- send_command([:sinterstore, destination, *keys])
153
+ keys.flatten!(1)
154
+ send_command([:sinterstore, destination].concat(keys))
166
155
  end
167
156
 
168
157
  # Add multiple sets.
@@ -170,7 +159,8 @@ class Redis
170
159
  # @param [String, Array<String>] keys keys pointing to sets to unify
171
160
  # @return [Array<String>] members in the union
172
161
  def sunion(*keys)
173
- send_command([:sunion, *keys])
162
+ keys.flatten!(1)
163
+ send_command([:sunion].concat(keys))
174
164
  end
175
165
 
176
166
  # Add multiple sets and store the resulting set in a key.
@@ -179,7 +169,8 @@ class Redis
179
169
  # @param [String, Array<String>] keys keys pointing to sets to unify
180
170
  # @return [Integer] number of elements in the resulting set
181
171
  def sunionstore(destination, *keys)
182
- send_command([:sunionstore, destination, *keys])
172
+ keys.flatten!(1)
173
+ send_command([:sunionstore, destination].concat(keys))
183
174
  end
184
175
 
185
176
  # Scan a set
@@ -193,6 +184,8 @@ class Redis
193
184
  # - `:count => Integer`: return count keys at most per iteration
194
185
  #
195
186
  # @return [String, Array<String>] the next cursor and all found members
187
+ #
188
+ # See the [Redis Server SSCAN documentation](https://redis.io/docs/latest/commands/sscan/) for further details
196
189
  def sscan(key, cursor, **options)
197
190
  _scan(:sscan, cursor, [key], **options)
198
191
  end
@@ -208,6 +201,8 @@ class Redis
208
201
  # - `:count => Integer`: return count keys at most per iteration
209
202
  #
210
203
  # @return [Enumerator] an enumerator for all keys in the set
204
+ #
205
+ # See the [Redis Server SSCAN documentation](https://redis.io/docs/latest/commands/sscan/) for further details
211
206
  def sscan_each(key, **options, &block)
212
207
  return to_enum(:sscan_each, key, **options) unless block_given?
213
208
 
@@ -136,7 +136,9 @@ class Redis
136
136
  # @return [Array<String, Float>] element and score pair if count is not specified
137
137
  # @return [Array<Array<String, Float>>] list of popped elements and scores
138
138
  def zpopmax(key, count = nil)
139
- send_command([:zpopmax, key, count].compact) do |members|
139
+ command = [:zpopmax, key]
140
+ command << Integer(count) if count
141
+ send_command(command) do |members|
140
142
  members = FloatifyPairs.call(members)
141
143
  count.to_i > 1 ? members : members.first
142
144
  end
@@ -157,12 +159,80 @@ class Redis
157
159
  # @return [Array<String, Float>] element and score pair if count is not specified
158
160
  # @return [Array<Array<String, Float>>] list of popped elements and scores
159
161
  def zpopmin(key, count = nil)
160
- send_command([:zpopmin, key, count].compact) do |members|
162
+ command = [:zpopmin, key]
163
+ command << Integer(count) if count
164
+ send_command(command) do |members|
161
165
  members = FloatifyPairs.call(members)
162
166
  count.to_i > 1 ? members : members.first
163
167
  end
164
168
  end
165
169
 
170
+ # Removes and returns up to count members with scores in the sorted set stored at key.
171
+ #
172
+ # @example Popping a member
173
+ # redis.bzmpop('zset')
174
+ # #=> ['zset', ['a', 1.0]]
175
+ # @example With count option
176
+ # redis.bzmpop('zset', count: 2)
177
+ # #=> ['zset', [['a', 1.0], ['b', 2.0]]
178
+ #
179
+ # @params timeout [Float] a float value specifying the maximum number of seconds to block) elapses.
180
+ # A timeout of zero can be used to block indefinitely.
181
+ # @params key [String, Array<String>] one or more keys with sorted sets
182
+ # @params modifier [String]
183
+ # - when `"MIN"` - the elements popped are those with lowest scores
184
+ # - when `"MAX"` - the elements popped are those with the highest scores
185
+ # @params count [Integer] a number of members to pop
186
+ #
187
+ # @return [Array<String, Array<String, Float>>] list of popped elements and scores
188
+ def bzmpop(timeout, *keys, modifier: "MIN", count: nil)
189
+ raise ArgumentError, "Pick either MIN or MAX" unless modifier == "MIN" || modifier == "MAX"
190
+
191
+ args = [:bzmpop, timeout, keys.size, *keys, modifier]
192
+ args << "COUNT" << Integer(count) if count
193
+
194
+ send_blocking_command(args, timeout) do |response|
195
+ response&.map do |entry|
196
+ case entry
197
+ when String then entry
198
+ when Array then entry.map { |pair| FloatifyPairs.call(pair) }.flatten(1)
199
+ end
200
+ end
201
+ end
202
+ end
203
+
204
+ # Removes and returns up to count members with scores in the sorted set stored at key.
205
+ #
206
+ # @example Popping a member
207
+ # redis.zmpop('zset')
208
+ # #=> ['zset', ['a', 1.0]]
209
+ # @example With count option
210
+ # redis.zmpop('zset', count: 2)
211
+ # #=> ['zset', [['a', 1.0], ['b', 2.0]]
212
+ #
213
+ # @params key [String, Array<String>] one or more keys with sorted sets
214
+ # @params modifier [String]
215
+ # - when `"MIN"` - the elements popped are those with lowest scores
216
+ # - when `"MAX"` - the elements popped are those with the highest scores
217
+ # @params count [Integer] a number of members to pop
218
+ #
219
+ # @return [Array<String, Array<String, Float>>] list of popped elements and scores
220
+ def zmpop(*keys, modifier: "MIN", count: nil)
221
+ raise ArgumentError, "Pick either MIN or MAX" unless modifier == "MIN" || modifier == "MAX"
222
+
223
+ args = [:zmpop, keys.size, *keys, modifier]
224
+ args << "COUNT" << Integer(count) if count
225
+
226
+ send_command(args) do |response|
227
+ response&.map do |entry|
228
+ case entry
229
+ when String then entry
230
+ when Array then entry.map { |pair| FloatifyPairs.call(pair) }.flatten(1)
231
+ end
232
+ end
233
+ end
234
+ end
235
+
166
236
  # Removes and returns up to count members with the highest scores in the sorted set stored at keys,
167
237
  # or block until one is available.
168
238
  #
@@ -261,7 +331,7 @@ class Redis
261
331
  end
262
332
 
263
333
  args = [:zrandmember, key]
264
- args << count if count
334
+ args << Integer(count) if count
265
335
 
266
336
  if with_scores
267
337
  args << "WITHSCORES"
@@ -313,7 +383,7 @@ class Redis
313
383
 
314
384
  if limit
315
385
  args << "LIMIT"
316
- args.concat(limit)
386
+ args.concat(limit.map { |l| Integer(l) })
317
387
  end
318
388
 
319
389
  if with_scores
@@ -354,7 +424,7 @@ class Redis
354
424
 
355
425
  if limit
356
426
  args << "LIMIT"
357
- args.concat(limit)
427
+ args.concat(limit.map { |l| Integer(l) })
358
428
  end
359
429
 
360
430
  send_command(args)
@@ -372,7 +442,7 @@ class Redis
372
442
  #
373
443
  # @see #zrange
374
444
  def zrevrange(key, start, stop, withscores: false, with_scores: withscores)
375
- args = [:zrevrange, key, start, stop]
445
+ args = [:zrevrange, key, Integer(start), Integer(stop)]
376
446
 
377
447
  if with_scores
378
448
  args << "WITHSCORES"
@@ -384,21 +454,55 @@ class Redis
384
454
 
385
455
  # Determine the index of a member in a sorted set.
386
456
  #
457
+ # @example Retrieve member rank
458
+ # redis.zrank("zset", "a")
459
+ # # => 3
460
+ # @example Retrieve member rank with their score
461
+ # redis.zrank("zset", "a", :with_score => true)
462
+ # # => [3, 32.0]
463
+ #
387
464
  # @param [String] key
388
465
  # @param [String] member
389
- # @return [Integer]
390
- def zrank(key, member)
391
- send_command([:zrank, key, member])
466
+ #
467
+ # @return [Integer, [Integer, Float]]
468
+ # - when `:with_score` is not specified, an Integer
469
+ # - when `:with_score` is specified, a `[rank, score]` pair
470
+ def zrank(key, member, withscore: false, with_score: withscore)
471
+ args = [:zrank, key, member]
472
+
473
+ if with_score
474
+ args << "WITHSCORE"
475
+ block = FloatifyPair
476
+ end
477
+
478
+ send_command(args, &block)
392
479
  end
393
480
 
394
481
  # Determine the index of a member in a sorted set, with scores ordered from
395
482
  # high to low.
396
483
  #
484
+ # @example Retrieve member rank
485
+ # redis.zrevrank("zset", "a")
486
+ # # => 3
487
+ # @example Retrieve member rank with their score
488
+ # redis.zrevrank("zset", "a", :with_score => true)
489
+ # # => [3, 32.0]
490
+ #
397
491
  # @param [String] key
398
492
  # @param [String] member
399
- # @return [Integer]
400
- def zrevrank(key, member)
401
- send_command([:zrevrank, key, member])
493
+ #
494
+ # @return [Integer, [Integer, Float]]
495
+ # - when `:with_score` is not specified, an Integer
496
+ # - when `:with_score` is specified, a `[rank, score]` pair
497
+ def zrevrank(key, member, withscore: false, with_score: withscore)
498
+ args = [:zrevrank, key, member]
499
+
500
+ if with_score
501
+ args << "WITHSCORE"
502
+ block = FloatifyPair
503
+ end
504
+
505
+ send_command(args, &block)
402
506
  end
403
507
 
404
508
  # Remove all members in a sorted set within the given indexes.
@@ -466,7 +570,7 @@ class Redis
466
570
 
467
571
  if limit
468
572
  args << "LIMIT"
469
- args.concat(limit)
573
+ args.concat(limit.map { |l| Integer(l) })
470
574
  end
471
575
 
472
576
  send_command(args)
@@ -488,7 +592,7 @@ class Redis
488
592
 
489
593
  if limit
490
594
  args << "LIMIT"
491
- args.concat(limit)
595
+ args.concat(limit.map { |l| Integer(l) })
492
596
  end
493
597
 
494
598
  send_command(args)
@@ -531,7 +635,7 @@ class Redis
531
635
 
532
636
  if limit
533
637
  args << "LIMIT"
534
- args.concat(limit)
638
+ args.concat(limit.map { |l| Integer(l) })
535
639
  end
536
640
 
537
641
  send_command(args, &block)
@@ -561,7 +665,7 @@ class Redis
561
665
 
562
666
  if limit
563
667
  args << "LIMIT"
564
- args.concat(limit)
668
+ args.concat(limit.map { |l| Integer(l) })
565
669
  end
566
670
 
567
671
  send_command(args, &block)
@@ -747,6 +851,8 @@ class Redis
747
851
  #
748
852
  # @return [String, Array<[String, Float]>] the next cursor and all found
749
853
  # members and scores
854
+ #
855
+ # See the [Redis Server ZSCAN documentation](https://redis.io/docs/latest/commands/zscan/) for further details
750
856
  def zscan(key, cursor, **options)
751
857
  _scan(:zscan, cursor, [key], **options) do |reply|
752
858
  [reply[0], FloatifyPairs.call(reply[1])]
@@ -764,6 +870,8 @@ class Redis
764
870
  # - `:count => Integer`: return count keys at most per iteration
765
871
  #
766
872
  # @return [Enumerator] an enumerator for all found scores and members
873
+ #
874
+ # See the [Redis Server ZSCAN documentation](https://redis.io/docs/latest/commands/zscan/) for further details
767
875
  def zscan_each(key, **options, &block)
768
876
  return to_enum(:zscan_each, key, **options) unless block_given?
769
877
 
@@ -778,7 +886,8 @@ class Redis
778
886
  private
779
887
 
780
888
  def _zsets_operation(cmd, *keys, weights: nil, aggregate: nil, with_scores: false)
781
- command = [cmd, keys.size, *keys]
889
+ keys.flatten!(1)
890
+ command = [cmd, keys.size].concat(keys)
782
891
 
783
892
  if weights
784
893
  command << "WEIGHTS"
@@ -796,7 +905,8 @@ class Redis
796
905
  end
797
906
 
798
907
  def _zsets_operation_store(cmd, destination, keys, weights: nil, aggregate: nil)
799
- command = [cmd, destination, keys.size, *keys]
908
+ keys.flatten!(1)
909
+ command = [cmd, destination, keys.size].concat(keys)
800
910
 
801
911
  if weights
802
912
  command << "WEIGHTS"
@@ -21,15 +21,12 @@ class Redis
21
21
  # @return [Array<Hash>] information of the consumers if subcommand is `consumers`
22
22
  def xinfo(subcommand, key, group = nil)
23
23
  args = [:xinfo, subcommand, key, group].compact
24
- synchronize do |client|
25
- client.call(args) do |reply|
26
- case subcommand.to_s.downcase
27
- when 'stream' then Hashify.call(reply)
28
- when 'groups', 'consumers' then reply.map { |arr| Hashify.call(arr) }
29
- else reply
30
- end
31
- end
24
+ block = case subcommand.to_s.downcase
25
+ when 'stream' then Hashify
26
+ when 'groups', 'consumers' then proc { |r| r.map(&Hashify) }
32
27
  end
28
+
29
+ send_command(args, &block)
33
30
  end
34
31
 
35
32
  # Add new entry to the stream.
@@ -37,26 +34,35 @@ class Redis
37
34
  # @example Without options
38
35
  # redis.xadd('mystream', f1: 'v1', f2: 'v2')
39
36
  # @example With options
40
- # redis.xadd('mystream', { f1: 'v1', f2: 'v2' }, id: '0-0', maxlen: 1000, approximate: true)
37
+ # redis.xadd('mystream', { f1: 'v1', f2: 'v2' }, id: '0-0', maxlen: 1000, approximate: true, nomkstream: true)
41
38
  #
42
39
  # @param key [String] the stream key
43
40
  # @param entry [Hash] one or multiple field-value pairs
44
41
  # @param opts [Hash] several options for `XADD` command
45
42
  #
46
43
  # @option opts [String] :id the entry id, default value is `*`, it means auto generation
47
- # @option opts [Integer] :maxlen max length of entries
48
- # @option opts [Boolean] :approximate whether to add `~` modifier of maxlen or not
44
+ # @option opts [Integer] :maxlen max length of entries to keep
45
+ # @option opts [Integer] :minid min id of entries to keep
46
+ # @option opts [Boolean] :approximate whether to add `~` modifier of maxlen/minid or not
47
+ # @option opts [Boolean] :nomkstream whether to add NOMKSTREAM, default is not to add
49
48
  #
50
49
  # @return [String] the entry id
51
- def xadd(key, entry, approximate: nil, maxlen: nil, id: '*')
50
+ def xadd(key, entry, approximate: nil, maxlen: nil, minid: nil, nomkstream: nil, id: '*')
52
51
  args = [:xadd, key]
52
+ args << 'NOMKSTREAM' if nomkstream
53
53
  if maxlen
54
+ raise ArgumentError, "can't supply both maxlen and minid" if minid
55
+
54
56
  args << "MAXLEN"
55
57
  args << "~" if approximate
56
58
  args << maxlen
59
+ elsif minid
60
+ args << "MINID"
61
+ args << "~" if approximate
62
+ args << minid
57
63
  end
58
64
  args << id
59
- args.concat(entry.to_a.flatten)
65
+ args.concat(entry.flatten)
60
66
  send_command(args)
61
67
  end
62
68
 
@@ -66,14 +72,30 @@ class Redis
66
72
  # redis.xtrim('mystream', 1000)
67
73
  # @example With options
68
74
  # redis.xtrim('mystream', 1000, approximate: true)
69
- #
70
- # @param key [String] the stream key
71
- # @param mexlen [Integer] max length of entries
72
- # @param approximate [Boolean] whether to add `~` modifier of maxlen or not
75
+ # @example With strategy
76
+ # redis.xtrim('mystream', '1-0', strategy: 'MINID')
77
+ #
78
+ # @overload xtrim(key, maxlen, strategy: 'MAXLEN', approximate: true)
79
+ # @param key [String] the stream key
80
+ # @param maxlen [Integer] max length of entries
81
+ # @param strategy [String] the limit strategy, must be MAXLEN
82
+ # @param approximate [Boolean] whether to add `~` modifier of maxlen or not
83
+ # @param limit [Integer] maximum count of entries to be evicted
84
+ # @overload xtrim(key, minid, strategy: 'MINID', approximate: true)
85
+ # @param key [String] the stream key
86
+ # @param minid [String] minimum id of entries
87
+ # @param strategy [String] the limit strategy, must be MINID
88
+ # @param approximate [Boolean] whether to add `~` modifier of minid or not
89
+ # @param limit [Integer] maximum count of entries to be evicted
73
90
  #
74
91
  # @return [Integer] the number of entries actually deleted
75
- def xtrim(key, maxlen, approximate: false)
76
- args = [:xtrim, key, 'MAXLEN', (approximate ? '~' : nil), maxlen].compact
92
+ def xtrim(key, len_or_id, strategy: 'MAXLEN', approximate: false, limit: nil)
93
+ strategy = strategy.to_s.upcase
94
+
95
+ args = [:xtrim, key, strategy]
96
+ args << '~' if approximate
97
+ args << len_or_id
98
+ args.concat(['LIMIT', limit]) if limit
77
99
  send_command(args)
78
100
  end
79
101
 
@@ -113,7 +135,7 @@ class Redis
113
135
  def xrange(key, start = '-', range_end = '+', count: nil)
114
136
  args = [:xrange, key, start, range_end]
115
137
  args.concat(['COUNT', count]) if count
116
- synchronize { |client| client.call(args, &HashifyStreamEntries) }
138
+ send_command(args, &HashifyStreamEntries)
117
139
  end
118
140
 
119
141
  # Fetches entries of the stream in descending order.
@@ -334,6 +356,8 @@ class Redis
334
356
  # redis.xpending('mystream', 'mygroup')
335
357
  # @example With range options
336
358
  # redis.xpending('mystream', 'mygroup', '-', '+', 10)
359
+ # @example With range and idle time options
360
+ # redis.xpending('mystream', 'mygroup', '-', '+', 10, idle: 9000)
337
361
  # @example With range and consumer options
338
362
  # redis.xpending('mystream', 'mygroup', '-', '+', 10, 'consumer1')
339
363
  #
@@ -344,10 +368,13 @@ class Redis
344
368
  # @param count [Integer] count the number of entries as limit
345
369
  # @param consumer [String] the consumer name
346
370
  #
371
+ # @option opts [Integer] :idle pending message minimum idle time in milliseconds
372
+ #
347
373
  # @return [Hash] the summary of pending entries
348
374
  # @return [Array<Hash>] the pending entries details if options were specified
349
- def xpending(key, group, *args)
375
+ def xpending(key, group, *args, idle: nil)
350
376
  command_args = [:xpending, key, group]
377
+ command_args << 'IDLE' << Integer(idle) if idle
351
378
  case args.size
352
379
  when 0, 3, 4
353
380
  command_args.concat(args)
@@ -25,7 +25,7 @@ class Redis
25
25
  # @param [Integer] decrement
26
26
  # @return [Integer] value after decrementing it
27
27
  def decrby(key, decrement)
28
- send_command([:decrby, key, decrement])
28
+ send_command([:decrby, key, Integer(decrement)])
29
29
  end
30
30
 
31
31
  # Increment the integer value of a key by one.
@@ -50,7 +50,7 @@ class Redis
50
50
  # @param [Integer] increment
51
51
  # @return [Integer] value after incrementing it
52
52
  def incrby(key, increment)
53
- send_command([:incrby, key, increment])
53
+ send_command([:incrby, key, Integer(increment)])
54
54
  end
55
55
 
56
56
  # Increment the numeric value of a key by the given float number.
@@ -63,7 +63,7 @@ class Redis
63
63
  # @param [Float] increment
64
64
  # @return [Float] value after incrementing it
65
65
  def incrbyfloat(key, increment)
66
- send_command([:incrbyfloat, key, increment], &Floatify)
66
+ send_command([:incrbyfloat, key, Float(increment)], &Floatify)
67
67
  end
68
68
 
69
69
  # Set the string value of a key.
@@ -82,10 +82,10 @@ class Redis
82
82
  # @return [String, Boolean] `"OK"` or true, false if `:nx => true` or `:xx => true`
83
83
  def set(key, value, ex: nil, px: nil, exat: nil, pxat: nil, nx: nil, xx: nil, keepttl: nil, get: nil)
84
84
  args = [:set, key, value.to_s]
85
- args << "EX" << ex if ex
86
- args << "PX" << px if px
87
- args << "EXAT" << exat if exat
88
- args << "PXAT" << pxat if pxat
85
+ args << "EX" << Integer(ex) if ex
86
+ args << "PX" << Integer(px) if px
87
+ args << "EXAT" << Integer(exat) if exat
88
+ args << "PXAT" << Integer(pxat) if pxat
89
89
  args << "NX" if nx
90
90
  args << "XX" if xx
91
91
  args << "KEEPTTL" if keepttl
@@ -105,7 +105,7 @@ class Redis
105
105
  # @param [String] value
106
106
  # @return [String] `"OK"`
107
107
  def setex(key, ttl, value)
108
- send_command([:setex, key, ttl, value.to_s])
108
+ send_command([:setex, key, Integer(ttl), value.to_s])
109
109
  end
110
110
 
111
111
  # Set the time to live in milliseconds of a key.
@@ -115,7 +115,7 @@ class Redis
115
115
  # @param [String] value
116
116
  # @return [String] `"OK"`
117
117
  def psetex(key, ttl, value)
118
- send_command([:psetex, key, ttl, value.to_s])
118
+ send_command([:psetex, key, Integer(ttl), value.to_s])
119
119
  end
120
120
 
121
121
  # Set the value of a key, only if the key does not exist.
@@ -152,7 +152,7 @@ class Redis
152
152
  #
153
153
  # @see #mset
154
154
  def mapped_mset(hash)
155
- mset(hash.to_a.flatten)
155
+ mset(hash.flatten)
156
156
  end
157
157
 
158
158
  # Set one or more values, only if none of the keys exist.
@@ -180,7 +180,7 @@ class Redis
180
180
  #
181
181
  # @see #msetnx
182
182
  def mapped_msetnx(hash)
183
- msetnx(hash.to_a.flatten)
183
+ msetnx(hash.flatten)
184
184
  end
185
185
 
186
186
  # Get the value of a key.
@@ -202,6 +202,7 @@ class Redis
202
202
  #
203
203
  # @see #mapped_mget
204
204
  def mget(*keys, &blk)
205
+ keys.flatten!(1)
205
206
  send_command([:mget, *keys], &blk)
206
207
  end
207
208
 
@@ -232,7 +233,7 @@ class Redis
232
233
  # @param [String] value
233
234
  # @return [Integer] length of the string after it was modified
234
235
  def setrange(key, offset, value)
235
- send_command([:setrange, key, offset, value.to_s])
236
+ send_command([:setrange, key, Integer(offset), value.to_s])
236
237
  end
237
238
 
238
239
  # Get a substring of the string stored at a key.
@@ -243,7 +244,7 @@ class Redis
243
244
  # the end of the string
244
245
  # @return [Integer] `0` or `1`
245
246
  def getrange(key, start, stop)
246
- send_command([:getrange, key, start, stop])
247
+ send_command([:getrange, key, Integer(start), Integer(stop)])
247
248
  end
248
249
 
249
250
  # Append a value to a key.
@@ -291,10 +292,10 @@ class Redis
291
292
  # @return [String] The value of key, or nil when key does not exist.
292
293
  def getex(key, ex: nil, px: nil, exat: nil, pxat: nil, persist: false)
293
294
  args = [:getex, key]
294
- args << "EX" << ex if ex
295
- args << "PX" << px if px
296
- args << "EXAT" << exat if exat
297
- args << "PXAT" << pxat if pxat
295
+ args << "EX" << Integer(ex) if ex
296
+ args << "PX" << Integer(px) if px
297
+ args << "EXAT" << Integer(exat) if exat
298
+ args << "PXAT" << Integer(pxat) if pxat
298
299
  args << "PERSIST" if persist
299
300
 
300
301
  send_command(args)