redis 4.6.0 → 5.0.6

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 +70 -1
  3. data/README.md +75 -146
  4. data/lib/redis/client.rb +77 -616
  5. data/lib/redis/commands/bitmaps.rb +4 -1
  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 +9 -6
  10. data/lib/redis/commands/hyper_log_log.rb +1 -1
  11. data/lib/redis/commands/keys.rb +53 -27
  12. data/lib/redis/commands/lists.rb +19 -23
  13. data/lib/redis/commands/pubsub.rb +7 -25
  14. data/lib/redis/commands/server.rb +15 -15
  15. data/lib/redis/commands/sets.rb +43 -36
  16. data/lib/redis/commands/sorted_sets.rb +27 -13
  17. data/lib/redis/commands/streams.rb +39 -19
  18. data/lib/redis/commands/strings.rb +18 -17
  19. data/lib/redis/commands/transactions.rb +26 -3
  20. data/lib/redis/commands.rb +4 -9
  21. data/lib/redis/distributed.rb +100 -67
  22. data/lib/redis/errors.rb +15 -41
  23. data/lib/redis/hash_ring.rb +26 -26
  24. data/lib/redis/pipeline.rb +56 -203
  25. data/lib/redis/subscribe.rb +23 -15
  26. data/lib/redis/version.rb +1 -1
  27. data/lib/redis.rb +90 -178
  28. metadata +9 -53
  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 -37
  35. data/lib/redis/cluster/option.rb +0 -93
  36. data/lib/redis/cluster/slot.rb +0 -86
  37. data/lib/redis/cluster/slot_loader.rb +0 -49
  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 -431
  43. data/lib/redis/connection/synchrony.rb +0 -148
  44. data/lib/redis/connection.rb +0 -11
@@ -15,40 +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
- send_command([:sadd, key, member]) do |reply|
24
- if member.is_a? Array
25
- # Variadic: return integer
26
- reply
27
- else
28
- # Single argument: return boolean
29
- Boolify.call(reply)
30
- end
31
- end
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))
22
+ end
23
+
24
+ # Add one or more members to a set.
25
+ #
26
+ # @param [String] key
27
+ # @param [String, Array<String>] member one member, or array of members
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)
32
32
  end
33
33
 
34
34
  # Remove one or more members from a set.
35
35
  #
36
36
  # @param [String] key
37
37
  # @param [String, Array<String>] member one member, or array of members
38
- # @return [Boolean, Integer] `Boolean` when a single member is specified,
39
- # holding whether or not removing the member succeeded, or `Integer` when an
40
- # array of members is specified, holding the number of members that were
41
- # successfully removed
42
- def srem(key, member)
43
- send_command([:srem, key, member]) do |reply|
44
- if member.is_a? Array
45
- # Variadic: return integer
46
- reply
47
- else
48
- # Single argument: return boolean
49
- Boolify.call(reply)
50
- end
51
- end
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))
42
+ end
43
+
44
+ # Remove one or more members from a set.
45
+ #
46
+ # @param [String] key
47
+ # @param [String, Array<String>] member one member, or array of members
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)
52
52
  end
53
53
 
54
54
  # Remove and return one or more random member from a set.
@@ -60,7 +60,7 @@ class Redis
60
60
  if count.nil?
61
61
  send_command([:spop, key])
62
62
  else
63
- send_command([:spop, key, count])
63
+ send_command([:spop, key, Integer(count)])
64
64
  end
65
65
  end
66
66
 
@@ -102,7 +102,8 @@ class Redis
102
102
  # @param [String, Array<String>] members
103
103
  # @return [Array<Boolean>]
104
104
  def smismember(key, *members)
105
- send_command([:smismember, key, *members]) do |reply|
105
+ members.flatten!(1)
106
+ send_command([:smismember, key].concat(members)) do |reply|
106
107
  reply.map(&Boolify)
107
108
  end
108
109
  end
@@ -120,7 +121,8 @@ class Redis
120
121
  # @param [String, Array<String>] keys keys pointing to sets to subtract
121
122
  # @return [Array<String>] members in the difference
122
123
  def sdiff(*keys)
123
- send_command([:sdiff, *keys])
124
+ keys.flatten!(1)
125
+ send_command([:sdiff].concat(keys))
124
126
  end
125
127
 
126
128
  # Subtract multiple sets and store the resulting set in a key.
@@ -129,7 +131,8 @@ class Redis
129
131
  # @param [String, Array<String>] keys keys pointing to sets to subtract
130
132
  # @return [Integer] number of elements in the resulting set
131
133
  def sdiffstore(destination, *keys)
132
- send_command([:sdiffstore, destination, *keys])
134
+ keys.flatten!(1)
135
+ send_command([:sdiffstore, destination].concat(keys))
133
136
  end
134
137
 
135
138
  # Intersect multiple sets.
@@ -137,7 +140,8 @@ class Redis
137
140
  # @param [String, Array<String>] keys keys pointing to sets to intersect
138
141
  # @return [Array<String>] members in the intersection
139
142
  def sinter(*keys)
140
- send_command([:sinter, *keys])
143
+ keys.flatten!(1)
144
+ send_command([:sinter].concat(keys))
141
145
  end
142
146
 
143
147
  # Intersect multiple sets and store the resulting set in a key.
@@ -146,7 +150,8 @@ class Redis
146
150
  # @param [String, Array<String>] keys keys pointing to sets to intersect
147
151
  # @return [Integer] number of elements in the resulting set
148
152
  def sinterstore(destination, *keys)
149
- send_command([:sinterstore, destination, *keys])
153
+ keys.flatten!(1)
154
+ send_command([:sinterstore, destination].concat(keys))
150
155
  end
151
156
 
152
157
  # Add multiple sets.
@@ -154,7 +159,8 @@ class Redis
154
159
  # @param [String, Array<String>] keys keys pointing to sets to unify
155
160
  # @return [Array<String>] members in the union
156
161
  def sunion(*keys)
157
- send_command([:sunion, *keys])
162
+ keys.flatten!(1)
163
+ send_command([:sunion].concat(keys))
158
164
  end
159
165
 
160
166
  # Add multiple sets and store the resulting set in a key.
@@ -163,7 +169,8 @@ class Redis
163
169
  # @param [String, Array<String>] keys keys pointing to sets to unify
164
170
  # @return [Integer] number of elements in the resulting set
165
171
  def sunionstore(destination, *keys)
166
- send_command([:sunionstore, destination, *keys])
172
+ keys.flatten!(1)
173
+ send_command([:sunionstore, destination].concat(keys))
167
174
  end
168
175
 
169
176
  # Scan a set
@@ -60,8 +60,11 @@ class Redis
60
60
  command << "INCR" if incr
61
61
 
62
62
  if args.size == 1 && args[0].is_a?(Array)
63
+ members_to_add = args[0]
64
+ return 0 if members_to_add.empty?
65
+
63
66
  # Variadic: return float if INCR, integer if !INCR
64
- send_command(command + args[0], &(incr ? Floatify : nil))
67
+ send_command(command + members_to_add, &(incr ? Floatify : nil))
65
68
  elsif args.size == 2
66
69
  # Single pair: return float if INCR, boolean if !INCR
67
70
  send_command(command + args, &(incr ? Floatify : Boolify))
@@ -102,6 +105,11 @@ class Redis
102
105
  # - `Integer` when an array of pairs is specified, holding the number of
103
106
  # members that were removed to the sorted set
104
107
  def zrem(key, member)
108
+ if member.is_a?(Array)
109
+ members_to_remove = member
110
+ return 0 if members_to_remove.empty?
111
+ end
112
+
105
113
  send_command([:zrem, key, member]) do |reply|
106
114
  if member.is_a? Array
107
115
  # Variadic: return integer
@@ -128,7 +136,9 @@ class Redis
128
136
  # @return [Array<String, Float>] element and score pair if count is not specified
129
137
  # @return [Array<Array<String, Float>>] list of popped elements and scores
130
138
  def zpopmax(key, count = nil)
131
- send_command([:zpopmax, key, count].compact) do |members|
139
+ command = [:zpopmax, key]
140
+ command << Integer(count) if count
141
+ send_command(command) do |members|
132
142
  members = FloatifyPairs.call(members)
133
143
  count.to_i > 1 ? members : members.first
134
144
  end
@@ -149,7 +159,9 @@ class Redis
149
159
  # @return [Array<String, Float>] element and score pair if count is not specified
150
160
  # @return [Array<Array<String, Float>>] list of popped elements and scores
151
161
  def zpopmin(key, count = nil)
152
- send_command([:zpopmin, key, count].compact) do |members|
162
+ command = [:zpopmin, key]
163
+ command << Integer(count) if count
164
+ send_command(command) do |members|
153
165
  members = FloatifyPairs.call(members)
154
166
  count.to_i > 1 ? members : members.first
155
167
  end
@@ -253,7 +265,7 @@ class Redis
253
265
  end
254
266
 
255
267
  args = [:zrandmember, key]
256
- args << count if count
268
+ args << Integer(count) if count
257
269
 
258
270
  if with_scores
259
271
  args << "WITHSCORES"
@@ -305,7 +317,7 @@ class Redis
305
317
 
306
318
  if limit
307
319
  args << "LIMIT"
308
- args.concat(limit)
320
+ args.concat(limit.map { |l| Integer(l) })
309
321
  end
310
322
 
311
323
  if with_scores
@@ -346,7 +358,7 @@ class Redis
346
358
 
347
359
  if limit
348
360
  args << "LIMIT"
349
- args.concat(limit)
361
+ args.concat(limit.map { |l| Integer(l) })
350
362
  end
351
363
 
352
364
  send_command(args)
@@ -364,7 +376,7 @@ class Redis
364
376
  #
365
377
  # @see #zrange
366
378
  def zrevrange(key, start, stop, withscores: false, with_scores: withscores)
367
- args = [:zrevrange, key, start, stop]
379
+ args = [:zrevrange, key, Integer(start), Integer(stop)]
368
380
 
369
381
  if with_scores
370
382
  args << "WITHSCORES"
@@ -458,7 +470,7 @@ class Redis
458
470
 
459
471
  if limit
460
472
  args << "LIMIT"
461
- args.concat(limit)
473
+ args.concat(limit.map { |l| Integer(l) })
462
474
  end
463
475
 
464
476
  send_command(args)
@@ -480,7 +492,7 @@ class Redis
480
492
 
481
493
  if limit
482
494
  args << "LIMIT"
483
- args.concat(limit)
495
+ args.concat(limit.map { |l| Integer(l) })
484
496
  end
485
497
 
486
498
  send_command(args)
@@ -523,7 +535,7 @@ class Redis
523
535
 
524
536
  if limit
525
537
  args << "LIMIT"
526
- args.concat(limit)
538
+ args.concat(limit.map { |l| Integer(l) })
527
539
  end
528
540
 
529
541
  send_command(args, &block)
@@ -553,7 +565,7 @@ class Redis
553
565
 
554
566
  if limit
555
567
  args << "LIMIT"
556
- args.concat(limit)
568
+ args.concat(limit.map { |l| Integer(l) })
557
569
  end
558
570
 
559
571
  send_command(args, &block)
@@ -770,7 +782,8 @@ class Redis
770
782
  private
771
783
 
772
784
  def _zsets_operation(cmd, *keys, weights: nil, aggregate: nil, with_scores: false)
773
- command = [cmd, keys.size, *keys]
785
+ keys.flatten!(1)
786
+ command = [cmd, keys.size].concat(keys)
774
787
 
775
788
  if weights
776
789
  command << "WEIGHTS"
@@ -788,7 +801,8 @@ class Redis
788
801
  end
789
802
 
790
803
  def _zsets_operation_store(cmd, destination, keys, weights: nil, aggregate: nil)
791
- command = [cmd, destination, keys.size, *keys]
804
+ keys.flatten!(1)
805
+ command = [cmd, destination, keys.size].concat(keys)
792
806
 
793
807
  if weights
794
808
  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,7 +34,7 @@ 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
@@ -46,17 +43,19 @@ class Redis
46
43
  # @option opts [String] :id the entry id, default value is `*`, it means auto generation
47
44
  # @option opts [Integer] :maxlen max length of entries
48
45
  # @option opts [Boolean] :approximate whether to add `~` modifier of maxlen or not
46
+ # @option opts [Boolean] :nomkstream whether to add NOMKSTREAM, default is not to add
49
47
  #
50
48
  # @return [String] the entry id
51
- def xadd(key, entry, approximate: nil, maxlen: nil, id: '*')
49
+ def xadd(key, entry, approximate: nil, maxlen: nil, nomkstream: nil, id: '*')
52
50
  args = [:xadd, key]
51
+ args << 'NOMKSTREAM' if nomkstream
53
52
  if maxlen
54
53
  args << "MAXLEN"
55
54
  args << "~" if approximate
56
55
  args << maxlen
57
56
  end
58
57
  args << id
59
- args.concat(entry.to_a.flatten)
58
+ args.concat(entry.flatten)
60
59
  send_command(args)
61
60
  end
62
61
 
@@ -66,14 +65,30 @@ class Redis
66
65
  # redis.xtrim('mystream', 1000)
67
66
  # @example With options
68
67
  # 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
68
+ # @example With strategy
69
+ # redis.xtrim('mystream', '1-0', strategy: 'MINID')
70
+ #
71
+ # @overload xtrim(key, maxlen, strategy: 'MAXLEN', approximate: true)
72
+ # @param key [String] the stream key
73
+ # @param maxlen [Integer] max length of entries
74
+ # @param strategy [String] the limit strategy, must be MAXLEN
75
+ # @param approximate [Boolean] whether to add `~` modifier of maxlen or not
76
+ # @param limit [Integer] maximum count of entries to be evicted
77
+ # @overload xtrim(key, minid, strategy: 'MINID', approximate: true)
78
+ # @param key [String] the stream key
79
+ # @param minid [String] minimum id of entries
80
+ # @param strategy [String] the limit strategy, must be MINID
81
+ # @param approximate [Boolean] whether to add `~` modifier of minid or not
82
+ # @param limit [Integer] maximum count of entries to be evicted
73
83
  #
74
84
  # @return [Integer] the number of entries actually deleted
75
- def xtrim(key, maxlen, approximate: false)
76
- args = [:xtrim, key, 'MAXLEN', (approximate ? '~' : nil), maxlen].compact
85
+ def xtrim(key, len_or_id, strategy: 'MAXLEN', approximate: false, limit: nil)
86
+ strategy = strategy.to_s.upcase
87
+
88
+ args = [:xtrim, key, strategy]
89
+ args << '~' if approximate
90
+ args << len_or_id
91
+ args.concat(['LIMIT', limit]) if limit
77
92
  send_command(args)
78
93
  end
79
94
 
@@ -113,7 +128,7 @@ class Redis
113
128
  def xrange(key, start = '-', range_end = '+', count: nil)
114
129
  args = [:xrange, key, start, range_end]
115
130
  args.concat(['COUNT', count]) if count
116
- synchronize { |client| client.call(args, &HashifyStreamEntries) }
131
+ send_command(args, &HashifyStreamEntries)
117
132
  end
118
133
 
119
134
  # Fetches entries of the stream in descending order.
@@ -334,6 +349,8 @@ class Redis
334
349
  # redis.xpending('mystream', 'mygroup')
335
350
  # @example With range options
336
351
  # redis.xpending('mystream', 'mygroup', '-', '+', 10)
352
+ # @example With range and idle time options
353
+ # redis.xpending('mystream', 'mygroup', '-', '+', 10, idle: 9000)
337
354
  # @example With range and consumer options
338
355
  # redis.xpending('mystream', 'mygroup', '-', '+', 10, 'consumer1')
339
356
  #
@@ -344,10 +361,13 @@ class Redis
344
361
  # @param count [Integer] count the number of entries as limit
345
362
  # @param consumer [String] the consumer name
346
363
  #
364
+ # @option opts [Integer] :idle pending message minimum idle time in milliseconds
365
+ #
347
366
  # @return [Hash] the summary of pending entries
348
367
  # @return [Array<Hash>] the pending entries details if options were specified
349
- def xpending(key, group, *args)
368
+ def xpending(key, group, *args, idle: nil)
350
369
  command_args = [:xpending, key, group]
370
+ command_args << 'IDLE' << Integer(idle) if idle
351
371
  case args.size
352
372
  when 0, 3, 4
353
373
  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)
@@ -3,6 +3,31 @@
3
3
  class Redis
4
4
  module Commands
5
5
  module Transactions
6
+ # Mark the start of a transaction block.
7
+ #
8
+ # @example With a block
9
+ # redis.multi do |multi|
10
+ # multi.set("key", "value")
11
+ # multi.incr("counter")
12
+ # end # => ["OK", 6]
13
+ #
14
+ # @yield [multi] the commands that are called inside this block are cached
15
+ # and written to the server upon returning from it
16
+ # @yieldparam [Redis] multi `self`
17
+ #
18
+ # @return [Array<...>]
19
+ # - an array with replies
20
+ #
21
+ # @see #watch
22
+ # @see #unwatch
23
+ def multi
24
+ synchronize do |client|
25
+ client.multi do |raw_transaction|
26
+ yield MultiConnection.new(raw_transaction)
27
+ end
28
+ end
29
+ end
30
+
6
31
  # Watch the given keys to determine execution of the MULTI/EXEC block.
7
32
  #
8
33
  # Using a block is optional, but is necessary for thread-safety.
@@ -35,7 +60,7 @@ class Redis
35
60
  # @see #multi
36
61
  def watch(*keys)
37
62
  synchronize do |client|
38
- res = client.call([:watch, *keys])
63
+ res = client.call_v([:watch] + keys)
39
64
 
40
65
  if block_given?
41
66
  begin
@@ -78,8 +103,6 @@ class Redis
78
103
 
79
104
  # Discard all commands issued after MULTI.
80
105
  #
81
- # Only call this method when `#multi` was called **without** a block.
82
- #
83
106
  # @return [String] `"OK"`
84
107
  #
85
108
  # @see #multi
@@ -40,14 +40,7 @@ class Redis
40
40
  # where the method call will return nil. Propagate the nil instead of falsely
41
41
  # returning false.
42
42
  Boolify = lambda { |value|
43
- case value
44
- when 1
45
- true
46
- when 0
47
- false
48
- else
49
- value
50
- end
43
+ value != 0 unless value.nil?
51
44
  }
52
45
 
53
46
  BoolifySet = lambda { |value|
@@ -126,7 +119,9 @@ class Redis
126
119
  HashifyStreamAutoclaim = lambda { |reply|
127
120
  {
128
121
  'next' => reply[0],
129
- 'entries' => reply[1].map { |entry| [entry[0], entry[1].each_slice(2).to_h] }
122
+ 'entries' => reply[1].compact.map do |entry, values|
123
+ [entry, values.each_slice(2)&.to_h]
124
+ end
130
125
  }
131
126
  }
132
127