Skip to content

Commit f8bbcee

Browse files
100% documentation coverage.
1 parent 9477961 commit f8bbcee

File tree

11 files changed

+165
-92
lines changed

11 files changed

+165
-92
lines changed

lib/protocol/grpc/body/readable_body.rb

Lines changed: 35 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@
88

99
module Protocol
1010
module GRPC
11+
# @namespace
1112
module Body
12-
# Reads length-prefixed gRPC messages from an HTTP body
13-
# This is the standard readable body for gRPC - all gRPC responses use message framing
13+
# Represents a readable body for gRPC messages with length-prefixed framing.
14+
# This is the standard readable body for gRPC - all gRPC responses use message framing.
1415
class ReadableBody
16+
# Initialize a new readable body for gRPC messages.
1517
# @parameter body [Protocol::HTTP::Body::Readable] The underlying HTTP body
16-
# @parameter message_class [Class, nil] Protobuf message class with .decode method
17-
# If nil, returns raw binary data (useful for channel adapters)
18-
# @parameter encoding [String, nil] Compression encoding (from grpc-encoding header)
18+
# @parameter message_class [Class | Nil] Protobuf message class with .decode method.
19+
# If `nil`, returns raw binary data (useful for channel adapters)
20+
# @parameter encoding [String | Nil] Compression encoding (from grpc-encoding header)
1921
def initialize(body, message_class: nil, encoding: nil)
2022
@body = body
2123
@message_class = message_class
@@ -24,15 +26,15 @@ def initialize(body, message_class: nil, encoding: nil)
2426
@closed = false
2527
end
2628

27-
# The input body.
28-
# @attribute [Protocol::HTTP::Body::Readable]
29+
# @attribute [Protocol::HTTP::Body::Readable] The underlying HTTP body.
2930
attr_reader :body
3031

31-
# The compression encoding.
32-
# @attribute [String, nil]
32+
# @attribute [String | Nil] The compression encoding.
3333
attr_reader :encoding
3434

35-
# Close the input and output bodies.
35+
# Close the input body.
36+
# @parameter error [Exception | Nil] Optional error that caused the close
37+
# @returns [Nil]
3638
def close(error = nil)
3739
@closed = true
3840

@@ -44,18 +46,20 @@ def close(error = nil)
4446
nil
4547
end
4648

47-
# Whether the stream has been closed.
49+
# Check if the stream has been closed.
50+
# @returns [Boolean] `true` if the stream is closed, `false` otherwise
4851
def closed?
4952
@closed or @body.nil?
5053
end
5154

52-
# Whether there are any input chunks remaining?
55+
# Check if there are any input chunks remaining.
56+
# @returns [Boolean] `true` if the body is empty, `false` otherwise
5357
def empty?
5458
@body.nil?
5559
end
5660

57-
# Read the next gRPC message
58-
# @returns [Object | String | Nil] Decoded message, raw binary, or nil if stream ended
61+
# Read the next gRPC message.
62+
# @returns [Object | String | Nil] Decoded message, raw binary, or `Nil` if stream ended
5963
def read
6064
return nil if closed?
6165

@@ -66,17 +70,17 @@ def read
6670
compressed = prefix[0].unpack1("C") == 1
6771
length = prefix[1..4].unpack1("N")
6872

69-
# Read the message body
73+
# Read the message body:
7074
data = read_exactly(length)
7175
return nil unless data
7276

73-
# Decompress if needed
77+
# Decompress if needed:
7478
data = decompress(data) if compressed
7579

76-
# Decode using message class if provided, otherwise return binary
80+
# Decode using message class if provided, otherwise return binary:
7781
# This allows binary mode for channel adapters
7882
if @message_class
79-
# Use protobuf gem's decode method
83+
# Use protobuf gem's decode method:
8084
@message_class.decode(data)
8185
else
8286
data # Return raw binary
@@ -103,39 +107,46 @@ def each
103107

104108
private
105109

110+
# Read exactly n bytes from the underlying body.
111+
# @parameter n [Integer] The number of bytes to read
112+
# @returns [String | Nil] The data read, or `Nil` if the stream ended
106113
def read_exactly(n)
107-
# Fill buffer until we have enough data
114+
# Fill buffer until we have enough data:
108115
while @buffer.bytesize < n
109116
return nil if closed?
110117

111-
# Read chunk from underlying body
118+
# Read chunk from underlying body:
112119
chunk = @body.read
113120

114121
if chunk.nil?
115-
# End of stream
122+
# End of stream:
116123
if @body && !@closed
117124
@body.close
118125
@closed = true
119126
end
120127
return nil
121128
end
122129

123-
# Append to buffer
130+
# Append to buffer:
124131
@buffer << chunk.force_encoding(Encoding::BINARY)
125132

126-
# Check if body is empty and close if needed
133+
# Check if body is empty and close if needed:
127134
if @body.empty?
128135
@body.close
129136
@closed = true
130137
end
131138
end
132139

133-
# Extract the required data
140+
# Extract the required data:
134141
data = @buffer[0...n]
135142
@buffer = @buffer[n..]
136143
data
137144
end
138145

146+
# Decompress data using the configured encoding.
147+
# @parameter data [String] The compressed data
148+
# @returns [String] The decompressed data
149+
# @raises [Error] If decompression fails
139150
def decompress(data)
140151
case @encoding
141152
when "gzip"

lib/protocol/grpc/body/writable_body.rb

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,64 +10,75 @@
1010

1111
module Protocol
1212
module GRPC
13+
# @namespace
1314
module Body
14-
# Writes length-prefixed gRPC messages
15-
# This is the standard writable body for gRPC - all gRPC requests use message framing
15+
# Represents a writable body for gRPC messages with length-prefixed framing.
16+
# This is the standard writable body for gRPC - all gRPC requests use message framing.
1617
class WritableBody < Protocol::HTTP::Body::Writable
17-
# @parameter encoding [String, nil] Compression encoding (gzip, deflate, identity)
18+
# Initialize a new writable body for gRPC messages.
19+
# @parameter encoding [String | Nil] Compression encoding (gzip, deflate, identity)
1820
# @parameter level [Integer] Compression level if encoding is used
19-
# @parameter message_class [Class, nil] Expected message class for validation
21+
# @parameter message_class [Class | Nil] Expected message class for validation
2022
def initialize(encoding: nil, level: Zlib::DEFAULT_COMPRESSION, message_class: nil, **options)
2123
super(**options)
2224
@encoding = encoding
2325
@level = level
2426
@message_class = message_class
2527
end
2628

29+
# @attribute [String | Nil] The compression encoding.
2730
attr_reader :encoding
31+
32+
# @attribute [Class | Nil] The expected message class for validation.
2833
attr_reader :message_class
2934

30-
# Write a message with gRPC framing
35+
# Write a message with gRPC framing.
3136
# @parameter message [Object, String] Protobuf message instance or raw binary data
32-
# @parameter compressed [Boolean] Whether to compress this specific message
37+
# @parameter compressed [Boolean | Nil] Whether to compress this specific message. If `nil`, uses the encoding setting.
3338
def write(message, compressed: nil)
34-
# Validate message type if message_class is specified
39+
# Validate message type if message_class is specified:
3540
if @message_class && !message.is_a?(String)
3641
unless message.is_a?(@message_class)
3742
raise TypeError, "Expected #{@message_class}, got #{message.class}"
3843
end
3944
end
40-
# Encode message to binary if it's not already a string
45+
46+
# Encode message to binary if it's not already a string:
4147
# This supports both high-level (protobuf objects) and low-level (binary) usage
4248
data = if message.is_a?(String)
4349
message # Already binary, use as-is (for channel adapters)
4450
elsif message.respond_to?(:to_proto)
45-
# Use protobuf gem's to_proto or encode method
51+
# Use protobuf gem's to_proto method:
4652
message.to_proto
4753
elsif message.respond_to?(:encode)
54+
# Use encode method:
4855
message.encode
4956
else
5057
raise ArgumentError, "Message must respond to :to_proto or :encode"
5158
end
5259

53-
# Determine if we should compress this message
60+
# Determine if we should compress this message:
5461
# If compressed param is nil, use the encoding setting
5562
should_compress = compressed.nil? ? (@encoding && @encoding != "identity") : compressed
5663

57-
# Compress if requested
64+
# Compress if requested:
5865
data = compress(data) if should_compress
5966

6067
# Build prefix: compression flag + length
6168
compression_flag = should_compress ? 1 : 0
6269
length = data.bytesize
6370
prefix = [compression_flag].pack("C") + [length].pack("N")
6471

65-
# Write prefix + data to underlying body
72+
# Write prefix + data to underlying body:
6673
super(prefix + data) # Call Protocol::HTTP::Body::Writable#write
6774
end
6875

69-
protected
76+
protected
7077

78+
# Compress data using the configured encoding.
79+
# @parameter data [String] The data to compress
80+
# @returns [String] The compressed data
81+
# @raises [Error] If compression fails
7182
def compress(data)
7283
case @encoding
7384
when "gzip"

lib/protocol/grpc/call.rb

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,53 +8,54 @@
88

99
module Protocol
1010
module GRPC
11-
# Represents context for a single RPC call
11+
# Represents context for a single RPC call.
1212
class Call
13+
# Initialize a new RPC call context.
1314
# @parameter request [Protocol::HTTP::Request] The HTTP request
14-
# @parameter deadline [Async::Deadline, nil] Deadline for the call
15+
# @parameter deadline [Async::Deadline | Nil] Deadline for the call
1516
def initialize(request, deadline: nil)
1617
@request = request
1718
@deadline = deadline
1819
@cancelled = false
1920
end
2021

21-
# @attribute [Protocol::HTTP::Request] The underlying HTTP request
22+
# @attribute [Protocol::HTTP::Request] The underlying HTTP request.
2223
attr_reader :request
2324

24-
# @attribute [Async::Deadline, nil] The deadline for this call
25+
# @attribute [Async::Deadline | Nil] The deadline for this call.
2526
attr_reader :deadline
2627

27-
# Extract metadata from request headers
28-
# @returns [Hash] Custom metadata
28+
# Extract metadata from request headers.
29+
# @returns [Hash] Custom metadata key-value pairs
2930
def metadata
3031
@metadata ||= Methods.extract_metadata(@request.headers)
3132
end
3233

33-
# Check if the deadline has expired
34-
# @returns [Boolean]
34+
# Check if the deadline has expired.
35+
# @returns [Boolean] `true` if the deadline has expired, `false` otherwise
3536
def deadline_exceeded?
3637
@deadline&.expired? || false
3738
end
3839

39-
# Time remaining until deadline
40-
# @returns [Numeric, nil] Seconds remaining, or nil if no deadline
40+
# Get the time remaining until the deadline.
41+
# @returns [Numeric | Nil] Seconds remaining, or `Nil` if no deadline is set
4142
def time_remaining
4243
@deadline&.remaining
4344
end
4445

45-
# Mark this call as cancelled
46+
# Mark this call as cancelled.
4647
def cancel!
4748
@cancelled = true
4849
end
4950

50-
# Check if call was cancelled
51-
# @returns [Boolean]
51+
# Check if the call was cancelled.
52+
# @returns [Boolean] `true` if the call was cancelled, `false` otherwise
5253
def cancelled?
5354
@cancelled
5455
end
5556

56-
# Get peer information (client address)
57-
# @returns [String, nil]
57+
# Get peer information (client address).
58+
# @returns [String | Nil] The peer address as a string, or `Nil` if not available
5859
def peer
5960
@request.peer&.to_s
6061
end

0 commit comments

Comments
 (0)