valkey-rb 1.0.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.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +58 -0
  3. data/.rubocop_todo.yml +22 -0
  4. data/README.md +95 -0
  5. data/Rakefile +23 -0
  6. data/lib/valkey/bindings.rb +224 -0
  7. data/lib/valkey/commands/bitmap_commands.rb +86 -0
  8. data/lib/valkey/commands/cluster_commands.rb +259 -0
  9. data/lib/valkey/commands/connection_commands.rb +318 -0
  10. data/lib/valkey/commands/function_commands.rb +255 -0
  11. data/lib/valkey/commands/generic_commands.rb +525 -0
  12. data/lib/valkey/commands/geo_commands.rb +87 -0
  13. data/lib/valkey/commands/hash_commands.rb +587 -0
  14. data/lib/valkey/commands/hyper_log_log_commands.rb +51 -0
  15. data/lib/valkey/commands/json_commands.rb +389 -0
  16. data/lib/valkey/commands/list_commands.rb +348 -0
  17. data/lib/valkey/commands/module_commands.rb +125 -0
  18. data/lib/valkey/commands/pubsub_commands.rb +237 -0
  19. data/lib/valkey/commands/scripting_commands.rb +286 -0
  20. data/lib/valkey/commands/server_commands.rb +961 -0
  21. data/lib/valkey/commands/set_commands.rb +220 -0
  22. data/lib/valkey/commands/sorted_set_commands.rb +971 -0
  23. data/lib/valkey/commands/stream_commands.rb +636 -0
  24. data/lib/valkey/commands/string_commands.rb +359 -0
  25. data/lib/valkey/commands/transaction_commands.rb +175 -0
  26. data/lib/valkey/commands/vector_search_commands.rb +271 -0
  27. data/lib/valkey/commands.rb +68 -0
  28. data/lib/valkey/errors.rb +41 -0
  29. data/lib/valkey/libglide_ffi.so +0 -0
  30. data/lib/valkey/opentelemetry.rb +207 -0
  31. data/lib/valkey/pipeline.rb +20 -0
  32. data/lib/valkey/protobuf/command_request_pb.rb +51 -0
  33. data/lib/valkey/protobuf/connection_request_pb.rb +51 -0
  34. data/lib/valkey/protobuf/response_pb.rb +39 -0
  35. data/lib/valkey/pubsub_callback.rb +10 -0
  36. data/lib/valkey/request_error_type.rb +10 -0
  37. data/lib/valkey/request_type.rb +436 -0
  38. data/lib/valkey/response_type.rb +20 -0
  39. data/lib/valkey/utils.rb +253 -0
  40. data/lib/valkey/version.rb +5 -0
  41. data/lib/valkey.rb +551 -0
  42. metadata +119 -0
@@ -0,0 +1,348 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Valkey
4
+ module Commands
5
+ # this module contains commands related to list data type.
6
+ #
7
+ # @see https://valkey.io/commands/#list
8
+ #
9
+ module ListCommands
10
+ # Get the length of a list.
11
+ #
12
+ # @param [String] key
13
+ # @return [Integer]
14
+ def llen(key)
15
+ send_command(RequestType::LLEN, [key])
16
+ end
17
+
18
+ # Remove the first/last element in a list, append/prepend it to another list and return it.
19
+ #
20
+ # @param [String] source source key
21
+ # @param [String] destination destination key
22
+ # @param [String, Symbol] where_source from where to remove the element from the source list
23
+ # e.g. 'LEFT' - from head, 'RIGHT' - from tail
24
+ # @param [String, Symbol] where_destination where to push the element to the source list
25
+ # e.g. 'LEFT' - to head, 'RIGHT' - to tail
26
+ #
27
+ # @return [nil, String] the element, or nil when the source key does not exist
28
+ #
29
+ # @note This command comes in place of the now deprecated RPOPLPUSH.
30
+ # Doing LMOVE RIGHT LEFT is equivalent.
31
+ def lmove(source, destination, where_source, where_destination)
32
+ where_source, where_destination = _normalize_move_wheres(where_source, where_destination)
33
+
34
+ send_command(RequestType::LMOVE, [source, destination, where_source, where_destination])
35
+ end
36
+
37
+ # Remove the first/last element in a list and append/prepend it
38
+ # to another list and return it, or block until one is available.
39
+ #
40
+ # @example With timeout
41
+ # element = valkey.blmove("foo", "bar", "LEFT", "RIGHT", timeout: 5)
42
+ # # => nil on timeout
43
+ # # => "element" on success
44
+ # @example Without timeout
45
+ # element = valkey.blmove("foo", "bar", "LEFT", "RIGHT")
46
+ # # => "element"
47
+ #
48
+ # @param [String] source source key
49
+ # @param [String] destination destination key
50
+ # @param [String, Symbol] where_source from where to remove the element from the source list
51
+ # e.g. 'LEFT' - from head, 'RIGHT' - from tail
52
+ # @param [String, Symbol] where_destination where to push the element to the source list
53
+ # e.g. 'LEFT' - to head, 'RIGHT' - to tail
54
+ # @param [Hash] options
55
+ # - `:timeout => [Float, Integer]`: timeout in seconds, defaults to no timeout
56
+ #
57
+ # @return [nil, String] the element, or nil when the source key does not exist or the timeout expired
58
+ #
59
+ def blmove(source, destination, where_source, where_destination, timeout: 0)
60
+ where_source, where_destination = _normalize_move_wheres(where_source, where_destination)
61
+
62
+ args = [:blmove, source, destination, where_source, where_destination, timeout]
63
+ send_command(RequestType::BLMOVE, args)
64
+ end
65
+
66
+ # Prepend one or more values to a list, creating the list if it doesn't exist
67
+ #
68
+ # @param [String] key
69
+ # @param [String, Array<String>] value string value, or array of string values to push
70
+ # @return [Integer] the length of the list after the push operation
71
+ def lpush(key, value)
72
+ send_command(RequestType::LPUSH, [key, *value])
73
+ end
74
+
75
+ # Prepend a value to a list, only if the list exists.
76
+ #
77
+ # @param [String] key
78
+ # @param [String] value
79
+ # @return [Integer] the length of the list after the push operation
80
+ def lpushx(key, value)
81
+ send_command(RequestType::LPUSHX, [key, value])
82
+ end
83
+
84
+ # Append one or more values to a list, creating the list if it doesn't exist
85
+ #
86
+ # @param [String] key
87
+ # @param [String, Array<String>] value string value, or array of string values to push
88
+ # @return [Integer] the length of the list after the push operation
89
+ def rpush(key, value)
90
+ value = [value] unless value.is_a?(Array)
91
+
92
+ args = [key] + value
93
+
94
+ send_command(RequestType::RPUSH, args)
95
+ end
96
+
97
+ # Append a value to a list, only if the list exists.
98
+ #
99
+ # @param [String] key
100
+ # @param [String] value
101
+ # @return [Integer] the length of the list after the push operation
102
+ def rpushx(key, value)
103
+ send_command(RequestType::RPUSHX, [key, value])
104
+ end
105
+
106
+ # Remove and get the first elements in a list.
107
+ #
108
+ # @param [String] key
109
+ # @param [Integer] count number of elements to remove
110
+ # @return [nil, String, Array<String>] the values of the first elements
111
+ def lpop(key, count = nil)
112
+ args = [key]
113
+ args << Integer(count) if count
114
+ send_command(RequestType::LPOP, args)
115
+ end
116
+
117
+ # Remove and get the last elements in a list.
118
+ #
119
+ # @param [String] key
120
+ # @param [Integer] count number of elements to remove
121
+ # @return [nil, String, Array<String>] the values of the last elements
122
+ def rpop(key, count = nil)
123
+ args = [key]
124
+ args << Integer(count) if count
125
+ send_command(RequestType::RPOP, args)
126
+ end
127
+
128
+ # Remove the last element in a list, append it to another list and return it.
129
+ #
130
+ # @param [String] source source key
131
+ # @param [String] destination destination key
132
+ # @return [nil, String] the element, or nil when the source key does not exist
133
+ def rpoplpush(source, destination)
134
+ send_command(RequestType::RPOPLPUSH, [source, destination])
135
+ end
136
+
137
+ # Remove and get the first element in a list, or block until one is available.
138
+ #
139
+ # @example With timeout
140
+ # list, element = valkey.blpop("list", :timeout => 5)
141
+ # # => nil on timeout
142
+ # # => ["list", "element"] on success
143
+ # @example Without timeout
144
+ # list, element = valkey.blpop("list")
145
+ # # => ["list", "element"]
146
+ # @example Blocking pop on multiple lists
147
+ # list, element = valkey.blpop(["list", "another_list"])
148
+ # # => ["list", "element"]
149
+ #
150
+ # @param [String, Array<String>] keys one or more keys to perform the
151
+ # blocking pop on
152
+ # @param [Hash] options
153
+ # - `:timeout => [Float, Integer]`: timeout in seconds, defaults to no timeout
154
+ #
155
+ # @return [nil, [String, String]]
156
+ # - `nil` when the operation timed out
157
+ # - tuple of the list that was popped from and element was popped otherwise
158
+ def blpop(*args)
159
+ _bpop(:blpop, args)
160
+ end
161
+
162
+ # Remove and get the last element in a list, or block until one is available.
163
+ #
164
+ # @param [String, Array<String>] keys one or more keys to perform the
165
+ # blocking pop on
166
+ # @param [Hash] options
167
+ # - `:timeout => [Float, Integer]`: timeout in seconds, defaults to no timeout
168
+ #
169
+ # @return [nil, [String, String]]
170
+ # - `nil` when the operation timed out
171
+ # - tuple of the list that was popped from and element was popped otherwise
172
+ #
173
+ # @see #blpop
174
+ def brpop(*args)
175
+ _bpop(RequestType::BRPOP, args.flatten)
176
+ end
177
+
178
+ # Pop a value from a list, push it to another list and return it; or block
179
+ # until one is available.
180
+ #
181
+ # @param [String] source source key
182
+ # @param [String] destination destination key
183
+ # @param [Hash] options
184
+ # - `:timeout => [Float, Integer]`: timeout in seconds, defaults to no timeout
185
+ #
186
+ # @return [nil, String]
187
+ # - `nil` when the operation timed out
188
+ # - the element was popped and pushed otherwise
189
+ def brpoplpush(source, destination, timeout: 0)
190
+ args = [:brpoplpush, source, destination, timeout]
191
+ send_blocking_command(RequestType::BRPOPLPUSH, args, timeout)
192
+ end
193
+
194
+ # Pops one or more elements from the first non-empty list key from the list
195
+ # of provided key names. If lists are empty, blocks until timeout has passed.
196
+ #
197
+ # @example Popping a element
198
+ # valkey.blmpop(1.0, 'list')
199
+ # #=> ['list', ['a']]
200
+ # @example With count option
201
+ # valkey.blmpop(1.0, 'list', count: 2)
202
+ # #=> ['list', ['a', 'b']]
203
+ #
204
+ # @params timeout [Float] a float value specifying the maximum number of seconds to block) elapses.
205
+ # A timeout of zero can be used to block indefinitely.
206
+ # @params key [String, Array<String>] one or more keys with lists
207
+ # @params modifier [String]
208
+ # - when `"LEFT"` - the elements popped are those from the left of the list
209
+ # - when `"RIGHT"` - the elements popped are those from the right of the list
210
+ # @params count [Integer] a number of elements to pop
211
+ #
212
+ # @return [Array<String, Array<String, Float>>] list of popped elements or nil
213
+ def blmpop(timeout, *keys, modifier: "LEFT", count: nil)
214
+ raise ArgumentError, "Pick either LEFT or RIGHT" unless %w[LEFT RIGHT].include?(modifier)
215
+
216
+ args = [timeout, keys.size, *keys, modifier]
217
+ args << "COUNT" << Integer(count) if count
218
+
219
+ send_command(RequestType::BLMPOP, args)
220
+ end
221
+
222
+ # Pops one or more elements from the first non-empty list key from the list
223
+ # of provided key names.
224
+ #
225
+ # @example Popping a element
226
+ # valkey.lmpop('list')
227
+ # #=> ['list', ['a']]
228
+ # @example With count option
229
+ # valkey.lmpop('list', count: 2)
230
+ # #=> ['list', ['a', 'b']]
231
+ #
232
+ # @params key [String, Array<String>] one or more keys with lists
233
+ # @params modifier [String]
234
+ # - when `"LEFT"` - the elements popped are those from the left of the list
235
+ # - when `"RIGHT"` - the elements popped are those from the right of the list
236
+ # @params count [Integer] a number of elements to pop
237
+ #
238
+ # @return [Array<String, Array<String, Float>>] list of popped elements or nil
239
+ def lmpop(*keys, modifier: "LEFT", count: nil)
240
+ raise ArgumentError, "Pick either LEFT or RIGHT" unless %w[LEFT RIGHT].include?(modifier)
241
+
242
+ args = [keys.size, *keys, modifier]
243
+ args << "COUNT" << Integer(count) if count
244
+
245
+ # pp args
246
+
247
+ send_command(RequestType::LMPOP, args)
248
+ end
249
+
250
+ # Get an element from a list by its index.
251
+ #
252
+ # @param [String] key
253
+ # @param [Integer] index
254
+ # @return [String]
255
+ def lindex(key, index)
256
+ send_command(RequestType::LINDEX, [key, Integer(index)])
257
+ end
258
+
259
+ # Insert an element before or after another element in a list.
260
+ #
261
+ # @param [String] key
262
+ # @param [String, Symbol] where `BEFORE` or `AFTER`
263
+ # @param [String] pivot reference element
264
+ # @param [String] value
265
+ # @return [Integer] length of the list after the insert operation, or `-1`
266
+ # when the element `pivot` was not found
267
+ def linsert(key, where, pivot, value)
268
+ send_command(RequestType::LINSERT, [key, where, pivot, value])
269
+ end
270
+
271
+ # Get a range of elements from a list.
272
+ #
273
+ # @param [String] key
274
+ # @param [Integer] start start index
275
+ # @param [Integer] stop stop index
276
+ # @return [Array<String>]
277
+ def lrange(key, start, stop)
278
+ send_command(RequestType::LRANGE, [key, Integer(start), Integer(stop)])
279
+ end
280
+
281
+ # Remove elements from a list.
282
+ #
283
+ # @param [String] key
284
+ # @param [Integer] count number of elements to remove. Use a positive
285
+ # value to remove the first `count` occurrences of `value`. A negative
286
+ # value to remove the last `count` occurrences of `value`. Or zero, to
287
+ # remove all occurrences of `value` from the list.
288
+ # @param [String] value
289
+ # @return [Integer] the number of removed elements
290
+ def lrem(key, count, value)
291
+ send_command(RequestType::LREM, [key, Integer(count), value])
292
+ end
293
+
294
+ # Set the value of an element in a list by its index.
295
+ #
296
+ # @param [String] key
297
+ # @param [Integer] index
298
+ # @param [String] value
299
+ # @return [String] `OK`
300
+ def lset(key, index, value)
301
+ send_command(RequestType::LSET, [key, Integer(index), value])
302
+ end
303
+
304
+ # Trim a list to the specified range.
305
+ #
306
+ # @param [String] key
307
+ # @param [Integer] start start index
308
+ # @param [Integer] stop stop index
309
+ # @return [String] `OK`
310
+ def ltrim(key, start, stop)
311
+ send_command(RequestType::LTRIM, [key, Integer(start), Integer(stop)])
312
+ end
313
+
314
+ private
315
+
316
+ def _bpop(cmd, args, &blk)
317
+ timeout = if args.last.is_a?(Hash)
318
+ options = args.pop
319
+ options[:timeout]
320
+ end
321
+
322
+ timeout ||= 0
323
+ unless timeout.is_a?(Integer) || timeout.is_a?(Float)
324
+ raise ArgumentError, "timeout must be an Integer or Float, got: #{timeout.class}"
325
+ end
326
+
327
+ args.flatten!(1)
328
+ args << timeout
329
+ send_blocking_command(cmd, args, &blk)
330
+ end
331
+
332
+ def _normalize_move_wheres(where_source, where_destination)
333
+ where_source = where_source.to_s.upcase
334
+ where_destination = where_destination.to_s.upcase
335
+
336
+ if where_source != "LEFT" && where_source != "RIGHT"
337
+ raise ArgumentError, "where_source must be 'LEFT' or 'RIGHT'"
338
+ end
339
+
340
+ if where_destination != "LEFT" && where_destination != "RIGHT"
341
+ raise ArgumentError, "where_destination must be 'LEFT' or 'RIGHT'"
342
+ end
343
+
344
+ [where_source, where_destination]
345
+ end
346
+ end
347
+ end
348
+ end
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Valkey
4
+ module Commands
5
+ # This module contains commands related to Valkey Modules.
6
+ #
7
+ # @see https://valkey.io/commands/#module
8
+ #
9
+ module ModuleCommands
10
+ # List all loaded modules.
11
+ #
12
+ # @example List all modules
13
+ # valkey.module_list
14
+ # # => [{"name" => "mymodule", "ver" => 1, ...}]
15
+ #
16
+ # @return [Array<Hash>] array of module information
17
+ #
18
+ # @see https://valkey.io/commands/module-list/
19
+ def module_list
20
+ send_command(RequestType::MODULE_LIST)
21
+ end
22
+
23
+ # Load a module.
24
+ #
25
+ # @example Load a module
26
+ # valkey.module_load("/path/to/mymodule.so")
27
+ # # => "OK"
28
+ # @example Load a module with arguments
29
+ # valkey.module_load("/path/to/mymodule.so", "arg1", "arg2")
30
+ # # => "OK"
31
+ #
32
+ # @param [String] path the path to the module file
33
+ # @param [Array<String>] args optional arguments to pass to the module
34
+ # @return [String] "OK"
35
+ #
36
+ # @see https://valkey.io/commands/module-load/
37
+ def module_load(path, *args)
38
+ command_args = [path] + args
39
+ send_command(RequestType::MODULE_LOAD, command_args)
40
+ end
41
+
42
+ # Unload a module.
43
+ #
44
+ # @example Unload a module
45
+ # valkey.module_unload("mymodule")
46
+ # # => "OK"
47
+ #
48
+ # @param [String] name the module name to unload
49
+ # @return [String] "OK"
50
+ #
51
+ # @see https://valkey.io/commands/module-unload/
52
+ def module_unload(name)
53
+ send_command(RequestType::MODULE_UNLOAD, [name])
54
+ end
55
+
56
+ # Load a module with extended options.
57
+ #
58
+ # @example Load a module with CONFIG option
59
+ # valkey.module_loadex("/path/to/mymodule.so", configs: {"param1" => "value1"})
60
+ # # => "OK"
61
+ # @example Load a module with ARGS option
62
+ # valkey.module_loadex("/path/to/mymodule.so", args: ["arg1", "arg2"])
63
+ # # => "OK"
64
+ # @example Load a module with both CONFIG and ARGS
65
+ # valkey.module_loadex("/path/to/mymodule.so", configs: {"param1" => "value1"}, args: ["arg1"])
66
+ # # => "OK"
67
+ #
68
+ # @param [String] path the path to the module file
69
+ # @param [Hash] configs configuration parameters as key-value pairs
70
+ # @param [Array<String>] args optional arguments to pass to the module
71
+ # @return [String] "OK"
72
+ #
73
+ # @see https://valkey.io/commands/module-loadex/
74
+ def module_loadex(path, configs: {}, args: [])
75
+ command_args = [path]
76
+
77
+ unless configs.empty?
78
+ command_args << "CONFIG"
79
+ configs.each do |key, value|
80
+ command_args << key.to_s
81
+ command_args << value.to_s
82
+ end
83
+ end
84
+
85
+ unless args.empty?
86
+ command_args << "ARGS"
87
+ command_args.concat(args)
88
+ end
89
+
90
+ send_command(RequestType::MODULE_LOAD_EX, command_args)
91
+ end
92
+
93
+ # Control module registry (convenience method).
94
+ #
95
+ # @example List all modules
96
+ # valkey.module(:list)
97
+ # # => [...]
98
+ # @example Load a module
99
+ # valkey.module(:load, "/path/to/mymodule.so")
100
+ # # => "OK"
101
+ # @example Unload a module
102
+ # valkey.module(:unload, "mymodule")
103
+ # # => "OK"
104
+ # @example Load a module with extended options
105
+ # valkey.module(:loadex, "/path/to/mymodule.so", configs: {"param1" => "value1"})
106
+ # # => "OK"
107
+ #
108
+ # @param [String, Symbol] subcommand the subcommand (list, load, unload, loadex)
109
+ # @param [Array] args arguments for the subcommand
110
+ # @param [Hash] options options for the subcommand
111
+ # @return [Object] depends on subcommand
112
+ def module(subcommand, *args, **options)
113
+ subcommand = subcommand.to_s.downcase
114
+
115
+ if args.empty? && options.empty?
116
+ send("module_#{subcommand}")
117
+ elsif options.empty?
118
+ send("module_#{subcommand}", *args)
119
+ else
120
+ send("module_#{subcommand}", *args, **options)
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end