@@ -30,10 +30,14 @@ def initialize(input = nil, output = Buffered.new)
30
30
31
31
# This provides a read-only interface for data, which is surprisingly tricky to implement correctly.
32
32
module Reader
33
- # rack.hijack_io must respond to:
34
- # read, write, read_nonblock, write_nonblock, flush, close, close_read, close_write, closed?
35
-
36
- # read behaves like IO#read. Its signature is read([length, [buffer]]). If given, length must be a non-negative Integer (>= 0) or nil, and buffer must be a String and may not be nil. If length is given and not nil, then this method reads at most length bytes from the input stream. If length is not given or nil, then this method reads all data until EOF. When EOF is reached, this method returns nil if length is given and not nil, or “” if length is not given or is nil. If buffer is given, then the read data will be placed into buffer instead of a newly created String object.
33
+ # Read data from the underlying stream.
34
+ #
35
+ # If given a non-negative length, it will read at most that many bytes from the stream. If the stream is at EOF, it will return nil.
36
+ #
37
+ # If the length is not given, it will read all data until EOF, or return an empty string if the stream is already at EOF.
38
+ #
39
+ # If buffer is given, then the read data will be placed into buffer instead of a newly created String object.
40
+ #
37
41
# @param length [Integer] the amount of data to read
38
42
# @param buffer [String] the buffer which will receive the data
39
43
# @return a buffer containing the data
@@ -78,7 +82,13 @@ def read(length = nil, buffer = nil)
78
82
end
79
83
end
80
84
81
- # Read at most `length` bytes from the stream. Will avoid reading from the underlying stream if possible.
85
+ # Read some bytes from the stream.
86
+ #
87
+ # If the length is given, at most length bytes will be read. Otherwise, one chunk of data from the underlying stream will be read.
88
+ #
89
+ # Will avoid reading from the underlying stream if there is buffered data available.
90
+ #
91
+ # @parameter length [Integer] The maximum number of bytes to read.
82
92
def read_partial ( length = nil )
83
93
if @buffer
84
94
buffer = @buffer
@@ -100,10 +110,12 @@ def read_partial(length = nil)
100
110
return buffer
101
111
end
102
112
113
+ # Similar to {read_partial} but raises an `EOFError` if the stream is at EOF.
103
114
def readpartial ( length )
104
115
read_partial ( length ) or raise EOFError , "End of file reached!"
105
116
end
106
117
118
+ # Read data from the stream without blocking if possible.
107
119
def read_nonblock ( length , buffer = nil , exception : nil )
108
120
@buffer ||= read_next
109
121
chunk = nil
@@ -130,8 +142,11 @@ def read_nonblock(length, buffer = nil, exception: nil)
130
142
return buffer
131
143
end
132
144
133
- # Efficiently read data from the stream until encountering pattern.
145
+ # Read data from the stream until encountering pattern.
146
+ #
134
147
# @parameter pattern [String] The pattern to match.
148
+ # @parameter offset [Integer] The offset to start searching from.
149
+ # @parameter chomp [Boolean] Whether to remove the pattern from the returned data.
135
150
# @returns [String] The contents of the stream up until the pattern, which is consumed but not returned.
136
151
def read_until ( pattern , offset = 0 , chomp : false )
137
152
# We don't want to split on the pattern, so we subtract the size of the pattern.
@@ -160,13 +175,21 @@ def read_until(pattern, offset = 0, chomp: false)
160
175
end
161
176
162
177
# Read a single line from the stream.
178
+ #
179
+ # @parameter separator [String] The line separator, defaults to `\n`.
180
+ # @parameter *options [Hash] Additional options, passed to {read_until}.
163
181
def gets ( separator = NEWLINE , **options )
164
182
read_until ( separator , **options )
165
183
end
166
184
end
167
185
168
186
include Reader
169
187
188
+ # Write data to the underlying stream.
189
+ #
190
+ # @parameter buffer [String] The data to write.
191
+ # @raises [IOError] If the stream is not writable.
192
+ # @returns [Integer] The number of bytes written.
170
193
def write ( buffer )
171
194
if @output
172
195
@output . write ( buffer )
@@ -176,14 +199,28 @@ def write(buffer)
176
199
end
177
200
end
178
201
179
- def write_nonblock ( buffer )
202
+ # Write data to the stream using {write}.
203
+ #
204
+ # Provided for compatibility with IO-like objects.
205
+ #
206
+ # @parameter buffer [String] The data to write.
207
+ # @parameter exception [Boolean] Whether to raise an exception if the write would block, currently ignored.
208
+ # @returns [Integer] The number of bytes written.
209
+ def write_nonblock ( buffer , exception : nil )
180
210
write ( buffer )
181
211
end
182
212
213
+ # Write data to the stream using {write}.
183
214
def <<( buffer )
184
215
write ( buffer )
185
216
end
186
217
218
+ # Write lines to the stream.
219
+ #
220
+ # The current implementation buffers the lines and writes them in a single operation.
221
+ #
222
+ # @parameter arguments [Array(String)] The lines to write.
223
+ # @parameter separator [String] The line separator, defaults to `\n`.
187
224
def puts ( *arguments , separator : NEWLINE )
188
225
buffer = ::String . new
189
226
@@ -194,28 +231,36 @@ def puts(*arguments, separator: NEWLINE)
194
231
write ( buffer )
195
232
end
196
233
234
+ # Flush the output stream.
235
+ #
236
+ # This is currently a no-op.
197
237
def flush
198
238
end
199
239
240
+ # Close the input body.
200
241
def close_read
201
- @closed_read = true
202
- @buffer = nil
203
-
204
- @input &.close
205
- @input = nil
242
+ if @input
243
+ @closed_read = true
244
+ @buffer = nil
245
+
246
+ @input &.close
247
+ @input = nil
248
+ end
206
249
end
207
250
208
- # close must never be called on the input stream. huh?
251
+ # Close the output body.
209
252
def close_write
210
- @output &.close
211
- @output = nil
253
+ if @output
254
+ @output &.close
255
+ @output = nil
256
+ end
212
257
end
213
258
214
259
# Close the input and output bodies.
215
260
def close ( error = nil )
216
261
self . close_read
217
262
self . close_write
218
-
263
+
219
264
return nil
220
265
ensure
221
266
@closed = true
0 commit comments