module IO::Like
This module provides most of the basic input and output functions of
IO
objects as implemented in Ruby version 1.8.6. Its use is
supported on all versions of Ruby. See the general documentation of
IO::Like
for a description of how to create a class capable of
using this module.
Include this module explicitly rather than IO::Like
if the
including class should always behave like Ruby 1.8.6 IO
no
matter what version of Ruby is running the class.
IO::Like is a module which provides most of the basic input and output functions of IO objects using methods named unbuffered_read, unbuffered_write, and unbuffered_seek.
The definition of this particular module is equivalent to whatever version-specific module provides the closest interface to the IO implementation of the Ruby interpreter running this library. For example, IO::Like will be equivalent to IO::Like_1_8_6 under Ruby version 1.8.6 while it will be equivalent to IO::Like_1_8_7 under Ruby version 1.8.7.
When considering any of the IO::Like modules for use in a class, the following documentation holds true.
Readers¶ ↑
In order to use this module to provide input methods, a class which includes it must provide the unbuffered_read method which takes one argument, a length, as follows:
def unbuffered_read(length) ... end
This method must return at most length bytes as a String, raise EOFError if reading begins at the end of data, and raise SystemCallError on error. Errno::EAGAIN should be raised if there is no data to return immediately and the read operation should not block. Errno::EINTR should be raised if the read operation is interrupted before any data is read.
In 1.9 the returned string is expected to have 'binary' encoding.
Writers¶ ↑
In order to use this module to provide output methods, a class which includes it must provide the unbuffered_write method which takes a single string argument as follows:
def unbuffered_write(string) ... end
In 1.9 the supplied string argument will have 'binary' encoding.
This method must either return the number of bytes written to the stream, which may be less than the length of string in bytes, OR must raise an instance of SystemCallError. Errno::EAGAIN should be raised if no data can be written immediately and the write operation should not block. Errno::EINTR should be raised if the write operation is interrupted before any data is written.
Seekers¶ ↑
In order to use this module to provide seeking methods, a class which includes it must provide the unbuffered_seek method which takes two required arguments, an offset and a start position, as follows:
def unbuffered_seek(offset, whence) ... end
This method must return the new position within the data stream relative to the beginning of the stream and should raise SystemCallError on error. offset can be any integer and whence can be any of IO::SEEK_SET, IO::SEEK_CUR, or IO::SEEK_END. They are interpreted together as follows:
whence | resulting position -------------+------------------------------------------------------------ IO::SEEK_SET | Add offset to the position of the beginning of the stream. -------------+------------------------------------------------------------ IO::SEEK_CUR | Add offset to the current position of the stream. -------------+------------------------------------------------------------ IO::SEEK_END | Add offset to the position of the end of the stream.
Duplexed Streams¶ ↑
In order to create a duplexed stream where writing and reading happen
independently of each other, override the duplexed? method to return
true
and then provide the unbuffered_read and
unbuffered_write methods. Do NOT provide an
unbuffered_seek method or the contents of the internal read and
write buffers may be lost unexpectedly.
NOTE: Due to limitations of Ruby's finalizer, IO::Like_1_8_6#close is not automatically called when the object is garbage collected, so it must be explicitly called when the object is no longer needed or risk losing whatever data remains in the internal write buffer.
Non-blocking Streams¶ ↑
As above unbuffered_read and unbuffered_write should raise
Errno::EAGAIN
if they should not block.
The read_nonblock and write_nonblock methods of IO::Like will attempt to call nonblock=(true) on the underlying stream before calling unbuffered_read or unbuffered_write. This is the equivalent of Ruby setting the O_NONBLOCK flag on traditional file descriptor based IO.
The default implementation of nonblock= raises
Errno::EBADF
. For streams where unbuffered_read is always non
blocking this can be overridden with a no-op.
Nonblocking streams should also provide a more optimal implementation of read_ready? and write_ready? which by
default simply calls Kernel.sleep(1)
and is called by the
blocking read and write methods in response to Errno::EAGAIN
.
Public Instance Methods
Writes obj to the stream using write and returns ios.
obj is converted to a String
using to_s
.
# File lib/io/like-1.8.6.rb, line 19 def <<(obj) write(obj) self end
Returns self
. Just for compatibility with IO
.
# File lib/io/like-1.8.6.rb, line 28 def binmode self end
Arranges for closed? to
return true
. Raises IOError
if closed? already returns
true
. For duplexed objects, calls close_read and close_write. For
non-duplexed objects, calls flush if writable? returns
true
and then sets a flag so that closed? will return
true
.
# File lib/io/like-1.8.6.rb, line 40 def close raise IOError, 'closed stream' if closed? __io_like__close_read flush if writable? __io_like__close_write nil end
Closes the read end of a duplexed object or the whole object if the object is read-only.
Raises IOError
if closed? returns
true
. Raises IOError
for duplexed objects if
called more than once. Raises IOError
for non-duplexed objects
if writable? returns
true
.
# File lib/io/like-1.8.6.rb, line 58 def close_read raise IOError, 'closed stream' if closed? if __io_like__closed_read? || ! duplexed? && writable? then raise IOError, 'closing non-duplex IO for reading' end if duplexed? then __io_like__close_read else close end nil end
Closes the write end of a duplexed object or the whole object if the object is write-only.
Raises IOError
if closed? returns
true
. Raises IOError
for duplexed objects if
called more than once. Raises IOError
for non-duplexed objects
if readable? returns
true
.
# File lib/io/like-1.8.6.rb, line 81 def close_write raise IOError, 'closed stream' if closed? if __io_like__closed_write? || ! duplexed? && readable? then raise IOError, 'closing non-duplex IO for reading' end if duplexed? then flush __io_like__close_write else close end nil end
Returns true
if this object is closed or otherwise unusable
for read and write operations.
# File lib/io/like-1.8.6.rb, line 100 def closed? (__io_like__closed_read? || ! readable?) && (__io_like__closed_write? || ! writable?) end
Returns false
. Override this to return true
when
creating duplexed IO
objects.
# File lib/io/like-1.8.6.rb, line 110 def duplexed? false end
Reads each byte (0..255) from the stream using getc and calls the given block once for each byte, passing the byte as an argument.
NOTE: This method ignores Errno::EAGAIN
and
Errno::EINTR
raised by unbuffered_read. Therefore, this
method always blocks. Aside from that exception and the conversion of
EOFError
results into nil
results, this method
will also raise the same errors and block at the same times as
unbuffered_read.
# File lib/io/like-1.8.6.rb, line 126 def each_byte while (byte = getc) do yield(byte) end self end
Reads each line from the stream using gets and calls the given block once for each line, passing the line as an argument.
NOTE: When sep_string is not nil
,
this method ignores Errno::EAGAIN
and
Errno::EINTR
raised by unbuffered_read. Therefore, this
method always blocks. Aside from that exception and the conversion of
EOFError
results into nil
results, this method
will also raise the same errors and block at the same times as
unbuffered_read.
# File lib/io/like-1.8.6.rb, line 146 def each_line(sep_string = $/) while (line = gets(sep_string)) do yield(line) end self end
Returns true
if there is no more data to read.
This works by using getc to fetch the next character and using ungetc to put the character back if one was fetched. It may be a good idea to replace this implementation in derivative classes.
NOTE: This method ignores Errno::EAGAIN
and
Errno::EINTR
raised by unbuffered_read. Therefore, this
method always blocks. Aside from that exception and the conversion of
EOFError
results into nil
results, this method
will also raise the same errors and block at the same times as
unbuffered_read.
# File lib/io/like-1.8.6.rb, line 170 def eof? if (char = getc) then ungetc(char) return false end true end
Raises NotImplementedError
.
# File lib/io/like-1.8.6.rb, line 183 def fcntl(*args) raise NotImplementedError, 'not implemented' end
Returns nil
. Just for compatibility with IO
.
# File lib/io/like-1.8.6.rb, line 191 def fileno nil end
Returns the number of bytes to read as a block whenever the internal buffer
needs to be refilled. Unless set explicitly via fill_size=, this defaults
to 4096
.
Raises IOError
if closed? returns
true
. Raises IOError
if the stream is not opened
for reading.
# File lib/io/like-1.8.6.rb, line 204 def fill_size raise IOError, 'closed stream' if closed? raise IOError, 'not opened for reading' unless readable? @__io_like__fill_size ||= 4096 end
Sets the number of bytes to read as a block whenever the internal read
buffer needs to be refilled. The new value must be a number greater than
or equal to 0
. Setting this to 0
effectively
disables buffering.
Raises IOError
if closed? returns
true
. Raises IOError
if the stream is not opened
for reading.
# File lib/io/like-1.8.6.rb, line 221 def fill_size=(fill_size) raise IOError, 'closed stream' if closed? raise IOError, 'not opened for reading' unless readable? unless fill_size >= 0 then raise ArgumentError, "non-positive fill_size #{fill_size} given" end @__io_like__fill_size = fill_size end
Flushes the internal write buffer to the underlying data stream.
Regardless of the blocking status of the data stream or interruptions during writing, this method will block until either all the data is flushed or until an error is raised.
Raises IOError
if closed? returns
true
. Raises IOError
unless writable? returns
true
.
NOTE: This method ignores Errno::EAGAIN
and
Errno::EINTR
raised by unbuffered_write. Therefore, this
method always blocks if unable to flush the internal write buffer. Aside
from that exception, this method will also raise the same errors and block
at the same times as unbuffered_write.
# File lib/io/like-1.8.6.rb, line 248 def flush begin __io_like__buffered_flush rescue Errno::EAGAIN, Errno::EINTR retry if write_ready? end self end
Returns the number of bytes at which the internal write buffer is flushed
automatically to the data stream. Unless set explicitly via flush_size=, this
defaults to 4096
.
Raises IOError
if closed? returns
true
. Raises IOError
unless writable? returns
true
.
# File lib/io/like-1.8.6.rb, line 266 def flush_size raise IOError, 'closed stream' if closed? raise IOError, 'not opened for writing' unless writable? @__io_like__flush_size ||= 4096 end
Sets the number of bytes at which the internal write buffer is flushed
automatically to the data stream. The new value must be a number greater
than or equal to 0
. Setting this to 0
effectively disables buffering.
Raises IOError
if closed? returns
true
. Raises IOError
unless writable? returns
true
.
# File lib/io/like-1.8.6.rb, line 283 def flush_size=(flush_size) raise IOError, 'closed stream' if closed? raise IOError, 'not opened for writing' unless writable? unless flush_size >= 0 then raise ArgumentError, "non-positive flush_size #{flush_size} given" end @__io_like__flush_size = flush_size end
Flush data to disk, default is not supported
# File lib/io/like-1.8.6.rb, line 297 def fsync() nil end
Calls readchar and either
returns the result or nil
if readchar raises
EOFError
.
Raises IOError
if closed? returns
true
. Raises IOError
unless readable? returns
true
. Raises all errors raised by unbuffered_read except for
EOFError
.
NOTE: This method ignores Errno::EAGAIN
and
Errno::EINTR
raised by unbuffered_read. Therefore, this
method always blocks. Aside from that exception and the conversion of
EOFError
results into nil
results, this method
will also raise the same errors and block at the same times as
unbuffered_read.
# File lib/io/like-1.8.6.rb, line 317 def getc readchar rescue EOFError nil end
Calls readline with
sep_string as an argument and either returns the result or
nil
if readline raises
EOFError
. If readline returns some data,
$.
is set to the value of lineno.
NOTE: Due to limitations of MRI up to version 1.9.x when
running managed (Ruby) code, this method fails to set $_
to
the returned data; however, other implementations may allow it.
Raises IOError
if closed? returns
true
. Raises IOError
unless readable? returns
true
. Raises all errors raised by unbuffered_read except for
EOFError
.
NOTE: When sep_string is not nil
,
this method ignores Errno::EAGAIN
and
Errno::EINTR
raised by unbuffered_read. Therefore, this
method will always block in that case. Aside from that exception, this
method will raise the same errors and block at the same times as
unbuffered_read.
# File lib/io/like-1.8.6.rb, line 344 def gets(sep_string = $/) # Set the last read line in the global. $_ = readline(sep_string) # Set the last line number in the global. $. = lineno # Return the last read line. $_ rescue EOFError nil end
Returns false
. Just for compatibility with IO
.
Raises IOError
if closed? returns
true
.
# File lib/io/like-1.8.6.rb, line 361 def isatty raise IOError, 'closed stream' if closed? false end
Returns the number of times gets was called and returned
non-nil
data. By default this is the number of lines read,
but calling gets or any of the
other line-based reading methods with a non-default value for
sep_string or after changing $/
will affect this.
Raises IOError
if closed? returns
true
. Raises IOError
unless readable? returns
true
.
# File lib/io/like-1.8.6.rb, line 378 def lineno raise IOError, 'closed stream' if closed? raise IOError, 'not opened for reading' unless readable? @__io_like__lineno ||= 0 end
Sets the current line number to the given value. $.
is
updated by the next call to gets. If the object given is not
an Integer
, it is converted to one using its
to_int
method.
Raises IOError
if closed? returns
true
. Raises IOError
unless readable? returns
true
.
# File lib/io/like-1.8.6.rb, line 394 def lineno=(integer) raise IOError, 'closed stream' if closed? raise IOError, 'not opened for reading' unless readable? if integer.nil? then raise TypeError, 'no implicit conversion from nil to integer' elsif ! integer.respond_to?(:to_int) then raise TypeError, "can't convert #{integer.class} into Integer" end @__io_like__lineno = integer.to_int end
read_nonblock, write_nonblock will call nonblock=(true) prior to calling unbuffered_read or unbuffered_write
The default implementation raises Errno::EBADF
to indicate
that nonblocking operations are not supported. IO
implementations that are always nonblocking should override with a no-op
# File lib/io/like-1.8.6.rb, line 414 def nonblock=(blocking_mode) raise Errno::EBADF end
Returns nil
. Just for compatibility with IO
.
# File lib/io/like-1.8.6.rb, line 422 def path nil end
Returns the current offest of ios.
Raises IOError
if closed? returns
true
. Raises Errno::ESPIPE
unless seekable? returns
true
.
As a side effect, the internal write buffer is flushed unless this is a
writable, non-duplexed object. This is for compatibility with the behavior
of IO#pos
.
NOTE: Because this method relies on unbuffered_seek and unbuffered_write (when the internal write buffer is not empty), it will also raise the same errors and block at the same times as those functions.
# File lib/io/like-1.8.6.rb, line 459 def pos # Flush the internal write buffer for writable, non-duplexed objects. __io_like__buffered_flush if writable? && ! duplexed? __io_like__buffered_seek(0, IO::SEEK_CUR) end
Sets the data position to position by calling seek.
As a side effect, the internal read and write buffers are flushed.
Raises IOError
if closed? returns
true
. Raises Errno::ESPIPE
unless seekable? returns
true
.
NOTE: Because this method relies on unbuffered_seek and unbuffered_write (when the internal write buffer is not empty), it will also raise the same errors and block at the same times as those functions.
# File lib/io/like-1.8.6.rb, line 439 def pos=(position) seek(position, IO::SEEK_SET) position end
Writes the given object(s), if any, to the stream using write after converting them to
strings by calling their to_s
methods. If no objects are
given, $_
is used. The field separator ($,
) is
written between successive objects if it is not nil
. The
output record separator ($\
) is written after all other data
if it is not nil
.
Raises IOError
if closed? returns
true
. Raises IOError
unless writable? returns
true
.
NOTE: This method ignores Errno::EAGAIN
and
Errno::EINTR
raised by unbuffered_write. Therefore, this
method always blocks if unable to immediately write [obj, ...]
completely. Aside from that exception, this method will also raise the
same errors and block at the same times as unbuffered_write.
# File lib/io/like-1.8.6.rb, line 485 def print(*args) args << $_ if args.empty? first_arg = true args.each do |arg| # Write a field separator before writing each argument after the first # one unless no field separator is specified. if first_arg then first_arg = false elsif ! $,.nil? then write($,) end # If the argument is nil, write 'nil'; otherwise, write the stringified # form of the argument. if arg.nil? then write('nil') else write(arg) end end # Write the output record separator if one is specified. write($\) unless $\.nil? nil end
Writes the String
returned by calling
Kernel.sprintf
using the given arguments.
Raises IOError
if closed? returns
true
. Raises IOError
unless writable? returns
true
.
NOTE: This method ignores Errno::EAGAIN
and
Errno::EINTR
raised by unbuffered_write. Therefore, this
method always blocks if unable to immediately write its arguments
completely. Aside from that exception, this method will also raise the
same errors and block at the same times as unbuffered_write.
# File lib/io/like-1.8.6.rb, line 525 def printf(*args) write(sprintf(*args)) nil end
If obj is a String
, write the first byte; otherwise,
convert obj to an Integer
using its
to_int
method and write the low order byte.
Raises IOError
if closed? returns
true
. Raises IOError
unless writable? returns
true
.
NOTE: This method ignores Errno::EAGAIN
and
Errno::EINTR
raised by unbuffered_write. Therefore, this
method always blocks if unable to immediately write obj
completely. Aside from that exception, this method will also raise the same
errors and block at the same times as unbuffered_write.
# File lib/io/like-1.8.6.rb, line 545 def putc(obj) char = case obj when String obj[0].chr else raise TypeError unless obj.respond_to?(:to_int) [obj.to_int].pack('V')[0].chr end write(char) obj end
Writes the given object(s), if any, to the stream using write after converting them to
strings using their to_s
methods. Unlike print, Array instances are
recursively processed. A record separator character is written after each
object which does not end with the record separator already. If no objects
are given, a single record separator is written.
Raises IOError
if closed? returns
true
. Raises IOError
unless writable? returns
true
.
NOTE: This method ignores Errno::EAGAIN
and
Errno::EINTR
raised by unbuffered_write. Therefore, this
method always blocks if unable to immediately write [obj, ...]
completely. Aside from that exception, this method will also raise the
same errors and block at the same times as unbuffered_write.
NOTE: In order to be compatible with IO#puts
,
the record separator is currently hardcoded to be a single newline
("\n"
) even though the documentation implies that
the output record separator ($\
) should be used.
# File lib/io/like-1.8.6.rb, line 581 def puts(*args) # Set the output record separator such that this method is compatible with # IO#puts. ors = "\n" # Write only the record separator if no arguments are given. if args.length == 0 then write(ors) return end # Write each argument followed by the record separator. Recursively # process arguments which are Array instances. __io_like__array_flatten(args) do |string| write(string || 'nil') write(ors) if string.nil? || string.index(ors, -ors.length).nil? end nil end
If length is specified and is a positive integer, at most length
bytes are returned. Truncated data will occur if there is insufficient
data left to fulfill the request. If the read starts at the end of data,
nil
is returned.
If length is unspecified or nil
, an attempt to return
all remaining data is made. Partial data will be returned if a low-level
error is raised after some data is retrieved. If no data would be returned
at all, an empty String
is returned.
If buffer is specified, it will be converted to a
String
using its to_str
method if necessary and
will be filled with the returned data if any.
Raises IOError
if closed? returns
true
. Raises IOError
unless readable? returns
true
.
NOTE: Because this method relies on unbuffered_read, it will also raise the same errors and block at the same times as that function.
# File lib/io/like-1.8.6.rb, line 623 def read(length = nil, buffer = nil) # Check the validity of the method arguments. unless length.nil? || length >= 0 then raise ArgumentError, "negative length #{length} given" end buffer = buffer.nil? ? '' : buffer.to_str buffer.slice!(0..-1) unless buffer.empty? if length.nil? then # Read and return everything. begin loop do buffer << __io_like__buffered_read(4096) end rescue EOFError # Ignore this. rescue SystemCallError # Reraise the error if there is nothing to return. raise if buffer.empty? end else # Read and return up to length bytes. begin buffer << __io_like__buffered_read(length) rescue EOFError # Return nil to the caller at end of file when requesting a specific # amount of data. return nil end end buffer end
Returns at most length bytes from the data stream using only the internal read buffer if the buffer is not empty.
If internal buffer is empty sets nonblocking mode via nonblock=(true) and then reads from the underlying stream
Raises Errno::EBADF
if nonblocking mode is not supported
Raises EOFError
when there is no more data in the stream.
Raises IOError
if closed? returns
true
. Raises IOError
unless readable? returns
true
.
This method will raise errors directly from buffered_read to be handled by the caller.
# File lib/io/like-1.8.6.rb, line 888 def read_nonblock(length, buffer = nil) __io_like__readpartial(length, buffer, true) end
Returns true
when the stream may be read without error,
false
otherwise. This method will block until one of the
conditions is known.
This default implementation of read_ready? is a hack
which should be able to work for both real IO
objects and
IO
-like objects; however, it is inefficient since it merely
sleeps for 1 second and then returns true
as long as readable? returns
true
. IO.select
should be used for real
IO
objects to wait for a readable condition on platforms with
support for IO.select
. Other solutions should be found as
necessary to improve this implementation on a case by case basis.
Basically, this method should be overridden in derivative classes.
# File lib/io/like-1.8.6.rb, line 673 def read_ready? return false unless readable? sleep(1) true end
Returns true
if the stream is both open and readable,
false
otherwise.
This implementation checks to see if unbuffered_read is defined in order to make its determination. Override this if the implementing class always provides the unbuffered_read method but may not always be open in a readable mode.
# File lib/io/like-1.8.6.rb, line 689 def readable? ! __io_like__closed_read? && respond_to?(:unbuffered_read, true) end
Reads and returns length bytes from the data stream.
Raises EOFError
if reading begins at the end of the stream.
Raises IOError
if closed? returns
true
. Raises IOError
unless readable? returns
true
. Raises TruncatedDataError
if insufficient
data is immediately available to satisfy the request.
In the case of TruncatedDataError
being raised, the retrieved
data can be fetched from the data
attribute of the exception.
This method is basically copied from IO#readbytes
.
NOTE: Because this method relies on unbuffered_read, it will also raise the same errors and block at the same times as that function.
# File lib/io/like-1.8.6.rb, line 711 def readbytes(length) buffer = read(length) if buffer.nil? then raise EOFError, "end of file reached" end if buffer.length < length then raise TruncatedDataError.new("data truncated", buffer) end buffer end
Returns the next 8-bit byte (0..255) from the stream.
Raises EOFError
when there is no more data in the stream.
Raises IOError
if closed? returns
true
. Raises IOError
unless readable? returns
true
.
NOTE: This method ignores Errno::EAGAIN
and
Errno::EINTR
raised by unbuffered_read. Therefore, this
method always blocks. Aside from that exception, this method will also
raise the same errors and block at the same times as unbuffered_read.
# File lib/io/like-1.8.6.rb, line 735 def readchar __io_like__buffered_read(1)[0] rescue Errno::EAGAIN, Errno::EINTR retry if read_ready? end
Returns the next line from the stream, where lines are separated by
sep_string. Increments lineno by 1
for
each call regardless of the value of sep_string.
If sep_string is not nil
and not a
String
, it is first converted to a String
using
its to_str
method and processing continues as follows.
If sep_string is nil
, a line is defined as the
remaining contents of the stream. Partial data will be returned if a
low-level error of any kind is raised after some data is retrieved. This
is equivalent to calling read
without any arguments except that this method will raise an
EOFError
if called at the end of the stream.
If sep_string is an empty String
, a paragraph is
returned, where a paragraph is defined as data followed by 2 or more
successive newline characters. A maximum of 2 newlines are returned at the
end of the returned data. Fewer may be returned if the stream ends before
at least 2 successive newlines are seen.
Any other value for sep_string is used as a delimiter to mark the end of a line. The returned data includes this delimiter unless the stream ends before the delimiter is seen.
In any case, the end of the stream terminates the current line.
Raises EOFError
when there is no more data in the stream.
Raises IOError
if closed? returns
true
. Raises IOError
unless readable? returns
true
.
NOTE: When sep_string is not nil
,
this method ignores Errno::EAGAIN
and
Errno::EINTR
raised by unbuffered_read. Therefore, this
method will always block in that case. Aside from that exception, this
method will raise the same errors and block at the same times as
unbuffered_read.
# File lib/io/like-1.8.6.rb, line 780 def readline(sep_string = $/) # Ensure that sep_string is either nil or a String. unless sep_string.nil? || sep_string.kind_of?(String) then sep_string = sep_string.to_str end buffer = '' begin if sep_string.nil? then # A nil line separator means that the user wants to capture all the # remaining input. loop do buffer << __io_like__buffered_read(4096) end else begin # Record if the user requested paragraphs rather than lines. paragraph_requested = sep_string.empty? # An empty line separator string indicates that the user wants to # return paragraphs. A pair of newlines in the stream is used to # mark this. sep_string = "\n\n" if paragraph_requested if paragraph_requested then # If the user requested paragraphs instead of lines, we need to # consume and discard all newlines remaining at the front of the # input. char = __io_like__buffered_read(1) char = __io_like__buffered_read(1) while char == "\n" # Put back the last character. ungetc(char[0]) end # Add each character from the input to the buffer until either the # buffer has the right ending or the end of the input is reached. while buffer.index(sep_string, -sep_string.length).nil? do buffer << __io_like__buffered_read(1) end if paragraph_requested then # If the user requested paragraphs instead of lines, we need to # consume and discard all newlines remaining at the front of the # input. char = __io_like__buffered_read(1) char = __io_like__buffered_read(1) while char == "\n" # Put back the last character. ungetc(char[0]) end rescue Errno::EAGAIN, Errno::EINTR retry if read_ready? end end rescue EOFError, SystemCallError # Reraise the error if there is nothing to return. raise if buffer.empty? end # Increment the number of times this method has returned a "line". self.lineno += 1 buffer end
Returns an Array
containing the lines in the stream using each_line.
If sep_string is nil
, a line is defined as the
remaining contents of the stream. If sep_string is not a
String
, it is converted to one using its to_str
method. If sep_string is empty, a paragraph is returned, where a
paragraph is defined as data followed by 2 or more successive newline
characters (only 2 newlines are returned at the end of the returned data).
In any case, the end of the stream terminates the current line.
Raises EOFError
when there is no more data in the stream.
Raises IOError
if closed? returns
true
. Raises IOError
unless readable? returns
true
.
NOTE: When sep_string is not nil
,
this method ignores Errno::EAGAIN
and
Errno::EINTR
raised by unbuffered_read. Therefore, this
method always blocks. Aside from that exception, this method will also
raise the same errors and block at the same times as unbuffered_read.
# File lib/io/like-1.8.6.rb, line 865 def readlines(sep_string = $/) lines = [] each_line(sep_string) { |line| lines << line } lines end
Returns at most length bytes from the data stream using only the internal read buffer if the buffer is not empty. Falls back to reading from the stream if the buffer is empty. Blocks if no data is available from either the internal read buffer or the data stream regardless of whether or not the data stream would block.
Raises EOFError
when there is no more data in the stream.
Raises IOError
if closed? returns
true
. Raises IOError
unless readable? returns
true
.
NOTE: This method ignores Errno::EAGAIN
and
Errno::EINTR
raised by unbuffered_read. Therefore, this
method always blocks (via read_ready?) if unable to
immediately return length bytes. Aside from that exception, this
method will also raise the same errors and block at the same times as
unbuffered_read.
# File lib/io/like-1.8.6.rb, line 910 def readpartial(length, buffer = nil) __io_like__readpartial(length, buffer, false) end
Sets the position of the file pointer to the beginning of the stream and
returns 0
when complete. The lineno
attribute is
reset to 0
if successful and the stream is readable according
to readable?.
As a side effect, the internal read and write buffers are flushed.
Raises IOError
if closed? returns
true
. Raises Errno::ESPIPE
unless seekable? returns
true
.
NOTE: Because this method relies on unbuffered_seek and unbuffered_write (when the internal write buffer is not empty), it will also raise the same errors and block at the same times as those functions.
# File lib/io/like-1.8.6.rb, line 930 def rewind seek(0, IO::SEEK_SET) self.lineno = 0 if readable? 0 end
Sets the current data position to offset based on the setting of
whence. If whence is unspecified or
IO::SEEK_SET
, offset counts from the beginning of the
data. If whence is IO::SEEK_END
, offset
counts from the end of the data (offset should be negative here).
If whence is IO::SEEK_CUR
, offset is
relative to the current position.
As a side effect, the internal read and write buffers are flushed except
when seeking relative to the current position (whence is
IO::SEEK_CUR
) to a location within the internal read buffer.
Raises IOError
if closed? returns
true
. Raises Errno::ESPIPE
unless seekable? returns
true
.
NOTE: Because this method relies on unbuffered_seek and unbuffered_write (when the internal write buffer is not empty), it will also raise the same errors and block at the same times as those functions.
# File lib/io/like-1.8.6.rb, line 957 def seek(offset, whence = IO::SEEK_SET) __io_like__buffered_seek(offset, whence) 0 end
Returns true
if the stream is seekable, false
otherwise.
This implementation always returns false
for duplexed objects
and checks to see if unbuffered_seek is defined in order to make its
determination otherwise. Override this if the implementing class always
provides the unbuffered_seek method but may not always be seekable.
# File lib/io/like-1.8.6.rb, line 972 def seekable? ! duplexed? && respond_to?(:unbuffered_seek, true) end
Returns true
if the internal write buffer is currently being
bypassed, false
otherwise.
Raises IOError
if closed? returns
true
.
# File lib/io/like-1.8.6.rb, line 983 def sync raise IOError, 'closed stream' if closed? @__io_like__sync ||= false end
When set to true
the internal write buffer will be bypassed.
Any data currently in the buffer will be flushed prior to the next output
operation. When set to false
, the internal write buffer will
be enabled.
Raises IOError
if closed? returns
true
.
# File lib/io/like-1.8.6.rb, line 997 def sync=(sync) raise IOError, 'closed stream' if closed? @__io_like__sync = sync ? true : false end
Reads and returns up to length bytes directly from the data stream, bypassing the internal read buffer.
Returns ""
if length is 0
regardless of the status of the data stream. This is for compatibility
with IO#sysread
.
Raises EOFError
if reading begins at the end of the stream.
Raises IOError
if the internal read buffer is not empty.
Raises IOError
if closed? returns
true
.
NOTE: Because this method relies on unbuffered_read, it will also raise the same errors and block at the same times as that function.
# File lib/io/like-1.8.6.rb, line 1018 def sysread(length, buffer = nil) buffer = buffer.nil? ? '' : buffer.to_str buffer.slice!(0..-1) unless buffer.empty? return buffer if length == 0 raise IOError, 'closed stream' if closed? raise IOError, 'not opened for reading' unless readable? unless __io_like__internal_read_buffer.empty? then raise IOError, 'sysread on buffered IO' end # Flush the internal write buffer for writable, non-duplexed objects. __io_like__buffered_flush if writable? && ! duplexed? buffer << unbuffered_read(length) end
Sets the current data position to offset based on the setting of
whence. If whence is unspecified or
IO::SEEK_SET
, offset counts from the beginning of the
data. If whence is IO::SEEK_END
, offset
counts from the end of the data (offset should be negative here).
If whence is IO::SEEK_CUR
, offset is
relative to the current position.
Raises IOError
if the internal read buffer is not empty.
Raises IOError
if closed? returns
true
. Raises Errno::ESPIPE
unless seekable? returns
true
.
NOTE: Because this method relies on unbuffered_seek, it will also raise the same errors and block at the same times as that function.
# File lib/io/like-1.8.6.rb, line 1052 def sysseek(offset, whence = IO::SEEK_SET) raise IOError, 'closed stream' if closed? raise Errno::ESPIPE unless seekable? unless __io_like__internal_read_buffer.empty? then raise IOError, 'sysseek on buffered IO' end unless __io_like__internal_write_buffer.empty? then warn('warning: sysseek on buffered IO') end unbuffered_seek(offset, whence) end
Writes string directly to the data stream, bypassing the internal write buffer and returns the number of bytes written.
As a side effect for non-duplex objects, the internal read buffer is flushed.
Raises IOError
if closed? returns
true
. Raises IOError
unless writable? returns
true
.
NOTE: Because this method relies on unbuffered_write, it will also raise the same errors and block at the same times as that function.
# File lib/io/like-1.8.6.rb, line 1079 def syswrite(string) raise IOError, 'closed stream' if closed? raise IOError, 'not opened for writing' unless writable? unless __io_like__internal_write_buffer.empty? then warn('warning: syswrite on buffered IO') end # Flush the internal read buffer and set the unbuffered position to the # buffered position when dealing with non-duplexed objects. unless duplexed? || __io_like__internal_read_buffer.empty? then unbuffered_seek(-__io_like__internal_read_buffer.length, IO::SEEK_CUR) __io_like__internal_read_buffer.slice!(0..-1) end unbuffered_write(string) end
Returns ios.
# File lib/io/like-1.8.6.rb, line 1100 def to_io self end
Pushes the given string onto the front of the internal read buffer and
returns nil
. If string is not a String
,
it is converted to one using its to_s
method.
Raises IOError
if closed? returns
true
. Raises IOError
unless readable? returns
true
.
# File lib/io/like-1.8.6.rb, line 1124 def unread(string) raise IOError, 'closed stream' if closed? raise IOError, 'not opened for reading' unless readable? __io_like__internal_read_buffer.insert(0, string.to_s) nil end
Returns true
if the stream is both open and writable,
false
otherwise.
This implementation checks to see if unbuffered_write is defined in order to make its determination. Override this if the implementing class always provides the unbuffered_write method but may not always be open in a writable mode.
# File lib/io/like-1.8.6.rb, line 1165 def writable? ! __io_like__closed_write? && respond_to?(:unbuffered_write, true) end
Writes the given string to the stream and returns the number of bytes
written. If string is not a String
, its
to_s
method is used to convert it into one. The entire
contents of string are written, blocking as necessary even if the
data stream does not block.
Raises IOError
if closed? returns
true
. Raises IOError
unless writable? returns
true
.
NOTE: This method ignores Errno::EAGAIN
and
Errno::EINTR
raised by unbuffered_write. Therefore, this
method always blocks if unable to immediately write string
completely. Aside from that exception, this method will also raise the
same errors and block at the same times as unbuffered_write.
# File lib/io/like-1.8.6.rb, line 1186 def write(string) string = string.to_s return 0 if string.empty? bytes_written = 0 while bytes_written < string.length do begin bytes_written += __io_like__buffered_write(string.to_s.slice(bytes_written..-1), false) rescue Errno::EAGAIN, Errno::EINTR retry if write_ready? end end bytes_written end
Writes the given string to the stream and returns the number of bytes
written. If string is not a String
, its
to_s
method is used to convert it into one.
As many bytes as possible are written without blocking or SystemCallEerror from unbuffered_write is passed directly through to be handled by the caller
Raises IOError
if closed? returns
true
. Raises IOError
unless writable? returns
true
.
# File lib/io/like-1.8.6.rb, line 1215 def write_nonblock(string) return __io_like__buffered_write(string.to_s, true) end
Returns true
when the stream may be written without error,
false
otherwise. This method will block until one of the
conditions is known.
This default implementation of write_ready? is a hack
which should be able to work for both real IO
objects and
IO
-like objects; however, it is inefficient since it merely
sleeps for 1 second and then returns true
as long as closed? returns
false
. IO.select
should be used for real
IO
objects to wait for a writeable condition on platforms with
support for IO.select
. Other solutions should be found as
necessary to improve this implementation on a case by case basis.
Basically, this method should be overridden in derivative classes.
# File lib/io/like-1.8.6.rb, line 1149 def write_ready? return false unless writable? sleep(1) true end
Private Instance Methods
This method yields the flattened elements of array as a string suitable for puts. seen is a list of # object IDs to detect recursive array nesting
This method exists only because Array#join apparently behaves in an implementation dependent manner when joining recursive arrays and so does not always produce the expected results. Specifically, MRI 1.8.6 and 1.8.7 behave as follows:
x = [] x << 1 << x << 2 x.join(', ') => "1, 1, [...], 2, 2"
The expected and necessary result for use with puts is:
"1, [...], 2"
Things get progressively worse as the nesting and recursion become more convoluted.
# File lib/io/like-1.8.6.rb, line 1489 def __io_like__array_flatten(array,seen = [],&block) seen.push(array.object_id) array.each do |item| if item.nil? yield nil elsif item.kind_of?(Array) if seen.include?(item.object_id) yield '[...]' else __io_like__array_flatten(item,seen,&block) end else yield item.to_s end end seen.pop end
Attempts to completely flush the internal write buffer to the data stream.
Raises IOError
unless writable? returns
true
.
NOTE: Because this method relies on unbuffered_write, it raises all errors raised by unbuffered_write and blocks when unbuffered_write blocks.
# File lib/io/like-1.8.6.rb, line 1231 def __io_like__buffered_flush raise IOError, 'closed stream' if closed? raise IOError, 'not opened for writing' unless writable? until __io_like__internal_write_buffer.empty? do __io_like__internal_write_buffer.slice!( 0, unbuffered_write(__io_like__internal_write_buffer) ) end 0 end
Reads at most length bytes first from an internal read buffer followed by the underlying stream if necessary and returns the resulting buffer.
Raises EOFError
if the internal read buffer is empty and
reading begins at the end of the stream. Raises IOError
unless readable? returns
true
.
NOTE: Because this method relies on unbuffered_read, it raises all errors raised by unbuffered_read and blocks when unbuffered_read blocks whenever the internal read buffer is unable to fulfill the request.
# File lib/io/like-1.8.6.rb, line 1257 def __io_like__buffered_read(length) # Check the validity of the method arguments. raise ArgumentError, "non-positive length #{length} given" if length < 0 raise IOError, 'closed stream' if closed? raise IOError, 'not opened for reading' unless readable? # Flush the internal write buffer for writable, non-duplexed objects. __io_like__buffered_flush if writable? && ! duplexed? # Ensure that the internal read buffer has at least enough data to satisfy # the request. if __io_like__internal_read_buffer.length < length then unbuffered_length = length - __io_like__internal_read_buffer.length unbuffered_length = fill_size if unbuffered_length < fill_size begin __io_like__internal_read_buffer << unbuffered_read(unbuffered_length) rescue EOFError, SystemCallError # Reraise the error if there is no data to return. raise if __io_like__internal_read_buffer.empty? end end # Read from the internal read buffer. buffer = __io_like__internal_read_buffer.slice!(0, length) buffer end
Sets the current data position to offset based on the setting of
whence. If whence is unspecified or
IO::SEEK_SET
, offset counts from the beginning of the
data. If whence is IO::SEEK_END
, offset
counts from the end of the data (offset should be negative here).
If whence is IO::SEEK_CUR
, offset is
relative to the current position.
As a side effect, the internal read and write buffers are flushed except
when seeking relative to the current position (whence is
IO::SEEK_CUR
) to a location within the internal read buffer.
Raises Errno::ESPIPE
unless seekable? returns
true
.
See seek for the usage of offset and whence.
NOTE: Because this method relies on unbuffered_seek and unbuffered_write (when the internal write buffer is not empty), it will raise the same errors and block at the same times as those functions.
# File lib/io/like-1.8.6.rb, line 1353 def __io_like__buffered_seek(offset, whence = IO::SEEK_SET) raise IOError, 'closed stream' if closed? raise Errno::ESPIPE unless seekable? if whence == IO::SEEK_CUR && offset == 0 then # The seek is only determining the current position, so return the # buffered position based on the read buffer if it's not empty and the # write buffer otherwise. __io_like__internal_read_buffer.empty? ? unbuffered_seek(0, IO::SEEK_CUR) + __io_like__internal_write_buffer.length : unbuffered_seek(0, IO::SEEK_CUR) - __io_like__internal_read_buffer.length elsif whence == IO::SEEK_CUR && offset > 0 && __io_like__internal_write_buffer.empty? && offset <= __io_like__internal_read_buffer.length then # The seek is within the read buffer, so just discard a sufficient # amount of the buffer and report the new buffered position. __io_like__internal_read_buffer.slice!(0, offset) unbuffered_seek(0, IO::SEEK_CUR) - __io_like__internal_read_buffer.length else # The seek target is outside of the buffers, so flush the buffers and # jump to the new position. if whence == IO::SEEK_CUR then # Adjust relative offsets based on the current buffered offset. offset += __io_like__internal_read_buffer.empty? ? __io_like__internal_write_buffer.length : -__io_like__internal_read_buffer.length end # Flush the internal buffers. __io_like__internal_read_buffer.slice!(0..-1) __io_like__buffered_flush if writable? # Move the data stream's position as requested. unbuffered_seek(offset, whence) end end
Writes string to the internal write buffer and returns the number of bytes written. If the internal write buffer is overfilled by string, it is repeatedly flushed until that last of string is consumed. A partial write will occur if part of string fills the internal write buffer but the internal write buffer cannot be immediately flushed due to the underlying stream not blocking when unable to accept more data.
NOTE: Because this method relies on unbuffered_write, it raises all errors raised by unbuffered_write and blocks when unbuffered_write blocks whenever the internal write buffer is unable to fulfill the request.
# File lib/io/like-1.8.6.rb, line 1408 def __io_like__buffered_write(string, nonblock = false) raise IOError, 'closed stream' if closed? raise IOError, 'not opened for writing' unless writable? # Flush the internal read buffer and set the unbuffered position to the # buffered position when dealing with non-duplexed objects. unless duplexed? || __io_like__internal_read_buffer.empty? then unbuffered_seek(-__io_like__internal_read_buffer.length, IO::SEEK_CUR) __io_like__internal_read_buffer.slice!(0..-1) end bytes_written = 0 if sync || nonblock || __io_like__internal_write_buffer.length + string.length >= flush_size then # Flush the internal write buffer and then bypass it when in synchronous # mode or the tipping point for the write buffer would be surpassed by this # request. __io_like__nonblock() if nonblock __io_like__buffered_flush bytes_written = unbuffered_write(string) else # The buffer can absorb the entire request. __io_like__internal_write_buffer << string bytes_written = string.length end return bytes_written end
Arranges for #__io_like__closed_read? to return true
.
# File lib/io/like-1.8.6.rb, line 1453 def __io_like__close_read @__io_like__closed_read = true nil end
Arranges for #__io_like__closed_write? to return true
.
# File lib/io/like-1.8.6.rb, line 1465 def __io_like__close_write @__io_like__closed_write = true nil end
Returns true
if this object has been closed for reading;
otherwise, returns false
.
# File lib/io/like-1.8.6.rb, line 1448 def __io_like__closed_read? @__io_like__closed_read ||= false end
Returns true
if this object has been closed for writing;
otherwise, returns false
.
# File lib/io/like-1.8.6.rb, line 1460 def __io_like__closed_write? @__io_like__closed_write ||= false end
Returns a reference to the internal read buffer.
# File lib/io/like-1.8.6.rb, line 1437 def __io_like__internal_read_buffer @__io_like__read_buffer ||= '' end
Returns a reference to the internal write buffer.
# File lib/io/like-1.8.6.rb, line 1442 def __io_like__internal_write_buffer @__io_like__write_buffer ||= '' end
Puts the underlying stream into blocking mode
See read_nonblock, write_nonblock nonblock=
# File lib/io/like-1.8.6.rb, line 1326 def __io_like__nonblock self.nonblock = true end
read_nonblock and read_partial are identical except for the way they handle exceptions from unbuffered_read.
# File lib/io/like-1.8.6.rb, line 1292 def __io_like__readpartial(length, buffer, nonblock) # Check the validity of the method arguments. unless length >= 0 then raise ArgumentError, "negative length #{length} given" end buffer = '' if buffer.nil? # Flush the buffer. buffer.slice!(0..-1) # set nonblocking if necessary __io_like__nonblock if nonblock # Read and return up to length bytes. if __io_like__internal_read_buffer.empty? then begin buffer << __io_like__buffered_read(length) rescue Errno::EINTR, Errno::EAGAIN if !nonblock && read_ready? then retry else raise end end else raise IOError, 'closed stream' if closed? raise IOError, 'not opened for reading' unless readable? buffer << __io_like__internal_read_buffer.slice!(0, length) end buffer end