redis 4.6.0 → 5.0.6

Sign up to get free protection for your applications and to get access to all the features.
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