redis 3.3.5 → 5.0.7
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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +290 -2
- data/README.md +146 -146
- data/lib/redis/client.rb +79 -541
- data/lib/redis/commands/bitmaps.rb +66 -0
- data/lib/redis/commands/cluster.rb +28 -0
- data/lib/redis/commands/connection.rb +53 -0
- data/lib/redis/commands/geo.rb +84 -0
- data/lib/redis/commands/hashes.rb +254 -0
- data/lib/redis/commands/hyper_log_log.rb +37 -0
- data/lib/redis/commands/keys.rb +437 -0
- data/lib/redis/commands/lists.rb +339 -0
- data/lib/redis/commands/pubsub.rb +54 -0
- data/lib/redis/commands/scripting.rb +114 -0
- data/lib/redis/commands/server.rb +188 -0
- data/lib/redis/commands/sets.rb +214 -0
- data/lib/redis/commands/sorted_sets.rb +884 -0
- data/lib/redis/commands/streams.rb +402 -0
- data/lib/redis/commands/strings.rb +314 -0
- data/lib/redis/commands/transactions.rb +115 -0
- data/lib/redis/commands.rb +237 -0
- data/lib/redis/distributed.rb +328 -108
- data/lib/redis/errors.rb +23 -1
- data/lib/redis/hash_ring.rb +36 -79
- data/lib/redis/pipeline.rb +69 -83
- data/lib/redis/subscribe.rb +26 -19
- data/lib/redis/version.rb +3 -1
- data/lib/redis.rb +115 -2695
- metadata +38 -218
- data/.gitignore +0 -16
- data/.travis/Gemfile +0 -11
- data/.travis.yml +0 -89
- data/.yardopts +0 -3
- data/Gemfile +0 -4
- data/Rakefile +0 -87
- data/benchmarking/logging.rb +0 -71
- data/benchmarking/pipeline.rb +0 -51
- data/benchmarking/speed.rb +0 -21
- data/benchmarking/suite.rb +0 -24
- data/benchmarking/worker.rb +0 -71
- data/examples/basic.rb +0 -15
- data/examples/consistency.rb +0 -114
- data/examples/dist_redis.rb +0 -43
- data/examples/incr-decr.rb +0 -17
- data/examples/list.rb +0 -26
- data/examples/pubsub.rb +0 -37
- data/examples/sentinel/sentinel.conf +0 -9
- data/examples/sentinel/start +0 -49
- data/examples/sentinel.rb +0 -41
- data/examples/sets.rb +0 -36
- data/examples/unicorn/config.ru +0 -3
- data/examples/unicorn/unicorn.rb +0 -20
- data/lib/redis/connection/command_helper.rb +0 -44
- data/lib/redis/connection/hiredis.rb +0 -66
- data/lib/redis/connection/registry.rb +0 -12
- data/lib/redis/connection/ruby.rb +0 -429
- data/lib/redis/connection/synchrony.rb +0 -133
- data/lib/redis/connection.rb +0 -9
- data/redis.gemspec +0 -44
- data/test/bitpos_test.rb +0 -69
- data/test/blocking_commands_test.rb +0 -42
- data/test/client_test.rb +0 -59
- data/test/command_map_test.rb +0 -30
- data/test/commands_on_hashes_test.rb +0 -21
- data/test/commands_on_hyper_log_log_test.rb +0 -21
- data/test/commands_on_lists_test.rb +0 -20
- data/test/commands_on_sets_test.rb +0 -77
- data/test/commands_on_sorted_sets_test.rb +0 -137
- data/test/commands_on_strings_test.rb +0 -101
- data/test/commands_on_value_types_test.rb +0 -133
- data/test/connection_handling_test.rb +0 -277
- data/test/connection_test.rb +0 -57
- data/test/db/.gitkeep +0 -0
- data/test/distributed_blocking_commands_test.rb +0 -46
- data/test/distributed_commands_on_hashes_test.rb +0 -10
- data/test/distributed_commands_on_hyper_log_log_test.rb +0 -33
- data/test/distributed_commands_on_lists_test.rb +0 -22
- data/test/distributed_commands_on_sets_test.rb +0 -83
- data/test/distributed_commands_on_sorted_sets_test.rb +0 -18
- data/test/distributed_commands_on_strings_test.rb +0 -59
- data/test/distributed_commands_on_value_types_test.rb +0 -95
- data/test/distributed_commands_requiring_clustering_test.rb +0 -164
- data/test/distributed_connection_handling_test.rb +0 -23
- data/test/distributed_internals_test.rb +0 -79
- data/test/distributed_key_tags_test.rb +0 -52
- data/test/distributed_persistence_control_commands_test.rb +0 -26
- data/test/distributed_publish_subscribe_test.rb +0 -92
- data/test/distributed_remote_server_control_commands_test.rb +0 -66
- data/test/distributed_scripting_test.rb +0 -102
- data/test/distributed_sorting_test.rb +0 -20
- data/test/distributed_test.rb +0 -58
- data/test/distributed_transactions_test.rb +0 -32
- data/test/encoding_test.rb +0 -18
- data/test/error_replies_test.rb +0 -59
- data/test/fork_safety_test.rb +0 -65
- data/test/helper.rb +0 -232
- data/test/helper_test.rb +0 -24
- data/test/internals_test.rb +0 -417
- data/test/lint/blocking_commands.rb +0 -150
- data/test/lint/hashes.rb +0 -162
- data/test/lint/hyper_log_log.rb +0 -60
- data/test/lint/lists.rb +0 -143
- data/test/lint/sets.rb +0 -140
- data/test/lint/sorted_sets.rb +0 -316
- data/test/lint/strings.rb +0 -260
- data/test/lint/value_types.rb +0 -122
- data/test/persistence_control_commands_test.rb +0 -26
- data/test/pipelining_commands_test.rb +0 -242
- data/test/publish_subscribe_test.rb +0 -282
- data/test/remote_server_control_commands_test.rb +0 -118
- data/test/scanning_test.rb +0 -413
- data/test/scripting_test.rb +0 -78
- data/test/sentinel_command_test.rb +0 -80
- data/test/sentinel_test.rb +0 -255
- data/test/sorting_test.rb +0 -59
- data/test/ssl_test.rb +0 -73
- data/test/support/connection/hiredis.rb +0 -1
- data/test/support/connection/ruby.rb +0 -1
- data/test/support/connection/synchrony.rb +0 -17
- data/test/support/redis_mock.rb +0 -130
- data/test/support/ssl/gen_certs.sh +0 -31
- data/test/support/ssl/trusted-ca.crt +0 -25
- data/test/support/ssl/trusted-ca.key +0 -27
- data/test/support/ssl/trusted-cert.crt +0 -81
- data/test/support/ssl/trusted-cert.key +0 -28
- data/test/support/ssl/untrusted-ca.crt +0 -26
- data/test/support/ssl/untrusted-ca.key +0 -27
- data/test/support/ssl/untrusted-cert.crt +0 -82
- data/test/support/ssl/untrusted-cert.key +0 -28
- data/test/support/wire/synchrony.rb +0 -24
- data/test/support/wire/thread.rb +0 -5
- data/test/synchrony_driver.rb +0 -88
- data/test/test.conf.erb +0 -9
- data/test/thread_safety_test.rb +0 -62
- data/test/transactions_test.rb +0 -264
- data/test/unknown_commands_test.rb +0 -14
- data/test/url_param_test.rb +0 -138
@@ -0,0 +1,314 @@
|
|
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, Integer(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, Integer(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, Float(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" << 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
|
+
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, Integer(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, Integer(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.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.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
|
+
keys.flatten!(1)
|
206
|
+
send_command([:mget, *keys], &blk)
|
207
|
+
end
|
208
|
+
|
209
|
+
# Get the values of all the given keys.
|
210
|
+
#
|
211
|
+
# @example
|
212
|
+
# redis.mapped_mget("key1", "key2")
|
213
|
+
# # => { "key1" => "v1", "key2" => "v2" }
|
214
|
+
#
|
215
|
+
# @param [Array<String>] keys array of keys
|
216
|
+
# @return [Hash] a hash mapping the specified keys to their values
|
217
|
+
#
|
218
|
+
# @see #mget
|
219
|
+
def mapped_mget(*keys)
|
220
|
+
mget(*keys) do |reply|
|
221
|
+
if reply.is_a?(Array)
|
222
|
+
Hash[keys.zip(reply)]
|
223
|
+
else
|
224
|
+
reply
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
# Overwrite part of a string at key starting at the specified offset.
|
230
|
+
#
|
231
|
+
# @param [String] key
|
232
|
+
# @param [Integer] offset byte offset
|
233
|
+
# @param [String] value
|
234
|
+
# @return [Integer] length of the string after it was modified
|
235
|
+
def setrange(key, offset, value)
|
236
|
+
send_command([:setrange, key, Integer(offset), value.to_s])
|
237
|
+
end
|
238
|
+
|
239
|
+
# Get a substring of the string stored at a key.
|
240
|
+
#
|
241
|
+
# @param [String] key
|
242
|
+
# @param [Integer] start zero-based start offset
|
243
|
+
# @param [Integer] stop zero-based end offset. Use -1 for representing
|
244
|
+
# the end of the string
|
245
|
+
# @return [Integer] `0` or `1`
|
246
|
+
def getrange(key, start, stop)
|
247
|
+
send_command([:getrange, key, Integer(start), Integer(stop)])
|
248
|
+
end
|
249
|
+
|
250
|
+
# Append a value to a key.
|
251
|
+
#
|
252
|
+
# @param [String] key
|
253
|
+
# @param [String] value value to append
|
254
|
+
# @return [Integer] length of the string after appending
|
255
|
+
def append(key, value)
|
256
|
+
send_command([:append, key, value])
|
257
|
+
end
|
258
|
+
|
259
|
+
# Set the string value of a key and return its old value.
|
260
|
+
#
|
261
|
+
# @param [String] key
|
262
|
+
# @param [String] value value to replace the current value with
|
263
|
+
# @return [String] the old value stored in the key, or `nil` if the key
|
264
|
+
# did not exist
|
265
|
+
def getset(key, value)
|
266
|
+
send_command([:getset, key, value.to_s])
|
267
|
+
end
|
268
|
+
|
269
|
+
# Get the value of key and delete the key. This command is similar to GET,
|
270
|
+
# except for the fact that it also deletes the key on success.
|
271
|
+
#
|
272
|
+
# @param [String] key
|
273
|
+
# @return [String] the old value stored in the key, or `nil` if the key
|
274
|
+
# did not exist
|
275
|
+
def getdel(key)
|
276
|
+
send_command([:getdel, key])
|
277
|
+
end
|
278
|
+
|
279
|
+
# Get the value of key and optionally set its expiration. GETEX is similar to
|
280
|
+
# GET, but is a write command with additional options. When no options are
|
281
|
+
# provided, GETEX behaves like GET.
|
282
|
+
#
|
283
|
+
# @param [String] key
|
284
|
+
# @param [Hash] options
|
285
|
+
# - `:ex => Integer`: Set the specified expire time, in seconds.
|
286
|
+
# - `:px => Integer`: Set the specified expire time, in milliseconds.
|
287
|
+
# - `:exat => true`: Set the specified Unix time at which the key will
|
288
|
+
# expire, in seconds.
|
289
|
+
# - `:pxat => true`: Set the specified Unix time at which the key will
|
290
|
+
# expire, in milliseconds.
|
291
|
+
# - `:persist => true`: Remove the time to live associated with the key.
|
292
|
+
# @return [String] The value of key, or nil when key does not exist.
|
293
|
+
def getex(key, ex: nil, px: nil, exat: nil, pxat: nil, persist: false)
|
294
|
+
args = [:getex, key]
|
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
|
299
|
+
args << "PERSIST" if persist
|
300
|
+
|
301
|
+
send_command(args)
|
302
|
+
end
|
303
|
+
|
304
|
+
# Get the length of the value stored in a key.
|
305
|
+
#
|
306
|
+
# @param [String] key
|
307
|
+
# @return [Integer] the length of the value stored in the key, or 0
|
308
|
+
# if the key does not exist
|
309
|
+
def strlen(key)
|
310
|
+
send_command([:strlen, key])
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Redis
|
4
|
+
module Commands
|
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
|
+
|
31
|
+
# Watch the given keys to determine execution of the MULTI/EXEC block.
|
32
|
+
#
|
33
|
+
# Using a block is optional, but is necessary for thread-safety.
|
34
|
+
#
|
35
|
+
# An `#unwatch` is automatically issued if an exception is raised within the
|
36
|
+
# block that is a subclass of StandardError and is not a ConnectionError.
|
37
|
+
#
|
38
|
+
# @example With a block
|
39
|
+
# redis.watch("key") do
|
40
|
+
# if redis.get("key") == "some value"
|
41
|
+
# redis.multi do |multi|
|
42
|
+
# multi.set("key", "other value")
|
43
|
+
# multi.incr("counter")
|
44
|
+
# end
|
45
|
+
# else
|
46
|
+
# redis.unwatch
|
47
|
+
# end
|
48
|
+
# end
|
49
|
+
# # => ["OK", 6]
|
50
|
+
#
|
51
|
+
# @example Without a block
|
52
|
+
# redis.watch("key")
|
53
|
+
# # => "OK"
|
54
|
+
#
|
55
|
+
# @param [String, Array<String>] keys one or more keys to watch
|
56
|
+
# @return [Object] if using a block, returns the return value of the block
|
57
|
+
# @return [String] if not using a block, returns `OK`
|
58
|
+
#
|
59
|
+
# @see #unwatch
|
60
|
+
# @see #multi
|
61
|
+
def watch(*keys)
|
62
|
+
synchronize do |client|
|
63
|
+
res = client.call_v([:watch] + keys)
|
64
|
+
|
65
|
+
if block_given?
|
66
|
+
begin
|
67
|
+
yield(self)
|
68
|
+
rescue ConnectionError
|
69
|
+
raise
|
70
|
+
rescue StandardError
|
71
|
+
unwatch
|
72
|
+
raise
|
73
|
+
end
|
74
|
+
else
|
75
|
+
res
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Forget about all watched keys.
|
81
|
+
#
|
82
|
+
# @return [String] `OK`
|
83
|
+
#
|
84
|
+
# @see #watch
|
85
|
+
# @see #multi
|
86
|
+
def unwatch
|
87
|
+
send_command([:unwatch])
|
88
|
+
end
|
89
|
+
|
90
|
+
# Execute all commands issued after MULTI.
|
91
|
+
#
|
92
|
+
# Only call this method when `#multi` was called **without** a block.
|
93
|
+
#
|
94
|
+
# @return [nil, Array<...>]
|
95
|
+
# - when commands were not executed, `nil`
|
96
|
+
# - when commands were executed, an array with their replies
|
97
|
+
#
|
98
|
+
# @see #multi
|
99
|
+
# @see #discard
|
100
|
+
def exec
|
101
|
+
send_command([:exec])
|
102
|
+
end
|
103
|
+
|
104
|
+
# Discard all commands issued after MULTI.
|
105
|
+
#
|
106
|
+
# @return [String] `"OK"`
|
107
|
+
#
|
108
|
+
# @see #multi
|
109
|
+
# @see #exec
|
110
|
+
def discard
|
111
|
+
send_command([:discard])
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,237 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "redis/commands/bitmaps"
|
4
|
+
require "redis/commands/cluster"
|
5
|
+
require "redis/commands/connection"
|
6
|
+
require "redis/commands/geo"
|
7
|
+
require "redis/commands/hashes"
|
8
|
+
require "redis/commands/hyper_log_log"
|
9
|
+
require "redis/commands/keys"
|
10
|
+
require "redis/commands/lists"
|
11
|
+
require "redis/commands/pubsub"
|
12
|
+
require "redis/commands/scripting"
|
13
|
+
require "redis/commands/server"
|
14
|
+
require "redis/commands/sets"
|
15
|
+
require "redis/commands/sorted_sets"
|
16
|
+
require "redis/commands/streams"
|
17
|
+
require "redis/commands/strings"
|
18
|
+
require "redis/commands/transactions"
|
19
|
+
|
20
|
+
class Redis
|
21
|
+
module Commands
|
22
|
+
include Bitmaps
|
23
|
+
include Cluster
|
24
|
+
include Connection
|
25
|
+
include Geo
|
26
|
+
include Hashes
|
27
|
+
include HyperLogLog
|
28
|
+
include Keys
|
29
|
+
include Lists
|
30
|
+
include Pubsub
|
31
|
+
include Scripting
|
32
|
+
include Server
|
33
|
+
include Sets
|
34
|
+
include SortedSets
|
35
|
+
include Streams
|
36
|
+
include Strings
|
37
|
+
include Transactions
|
38
|
+
|
39
|
+
# Commands returning 1 for true and 0 for false may be executed in a pipeline
|
40
|
+
# where the method call will return nil. Propagate the nil instead of falsely
|
41
|
+
# returning false.
|
42
|
+
Boolify = lambda { |value|
|
43
|
+
value != 0 unless value.nil?
|
44
|
+
}
|
45
|
+
|
46
|
+
BoolifySet = lambda { |value|
|
47
|
+
case value
|
48
|
+
when "OK"
|
49
|
+
true
|
50
|
+
when nil
|
51
|
+
false
|
52
|
+
else
|
53
|
+
value
|
54
|
+
end
|
55
|
+
}
|
56
|
+
|
57
|
+
Hashify = lambda { |value|
|
58
|
+
if value.respond_to?(:each_slice)
|
59
|
+
value.each_slice(2).to_h
|
60
|
+
else
|
61
|
+
value
|
62
|
+
end
|
63
|
+
}
|
64
|
+
|
65
|
+
Pairify = lambda { |value|
|
66
|
+
if value.respond_to?(:each_slice)
|
67
|
+
value.each_slice(2).to_a
|
68
|
+
else
|
69
|
+
value
|
70
|
+
end
|
71
|
+
}
|
72
|
+
|
73
|
+
Floatify = lambda { |value|
|
74
|
+
case value
|
75
|
+
when "inf"
|
76
|
+
Float::INFINITY
|
77
|
+
when "-inf"
|
78
|
+
-Float::INFINITY
|
79
|
+
when String
|
80
|
+
Float(value)
|
81
|
+
else
|
82
|
+
value
|
83
|
+
end
|
84
|
+
}
|
85
|
+
|
86
|
+
FloatifyPairs = lambda { |value|
|
87
|
+
return value unless value.respond_to?(:each_slice)
|
88
|
+
|
89
|
+
value.each_slice(2).map do |member, score|
|
90
|
+
[member, Floatify.call(score)]
|
91
|
+
end
|
92
|
+
}
|
93
|
+
|
94
|
+
HashifyInfo = lambda { |reply|
|
95
|
+
lines = reply.split("\r\n").grep_v(/^(#|$)/)
|
96
|
+
lines.map! { |line| line.split(':', 2) }
|
97
|
+
lines.compact!
|
98
|
+
lines.to_h
|
99
|
+
}
|
100
|
+
|
101
|
+
HashifyStreams = lambda { |reply|
|
102
|
+
case reply
|
103
|
+
when nil
|
104
|
+
{}
|
105
|
+
else
|
106
|
+
reply.map { |key, entries| [key, HashifyStreamEntries.call(entries)] }.to_h
|
107
|
+
end
|
108
|
+
}
|
109
|
+
|
110
|
+
EMPTY_STREAM_RESPONSE = [nil].freeze
|
111
|
+
private_constant :EMPTY_STREAM_RESPONSE
|
112
|
+
|
113
|
+
HashifyStreamEntries = lambda { |reply|
|
114
|
+
reply.compact.map do |entry_id, values|
|
115
|
+
[entry_id, values&.each_slice(2)&.to_h]
|
116
|
+
end
|
117
|
+
}
|
118
|
+
|
119
|
+
HashifyStreamAutoclaim = lambda { |reply|
|
120
|
+
{
|
121
|
+
'next' => reply[0],
|
122
|
+
'entries' => reply[1].compact.map do |entry, values|
|
123
|
+
[entry, values.each_slice(2)&.to_h]
|
124
|
+
end
|
125
|
+
}
|
126
|
+
}
|
127
|
+
|
128
|
+
HashifyStreamAutoclaimJustId = lambda { |reply|
|
129
|
+
{
|
130
|
+
'next' => reply[0],
|
131
|
+
'entries' => reply[1]
|
132
|
+
}
|
133
|
+
}
|
134
|
+
|
135
|
+
HashifyStreamPendings = lambda { |reply|
|
136
|
+
{
|
137
|
+
'size' => reply[0],
|
138
|
+
'min_entry_id' => reply[1],
|
139
|
+
'max_entry_id' => reply[2],
|
140
|
+
'consumers' => reply[3].nil? ? {} : reply[3].to_h
|
141
|
+
}
|
142
|
+
}
|
143
|
+
|
144
|
+
HashifyStreamPendingDetails = lambda { |reply|
|
145
|
+
reply.map do |arr|
|
146
|
+
{
|
147
|
+
'entry_id' => arr[0],
|
148
|
+
'consumer' => arr[1],
|
149
|
+
'elapsed' => arr[2],
|
150
|
+
'count' => arr[3]
|
151
|
+
}
|
152
|
+
end
|
153
|
+
}
|
154
|
+
|
155
|
+
HashifyClusterNodeInfo = lambda { |str|
|
156
|
+
arr = str.split(' ')
|
157
|
+
{
|
158
|
+
'node_id' => arr[0],
|
159
|
+
'ip_port' => arr[1],
|
160
|
+
'flags' => arr[2].split(','),
|
161
|
+
'master_node_id' => arr[3],
|
162
|
+
'ping_sent' => arr[4],
|
163
|
+
'pong_recv' => arr[5],
|
164
|
+
'config_epoch' => arr[6],
|
165
|
+
'link_state' => arr[7],
|
166
|
+
'slots' => arr[8].nil? ? nil : Range.new(*arr[8].split('-'))
|
167
|
+
}
|
168
|
+
}
|
169
|
+
|
170
|
+
HashifyClusterSlots = lambda { |reply|
|
171
|
+
reply.map do |arr|
|
172
|
+
first_slot, last_slot = arr[0..1]
|
173
|
+
master = { 'ip' => arr[2][0], 'port' => arr[2][1], 'node_id' => arr[2][2] }
|
174
|
+
replicas = arr[3..-1].map { |r| { 'ip' => r[0], 'port' => r[1], 'node_id' => r[2] } }
|
175
|
+
{
|
176
|
+
'start_slot' => first_slot,
|
177
|
+
'end_slot' => last_slot,
|
178
|
+
'master' => master,
|
179
|
+
'replicas' => replicas
|
180
|
+
}
|
181
|
+
end
|
182
|
+
}
|
183
|
+
|
184
|
+
HashifyClusterNodes = lambda { |reply|
|
185
|
+
reply.split(/[\r\n]+/).map { |str| HashifyClusterNodeInfo.call(str) }
|
186
|
+
}
|
187
|
+
|
188
|
+
HashifyClusterSlaves = lambda { |reply|
|
189
|
+
reply.map { |str| HashifyClusterNodeInfo.call(str) }
|
190
|
+
}
|
191
|
+
|
192
|
+
Noop = ->(reply) { reply }
|
193
|
+
|
194
|
+
# Sends a command to Redis and returns its reply.
|
195
|
+
#
|
196
|
+
# Replies are converted to Ruby objects according to the RESP protocol, so
|
197
|
+
# you can expect a Ruby array, integer or nil when Redis sends one. Higher
|
198
|
+
# level transformations, such as converting an array of pairs into a Ruby
|
199
|
+
# hash, are up to consumers.
|
200
|
+
#
|
201
|
+
# Redis error replies are raised as Ruby exceptions.
|
202
|
+
def call(*command)
|
203
|
+
send_command(command)
|
204
|
+
end
|
205
|
+
|
206
|
+
# Interact with the sentinel command (masters, master, slaves, failover)
|
207
|
+
#
|
208
|
+
# @param [String] subcommand e.g. `masters`, `master`, `slaves`
|
209
|
+
# @param [Array<String>] args depends on subcommand
|
210
|
+
# @return [Array<String>, Hash<String, String>, String] depends on subcommand
|
211
|
+
def sentinel(subcommand, *args)
|
212
|
+
subcommand = subcommand.to_s.downcase
|
213
|
+
send_command([:sentinel, subcommand] + args) do |reply|
|
214
|
+
case subcommand
|
215
|
+
when "get-master-addr-by-name"
|
216
|
+
reply
|
217
|
+
else
|
218
|
+
if reply.is_a?(Array)
|
219
|
+
if reply[0].is_a?(Array)
|
220
|
+
reply.map(&Hashify)
|
221
|
+
else
|
222
|
+
Hashify.call(reply)
|
223
|
+
end
|
224
|
+
else
|
225
|
+
reply
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
private
|
232
|
+
|
233
|
+
def method_missing(*command) # rubocop:disable Style/MissingRespondToMissing
|
234
|
+
send_command(command)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|