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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f4eaf2e185b9e0b2f5a29527a83e803e56bd89470ebbd800519338db47c33ae8
4
- data.tar.gz: 6954fa6365d178dca0ff302b5b1739e9d731dd72df61a9238f6d16a152640318
3
+ metadata.gz: 77a3cf15694be52495a873fe8e807a5fb96909bdf0642a6619d26281b14a5aa8
4
+ data.tar.gz: fd4602f3b11bbf724a863a1012e1105add29017a8e4e6b6f7ae50be502df9cd8
5
5
  SHA512:
6
- metadata.gz: d85825ba053ca54ae00e329edef4ae7bcb0b06afc5c3752d777faa20439fff39d0a4bf9cd293f675a36859b5bb7fa8417c449fc6efe3fa0664892ad8f7b7593e
7
- data.tar.gz: 503526c37e829a7272c0a43c458f514544d9b1514410f957c1b8ef669bab5701c0a892143cd9c4f83b9e3186551e6a7be135f6b8d087fc4d223cbb1e39571a60
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.
@@ -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 'error'
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
@@ -5,9 +5,11 @@
5
5
 
6
6
  module Protocol
7
7
  module Redis
8
+ # Represents a general Redis protocol error.
8
9
  class Error < StandardError
9
10
  end
10
11
 
12
+ # Represents an error response from the Redis server.
11
13
  class ServerError < Error
12
14
  end
13
15
  end
@@ -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
- # @see https://redis.io/commands/cluster-addslots/
12
- # @param subcommand [String, Symbol] the subcommand of cluster command
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
- # @return [Object] depends on the subcommand provided
16
- def cluster(subcommand, *args)
17
- call("CLUSTER", subcommand.to_s, *args)
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
- # @see https://redis.io/commands/asking/
23
+ # See <https://redis.io/commands/asking/> for more details.
22
24
  #
23
- # @return [String] `'OK'`
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
- # @see https://redis.io/commands/auth
12
- # @param username [String] Optional username, if Redis ACLs are used.
13
- # @param password [String] Required password.
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
- # @see https://redis.io/commands/echo
20
- # @param message [String]
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
- # @see https://redis.io/commands/ping
27
- # @param message [String]
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
- # @see https://redis.io/commands/quit
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
- # @see https://redis.io/commands/pfadd
12
- # @param key [Key]
13
- # @param element [String]
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
- # @see https://redis.io/commands/pfcount
20
- # @param key [Key]
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
- # @see https://redis.io/commands/pfmerge
27
- # @param destkey [Key]
28
- # @param sourcekey [Key]
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