redis 4.5.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.
@@ -0,0 +1,382 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Redis
4
+ module Commands
5
+ module Streams
6
+ # Returns the stream information each subcommand.
7
+ #
8
+ # @example stream
9
+ # redis.xinfo(:stream, 'mystream')
10
+ # @example groups
11
+ # redis.xinfo(:groups, 'mystream')
12
+ # @example consumers
13
+ # redis.xinfo(:consumers, 'mystream', 'mygroup')
14
+ #
15
+ # @param subcommand [String] e.g. `stream` `groups` `consumers`
16
+ # @param key [String] the stream key
17
+ # @param group [String] the consumer group name, required if subcommand is `consumers`
18
+ #
19
+ # @return [Hash] information of the stream if subcommand is `stream`
20
+ # @return [Array<Hash>] information of the consumer groups if subcommand is `groups`
21
+ # @return [Array<Hash>] information of the consumers if subcommand is `consumers`
22
+ def xinfo(subcommand, key, group = nil)
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
32
+ end
33
+ end
34
+
35
+ # Add new entry to the stream.
36
+ #
37
+ # @example Without options
38
+ # redis.xadd('mystream', f1: 'v1', f2: 'v2')
39
+ # @example With options
40
+ # redis.xadd('mystream', { f1: 'v1', f2: 'v2' }, id: '0-0', maxlen: 1000, approximate: true)
41
+ #
42
+ # @param key [String] the stream key
43
+ # @param entry [Hash] one or multiple field-value pairs
44
+ # @param opts [Hash] several options for `XADD` command
45
+ #
46
+ # @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
49
+ #
50
+ # @return [String] the entry id
51
+ def xadd(key, entry, approximate: nil, maxlen: nil, id: '*')
52
+ args = [:xadd, key]
53
+ if maxlen
54
+ args << "MAXLEN"
55
+ args << "~" if approximate
56
+ args << maxlen
57
+ end
58
+ args << id
59
+ args.concat(entry.to_a.flatten)
60
+ send_command(args)
61
+ end
62
+
63
+ # Trims older entries of the stream if needed.
64
+ #
65
+ # @example Without options
66
+ # redis.xtrim('mystream', 1000)
67
+ # @example With options
68
+ # 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
73
+ #
74
+ # @return [Integer] the number of entries actually deleted
75
+ def xtrim(key, maxlen, approximate: false)
76
+ args = [:xtrim, key, 'MAXLEN', (approximate ? '~' : nil), maxlen].compact
77
+ send_command(args)
78
+ end
79
+
80
+ # Delete entries by entry ids.
81
+ #
82
+ # @example With splatted entry ids
83
+ # redis.xdel('mystream', '0-1', '0-2')
84
+ # @example With arrayed entry ids
85
+ # redis.xdel('mystream', ['0-1', '0-2'])
86
+ #
87
+ # @param key [String] the stream key
88
+ # @param ids [Array<String>] one or multiple entry ids
89
+ #
90
+ # @return [Integer] the number of entries actually deleted
91
+ def xdel(key, *ids)
92
+ args = [:xdel, key].concat(ids.flatten)
93
+ send_command(args)
94
+ end
95
+
96
+ # Fetches entries of the stream in ascending order.
97
+ #
98
+ # @example Without options
99
+ # redis.xrange('mystream')
100
+ # @example With a specific start
101
+ # redis.xrange('mystream', '0-1')
102
+ # @example With a specific start and end
103
+ # redis.xrange('mystream', '0-1', '0-3')
104
+ # @example With count options
105
+ # redis.xrange('mystream', count: 10)
106
+ #
107
+ # @param key [String] the stream key
108
+ # @param start [String] first entry id of range, default value is `-`
109
+ # @param end [String] last entry id of range, default value is `+`
110
+ # @param count [Integer] the number of entries as limit
111
+ #
112
+ # @return [Array<Array<String, Hash>>] the ids and entries pairs
113
+ def xrange(key, start = '-', range_end = '+', count: nil)
114
+ args = [:xrange, key, start, range_end]
115
+ args.concat(['COUNT', count]) if count
116
+ synchronize { |client| client.call(args, &HashifyStreamEntries) }
117
+ end
118
+
119
+ # Fetches entries of the stream in descending order.
120
+ #
121
+ # @example Without options
122
+ # redis.xrevrange('mystream')
123
+ # @example With a specific end
124
+ # redis.xrevrange('mystream', '0-3')
125
+ # @example With a specific end and start
126
+ # redis.xrevrange('mystream', '0-3', '0-1')
127
+ # @example With count options
128
+ # redis.xrevrange('mystream', count: 10)
129
+ #
130
+ # @param key [String] the stream key
131
+ # @param end [String] first entry id of range, default value is `+`
132
+ # @param start [String] last entry id of range, default value is `-`
133
+ # @params count [Integer] the number of entries as limit
134
+ #
135
+ # @return [Array<Array<String, Hash>>] the ids and entries pairs
136
+ def xrevrange(key, range_end = '+', start = '-', count: nil)
137
+ args = [:xrevrange, key, range_end, start]
138
+ args.concat(['COUNT', count]) if count
139
+ send_command(args, &HashifyStreamEntries)
140
+ end
141
+
142
+ # Returns the number of entries inside a stream.
143
+ #
144
+ # @example With key
145
+ # redis.xlen('mystream')
146
+ #
147
+ # @param key [String] the stream key
148
+ #
149
+ # @return [Integer] the number of entries
150
+ def xlen(key)
151
+ send_command([:xlen, key])
152
+ end
153
+
154
+ # Fetches entries from one or multiple streams. Optionally blocking.
155
+ #
156
+ # @example With a key
157
+ # redis.xread('mystream', '0-0')
158
+ # @example With multiple keys
159
+ # redis.xread(%w[mystream1 mystream2], %w[0-0 0-0])
160
+ # @example With count option
161
+ # redis.xread('mystream', '0-0', count: 2)
162
+ # @example With block option
163
+ # redis.xread('mystream', '$', block: 1000)
164
+ #
165
+ # @param keys [Array<String>] one or multiple stream keys
166
+ # @param ids [Array<String>] one or multiple entry ids
167
+ # @param count [Integer] the number of entries as limit per stream
168
+ # @param block [Integer] the number of milliseconds as blocking timeout
169
+ #
170
+ # @return [Hash{String => Hash{String => Hash}}] the entries
171
+ def xread(keys, ids, count: nil, block: nil)
172
+ args = [:xread]
173
+ args << 'COUNT' << count if count
174
+ args << 'BLOCK' << block.to_i if block
175
+ _xread(args, keys, ids, block)
176
+ end
177
+
178
+ # Manages the consumer group of the stream.
179
+ #
180
+ # @example With `create` subcommand
181
+ # redis.xgroup(:create, 'mystream', 'mygroup', '$')
182
+ # @example With `setid` subcommand
183
+ # redis.xgroup(:setid, 'mystream', 'mygroup', '$')
184
+ # @example With `destroy` subcommand
185
+ # redis.xgroup(:destroy, 'mystream', 'mygroup')
186
+ # @example With `delconsumer` subcommand
187
+ # redis.xgroup(:delconsumer, 'mystream', 'mygroup', 'consumer1')
188
+ #
189
+ # @param subcommand [String] `create` `setid` `destroy` `delconsumer`
190
+ # @param key [String] the stream key
191
+ # @param group [String] the consumer group name
192
+ # @param id_or_consumer [String]
193
+ # * the entry id or `$`, required if subcommand is `create` or `setid`
194
+ # * the consumer name, required if subcommand is `delconsumer`
195
+ # @param mkstream [Boolean] whether to create an empty stream automatically or not
196
+ #
197
+ # @return [String] `OK` if subcommand is `create` or `setid`
198
+ # @return [Integer] effected count if subcommand is `destroy` or `delconsumer`
199
+ def xgroup(subcommand, key, group, id_or_consumer = nil, mkstream: false)
200
+ args = [:xgroup, subcommand, key, group, id_or_consumer, (mkstream ? 'MKSTREAM' : nil)].compact
201
+ send_command(args)
202
+ end
203
+
204
+ # Fetches a subset of the entries from one or multiple streams related with the consumer group.
205
+ # Optionally blocking.
206
+ #
207
+ # @example With a key
208
+ # redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>')
209
+ # @example With multiple keys
210
+ # redis.xreadgroup('mygroup', 'consumer1', %w[mystream1 mystream2], %w[> >])
211
+ # @example With count option
212
+ # redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', count: 2)
213
+ # @example With block option
214
+ # redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', block: 1000)
215
+ # @example With noack option
216
+ # redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', noack: true)
217
+ #
218
+ # @param group [String] the consumer group name
219
+ # @param consumer [String] the consumer name
220
+ # @param keys [Array<String>] one or multiple stream keys
221
+ # @param ids [Array<String>] one or multiple entry ids
222
+ # @param opts [Hash] several options for `XREADGROUP` command
223
+ #
224
+ # @option opts [Integer] :count the number of entries as limit
225
+ # @option opts [Integer] :block the number of milliseconds as blocking timeout
226
+ # @option opts [Boolean] :noack whether message loss is acceptable or not
227
+ #
228
+ # @return [Hash{String => Hash{String => Hash}}] the entries
229
+ def xreadgroup(group, consumer, keys, ids, count: nil, block: nil, noack: nil)
230
+ args = [:xreadgroup, 'GROUP', group, consumer]
231
+ args << 'COUNT' << count if count
232
+ args << 'BLOCK' << block.to_i if block
233
+ args << 'NOACK' if noack
234
+ _xread(args, keys, ids, block)
235
+ end
236
+
237
+ # Removes one or multiple entries from the pending entries list of a stream consumer group.
238
+ #
239
+ # @example With a entry id
240
+ # redis.xack('mystream', 'mygroup', '1526569495631-0')
241
+ # @example With splatted entry ids
242
+ # redis.xack('mystream', 'mygroup', '0-1', '0-2')
243
+ # @example With arrayed entry ids
244
+ # redis.xack('mystream', 'mygroup', %w[0-1 0-2])
245
+ #
246
+ # @param key [String] the stream key
247
+ # @param group [String] the consumer group name
248
+ # @param ids [Array<String>] one or multiple entry ids
249
+ #
250
+ # @return [Integer] the number of entries successfully acknowledged
251
+ def xack(key, group, *ids)
252
+ args = [:xack, key, group].concat(ids.flatten)
253
+ send_command(args)
254
+ end
255
+
256
+ # Changes the ownership of a pending entry
257
+ #
258
+ # @example With splatted entry ids
259
+ # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-1', '0-2')
260
+ # @example With arrayed entry ids
261
+ # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2])
262
+ # @example With idle option
263
+ # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], idle: 1000)
264
+ # @example With time option
265
+ # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], time: 1542866959000)
266
+ # @example With retrycount option
267
+ # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], retrycount: 10)
268
+ # @example With force option
269
+ # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], force: true)
270
+ # @example With justid option
271
+ # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], justid: true)
272
+ #
273
+ # @param key [String] the stream key
274
+ # @param group [String] the consumer group name
275
+ # @param consumer [String] the consumer name
276
+ # @param min_idle_time [Integer] the number of milliseconds
277
+ # @param ids [Array<String>] one or multiple entry ids
278
+ # @param opts [Hash] several options for `XCLAIM` command
279
+ #
280
+ # @option opts [Integer] :idle the number of milliseconds as last time it was delivered of the entry
281
+ # @option opts [Integer] :time the number of milliseconds as a specific Unix Epoch time
282
+ # @option opts [Integer] :retrycount the number of retry counter
283
+ # @option opts [Boolean] :force whether to create the pending entry to the pending entries list or not
284
+ # @option opts [Boolean] :justid whether to fetch just an array of entry ids or not
285
+ #
286
+ # @return [Hash{String => Hash}] the entries successfully claimed
287
+ # @return [Array<String>] the entry ids successfully claimed if justid option is `true`
288
+ def xclaim(key, group, consumer, min_idle_time, *ids, **opts)
289
+ args = [:xclaim, key, group, consumer, min_idle_time].concat(ids.flatten)
290
+ args.concat(['IDLE', opts[:idle].to_i]) if opts[:idle]
291
+ args.concat(['TIME', opts[:time].to_i]) if opts[:time]
292
+ args.concat(['RETRYCOUNT', opts[:retrycount]]) if opts[:retrycount]
293
+ args << 'FORCE' if opts[:force]
294
+ args << 'JUSTID' if opts[:justid]
295
+ blk = opts[:justid] ? Noop : HashifyStreamEntries
296
+ send_command(args, &blk)
297
+ end
298
+
299
+ # Transfers ownership of pending stream entries that match the specified criteria.
300
+ #
301
+ # @example Claim next pending message stuck > 5 minutes and mark as retry
302
+ # redis.xautoclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0')
303
+ # @example Claim 50 next pending messages stuck > 5 minutes and mark as retry
304
+ # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0', count: 50)
305
+ # @example Claim next pending message stuck > 5 minutes and don't mark as retry
306
+ # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0', justid: true)
307
+ # @example Claim next pending message after this id stuck > 5 minutes and mark as retry
308
+ # redis.xautoclaim('mystream', 'mygroup', 'consumer1', 3600000, '1641321233-0')
309
+ #
310
+ # @param key [String] the stream key
311
+ # @param group [String] the consumer group name
312
+ # @param consumer [String] the consumer name
313
+ # @param min_idle_time [Integer] the number of milliseconds
314
+ # @param start [String] entry id to start scanning from or 0-0 for everything
315
+ # @param count [Integer] number of messages to claim (default 1)
316
+ # @param justid [Boolean] whether to fetch just an array of entry ids or not.
317
+ # Does not increment retry count when true
318
+ #
319
+ # @return [Hash{String => Hash}] the entries successfully claimed
320
+ # @return [Array<String>] the entry ids successfully claimed if justid option is `true`
321
+ def xautoclaim(key, group, consumer, min_idle_time, start, count: nil, justid: false)
322
+ args = [:xautoclaim, key, group, consumer, min_idle_time, start]
323
+ if count
324
+ args << 'COUNT' << count.to_s
325
+ end
326
+ args << 'JUSTID' if justid
327
+ blk = justid ? HashifyStreamAutoclaimJustId : HashifyStreamAutoclaim
328
+ send_command(args, &blk)
329
+ end
330
+
331
+ # Fetches not acknowledging pending entries
332
+ #
333
+ # @example With key and group
334
+ # redis.xpending('mystream', 'mygroup')
335
+ # @example With range options
336
+ # redis.xpending('mystream', 'mygroup', '-', '+', 10)
337
+ # @example With range and consumer options
338
+ # redis.xpending('mystream', 'mygroup', '-', '+', 10, 'consumer1')
339
+ #
340
+ # @param key [String] the stream key
341
+ # @param group [String] the consumer group name
342
+ # @param start [String] start first entry id of range
343
+ # @param end [String] end last entry id of range
344
+ # @param count [Integer] count the number of entries as limit
345
+ # @param consumer [String] the consumer name
346
+ #
347
+ # @return [Hash] the summary of pending entries
348
+ # @return [Array<Hash>] the pending entries details if options were specified
349
+ def xpending(key, group, *args)
350
+ command_args = [:xpending, key, group]
351
+ case args.size
352
+ when 0, 3, 4
353
+ command_args.concat(args)
354
+ else
355
+ raise ArgumentError, "wrong number of arguments (given #{args.size + 2}, expected 2, 5 or 6)"
356
+ end
357
+
358
+ summary_needed = args.empty?
359
+ blk = summary_needed ? HashifyStreamPendings : HashifyStreamPendingDetails
360
+ send_command(command_args, &blk)
361
+ end
362
+
363
+ private
364
+
365
+ def _xread(args, keys, ids, blocking_timeout_msec)
366
+ keys = keys.is_a?(Array) ? keys : [keys]
367
+ ids = ids.is_a?(Array) ? ids : [ids]
368
+ args << 'STREAMS'
369
+ args.concat(keys)
370
+ args.concat(ids)
371
+
372
+ if blocking_timeout_msec.nil?
373
+ send_command(args, &HashifyStreams)
374
+ elsif blocking_timeout_msec.to_f.zero?
375
+ send_blocking_command(args, 0, &HashifyStreams)
376
+ else
377
+ send_blocking_command(args, blocking_timeout_msec.to_f / 1_000, &HashifyStreams)
378
+ end
379
+ end
380
+ end
381
+ end
382
+ end
@@ -0,0 +1,313 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Redis
4
+ module Commands
5
+ module Strings
6
+ # Decrement the integer value of a key by one.
7
+ #
8
+ # @example
9
+ # redis.decr("value")
10
+ # # => 4
11
+ #
12
+ # @param [String] key
13
+ # @return [Integer] value after decrementing it
14
+ def decr(key)
15
+ send_command([:decr, key])
16
+ end
17
+
18
+ # Decrement the integer value of a key by the given number.
19
+ #
20
+ # @example
21
+ # redis.decrby("value", 5)
22
+ # # => 0
23
+ #
24
+ # @param [String] key
25
+ # @param [Integer] decrement
26
+ # @return [Integer] value after decrementing it
27
+ def decrby(key, decrement)
28
+ send_command([:decrby, key, decrement])
29
+ end
30
+
31
+ # Increment the integer value of a key by one.
32
+ #
33
+ # @example
34
+ # redis.incr("value")
35
+ # # => 6
36
+ #
37
+ # @param [String] key
38
+ # @return [Integer] value after incrementing it
39
+ def incr(key)
40
+ send_command([:incr, key])
41
+ end
42
+
43
+ # Increment the integer value of a key by the given integer number.
44
+ #
45
+ # @example
46
+ # redis.incrby("value", 5)
47
+ # # => 10
48
+ #
49
+ # @param [String] key
50
+ # @param [Integer] increment
51
+ # @return [Integer] value after incrementing it
52
+ def incrby(key, increment)
53
+ send_command([:incrby, key, increment])
54
+ end
55
+
56
+ # Increment the numeric value of a key by the given float number.
57
+ #
58
+ # @example
59
+ # redis.incrbyfloat("value", 1.23)
60
+ # # => 1.23
61
+ #
62
+ # @param [String] key
63
+ # @param [Float] increment
64
+ # @return [Float] value after incrementing it
65
+ def incrbyfloat(key, increment)
66
+ send_command([:incrbyfloat, key, increment], &Floatify)
67
+ end
68
+
69
+ # Set the string value of a key.
70
+ #
71
+ # @param [String] key
72
+ # @param [String] value
73
+ # @param [Hash] options
74
+ # - `:ex => Integer`: Set the specified expire time, in seconds.
75
+ # - `:px => Integer`: Set the specified expire time, in milliseconds.
76
+ # - `:exat => Integer` : Set the specified Unix time at which the key will expire, in seconds.
77
+ # - `:pxat => Integer` : Set the specified Unix time at which the key will expire, in milliseconds.
78
+ # - `:nx => true`: Only set the key if it does not already exist.
79
+ # - `:xx => true`: Only set the key if it already exist.
80
+ # - `:keepttl => true`: Retain the time to live associated with the key.
81
+ # - `:get => true`: Return the old string stored at key, or nil if key did not exist.
82
+ # @return [String, Boolean] `"OK"` or true, false if `:nx => true` or `:xx => true`
83
+ def set(key, value, ex: nil, px: nil, exat: nil, pxat: nil, nx: nil, xx: nil, keepttl: nil, get: nil)
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
89
+ args << "NX" if nx
90
+ args << "XX" if xx
91
+ args << "KEEPTTL" if keepttl
92
+ args << "GET" if get
93
+
94
+ if nx || xx
95
+ send_command(args, &BoolifySet)
96
+ else
97
+ send_command(args)
98
+ end
99
+ end
100
+
101
+ # Set the time to live in seconds of a key.
102
+ #
103
+ # @param [String] key
104
+ # @param [Integer] ttl
105
+ # @param [String] value
106
+ # @return [String] `"OK"`
107
+ def setex(key, ttl, value)
108
+ send_command([:setex, key, ttl, value.to_s])
109
+ end
110
+
111
+ # Set the time to live in milliseconds of a key.
112
+ #
113
+ # @param [String] key
114
+ # @param [Integer] ttl
115
+ # @param [String] value
116
+ # @return [String] `"OK"`
117
+ def psetex(key, ttl, value)
118
+ send_command([:psetex, key, ttl, value.to_s])
119
+ end
120
+
121
+ # Set the value of a key, only if the key does not exist.
122
+ #
123
+ # @param [String] key
124
+ # @param [String] value
125
+ # @return [Boolean] whether the key was set or not
126
+ def setnx(key, value)
127
+ send_command([:setnx, key, value.to_s], &Boolify)
128
+ end
129
+
130
+ # Set one or more values.
131
+ #
132
+ # @example
133
+ # redis.mset("key1", "v1", "key2", "v2")
134
+ # # => "OK"
135
+ #
136
+ # @param [Array<String>] args array of keys and values
137
+ # @return [String] `"OK"`
138
+ #
139
+ # @see #mapped_mset
140
+ def mset(*args)
141
+ send_command([:mset] + args)
142
+ end
143
+
144
+ # Set one or more values.
145
+ #
146
+ # @example
147
+ # redis.mapped_mset({ "f1" => "v1", "f2" => "v2" })
148
+ # # => "OK"
149
+ #
150
+ # @param [Hash] hash keys mapping to values
151
+ # @return [String] `"OK"`
152
+ #
153
+ # @see #mset
154
+ def mapped_mset(hash)
155
+ mset(hash.to_a.flatten)
156
+ end
157
+
158
+ # Set one or more values, only if none of the keys exist.
159
+ #
160
+ # @example
161
+ # redis.msetnx("key1", "v1", "key2", "v2")
162
+ # # => true
163
+ #
164
+ # @param [Array<String>] args array of keys and values
165
+ # @return [Boolean] whether or not all values were set
166
+ #
167
+ # @see #mapped_msetnx
168
+ def msetnx(*args)
169
+ send_command([:msetnx, *args], &Boolify)
170
+ end
171
+
172
+ # Set one or more values, only if none of the keys exist.
173
+ #
174
+ # @example
175
+ # redis.mapped_msetnx({ "key1" => "v1", "key2" => "v2" })
176
+ # # => true
177
+ #
178
+ # @param [Hash] hash keys mapping to values
179
+ # @return [Boolean] whether or not all values were set
180
+ #
181
+ # @see #msetnx
182
+ def mapped_msetnx(hash)
183
+ msetnx(hash.to_a.flatten)
184
+ end
185
+
186
+ # Get the value of a key.
187
+ #
188
+ # @param [String] key
189
+ # @return [String]
190
+ def get(key)
191
+ send_command([:get, key])
192
+ end
193
+
194
+ # Get the values of all the given keys.
195
+ #
196
+ # @example
197
+ # redis.mget("key1", "key2")
198
+ # # => ["v1", "v2"]
199
+ #
200
+ # @param [Array<String>] keys
201
+ # @return [Array<String>] an array of values for the specified keys
202
+ #
203
+ # @see #mapped_mget
204
+ def mget(*keys, &blk)
205
+ send_command([:mget, *keys], &blk)
206
+ end
207
+
208
+ # Get the values of all the given keys.
209
+ #
210
+ # @example
211
+ # redis.mapped_mget("key1", "key2")
212
+ # # => { "key1" => "v1", "key2" => "v2" }
213
+ #
214
+ # @param [Array<String>] keys array of keys
215
+ # @return [Hash] a hash mapping the specified keys to their values
216
+ #
217
+ # @see #mget
218
+ def mapped_mget(*keys)
219
+ mget(*keys) do |reply|
220
+ if reply.is_a?(Array)
221
+ Hash[keys.zip(reply)]
222
+ else
223
+ reply
224
+ end
225
+ end
226
+ end
227
+
228
+ # Overwrite part of a string at key starting at the specified offset.
229
+ #
230
+ # @param [String] key
231
+ # @param [Integer] offset byte offset
232
+ # @param [String] value
233
+ # @return [Integer] length of the string after it was modified
234
+ def setrange(key, offset, value)
235
+ send_command([:setrange, key, offset, value.to_s])
236
+ end
237
+
238
+ # Get a substring of the string stored at a key.
239
+ #
240
+ # @param [String] key
241
+ # @param [Integer] start zero-based start offset
242
+ # @param [Integer] stop zero-based end offset. Use -1 for representing
243
+ # the end of the string
244
+ # @return [Integer] `0` or `1`
245
+ def getrange(key, start, stop)
246
+ send_command([:getrange, key, start, stop])
247
+ end
248
+
249
+ # Append a value to a key.
250
+ #
251
+ # @param [String] key
252
+ # @param [String] value value to append
253
+ # @return [Integer] length of the string after appending
254
+ def append(key, value)
255
+ send_command([:append, key, value])
256
+ end
257
+
258
+ # Set the string value of a key and return its old value.
259
+ #
260
+ # @param [String] key
261
+ # @param [String] value value to replace the current value with
262
+ # @return [String] the old value stored in the key, or `nil` if the key
263
+ # did not exist
264
+ def getset(key, value)
265
+ send_command([:getset, key, value.to_s])
266
+ end
267
+
268
+ # Get the value of key and delete the key. This command is similar to GET,
269
+ # except for the fact that it also deletes the key on success.
270
+ #
271
+ # @param [String] key
272
+ # @return [String] the old value stored in the key, or `nil` if the key
273
+ # did not exist
274
+ def getdel(key)
275
+ send_command([:getdel, key])
276
+ end
277
+
278
+ # Get the value of key and optionally set its expiration. GETEX is similar to
279
+ # GET, but is a write command with additional options. When no options are
280
+ # provided, GETEX behaves like GET.
281
+ #
282
+ # @param [String] key
283
+ # @param [Hash] options
284
+ # - `:ex => Integer`: Set the specified expire time, in seconds.
285
+ # - `:px => Integer`: Set the specified expire time, in milliseconds.
286
+ # - `:exat => true`: Set the specified Unix time at which the key will
287
+ # expire, in seconds.
288
+ # - `:pxat => true`: Set the specified Unix time at which the key will
289
+ # expire, in milliseconds.
290
+ # - `:persist => true`: Remove the time to live associated with the key.
291
+ # @return [String] The value of key, or nil when key does not exist.
292
+ def getex(key, ex: nil, px: nil, exat: nil, pxat: nil, persist: false)
293
+ 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
298
+ args << "PERSIST" if persist
299
+
300
+ send_command(args)
301
+ end
302
+
303
+ # Get the length of the value stored in a key.
304
+ #
305
+ # @param [String] key
306
+ # @return [Integer] the length of the value stored in the key, or 0
307
+ # if the key does not exist
308
+ def strlen(key)
309
+ send_command([:strlen, key])
310
+ end
311
+ end
312
+ end
313
+ end