@@ -30,10 +30,14 @@ def initialize(input = nil, output = Buffered.new)
3030
3131 # This provides a read-only interface for data, which is surprisingly tricky to implement correctly.
3232 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+ #
3741 # @param length [Integer] the amount of data to read
3842 # @param buffer [String] the buffer which will receive the data
3943 # @return a buffer containing the data
@@ -78,7 +82,13 @@ def read(length = nil, buffer = nil)
7882 end
7983 end
8084
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.
8292 def read_partial ( length = nil )
8393 if @buffer
8494 buffer = @buffer
@@ -100,10 +110,12 @@ def read_partial(length = nil)
100110 return buffer
101111 end
102112
113+ # Similar to {read_partial} but raises an `EOFError` if the stream is at EOF.
103114 def readpartial ( length )
104115 read_partial ( length ) or raise EOFError , "End of file reached!"
105116 end
106117
118+ # Read data from the stream without blocking if possible.
107119 def read_nonblock ( length , buffer = nil , exception : nil )
108120 @buffer ||= read_next
109121 chunk = nil
@@ -130,8 +142,11 @@ def read_nonblock(length, buffer = nil, exception: nil)
130142 return buffer
131143 end
132144
133- # Efficiently read data from the stream until encountering pattern.
145+ # Read data from the stream until encountering pattern.
146+ #
134147 # @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.
135150 # @returns [String] The contents of the stream up until the pattern, which is consumed but not returned.
136151 def read_until ( pattern , offset = 0 , chomp : false )
137152 # 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)
160175 end
161176
162177 # 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}.
163181 def gets ( separator = NEWLINE , **options )
164182 read_until ( separator , **options )
165183 end
166184 end
167185
168186 include Reader
169187
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.
170193 def write ( buffer )
171194 if @output
172195 @output . write ( buffer )
@@ -176,14 +199,28 @@ def write(buffer)
176199 end
177200 end
178201
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 )
180210 write ( buffer )
181211 end
182212
213+ # Write data to the stream using {write}.
183214 def <<( buffer )
184215 write ( buffer )
185216 end
186217
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`.
187224 def puts ( *arguments , separator : NEWLINE )
188225 buffer = ::String . new
189226
@@ -194,28 +231,36 @@ def puts(*arguments, separator: NEWLINE)
194231 write ( buffer )
195232 end
196233
234+ # Flush the output stream.
235+ #
236+ # This is currently a no-op.
197237 def flush
198238 end
199239
240+ # Close the input body.
200241 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
206249 end
207250
208- # close must never be called on the input stream. huh?
251+ # Close the output body.
209252 def close_write
210- @output &.close
211- @output = nil
253+ if @output
254+ @output &.close
255+ @output = nil
256+ end
212257 end
213258
214259 # Close the input and output bodies.
215260 def close ( error = nil )
216261 self . close_read
217262 self . close_write
218-
263+
219264 return nil
220265 ensure
221266 @closed = true
0 commit comments