valkey-glide-rb 1.0.0.pre.rc5 → 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.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -1
- data/AGENTS.md +8 -8
- data/CLAUDE.md +1 -1
- data/DEVELOPER.md +3 -3
- data/README.md +13 -17
- data/lib/valkey/commands/connection_commands.rb +27 -29
- data/lib/valkey/commands/function_commands.rb +2 -2
- data/lib/valkey/commands/list_commands.rb +4 -4
- data/lib/valkey/commands/server_commands.rb +19 -10
- data/lib/valkey/commands/string_commands.rb +4 -11
- data/lib/valkey/commands/transaction_commands.rb +15 -30
- data/lib/valkey/commands.rb +2 -1
- data/lib/valkey/native/aarch64-apple-darwin/libglide_ffi.dylib +0 -0
- data/lib/valkey/native/aarch64-unknown-linux-gnu/libglide_ffi.so +0 -0
- data/lib/valkey/native/aarch64-unknown-linux-musl/libglide_ffi.so +0 -0
- data/lib/valkey/native/x86_64-unknown-linux-gnu/libglide_ffi.so +0 -0
- data/lib/valkey/native/x86_64-unknown-linux-musl/libglide_ffi.so +0 -0
- data/lib/valkey/utils.rb +3 -3
- data/lib/valkey/version.rb +1 -1
- data/lib/valkey.rb +5 -5
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4f24251c31a635bac9aff04c0f4ae8027c9badf52acc0547f4dc463515b3eba7
|
|
4
|
+
data.tar.gz: dd7e0050d647a2782a551d9266c96ff73a9a6d3c9405dc2eecb921a6359e5aff
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ee2da87f8c6c1806541476ccd752750c7320561c1010373fb7a9f679d41a321642adc6f3833b8dd04a142088a47b0ed6e9a3df03b8e34bd3c7cd0d8a2789f016
|
|
7
|
+
data.tar.gz: 8d862e3f9d1709a09a49dc8a2523afd5b61241a2068d73a64b2aa7db5e2d6c04d0402318877e21c7f15f96d5146c4e060b5a5085bb4236f99999012757684e8e
|
data/.rubocop.yml
CHANGED
data/AGENTS.md
CHANGED
|
@@ -4,7 +4,7 @@ This file provides AI agents and developers with the minimum but sufficient cont
|
|
|
4
4
|
|
|
5
5
|
## Repository Overview
|
|
6
6
|
|
|
7
|
-
This is the **Ruby client** for Valkey GLIDE, published as the `valkey-glide-rb` gem. It provides a synchronous
|
|
7
|
+
This is the **Ruby client** for Valkey GLIDE, published as the `valkey-glide-rb` gem. It provides a synchronous API on top of the Rust GLIDE core via FFI.
|
|
8
8
|
|
|
9
9
|
**Primary Languages:** Ruby, Rust (FFI native library, built separately from [valkey-glide](https://github.com/valkey-io/valkey-glide))
|
|
10
10
|
|
|
@@ -20,7 +20,7 @@ This is the **Ruby client** for Valkey GLIDE, published as the `valkey-glide-rb`
|
|
|
20
20
|
- `lib/valkey/opentelemetry.rb` — Native OTel configuration
|
|
21
21
|
- `test/valkey/` — Standalone integration tests
|
|
22
22
|
- `test/cluster/` — Cluster integration tests
|
|
23
|
-
- `test/lint/` —
|
|
23
|
+
- `test/lint/` — Lint suites
|
|
24
24
|
|
|
25
25
|
## Architecture Quick Facts
|
|
26
26
|
|
|
@@ -28,7 +28,7 @@ This is the **Ruby client** for Valkey GLIDE, published as the `valkey-glide-rb`
|
|
|
28
28
|
|
|
29
29
|
**Client Types:** `Valkey` — standalone or cluster (`cluster_mode: true`)
|
|
30
30
|
|
|
31
|
-
**API Style:** Synchronous, blocking calls
|
|
31
|
+
**API Style:** Synchronous, blocking calls.
|
|
32
32
|
|
|
33
33
|
**Communication:** Direct FFI (`Bindings.command`, `Bindings.batch`)
|
|
34
34
|
|
|
@@ -38,7 +38,7 @@ This is the **Ruby client** for Valkey GLIDE, published as the `valkey-glide-rb`
|
|
|
38
38
|
- Alpine Linux 3.18+ (x86_64, aarch64) — musl libc
|
|
39
39
|
- macOS: 13.7+ (x86_64), 14.7+ (aarch64)
|
|
40
40
|
|
|
41
|
-
**Ruby Versions:**
|
|
41
|
+
**Ruby Versions:** 3.0, 3.1, 3.2, 3.3, 3.4, JRuby (CI matrix)
|
|
42
42
|
|
|
43
43
|
**Gem name:** `valkey-glide-rb` on RubyGems
|
|
44
44
|
|
|
@@ -162,10 +162,10 @@ cargo fmt --manifest-path ./Cargo.toml --all
|
|
|
162
162
|
|
|
163
163
|
### Ruby-Specific Rules
|
|
164
164
|
|
|
165
|
-
- **Ruby
|
|
165
|
+
- **Ruby 3.0+ Required:** Minimum per `valkey.gemspec`
|
|
166
166
|
- **FFI dependency:** `ffi ~> 1.17.0` — do not break ABI without rebuilding native lib
|
|
167
167
|
- **Synchronous only:** No async client in this repo; do not add EventMachine/async patterns without design review
|
|
168
|
-
- **redis-rb
|
|
168
|
+
- **redis-rb conventions:** Prefer matching redis-rb method signatures and return types when implementing commands for familiarity.
|
|
169
169
|
- **Command args:** All FFI args are strings; convert types in Ruby before `send_command`
|
|
170
170
|
- **Pipeline transactions:** `MULTI`/`EXEC`/`DISCARD` in `pipelined` use sequential fallback — do not remove without fixing FFI batch stability
|
|
171
171
|
- **OpenTelemetry:** Init once per process via `Valkey::OpenTelemetry.init`; spans created in FFI layer
|
|
@@ -220,10 +220,10 @@ valkey-glide-ruby/
|
|
|
220
220
|
## Quick Facts for Reasoners
|
|
221
221
|
|
|
222
222
|
**Package:** `valkey-glide-rb` on RubyGems
|
|
223
|
-
**API Style:** Synchronous
|
|
223
|
+
**API Style:** Synchronous.
|
|
224
224
|
**Client:** `Valkey.new` — standalone or `cluster_mode: true`
|
|
225
225
|
**Key Features:** Pipelining, OpenTelemetry (native), statistics, TLS, URL parsing, cluster routing
|
|
226
|
-
**Testing:** Minitest + rake tasks; lint suites
|
|
226
|
+
**Testing:** Minitest + rake tasks; lint suites.
|
|
227
227
|
**Core repo:** [valkey-glide](https://github.com/valkey-io/valkey-glide) (`ffi/`, `glide-core/`)
|
|
228
228
|
**This repo:** [valkey-glide-ruby](https://github.com/valkey-io/valkey-glide-ruby)
|
|
229
229
|
|
data/CLAUDE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# CLAUDE.md
|
|
2
2
|
|
|
3
|
-
Valkey GLIDE Ruby (`valkey-glide-rb`) is the official Ruby binding for Valkey and Redis OSS. It uses the shared Rust **glide-core** driver via the **glide-ffi** C library
|
|
3
|
+
Valkey GLIDE Ruby (`valkey-glide-rb`) is the official Ruby binding for Valkey and Redis OSS. It uses the shared Rust **glide-core** driver via the **glide-ffi** C library. This repository is separate from the [valkey-glide](https://github.com/valkey-io/valkey-glide) mono-repo (Python, Java, Node, Go).
|
|
4
4
|
|
|
5
5
|
## Hard Constraints (non-negotiable)
|
|
6
6
|
|
data/DEVELOPER.md
CHANGED
|
@@ -54,7 +54,7 @@ valkey-glide-ruby/
|
|
|
54
54
|
|
|
55
55
|
### Software Dependencies
|
|
56
56
|
|
|
57
|
-
- **Ruby**
|
|
57
|
+
- **Ruby** 3.0+
|
|
58
58
|
- **Bundler**
|
|
59
59
|
- **git**
|
|
60
60
|
- **Valkey** or Redis OSS (for integration tests)
|
|
@@ -352,8 +352,8 @@ GitHub Actions (`.github/workflows/ci.yml`):
|
|
|
352
352
|
| Job | Matrix |
|
|
353
353
|
|-----|--------|
|
|
354
354
|
| `lint` | Ruby 3.3, RuboCop |
|
|
355
|
-
| `standalone` | Ruby
|
|
356
|
-
| `cluster` | Ruby
|
|
355
|
+
| `standalone` | Ruby 3.0–3.4 + JRuby; Valkey 7.2, 8, 8.1 |
|
|
356
|
+
| `cluster` | Ruby 3.0–3.4; grokzen/redis-cluster |
|
|
357
357
|
|
|
358
358
|
## Building the Gem Locally
|
|
359
359
|
|
data/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
# Valkey GLIDE for Ruby
|
|
3
3
|
|
|
4
|
-
Valkey General Language Independent Driver for the Enterprise (GLIDE) is the official open-source Valkey client library, proudly part of the [Valkey](https://valkey.io) organization. The Ruby gem (`valkey-glide-rb`) wraps [Valkey GLIDE Core](https://github.com/valkey-io/valkey-glide)
|
|
4
|
+
Valkey General Language Independent Driver for the Enterprise (GLIDE) is the official open-source Valkey client library, proudly part of the [Valkey](https://valkey.io) organization. The Ruby gem (`valkey-glide-rb`) wraps [Valkey GLIDE Core](https://github.com/valkey-io/valkey-glide), delivering GLIDE performance, reliability, and enterprise features.
|
|
5
5
|
|
|
6
6
|
## Why Choose Valkey GLIDE?
|
|
7
7
|
|
|
@@ -10,7 +10,6 @@ Valkey General Language Independent Driver for the Enterprise (GLIDE) is the off
|
|
|
10
10
|
- **Performance**: Optimized for high performance and low latency via the Rust-based GLIDE core.
|
|
11
11
|
- **High Availability**: Cluster-aware routing, reconnection, and fault tolerance.
|
|
12
12
|
- **Cross-Language Consistency**: Same core driver as Python, Java, Node.js, and Go clients.
|
|
13
|
-
- **Drop-in Replacement**: Familiar redis-rb-style API (`Valkey.new`, command methods, `pipelined`, URL parsing).
|
|
14
13
|
- **Observability**: Native OpenTelemetry tracing and client statistics.
|
|
15
14
|
|
|
16
15
|
## Documentation
|
|
@@ -36,16 +35,13 @@ Valkey General Language Independent Driver for the Enterprise (GLIDE) is the off
|
|
|
36
35
|
|
|
37
36
|
#### Supported OS
|
|
38
37
|
|
|
39
|
-
The following platforms
|
|
40
|
-
- Ubuntu 24
|
|
41
|
-
- Alpine Linux 3
|
|
42
|
-
- macOS 14 (Apple silicon /
|
|
38
|
+
The following platforms are tested in CI:
|
|
39
|
+
- Ubuntu 24 (x86_64 and arm64)
|
|
40
|
+
- Alpine Linux 3 (x86_64 and arm64, via musl targets)
|
|
41
|
+
- macOS 14+ (Apple silicon / arm64)
|
|
43
42
|
|
|
44
|
-
**Notes:**
|
|
45
|
-
|
|
46
|
-
meet the glibc 2.17+ / musl 1.2.3+ requirement above.
|
|
47
|
-
* Only Apple silicon (ARM) macOS is shipped, with a minimum of macOS 11.
|
|
48
|
-
Intel macOS users must build locally — see [build from source](./DEVELOPER.md).
|
|
43
|
+
**Notes:** valkey-glide-rb gem only support ARM MacOS. For Intel Mac users
|
|
44
|
+
you will need to build the client locally.
|
|
49
45
|
|
|
50
46
|
### Installation and Setup
|
|
51
47
|
|
|
@@ -87,7 +83,7 @@ client.get("mykey")
|
|
|
87
83
|
client.close
|
|
88
84
|
```
|
|
89
85
|
|
|
90
|
-
### Standalone with URL
|
|
86
|
+
### Standalone with URL
|
|
91
87
|
|
|
92
88
|
Accepted URL schemes: `redis://`, `rediss://` (TLS), `valkey://`, `valkeys://` (TLS).
|
|
93
89
|
|
|
@@ -328,17 +324,17 @@ See https://github.com/valkey-io/valkey-glide-ruby/issues/135
|
|
|
328
324
|
| `lib/valkey/pipeline.rb` | Pipeline command batching |
|
|
329
325
|
| `test/valkey/` | Standalone integration tests |
|
|
330
326
|
| `test/cluster/` | Cluster integration tests |
|
|
331
|
-
| `test/lint/` | Shared lint tests (redis-rb
|
|
327
|
+
| `test/lint/` | Shared lint tests (redis-rb convention patterns) |
|
|
332
328
|
|
|
333
|
-
##
|
|
329
|
+
## API Conventions
|
|
334
330
|
|
|
335
|
-
This client
|
|
331
|
+
This client is **not** a drop-in replacement for redis-rb, but it follows familiar Ruby conventions to ease adoption:
|
|
336
332
|
|
|
337
333
|
- `Valkey.new` with `url`, `host`, `port`, `db`, `ssl_params`
|
|
338
|
-
-
|
|
334
|
+
- Conventional command method names and argument ordering
|
|
339
335
|
- `pipelined`, `multi` / `exec`, `disconnect!` (alias of `close`)
|
|
340
336
|
|
|
341
|
-
|
|
337
|
+
APIs and behavior may differ from redis-rb; verify against your usage. See the [command implementation wiki](https://github.com/valkey-io/valkey-glide-ruby/wiki/The-implementation-status-of-the-Valkey-commands) for coverage.
|
|
342
338
|
|
|
343
339
|
## Building and Testing
|
|
344
340
|
|
|
@@ -19,13 +19,13 @@ class Valkey
|
|
|
19
19
|
# Ping the server.
|
|
20
20
|
#
|
|
21
21
|
# @param message [String, nil] optional message to echo back
|
|
22
|
-
# @param route [Valkey::Route, nil] cluster routing.
|
|
23
|
-
# @return [String]
|
|
22
|
+
# @param route [Valkey::Route, nil] cluster routing.
|
|
23
|
+
# @return [String] `"PONG"`, or the echoed `message` if given.
|
|
24
24
|
#
|
|
25
25
|
# @example
|
|
26
|
-
# ping
|
|
27
|
-
# ping("hello")
|
|
28
|
-
# ping(route: Valkey::Route.all_nodes) #=>
|
|
26
|
+
# ping #=> "PONG"
|
|
27
|
+
# ping("hello") #=> "hello"
|
|
28
|
+
# ping(route: Valkey::Route.all_nodes) #=> "PONG"
|
|
29
29
|
def ping(message = nil, route: nil)
|
|
30
30
|
send_command(RequestType::PING, [message].compact, route: route)
|
|
31
31
|
end
|
|
@@ -33,8 +33,9 @@ class Valkey
|
|
|
33
33
|
# Echo the given string.
|
|
34
34
|
#
|
|
35
35
|
# @param value [String]
|
|
36
|
-
# @param route [Valkey::Route, nil] cluster routing.
|
|
37
|
-
#
|
|
36
|
+
# @param route [Valkey::Route, nil] cluster routing. Default is a single random node.
|
|
37
|
+
# A multi-node route returns a `Hash` of `"host:port" => reply`.
|
|
38
|
+
# @return [String, Hash{String => String}]
|
|
38
39
|
def echo(value, route: nil)
|
|
39
40
|
send_command(RequestType::ECHO, [value], route: route)
|
|
40
41
|
end
|
|
@@ -87,8 +88,9 @@ class Valkey
|
|
|
87
88
|
|
|
88
89
|
# Get the current client's ID.
|
|
89
90
|
#
|
|
90
|
-
# @param route [Valkey::Route, nil] cluster routing.
|
|
91
|
-
#
|
|
91
|
+
# @param route [Valkey::Route, nil] cluster routing. Default is a single random node.
|
|
92
|
+
# A multi-node route returns a `Hash` of `"host:port" => Integer`.
|
|
93
|
+
# @return [Integer, Hash{String => Integer}]
|
|
92
94
|
def client_id(route: nil)
|
|
93
95
|
send_command(RequestType::CLIENT_ID, [], route: route)
|
|
94
96
|
end
|
|
@@ -161,26 +163,6 @@ class Valkey
|
|
|
161
163
|
send_command(RequestType::CLIENT_KILL_SIMPLE, [addr])
|
|
162
164
|
end
|
|
163
165
|
|
|
164
|
-
private
|
|
165
|
-
|
|
166
|
-
def build_client_kill_args(addr, options)
|
|
167
|
-
args = []
|
|
168
|
-
args << "ADDR" << addr if addr
|
|
169
|
-
options.each do |key, value|
|
|
170
|
-
case key
|
|
171
|
-
when :id then args << "ID" << value.to_s
|
|
172
|
-
when :type then args << "TYPE" << value.to_s
|
|
173
|
-
when :user then args << "USER" << value.to_s
|
|
174
|
-
when :addr then args << "ADDR" << value.to_s
|
|
175
|
-
when :laddr then args << "LADDR" << value.to_s
|
|
176
|
-
when :skipme then args << "SKIPME" << (value ? "yes" : "no")
|
|
177
|
-
end
|
|
178
|
-
end
|
|
179
|
-
args
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
public
|
|
183
|
-
|
|
184
166
|
# Pause client processing.
|
|
185
167
|
#
|
|
186
168
|
# @param [Integer] timeout Pause duration in milliseconds
|
|
@@ -271,6 +253,22 @@ class Valkey
|
|
|
271
253
|
|
|
272
254
|
private
|
|
273
255
|
|
|
256
|
+
def build_client_kill_args(addr, options)
|
|
257
|
+
args = []
|
|
258
|
+
args << "ADDR" << addr if addr
|
|
259
|
+
options.each do |key, value|
|
|
260
|
+
case key
|
|
261
|
+
when :id then args << "ID" << value.to_s
|
|
262
|
+
when :type then args << "TYPE" << value.to_s
|
|
263
|
+
when :user then args << "USER" << value.to_s
|
|
264
|
+
when :addr then args << "ADDR" << value.to_s
|
|
265
|
+
when :laddr then args << "LADDR" << value.to_s
|
|
266
|
+
when :skipme then args << "SKIPME" << (value ? "yes" : "no")
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
args
|
|
270
|
+
end
|
|
271
|
+
|
|
274
272
|
def build_client_tracking_args(options)
|
|
275
273
|
args = []
|
|
276
274
|
options.each do |key, value|
|
|
@@ -169,10 +169,10 @@ class Valkey
|
|
|
169
169
|
#
|
|
170
170
|
# @example Get function stats
|
|
171
171
|
# valkey.function_stats
|
|
172
|
-
# # => {"running_script" =>
|
|
172
|
+
# # => {"127.0.0.1:6379" => {"running_script" => nil, "engines" => {...}}}
|
|
173
173
|
#
|
|
174
174
|
# @param route [Valkey::Route, nil] cluster routing. When routed, may return a Hash of node => value.
|
|
175
|
-
# @return [Hash]
|
|
175
|
+
# @return [Hash{String => Hash}] a Hash keyed by `"host:port"`.
|
|
176
176
|
#
|
|
177
177
|
# @see https://valkey.io/commands/function-stats/
|
|
178
178
|
def function_stats(route: nil)
|
|
@@ -232,7 +232,8 @@ class Valkey
|
|
|
232
232
|
#
|
|
233
233
|
# @params timeout [Float] a float value specifying the maximum number of seconds to block) elapses.
|
|
234
234
|
# A timeout of zero can be used to block indefinitely.
|
|
235
|
-
# @params
|
|
235
|
+
# @params keys [String] one or more list keys, passed as separate arguments
|
|
236
|
+
# (splat). To pass a pre-built array, splat it at the call site: `blmpop(1.0, *keys)`.
|
|
236
237
|
# @params modifier [String]
|
|
237
238
|
# - when `"LEFT"` - the elements popped are those from the left of the list
|
|
238
239
|
# - when `"RIGHT"` - the elements popped are those from the right of the list
|
|
@@ -258,7 +259,8 @@ class Valkey
|
|
|
258
259
|
# valkey.lmpop('list', count: 2)
|
|
259
260
|
# #=> ['list', ['a', 'b']]
|
|
260
261
|
#
|
|
261
|
-
# @params
|
|
262
|
+
# @params keys [String] one or more list keys, passed as separate arguments
|
|
263
|
+
# (splat). To pass a pre-built array, splat it at the call site: `lmpop(*keys)`.
|
|
262
264
|
# @params modifier [String]
|
|
263
265
|
# - when `"LEFT"` - the elements popped are those from the left of the list
|
|
264
266
|
# - when `"RIGHT"` - the elements popped are those from the right of the list
|
|
@@ -271,8 +273,6 @@ class Valkey
|
|
|
271
273
|
args = [keys.size, *keys, modifier]
|
|
272
274
|
args << "COUNT" << Integer(count) if count
|
|
273
275
|
|
|
274
|
-
# pp args
|
|
275
|
-
|
|
276
276
|
send_command(RequestType::LMPOP, args)
|
|
277
277
|
end
|
|
278
278
|
|
|
@@ -37,8 +37,10 @@ class Valkey
|
|
|
37
37
|
# Sends the CONFIG GET command with the given arguments.
|
|
38
38
|
#
|
|
39
39
|
# @param [Array<String>] args Configuration parameters to get
|
|
40
|
-
# @param route [Valkey::Route, nil] cluster routing.
|
|
41
|
-
#
|
|
40
|
+
# @param route [Valkey::Route, nil] cluster routing. Default is a single random node.
|
|
41
|
+
# A multi-node route returns a `Hash` of `"host:port" => { parameter => value }`.
|
|
42
|
+
# @return [Hash] a flat `{ parameter => value }` Hash, or a per-node Hash under a
|
|
43
|
+
# multi-node route.
|
|
42
44
|
#
|
|
43
45
|
# @example Get all configuration parameters
|
|
44
46
|
# config_get('*')
|
|
@@ -97,7 +99,8 @@ class Valkey
|
|
|
97
99
|
|
|
98
100
|
# Return the number of keys in the selected database.
|
|
99
101
|
#
|
|
100
|
-
# @param route [Valkey::Route, nil] cluster routing.
|
|
102
|
+
# @param route [Valkey::Route, nil] cluster routing. Multi-node routes return the
|
|
103
|
+
# aggregated sum across nodes, not a per-node Hash.
|
|
101
104
|
# @return [Integer]
|
|
102
105
|
def dbsize(route: nil)
|
|
103
106
|
send_command(RequestType::DB_SIZE, [], route: route)
|
|
@@ -134,8 +137,11 @@ class Valkey
|
|
|
134
137
|
# Get information and statistics about the server.
|
|
135
138
|
#
|
|
136
139
|
# @param cmd [String, Symbol, nil] section name (e.g. "commandstats")
|
|
137
|
-
# @param route [Valkey::Route, nil] cluster routing.
|
|
138
|
-
#
|
|
140
|
+
# @param route [Valkey::Route, nil] cluster routing. On cluster the default is all primaries,
|
|
141
|
+
# so the reply is a per-node Hash even with no route. Standalone (or a single-node route)
|
|
142
|
+
# returns a flat Hash.
|
|
143
|
+
# @return [Hash] a flat `{ field => value }` Hash, or a per-node
|
|
144
|
+
# `{ "host:port" => { field => value } }` Hash under a multi-node route.
|
|
139
145
|
def info(cmd = nil, route: nil)
|
|
140
146
|
send_command(RequestType::INFO, [cmd].compact, route: route) do |reply|
|
|
141
147
|
if reply.is_a?(Hash)
|
|
@@ -222,11 +228,14 @@ class Valkey
|
|
|
222
228
|
# Return the server time.
|
|
223
229
|
#
|
|
224
230
|
# @example
|
|
225
|
-
# r.time # => [
|
|
226
|
-
#
|
|
227
|
-
# @param route [Valkey::Route, nil] cluster routing.
|
|
228
|
-
#
|
|
229
|
-
#
|
|
231
|
+
# r.time # => ["1333093196", "606806"]
|
|
232
|
+
#
|
|
233
|
+
# @param route [Valkey::Route, nil] cluster routing. Default is a single random node.
|
|
234
|
+
# A multi-node route returns a `Hash` of `"host:port" => [seconds, microseconds]`.
|
|
235
|
+
# @return [Array<String>, Hash{String => Array<String>}] a two-element array of
|
|
236
|
+
# seconds since UNIX epoch and microseconds in the current second (both as
|
|
237
|
+
# strings), or a per-node Hash under a multi-node route. Call `.map(&:to_i)` on
|
|
238
|
+
# the array elements for arithmetic.
|
|
230
239
|
def time(route: nil)
|
|
231
240
|
send_command(RequestType::TIME, [], route: route)
|
|
232
241
|
end
|
|
@@ -83,13 +83,11 @@ class Valkey
|
|
|
83
83
|
# - `:xx => true`: Only set the key if it already exist.
|
|
84
84
|
# - `:keepttl => true`: Retain the time to live associated with the key.
|
|
85
85
|
# - `:get => true`: Return the old string stored at key, or nil if key did not exist.
|
|
86
|
-
# @return [String,
|
|
86
|
+
# @return [String, nil] `"OK"` on write; `nil` when `:nx` or `:xx` skipped the write.
|
|
87
87
|
def set(key, value, ex: nil, px: nil, exat: nil, pxat: nil, nx: nil, xx: nil, keepttl: nil, get: nil)
|
|
88
|
-
#
|
|
89
|
-
#
|
|
90
|
-
#
|
|
91
|
-
# see build_command_args's flat_map fix for why this must happen
|
|
92
|
-
# before command_args is built, not be left to that generic layer.
|
|
88
|
+
# A non-String value (e.g. an Array) must become ONE opaque value here, not
|
|
89
|
+
# get flattened as if it were a multi-value list -- see build_command_args's
|
|
90
|
+
# flat_map fix for why this must happen before command_args is built.
|
|
93
91
|
args = [key, value.to_s]
|
|
94
92
|
args << "EX" << Integer(ex) if ex
|
|
95
93
|
args << "PX" << Integer(px) if px
|
|
@@ -101,11 +99,6 @@ class Valkey
|
|
|
101
99
|
args << "GET" if get
|
|
102
100
|
|
|
103
101
|
send_command(RequestType::SET, args)
|
|
104
|
-
# if nx || xx
|
|
105
|
-
# send_command(RequestType::SET, &Utils::BoolifySet))
|
|
106
|
-
# else
|
|
107
|
-
# send_command(RequestType::SET, args)
|
|
108
|
-
# end
|
|
109
102
|
end
|
|
110
103
|
|
|
111
104
|
# Set the time to live in seconds of a key.
|
|
@@ -175,31 +175,17 @@ class Valkey
|
|
|
175
175
|
@queued_commands = []
|
|
176
176
|
end
|
|
177
177
|
|
|
178
|
-
# Commands
|
|
179
|
-
#
|
|
180
|
-
#
|
|
181
|
-
#
|
|
182
|
-
#
|
|
183
|
-
#
|
|
184
|
-
# `
|
|
185
|
-
#
|
|
186
|
-
#
|
|
187
|
-
#
|
|
188
|
-
#
|
|
189
|
-
# bool->int block). Those are already handled correctly by the `if block`
|
|
190
|
-
# branch below, before this list is even consulted - listing them here too
|
|
191
|
-
# would be redundant, not wrong. Every RequestType below calls `send_command`
|
|
192
|
-
# with NO block at all, so this static list is the only place their boolean-ness
|
|
193
|
-
# is recorded.
|
|
194
|
-
#
|
|
195
|
-
# NOTE: SETNX is deliberately NOT in glide-core's coercion table (and so isn't
|
|
196
|
-
# here as a "no block" entry either) - it's `redis-rb`-style boolean-ness only,
|
|
197
|
-
# not something the server/`glide-core` treats as boolean, since coercion there
|
|
198
|
-
# is keyed by command name alone and can't distinguish this dedicated RequestType
|
|
199
|
-
# from a raw `customCommand(["SETNX", ...])` call, which other bindings' existing
|
|
200
|
-
# contracts expect to keep returning a plain integer. `setnx`'s own Ruby method
|
|
201
|
-
# passes `&Utils::Boolify` directly instead - see string_commands.rb - so it's
|
|
202
|
-
# already covered by the `if block` branch, same as hexists/hsetnx.
|
|
178
|
+
# Commands the server coerces to boolean by name, but which arrive as raw 0/1
|
|
179
|
+
# inside an EXEC array (glide-core keys coercion on the executed command - EXEC -
|
|
180
|
+
# not the queued ones). Listed here so #reconvert_queued_replies can restore it.
|
|
181
|
+
#
|
|
182
|
+
# This is glide-core's boolean table (`value_conversion.rs::expected_type_for_cmd`)
|
|
183
|
+
# minus commands whose Ruby method passes its own conversion block - those are
|
|
184
|
+
# handled by the `if block` branch below. Only block-less RequestTypes belong here.
|
|
185
|
+
#
|
|
186
|
+
# SETNX is absent by design: glide-core doesn't treat it as boolean (name-keyed
|
|
187
|
+
# coercion can't distinguish it from a raw `customCommand(["SETNX", ...])`), so
|
|
188
|
+
# `setnx` passes `&Utils::Boolify` itself (string_commands.rb) - covered by `if block`.
|
|
203
189
|
BOOLEAN_REQUEST_TYPES = [
|
|
204
190
|
RequestType::EXPIRE, RequestType::EXPIRE_AT, RequestType::PEXPIRE, RequestType::PEXPIRE_AT,
|
|
205
191
|
RequestType::PERSIST, RequestType::SISMEMBER, RequestType::S_MOVE, RequestType::PFADD,
|
|
@@ -208,11 +194,10 @@ class Valkey
|
|
|
208
194
|
|
|
209
195
|
private
|
|
210
196
|
|
|
211
|
-
# Re-applies each queued command's
|
|
212
|
-
#
|
|
213
|
-
# (
|
|
214
|
-
# queued commands
|
|
215
|
-
# memory - see BOOLEAN_REQUEST_TYPES above for the case with no explicit block.
|
|
197
|
+
# Re-applies each queued command's reply conversion to EXEC's raw array: its own
|
|
198
|
+
# block if it had one, else Boolify for a BOOLEAN_REQUEST_TYPES command. Mirrors
|
|
199
|
+
# what redis-rb's Futures do (each remembers its conversion and re-applies it once
|
|
200
|
+
# EXEC resolves); glide's queued commands have no such memory, so we restore it here.
|
|
216
201
|
def reconvert_queued_replies(result, queued_commands)
|
|
217
202
|
return result unless result.size == queued_commands.size
|
|
218
203
|
|
data/lib/valkey/commands.rb
CHANGED
|
@@ -47,7 +47,8 @@ class Valkey
|
|
|
47
47
|
include SetCommands
|
|
48
48
|
include ScriptingCommands
|
|
49
49
|
include FunctionCommands
|
|
50
|
-
|
|
50
|
+
# TODO: https://github.com/valkey-io/valkey-glide-ruby/issues/135
|
|
51
|
+
# include PubSubCommands
|
|
51
52
|
include ClusterCommands
|
|
52
53
|
include TransactionCommands
|
|
53
54
|
include StreamCommands
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/lib/valkey/utils.rb
CHANGED
|
@@ -255,11 +255,11 @@ class Valkey
|
|
|
255
255
|
|
|
256
256
|
# `URI.parse` returns userinfo *percent-encoded* (RFC 3986 §3.2.1); we
|
|
257
257
|
# want the raw credential value. Callers (`Valkey#initialize`) will
|
|
258
|
-
# re-encode with `URI::
|
|
258
|
+
# re-encode with `URI::DEFAULT_PARSER.escape` when composing the URI
|
|
259
259
|
# they hand to the FFI, so leaving the encoded form here would
|
|
260
260
|
# double-encode (`p%40ss` → `p%2540ss`). See valkey-glide/issues/6659.
|
|
261
|
-
result[:username] = URI::
|
|
262
|
-
result[:password] = URI::
|
|
261
|
+
result[:username] = URI::DEFAULT_PARSER.unescape(uri.user) if uri.user && !uri.user.empty?
|
|
262
|
+
result[:password] = URI::DEFAULT_PARSER.unescape(uri.password) if uri.password && !uri.password.empty?
|
|
263
263
|
|
|
264
264
|
result
|
|
265
265
|
end
|
data/lib/valkey/version.rb
CHANGED
data/lib/valkey.rb
CHANGED
|
@@ -46,7 +46,7 @@ class Valkey
|
|
|
46
46
|
if options[:url]
|
|
47
47
|
url_options = Utils.parse_redis_url(options[:url])
|
|
48
48
|
# Merge URL options, but explicit options take precedence
|
|
49
|
-
options = url_options.merge(options.
|
|
49
|
+
options = url_options.merge(options.except(:url))
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
# Extract connection parameters
|
|
@@ -90,19 +90,19 @@ class Valkey
|
|
|
90
90
|
#
|
|
91
91
|
# We pass an explicit `unsafe` regex — anything outside RFC 3986 §2.3
|
|
92
92
|
# "unreserved" (`ALPHA / DIGIT / - . _ ~`) plus the RFC 2396 "mark" chars
|
|
93
|
-
# (`! ~ * ' ( )`) — because `URI::
|
|
93
|
+
# (`! ~ * ' ( )`) — because `URI::DEFAULT_PARSER.escape`'s default set
|
|
94
94
|
# leaves `/` and `?` raw in userinfo, which the Rust `url` crate on the
|
|
95
95
|
# FFI side then rejects with "Invalid connection URI". Encoding more is
|
|
96
96
|
# always safe because the FFI decodes uniformly.
|
|
97
97
|
userinfo_unsafe = /[^\-_.!~*'()a-zA-Z0-9]/
|
|
98
98
|
if options[:username] && options[:password]
|
|
99
|
-
uri_parts << URI::
|
|
99
|
+
uri_parts << URI::DEFAULT_PARSER.escape(options[:username], userinfo_unsafe)
|
|
100
100
|
uri_parts << ":"
|
|
101
|
-
uri_parts << URI::
|
|
101
|
+
uri_parts << URI::DEFAULT_PARSER.escape(options[:password], userinfo_unsafe)
|
|
102
102
|
uri_parts << "@"
|
|
103
103
|
elsif options[:password]
|
|
104
104
|
uri_parts << ":"
|
|
105
|
-
uri_parts << URI::
|
|
105
|
+
uri_parts << URI::DEFAULT_PARSER.escape(options[:password], userinfo_unsafe)
|
|
106
106
|
uri_parts << "@"
|
|
107
107
|
end
|
|
108
108
|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: valkey-glide-rb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.0
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Valkey GLIDE Maintainers
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-08-
|
|
11
|
+
date: 2026-08-13 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: ffi
|
|
@@ -101,7 +101,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
101
101
|
requirements:
|
|
102
102
|
- - ">="
|
|
103
103
|
- !ruby/object:Gem::Version
|
|
104
|
-
version:
|
|
104
|
+
version: 3.0.0
|
|
105
105
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
106
106
|
requirements:
|
|
107
107
|
- - ">="
|