redis 4.5.1 → 4.7.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,411 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Redis
4
+ module Commands
5
+ module Keys
6
+ # Scan the keyspace
7
+ #
8
+ # @example Retrieve the first batch of keys
9
+ # redis.scan(0)
10
+ # # => ["4", ["key:21", "key:47", "key:42"]]
11
+ # @example Retrieve a batch of keys matching a pattern
12
+ # redis.scan(4, :match => "key:1?")
13
+ # # => ["92", ["key:13", "key:18"]]
14
+ # @example Retrieve a batch of keys of a certain type
15
+ # redis.scan(92, :type => "zset")
16
+ # # => ["173", ["sortedset:14", "sortedset:78"]]
17
+ #
18
+ # @param [String, Integer] cursor the cursor of the iteration
19
+ # @param [Hash] options
20
+ # - `:match => String`: only return keys matching the pattern
21
+ # - `:count => Integer`: return count keys at most per iteration
22
+ # - `:type => String`: return keys only of the given type
23
+ #
24
+ # @return [String, Array<String>] the next cursor and all found keys
25
+ def scan(cursor, **options)
26
+ _scan(:scan, cursor, [], **options)
27
+ end
28
+
29
+ # Scan the keyspace
30
+ #
31
+ # @example Retrieve all of the keys (with possible duplicates)
32
+ # redis.scan_each.to_a
33
+ # # => ["key:21", "key:47", "key:42"]
34
+ # @example Execute block for each key matching a pattern
35
+ # redis.scan_each(:match => "key:1?") {|key| puts key}
36
+ # # => key:13
37
+ # # => key:18
38
+ # @example Execute block for each key of a type
39
+ # redis.scan_each(:type => "hash") {|key| puts redis.type(key)}
40
+ # # => "hash"
41
+ # # => "hash"
42
+ #
43
+ # @param [Hash] options
44
+ # - `:match => String`: only return keys matching the pattern
45
+ # - `:count => Integer`: return count keys at most per iteration
46
+ # - `:type => String`: return keys only of the given type
47
+ #
48
+ # @return [Enumerator] an enumerator for all found keys
49
+ def scan_each(**options, &block)
50
+ return to_enum(:scan_each, **options) unless block_given?
51
+
52
+ cursor = 0
53
+ loop do
54
+ cursor, keys = scan(cursor, **options)
55
+ keys.each(&block)
56
+ break if cursor == "0"
57
+ end
58
+ end
59
+
60
+ # Remove the expiration from a key.
61
+ #
62
+ # @param [String] key
63
+ # @return [Boolean] whether the timeout was removed or not
64
+ def persist(key)
65
+ send_command([:persist, key], &Boolify)
66
+ end
67
+
68
+ # Set a key's time to live in seconds.
69
+ #
70
+ # @param [String] key
71
+ # @param [Integer] seconds time to live
72
+ # @return [Boolean] whether the timeout was set or not
73
+ def expire(key, seconds)
74
+ send_command([:expire, key, seconds], &Boolify)
75
+ end
76
+
77
+ # Set the expiration for a key as a UNIX timestamp.
78
+ #
79
+ # @param [String] key
80
+ # @param [Integer] unix_time expiry time specified as a UNIX timestamp
81
+ # @return [Boolean] whether the timeout was set or not
82
+ def expireat(key, unix_time)
83
+ send_command([:expireat, key, unix_time], &Boolify)
84
+ end
85
+
86
+ # Get the time to live (in seconds) for a key.
87
+ #
88
+ # @param [String] key
89
+ # @return [Integer] remaining time to live in seconds.
90
+ #
91
+ # In Redis 2.6 or older the command returns -1 if the key does not exist or if
92
+ # the key exist but has no associated expire.
93
+ #
94
+ # Starting with Redis 2.8 the return value in case of error changed:
95
+ #
96
+ # - The command returns -2 if the key does not exist.
97
+ # - The command returns -1 if the key exists but has no associated expire.
98
+ def ttl(key)
99
+ send_command([:ttl, key])
100
+ end
101
+
102
+ # Set a key's time to live in milliseconds.
103
+ #
104
+ # @param [String] key
105
+ # @param [Integer] milliseconds time to live
106
+ # @return [Boolean] whether the timeout was set or not
107
+ def pexpire(key, milliseconds)
108
+ send_command([:pexpire, key, milliseconds], &Boolify)
109
+ end
110
+
111
+ # Set the expiration for a key as number of milliseconds from UNIX Epoch.
112
+ #
113
+ # @param [String] key
114
+ # @param [Integer] ms_unix_time expiry time specified as number of milliseconds from UNIX Epoch.
115
+ # @return [Boolean] whether the timeout was set or not
116
+ def pexpireat(key, ms_unix_time)
117
+ send_command([:pexpireat, key, ms_unix_time], &Boolify)
118
+ end
119
+
120
+ # Get the time to live (in milliseconds) for a key.
121
+ #
122
+ # @param [String] key
123
+ # @return [Integer] remaining time to live in milliseconds
124
+ # In Redis 2.6 or older the command returns -1 if the key does not exist or if
125
+ # the key exist but has no associated expire.
126
+ #
127
+ # Starting with Redis 2.8 the return value in case of error changed:
128
+ #
129
+ # - The command returns -2 if the key does not exist.
130
+ # - The command returns -1 if the key exists but has no associated expire.
131
+ def pttl(key)
132
+ send_command([:pttl, key])
133
+ end
134
+
135
+ # Return a serialized version of the value stored at a key.
136
+ #
137
+ # @param [String] key
138
+ # @return [String] serialized_value
139
+ def dump(key)
140
+ send_command([:dump, key])
141
+ end
142
+
143
+ # Create a key using the serialized value, previously obtained using DUMP.
144
+ #
145
+ # @param [String] key
146
+ # @param [String] ttl
147
+ # @param [String] serialized_value
148
+ # @param [Hash] options
149
+ # - `:replace => Boolean`: if false, raises an error if key already exists
150
+ # @raise [Redis::CommandError]
151
+ # @return [String] `"OK"`
152
+ def restore(key, ttl, serialized_value, replace: nil)
153
+ args = [:restore, key, ttl, serialized_value]
154
+ args << 'REPLACE' if replace
155
+
156
+ send_command(args)
157
+ end
158
+
159
+ # Transfer a key from the connected instance to another instance.
160
+ #
161
+ # @param [String, Array<String>] key
162
+ # @param [Hash] options
163
+ # - `:host => String`: host of instance to migrate to
164
+ # - `:port => Integer`: port of instance to migrate to
165
+ # - `:db => Integer`: database to migrate to (default: same as source)
166
+ # - `:timeout => Integer`: timeout (default: same as connection timeout)
167
+ # - `:copy => Boolean`: Do not remove the key from the local instance.
168
+ # - `:replace => Boolean`: Replace existing key on the remote instance.
169
+ # @return [String] `"OK"`
170
+ def migrate(key, options)
171
+ args = [:migrate]
172
+ args << (options[:host] || raise(':host not specified'))
173
+ args << (options[:port] || raise(':port not specified'))
174
+ args << (key.is_a?(String) ? key : '')
175
+ args << (options[:db] || @client.db).to_i
176
+ args << (options[:timeout] || @client.timeout).to_i
177
+ args << 'COPY' if options[:copy]
178
+ args << 'REPLACE' if options[:replace]
179
+ args += ['KEYS', *key] if key.is_a?(Array)
180
+
181
+ send_command(args)
182
+ end
183
+
184
+ # Delete one or more keys.
185
+ #
186
+ # @param [String, Array<String>] keys
187
+ # @return [Integer] number of keys that were deleted
188
+ def del(*keys)
189
+ keys.flatten!(1)
190
+ return 0 if keys.empty?
191
+
192
+ send_command([:del] + keys)
193
+ end
194
+
195
+ # Unlink one or more keys.
196
+ #
197
+ # @param [String, Array<String>] keys
198
+ # @return [Integer] number of keys that were unlinked
199
+ def unlink(*keys)
200
+ send_command([:unlink] + keys)
201
+ end
202
+
203
+ # Determine how many of the keys exists.
204
+ #
205
+ # @param [String, Array<String>] keys
206
+ # @return [Integer]
207
+ def exists(*keys)
208
+ if !Redis.exists_returns_integer && keys.size == 1
209
+ if Redis.exists_returns_integer.nil?
210
+ message = "`Redis#exists(key)` will return an Integer in redis-rb 4.3. `exists?` returns a boolean, you " \
211
+ "should use it instead. To opt-in to the new behavior now you can set Redis.exists_returns_integer = " \
212
+ "true. To disable this message and keep the current (boolean) behaviour of 'exists' you can set " \
213
+ "`Redis.exists_returns_integer = false`, but this option will be removed in 5.0.0. " \
214
+ "(#{::Kernel.caller(1, 1).first})\n"
215
+
216
+ ::Redis.deprecate!(message)
217
+ end
218
+
219
+ exists?(*keys)
220
+ else
221
+ _exists(*keys)
222
+ end
223
+ end
224
+
225
+ def _exists(*keys)
226
+ send_command([:exists, *keys])
227
+ end
228
+
229
+ # Determine if any of the keys exists.
230
+ #
231
+ # @param [String, Array<String>] keys
232
+ # @return [Boolean]
233
+ def exists?(*keys)
234
+ send_command([:exists, *keys]) do |value|
235
+ value > 0
236
+ end
237
+ end
238
+
239
+ # Find all keys matching the given pattern.
240
+ #
241
+ # @param [String] pattern
242
+ # @return [Array<String>]
243
+ def keys(pattern = "*")
244
+ send_command([:keys, pattern]) do |reply|
245
+ if reply.is_a?(String)
246
+ reply.split(" ")
247
+ else
248
+ reply
249
+ end
250
+ end
251
+ end
252
+
253
+ # Move a key to another database.
254
+ #
255
+ # @example Move a key to another database
256
+ # redis.set "foo", "bar"
257
+ # # => "OK"
258
+ # redis.move "foo", 2
259
+ # # => true
260
+ # redis.exists "foo"
261
+ # # => false
262
+ # redis.select 2
263
+ # # => "OK"
264
+ # redis.exists "foo"
265
+ # # => true
266
+ # redis.get "foo"
267
+ # # => "bar"
268
+ #
269
+ # @param [String] key
270
+ # @param [Integer] db
271
+ # @return [Boolean] whether the key was moved or not
272
+ def move(key, db)
273
+ send_command([:move, key, db], &Boolify)
274
+ end
275
+
276
+ # Copy a value from one key to another.
277
+ #
278
+ # @example Copy a value to another key
279
+ # redis.set "foo", "value"
280
+ # # => "OK"
281
+ # redis.copy "foo", "bar"
282
+ # # => true
283
+ # redis.get "bar"
284
+ # # => "value"
285
+ #
286
+ # @example Copy a value to a key in another database
287
+ # redis.set "foo", "value"
288
+ # # => "OK"
289
+ # redis.copy "foo", "bar", db: 2
290
+ # # => true
291
+ # redis.select 2
292
+ # # => "OK"
293
+ # redis.get "bar"
294
+ # # => "value"
295
+ #
296
+ # @param [String] source
297
+ # @param [String] destination
298
+ # @param [Integer] db
299
+ # @param [Boolean] replace removes the `destination` key before copying value to it
300
+ # @return [Boolean] whether the key was copied or not
301
+ def copy(source, destination, db: nil, replace: false)
302
+ command = [:copy, source, destination]
303
+ command << "DB" << db if db
304
+ command << "REPLACE" if replace
305
+
306
+ send_command(command, &Boolify)
307
+ end
308
+
309
+ def object(*args)
310
+ send_command([:object] + args)
311
+ end
312
+
313
+ # Return a random key from the keyspace.
314
+ #
315
+ # @return [String]
316
+ def randomkey
317
+ send_command([:randomkey])
318
+ end
319
+
320
+ # Rename a key. If the new key already exists it is overwritten.
321
+ #
322
+ # @param [String] old_name
323
+ # @param [String] new_name
324
+ # @return [String] `OK`
325
+ def rename(old_name, new_name)
326
+ send_command([:rename, old_name, new_name])
327
+ end
328
+
329
+ # Rename a key, only if the new key does not exist.
330
+ #
331
+ # @param [String] old_name
332
+ # @param [String] new_name
333
+ # @return [Boolean] whether the key was renamed or not
334
+ def renamenx(old_name, new_name)
335
+ send_command([:renamenx, old_name, new_name], &Boolify)
336
+ end
337
+
338
+ # Sort the elements in a list, set or sorted set.
339
+ #
340
+ # @example Retrieve the first 2 elements from an alphabetically sorted "list"
341
+ # redis.sort("list", :order => "alpha", :limit => [0, 2])
342
+ # # => ["a", "b"]
343
+ # @example Store an alphabetically descending list in "target"
344
+ # redis.sort("list", :order => "desc alpha", :store => "target")
345
+ # # => 26
346
+ #
347
+ # @param [String] key
348
+ # @param [Hash] options
349
+ # - `:by => String`: use external key to sort elements by
350
+ # - `:limit => [offset, count]`: skip `offset` elements, return a maximum
351
+ # of `count` elements
352
+ # - `:get => [String, Array<String>]`: single key or array of keys to
353
+ # retrieve per element in the result
354
+ # - `:order => String`: combination of `ASC`, `DESC` and optionally `ALPHA`
355
+ # - `:store => String`: key to store the result at
356
+ #
357
+ # @return [Array<String>, Array<Array<String>>, Integer]
358
+ # - when `:get` is not specified, or holds a single element, an array of elements
359
+ # - when `:get` is specified, and holds more than one element, an array of
360
+ # elements where every element is an array with the result for every
361
+ # element specified in `:get`
362
+ # - when `:store` is specified, the number of elements in the stored result
363
+ def sort(key, by: nil, limit: nil, get: nil, order: nil, store: nil)
364
+ args = [:sort, key]
365
+ args << "BY" << by if by
366
+
367
+ if limit
368
+ args << "LIMIT"
369
+ args.concat(limit)
370
+ end
371
+
372
+ get = Array(get)
373
+ get.each do |item|
374
+ args << "GET" << item
375
+ end
376
+
377
+ args.concat(order.split(" ")) if order
378
+ args << "STORE" << store if store
379
+
380
+ send_command(args) do |reply|
381
+ if get.size > 1 && !store
382
+ reply.each_slice(get.size).to_a if reply
383
+ else
384
+ reply
385
+ end
386
+ end
387
+ end
388
+
389
+ # Determine the type stored at key.
390
+ #
391
+ # @param [String] key
392
+ # @return [String] `string`, `list`, `set`, `zset`, `hash` or `none`
393
+ def type(key)
394
+ send_command([:type, key])
395
+ end
396
+
397
+ private
398
+
399
+ def _scan(command, cursor, args, match: nil, count: nil, type: nil, &block)
400
+ # SSCAN/ZSCAN/HSCAN already prepend the key to +args+.
401
+
402
+ args << cursor
403
+ args << "MATCH" << match if match
404
+ args << "COUNT" << count if count
405
+ args << "TYPE" << type if type
406
+
407
+ send_command([command] + args, &block)
408
+ end
409
+ end
410
+ end
411
+ end
@@ -0,0 +1,289 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Redis
4
+ module Commands
5
+ module Lists
6
+ # Get the length of a list.
7
+ #
8
+ # @param [String] key
9
+ # @return [Integer]
10
+ def llen(key)
11
+ send_command([:llen, key])
12
+ end
13
+
14
+ # Remove the first/last element in a list, append/prepend it to another list and return it.
15
+ #
16
+ # @param [String] source source key
17
+ # @param [String] destination destination key
18
+ # @param [String, Symbol] where_source from where to remove the element from the source list
19
+ # e.g. 'LEFT' - from head, 'RIGHT' - from tail
20
+ # @param [String, Symbol] where_destination where to push the element to the source list
21
+ # e.g. 'LEFT' - to head, 'RIGHT' - to tail
22
+ #
23
+ # @return [nil, String] the element, or nil when the source key does not exist
24
+ #
25
+ # @note This command comes in place of the now deprecated RPOPLPUSH.
26
+ # Doing LMOVE RIGHT LEFT is equivalent.
27
+ def lmove(source, destination, where_source, where_destination)
28
+ where_source, where_destination = _normalize_move_wheres(where_source, where_destination)
29
+
30
+ send_command([:lmove, source, destination, where_source, where_destination])
31
+ end
32
+
33
+ # Remove the first/last element in a list and append/prepend it
34
+ # to another list and return it, or block until one is available.
35
+ #
36
+ # @example With timeout
37
+ # element = redis.blmove("foo", "bar", "LEFT", "RIGHT", timeout: 5)
38
+ # # => nil on timeout
39
+ # # => "element" on success
40
+ # @example Without timeout
41
+ # element = redis.blmove("foo", "bar", "LEFT", "RIGHT")
42
+ # # => "element"
43
+ #
44
+ # @param [String] source source key
45
+ # @param [String] destination destination key
46
+ # @param [String, Symbol] where_source from where to remove the element from the source list
47
+ # e.g. 'LEFT' - from head, 'RIGHT' - from tail
48
+ # @param [String, Symbol] where_destination where to push the element to the source list
49
+ # e.g. 'LEFT' - to head, 'RIGHT' - to tail
50
+ # @param [Hash] options
51
+ # - `:timeout => Numeric`: timeout in seconds, defaults to no timeout
52
+ #
53
+ # @return [nil, String] the element, or nil when the source key does not exist or the timeout expired
54
+ #
55
+ def blmove(source, destination, where_source, where_destination, timeout: 0)
56
+ where_source, where_destination = _normalize_move_wheres(where_source, where_destination)
57
+
58
+ command = [:blmove, source, destination, where_source, where_destination, timeout]
59
+ send_blocking_command(command, timeout)
60
+ end
61
+
62
+ # Prepend one or more values to a list, creating the list if it doesn't exist
63
+ #
64
+ # @param [String] key
65
+ # @param [String, Array<String>] value string value, or array of string values to push
66
+ # @return [Integer] the length of the list after the push operation
67
+ def lpush(key, value)
68
+ send_command([:lpush, key, value])
69
+ end
70
+
71
+ # Prepend a value to a list, only if the list exists.
72
+ #
73
+ # @param [String] key
74
+ # @param [String] value
75
+ # @return [Integer] the length of the list after the push operation
76
+ def lpushx(key, value)
77
+ send_command([:lpushx, key, value])
78
+ end
79
+
80
+ # Append one or more values to a list, creating the list if it doesn't exist
81
+ #
82
+ # @param [String] key
83
+ # @param [String, Array<String>] value string value, or array of string values to push
84
+ # @return [Integer] the length of the list after the push operation
85
+ def rpush(key, value)
86
+ send_command([:rpush, key, value])
87
+ end
88
+
89
+ # Append a value to a list, only if the list exists.
90
+ #
91
+ # @param [String] key
92
+ # @param [String] value
93
+ # @return [Integer] the length of the list after the push operation
94
+ def rpushx(key, value)
95
+ send_command([:rpushx, key, value])
96
+ end
97
+
98
+ # Remove and get the first elements in a list.
99
+ #
100
+ # @param [String] key
101
+ # @param [Integer] count number of elements to remove
102
+ # @return [String, Array<String>] the values of the first elements
103
+ def lpop(key, count = nil)
104
+ command = [:lpop, key]
105
+ command << count if count
106
+ send_command(command)
107
+ end
108
+
109
+ # Remove and get the last elements in a list.
110
+ #
111
+ # @param [String] key
112
+ # @param [Integer] count number of elements to remove
113
+ # @return [String, Array<String>] the values of the last elements
114
+ def rpop(key, count = nil)
115
+ command = [:rpop, key]
116
+ command << count if count
117
+ send_command(command)
118
+ end
119
+
120
+ # Remove the last element in a list, append it to another list and return it.
121
+ #
122
+ # @param [String] source source key
123
+ # @param [String] destination destination key
124
+ # @return [nil, String] the element, or nil when the source key does not exist
125
+ def rpoplpush(source, destination)
126
+ send_command([:rpoplpush, source, destination])
127
+ end
128
+
129
+ # Remove and get the first element in a list, or block until one is available.
130
+ #
131
+ # @example With timeout
132
+ # list, element = redis.blpop("list", :timeout => 5)
133
+ # # => nil on timeout
134
+ # # => ["list", "element"] on success
135
+ # @example Without timeout
136
+ # list, element = redis.blpop("list")
137
+ # # => ["list", "element"]
138
+ # @example Blocking pop on multiple lists
139
+ # list, element = redis.blpop(["list", "another_list"])
140
+ # # => ["list", "element"]
141
+ #
142
+ # @param [String, Array<String>] keys one or more keys to perform the
143
+ # blocking pop on
144
+ # @param [Hash] options
145
+ # - `:timeout => Integer`: timeout in seconds, defaults to no timeout
146
+ #
147
+ # @return [nil, [String, String]]
148
+ # - `nil` when the operation timed out
149
+ # - tuple of the list that was popped from and element was popped otherwise
150
+ def blpop(*args)
151
+ _bpop(:blpop, args)
152
+ end
153
+
154
+ # Remove and get the last element in a list, or block until one is available.
155
+ #
156
+ # @param [String, Array<String>] keys one or more keys to perform the
157
+ # blocking pop on
158
+ # @param [Hash] options
159
+ # - `:timeout => Integer`: timeout in seconds, defaults to no timeout
160
+ #
161
+ # @return [nil, [String, String]]
162
+ # - `nil` when the operation timed out
163
+ # - tuple of the list that was popped from and element was popped otherwise
164
+ #
165
+ # @see #blpop
166
+ def brpop(*args)
167
+ _bpop(:brpop, args)
168
+ end
169
+
170
+ # Pop a value from a list, push it to another list and return it; or block
171
+ # until one is available.
172
+ #
173
+ # @param [String] source source key
174
+ # @param [String] destination destination key
175
+ # @param [Hash] options
176
+ # - `:timeout => Integer`: timeout in seconds, defaults to no timeout
177
+ #
178
+ # @return [nil, String]
179
+ # - `nil` when the operation timed out
180
+ # - the element was popped and pushed otherwise
181
+ def brpoplpush(source, destination, deprecated_timeout = 0, timeout: deprecated_timeout)
182
+ command = [:brpoplpush, source, destination, timeout]
183
+ send_blocking_command(command, timeout)
184
+ end
185
+
186
+ # Get an element from a list by its index.
187
+ #
188
+ # @param [String] key
189
+ # @param [Integer] index
190
+ # @return [String]
191
+ def lindex(key, index)
192
+ send_command([:lindex, key, index])
193
+ end
194
+
195
+ # Insert an element before or after another element in a list.
196
+ #
197
+ # @param [String] key
198
+ # @param [String, Symbol] where `BEFORE` or `AFTER`
199
+ # @param [String] pivot reference element
200
+ # @param [String] value
201
+ # @return [Integer] length of the list after the insert operation, or `-1`
202
+ # when the element `pivot` was not found
203
+ def linsert(key, where, pivot, value)
204
+ send_command([:linsert, key, where, pivot, value])
205
+ end
206
+
207
+ # Get a range of elements from a list.
208
+ #
209
+ # @param [String] key
210
+ # @param [Integer] start start index
211
+ # @param [Integer] stop stop index
212
+ # @return [Array<String>]
213
+ def lrange(key, start, stop)
214
+ send_command([:lrange, key, start, stop])
215
+ end
216
+
217
+ # Remove elements from a list.
218
+ #
219
+ # @param [String] key
220
+ # @param [Integer] count number of elements to remove. Use a positive
221
+ # value to remove the first `count` occurrences of `value`. A negative
222
+ # value to remove the last `count` occurrences of `value`. Or zero, to
223
+ # remove all occurrences of `value` from the list.
224
+ # @param [String] value
225
+ # @return [Integer] the number of removed elements
226
+ def lrem(key, count, value)
227
+ send_command([:lrem, key, count, value])
228
+ end
229
+
230
+ # Set the value of an element in a list by its index.
231
+ #
232
+ # @param [String] key
233
+ # @param [Integer] index
234
+ # @param [String] value
235
+ # @return [String] `OK`
236
+ def lset(key, index, value)
237
+ send_command([:lset, key, index, value])
238
+ end
239
+
240
+ # Trim a list to the specified range.
241
+ #
242
+ # @param [String] key
243
+ # @param [Integer] start start index
244
+ # @param [Integer] stop stop index
245
+ # @return [String] `OK`
246
+ def ltrim(key, start, stop)
247
+ send_command([:ltrim, key, start, stop])
248
+ end
249
+
250
+ private
251
+
252
+ def _bpop(cmd, args, &blk)
253
+ timeout = if args.last.is_a?(Hash)
254
+ options = args.pop
255
+ options[:timeout]
256
+ elsif args.last.respond_to?(:to_int)
257
+ # Issue deprecation notice in obnoxious mode...
258
+ args.pop.to_int
259
+ end
260
+
261
+ timeout ||= 0
262
+
263
+ if args.size > 1
264
+ # Issue deprecation notice in obnoxious mode...
265
+ end
266
+
267
+ keys = args.flatten
268
+
269
+ command = [cmd, keys, timeout]
270
+ send_blocking_command(command, timeout, &blk)
271
+ end
272
+
273
+ def _normalize_move_wheres(where_source, where_destination)
274
+ where_source = where_source.to_s.upcase
275
+ where_destination = where_destination.to_s.upcase
276
+
277
+ if where_source != "LEFT" && where_source != "RIGHT"
278
+ raise ArgumentError, "where_source must be 'LEFT' or 'RIGHT'"
279
+ end
280
+
281
+ if where_destination != "LEFT" && where_destination != "RIGHT"
282
+ raise ArgumentError, "where_destination must be 'LEFT' or 'RIGHT'"
283
+ end
284
+
285
+ [where_source, where_destination]
286
+ end
287
+ end
288
+ end
289
+ end