protocol-redis 0.9.0 → 0.10.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
- checksums.yaml.gz.sig +0 -0
- data/context/getting-started.md +55 -0
- data/context/index.yaml +13 -0
- data/lib/protocol/redis/connection.rb +40 -11
- data/lib/protocol/redis/error.rb +2 -0
- data/lib/protocol/redis/methods/cluster.rb +9 -7
- data/lib/protocol/redis/methods/connection.rb +9 -8
- data/lib/protocol/redis/methods/counting.rb +9 -8
- data/lib/protocol/redis/methods/generic.rb +100 -99
- data/lib/protocol/redis/methods/geospatial.rb +42 -49
- data/lib/protocol/redis/methods/hashes.rb +84 -83
- data/lib/protocol/redis/methods/lists.rb +75 -74
- data/lib/protocol/redis/methods/pubsub.rb +5 -4
- data/lib/protocol/redis/methods/scripting.rb +14 -13
- data/lib/protocol/redis/methods/server.rb +13 -9
- data/lib/protocol/redis/methods/sets.rb +42 -41
- data/lib/protocol/redis/methods/sorted_sets.rb +110 -109
- data/lib/protocol/redis/methods/streams.rb +48 -47
- data/lib/protocol/redis/methods/strings.rb +109 -108
- data/lib/protocol/redis/methods.rb +16 -14
- data/lib/protocol/redis/version.rb +1 -1
- data/lib/protocol/redis.rb +9 -2
- data/readme.md +49 -21
- data/releases.md +94 -0
- data.tar.gz.sig +0 -0
- metadata +7 -9
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 77a3cf15694be52495a873fe8e807a5fb96909bdf0642a6619d26281b14a5aa8
|
4
|
+
data.tar.gz: fd4602f3b11bbf724a863a1012e1105add29017a8e4e6b6f7ae50be502df9cd8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b9e1310a24336a7aff3d5d23d3f43adbb18cbc70089a1daae360f9c835fc843ad886d81da80b34f5140f1bc58cde08630855fbc5e81486512a75944cd9c72b3e
|
7
|
+
data.tar.gz: 888798d1c44e1f8b8883c3f016ef00f0f47b13113b5a8cc5d087b1039d340d4196c3bcb54064d3e9526b86a80139eeb0178592df30df731cbc8c53f9e38cd89d
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# Getting Started
|
2
|
+
|
3
|
+
This guide explains how to use the `Protocol::Redis` gem to implement the RESP2 and RESP3 Redis protocols for low level client and server implementations.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add the gem to your project:
|
8
|
+
|
9
|
+
```bash
|
10
|
+
$ bundle add protocol-redis
|
11
|
+
```
|
12
|
+
|
13
|
+
## Usage
|
14
|
+
|
15
|
+
Here is a basic example communicating over a bi-directional socket pair:
|
16
|
+
|
17
|
+
``` ruby
|
18
|
+
sockets = Socket.pair(Socket::PF_UNIX, Socket::SOCK_STREAM)
|
19
|
+
|
20
|
+
client = Protocol::Redis::Connection.new(sockets.first)
|
21
|
+
server = Protocol::Redis::Connection.new(sockets.last)
|
22
|
+
|
23
|
+
client.write_object("Hello World!")
|
24
|
+
puts server.read_object
|
25
|
+
# => "Hello World!"
|
26
|
+
```
|
27
|
+
|
28
|
+
## Methods
|
29
|
+
|
30
|
+
{ruby Protocol::Redis::Methods} provides access to documented Redis commands. You can use these methods by inluding the module in your class:
|
31
|
+
|
32
|
+
``` ruby
|
33
|
+
class MyRedisClient
|
34
|
+
include Protocol::Redis::Methods
|
35
|
+
|
36
|
+
def call(*arguments)
|
37
|
+
connection = self.acquire # Connection management is up to you
|
38
|
+
|
39
|
+
connection.write_request(arguments)
|
40
|
+
connection.flush
|
41
|
+
|
42
|
+
return connection.read_response
|
43
|
+
end
|
44
|
+
end
|
45
|
+
```
|
46
|
+
You can then call Redis commands like this:
|
47
|
+
|
48
|
+
``` ruby
|
49
|
+
client = MyRedisClient.new
|
50
|
+
client.set("key", "value")
|
51
|
+
```
|
52
|
+
|
53
|
+
### Valkey Support
|
54
|
+
|
55
|
+
You can always use `#call` to send any command. This library provides a set of methods for what we believe are the most commonly used commands (in other words, the intersection of Redis and Valkey commands). If you need more commands, youn could define these yourself similarly to how {ruby Protocol::Redis::Methods} does.
|
data/context/index.yaml
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Automatically generated context index for Utopia::Project guides.
|
2
|
+
# Do not edit then files in this directory directly, instead edit the guides and then run `bake utopia:project:agent:context:update`.
|
3
|
+
---
|
4
|
+
description: A transport agnostic RESP protocol client/server.
|
5
|
+
metadata:
|
6
|
+
documentation_uri: https://socketry.github.io/protocol-redis/
|
7
|
+
funding_uri: https://github.com/sponsors/ioquatix
|
8
|
+
source_code_uri: https://github.com/socketry/protocol-redis.git
|
9
|
+
files:
|
10
|
+
- path: getting-started.md
|
11
|
+
title: Getting Started
|
12
|
+
description: This guide explains how to use the `Protocol::Redis` gem to implement
|
13
|
+
the RESP2 and RESP3 Redis protocols for low level client and server implementations.
|
@@ -3,13 +3,16 @@
|
|
3
3
|
# Released under the MIT License.
|
4
4
|
# Copyright, 2019-2023, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative
|
6
|
+
require_relative "error"
|
7
7
|
|
8
8
|
module Protocol
|
9
9
|
module Redis
|
10
|
+
# Represents a Redis protocol connection handling low-level communication.
|
10
11
|
class Connection
|
11
12
|
CRLF = "\r\n".freeze
|
12
13
|
|
14
|
+
# Initialize a new connection with the provided stream.
|
15
|
+
# @parameter stream [IO] The underlying stream for communication.
|
13
16
|
def initialize(stream)
|
14
17
|
@stream = stream
|
15
18
|
|
@@ -22,18 +25,23 @@ module Protocol
|
|
22
25
|
# @attr [Integer] Number of requests sent.
|
23
26
|
attr :count
|
24
27
|
|
28
|
+
# Close the underlying stream connection.
|
25
29
|
def close
|
26
30
|
@stream.close
|
27
31
|
end
|
28
32
|
|
29
33
|
class << self
|
34
|
+
# Create a new client connection instance.
|
30
35
|
alias client new
|
31
36
|
end
|
32
37
|
|
38
|
+
# Flush any buffered data to the stream.
|
33
39
|
def flush
|
34
40
|
@stream.flush
|
35
41
|
end
|
36
42
|
|
43
|
+
# Check if the connection is closed.
|
44
|
+
# @returns [Boolean] True if the connection is closed.
|
37
45
|
def closed?
|
38
46
|
@stream.closed?
|
39
47
|
end
|
@@ -51,6 +59,8 @@ module Protocol
|
|
51
59
|
end
|
52
60
|
end
|
53
61
|
|
62
|
+
# Write a Redis object to the stream.
|
63
|
+
# @parameter object [Object] The object to write (String, Array, Integer, or object with to_redis method).
|
54
64
|
def write_object(object)
|
55
65
|
case object
|
56
66
|
when String
|
@@ -59,11 +69,26 @@ module Protocol
|
|
59
69
|
write_array(object)
|
60
70
|
when Integer
|
61
71
|
write_lines(":#{object}")
|
72
|
+
when nil
|
73
|
+
write_lines("$-1")
|
62
74
|
else
|
63
75
|
write_object(object.to_redis)
|
64
76
|
end
|
65
77
|
end
|
66
78
|
|
79
|
+
# Write a Redis array to the stream.
|
80
|
+
# @parameter array [Array] The array to write.
|
81
|
+
def write_array(array)
|
82
|
+
write_lines("*#{array.size}")
|
83
|
+
|
84
|
+
array.each do |element|
|
85
|
+
write_object(element)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Read data of specified length from the stream.
|
90
|
+
# @parameter length [Integer] The number of bytes to read.
|
91
|
+
# @returns [String] The data read from the stream.
|
67
92
|
def read_data(length)
|
68
93
|
buffer = @stream.read(length) or @stream.eof!
|
69
94
|
|
@@ -73,13 +98,17 @@ module Protocol
|
|
73
98
|
return buffer
|
74
99
|
end
|
75
100
|
|
101
|
+
# Read and parse a Redis object from the stream.
|
102
|
+
# @returns [Object] The parsed Redis object (String, Array, Integer, or nil).
|
103
|
+
# @raises [ServerError] If the server returns an error response.
|
104
|
+
# @raises [EOFError] If the stream reaches end of file.
|
76
105
|
def read_object
|
77
106
|
line = read_line or raise EOFError
|
78
107
|
|
79
108
|
token = line.slice!(0, 1)
|
80
109
|
|
81
110
|
case token
|
82
|
-
when
|
111
|
+
when "$"
|
83
112
|
length = line.to_i
|
84
113
|
|
85
114
|
if length == -1
|
@@ -87,7 +116,7 @@ module Protocol
|
|
87
116
|
else
|
88
117
|
return read_data(length)
|
89
118
|
end
|
90
|
-
when
|
119
|
+
when "*"
|
91
120
|
count = line.to_i
|
92
121
|
|
93
122
|
# Null array (https://redis.io/topics/protocol#resp-arrays):
|
@@ -96,15 +125,15 @@ module Protocol
|
|
96
125
|
array = Array.new(count) {read_object}
|
97
126
|
|
98
127
|
return array
|
99
|
-
when
|
128
|
+
when ":"
|
100
129
|
return line.to_i
|
101
|
-
|
102
|
-
when
|
130
|
+
|
131
|
+
when "-"
|
103
132
|
raise ServerError.new(line)
|
104
|
-
|
105
|
-
when
|
133
|
+
|
134
|
+
when "+"
|
106
135
|
return line
|
107
|
-
|
136
|
+
|
108
137
|
else
|
109
138
|
@stream.flush
|
110
139
|
|
@@ -115,9 +144,9 @@ module Protocol
|
|
115
144
|
end
|
116
145
|
|
117
146
|
alias read_response read_object
|
118
|
-
|
147
|
+
|
119
148
|
private
|
120
|
-
|
149
|
+
|
121
150
|
# In the case of Redis, we do not want to perform a flush in every line,
|
122
151
|
# because each Redis command contains several lines. Flushing once per
|
123
152
|
# command is more efficient because it avoids unnecessary writes to the
|
data/lib/protocol/redis/error.rb
CHANGED
@@ -2,25 +2,27 @@
|
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
4
|
# Copyright, 2023, by Nick Burwell.
|
5
|
+
# Copyright, 2024, by Samuel Williams.
|
5
6
|
|
6
7
|
module Protocol
|
7
8
|
module Redis
|
8
9
|
module Methods
|
10
|
+
# Methods for managing Redis clusters.
|
9
11
|
module Cluster
|
10
12
|
# Sends the `CLUSTER *` command to random node and returns its reply.
|
11
|
-
#
|
12
|
-
# @
|
13
|
+
# See <https://redis.io/commands/cluster-addslots/> for more details.
|
14
|
+
# @parameter subcommand [String, Symbol] the subcommand of cluster command
|
13
15
|
# e.g. `:addslots`, `:delslots`, `:nodes`, `:replicas`, `:info`
|
14
16
|
#
|
15
|
-
# @
|
16
|
-
def cluster(subcommand, *
|
17
|
-
call("CLUSTER", subcommand.to_s, *
|
17
|
+
# @returns [Object] depends on the subcommand provided
|
18
|
+
def cluster(subcommand, *arguments)
|
19
|
+
call("CLUSTER", subcommand.to_s, *arguments)
|
18
20
|
end
|
19
21
|
|
20
22
|
# Sends `ASKING` command to random node and returns its reply.
|
21
|
-
#
|
23
|
+
# See <https://redis.io/commands/asking/> for more details.
|
22
24
|
#
|
23
|
-
# @
|
25
|
+
# @returns [String] `'OK'`
|
24
26
|
def asking
|
25
27
|
call("ASKING")
|
26
28
|
end
|
@@ -6,31 +6,32 @@
|
|
6
6
|
module Protocol
|
7
7
|
module Redis
|
8
8
|
module Methods
|
9
|
+
# Methods for managing Redis connections.
|
9
10
|
module Connection
|
10
11
|
# Authenticate to the server.
|
11
|
-
#
|
12
|
-
# @
|
13
|
-
# @
|
12
|
+
# See <https://redis.io/commands/auth> for more details.
|
13
|
+
# @parameter username [String] Optional username, if Redis ACLs are used.
|
14
|
+
# @parameter password [String] Required password.
|
14
15
|
def auth(*arguments)
|
15
16
|
call("AUTH", *arguments)
|
16
17
|
end
|
17
18
|
|
18
19
|
# Echo the given string.
|
19
|
-
#
|
20
|
-
# @
|
20
|
+
# See <https://redis.io/commands/echo> for more details.
|
21
|
+
# @parameter message [String]
|
21
22
|
def echo(message)
|
22
23
|
call("ECHO", message)
|
23
24
|
end
|
24
25
|
|
25
26
|
# Ping the server.
|
26
|
-
#
|
27
|
-
# @
|
27
|
+
# See <https://redis.io/commands/ping> for more details.
|
28
|
+
# @parameter message [String]
|
28
29
|
def ping(message)
|
29
30
|
call("PING", message)
|
30
31
|
end
|
31
32
|
|
32
33
|
# Close the connection.
|
33
|
-
#
|
34
|
+
# See <https://redis.io/commands/quit> for more details.
|
34
35
|
def quit
|
35
36
|
call("QUIT")
|
36
37
|
end
|
@@ -6,26 +6,27 @@
|
|
6
6
|
module Protocol
|
7
7
|
module Redis
|
8
8
|
module Methods
|
9
|
+
# Methods for managing Redis HyperLogLogs.
|
9
10
|
module Counting
|
10
11
|
# Adds the specified elements to the specified HyperLogLog. O(1) to add every element.
|
11
|
-
#
|
12
|
-
# @
|
13
|
-
# @
|
12
|
+
# See <https://redis.io/commands/pfadd> for more details.
|
13
|
+
# @parameter key [Key]
|
14
|
+
# @parameter element [String]
|
14
15
|
def pfadd(key, element, *elements)
|
15
16
|
call("PFADD", key, element, *elements)
|
16
17
|
end
|
17
18
|
|
18
19
|
# Return the approximated cardinality of the set(s) observed by the HyperLogLog at key(s). O(1) with a very small average constant time when called with a single key. O(N) with N being the number of keys, and much bigger constant times, when called with multiple keys.
|
19
|
-
#
|
20
|
-
# @
|
20
|
+
# See <https://redis.io/commands/pfcount> for more details.
|
21
|
+
# @parameter key [Key]
|
21
22
|
def pfcount(key, *keys)
|
22
23
|
call("PFCOUNT", key, *keys)
|
23
24
|
end
|
24
25
|
|
25
26
|
# Merge N different HyperLogLogs into a single one. O(N) to merge N HyperLogLogs, but with high constant times.
|
26
|
-
#
|
27
|
-
# @
|
28
|
-
# @
|
27
|
+
# See <https://redis.io/commands/pfmerge> for more details.
|
28
|
+
# @parameter destkey [Key]
|
29
|
+
# @parameter sourcekey [Key]
|
29
30
|
def pfmerge(destkey, sourcekey, *sourcekeys)
|
30
31
|
call("PFMERGE", destkey, sourcekey, *sourcekeys)
|
31
32
|
end
|