Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make IO a class #4901

Merged
merged 1 commit into from
Oct 14, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion samples/conway.cr
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ struct ANSI
end
end

module IO
class IO
def ansi
ANSI.new self
end
Expand Down
10 changes: 5 additions & 5 deletions spec/compiler/codegen/module_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -318,19 +318,19 @@ describe "Code gen: module" do
end
end

module IO2
module Moo
end

module IO2::Sub
include IO2
module Moo::Sub
include Moo
end

class File2
include IO2::Sub
include Moo::Sub
end

file = File2.new
file2 = file.as(IO2)
file2 = file.as(Moo)

file.method(file2)
)).to_i.should eq(1)
Expand Down
4 changes: 1 addition & 3 deletions spec/std/file_utils_spec.cr
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
require "spec"
require "file_utils"

private class OneByOneIO
include IO

private class OneByOneIO < IO
@bytes : Bytes

def initialize(string)
Expand Down
8 changes: 2 additions & 6 deletions spec/std/http/server/server_spec.cr
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
require "spec"
require "http/server"

private class RaiseErrno
private class RaiseErrno < IO
def initialize(@value : Int32)
end

include IO

def read(slice : Bytes)
Errno.value = @value
raise Errno.new "..."
Expand All @@ -17,9 +15,7 @@ private class RaiseErrno
end
end

private class ReverseResponseOutput
include IO

private class ReverseResponseOutput < IO
@output : IO

def initialize(@output : IO)
Expand Down
2 changes: 1 addition & 1 deletion spec/std/io/buffered_spec.cr
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
require "spec"

private class BufferedWrapper
private class BufferedWrapper < IO
include IO::Buffered

getter called_unbuffered_read
Expand Down
4 changes: 1 addition & 3 deletions spec/std/io/delimited_spec.cr
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
require "spec"

private class PartialReaderIO
include IO

private class PartialReaderIO < IO
@slice : Bytes

def initialize(data : String)
Expand Down
4 changes: 1 addition & 3 deletions spec/std/io/io_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ require "base64"
# This is a non-optimized version of IO::Memory so we can test
# raw IO. Optimizations for specific IOs are tested separately
# (for example in buffered_io_spec)
private class SimpleIOMemory
include IO

private class SimpleIOMemory < IO
getter buffer : UInt8*
getter bytesize : Int32
@capacity : Int32
Expand Down
4 changes: 1 addition & 3 deletions spec/std/io/sized_spec.cr
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
require "spec"

private class NoPeekIO
include IO

private class NoPeekIO < IO
def read(bytes : Bytes)
0
end
Expand Down
2 changes: 1 addition & 1 deletion src/file/preader.cr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# :nodoc:
class File::PReader
class File::PReader < IO
include IO::Buffered

getter? closed = false
Expand Down
4 changes: 1 addition & 3 deletions src/flate/reader.cr
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
# Instances of this class wrap another IO object. When you read from this instance
# instance, it reads data from the underlying IO, decompresses it, and returns
# it to the caller.
class Flate::Reader
include IO

class Flate::Reader < IO
# If `#sync_close?` is `true`, closing this IO will close the underlying IO.
property? sync_close : Bool

Expand Down
4 changes: 1 addition & 3 deletions src/flate/writer.cr
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@
#
# NOTE: unless created with a block, `close` must be invoked after all
# data has been written to a Flate::Writer instance.
class Flate::Writer
include IO

class Flate::Writer < IO
# If `#sync_close?` is `true`, closing this IO will close the underlying IO.
property? sync_close : Bool

Expand Down
4 changes: 1 addition & 3 deletions src/gzip/reader.cr
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@
# end
# string # => "abc"
# ```
class Gzip::Reader
include IO

class Gzip::Reader < IO
# Whether to close the enclosed `IO` when closing this reader.
property? sync_close = false

Expand Down
4 changes: 1 addition & 3 deletions src/gzip/writer.cr
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@
# end
# end
# ```
class Gzip::Writer
include IO

class Gzip::Writer < IO
# Whether to close the enclosed `IO` when closing this writer.
property? sync_close = false

Expand Down
6 changes: 2 additions & 4 deletions src/http/content.cr
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ module HTTP
end

# :nodoc:
class UnknownLengthContent
include IO
class UnknownLengthContent < IO
include Content

def initialize(@io : IO)
Expand Down Expand Up @@ -46,8 +45,7 @@ module HTTP
end

# :nodoc:
class ChunkedContent
include IO
class ChunkedContent < IO
include Content
@chunk_remaining : Int32

Expand Down
6 changes: 2 additions & 4 deletions src/http/server/response.cr
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ class HTTP::Server
# are written and the connection `IO` (a socket) is yielded to the given block.
# The block must invoke `close` afterwards, the server won't do it in this case.
# This is useful to implement protocol upgrades, such as websockets.
class Response
include IO

class Response < IO
# The response headers (`HTTP::Headers`). These must be set before writing to the response.
getter headers : HTTP::Headers

Expand Down Expand Up @@ -136,7 +134,7 @@ class HTTP::Server
end

# :nodoc:
class Output
class Output < IO
include IO::Buffered

property! response : Response
Expand Down
4 changes: 1 addition & 3 deletions src/http/web_socket/protocol.cr
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,7 @@ class HTTP::WebSocket::Protocol
@masked = !!masked
end

class StreamIO
include IO

class StreamIO < IO
def initialize(@websocket : Protocol, binary, frame_size)
@opcode = binary ? Opcode::BINARY : Opcode::TEXT
@buffer = Bytes.new(frame_size)
Expand Down
79 changes: 73 additions & 6 deletions src/io.cr
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ require "c/sys/wait"
require "c/errno"
require "c/unistd"

# The `IO` module is the basis for all input and output in Crystal.
# The `IO` class is the basis for all input and output in Crystal.
#
# This module is included by types like `File`, `Socket` and `IO::Memory` and
# This class is inherited by types like `File`, `Socket` and `IO::Memory` and
# provide many useful methods for reading to and writing from an IO, like `print`, `puts`,
# `gets` and `printf`.
#
Expand All @@ -20,9 +20,7 @@ require "c/unistd"
# For example, this is a simple `IO` on top of a `Bytes`:
#
# ```
# class SimpleSliceIO
# include IO
#
# class SimpleSliceIO < IO
# def initialize(@slice : Bytes)
# end
#
Expand Down Expand Up @@ -61,7 +59,7 @@ require "c/unistd"
# Mixing string and byte operations might not give correct results and should be
# avoided, as string operations might need to read extra bytes in order to get characters
# in the given encoding.
module IO
abstract class IO
# Argument to a `seek` operation.
enum Seek
# Seeks to an absolute location
Expand Down Expand Up @@ -1048,6 +1046,75 @@ module IO
@encoding.try(&.name) || "UTF-8"
end

# Seeks to a given *offset* (in bytes) according to the *whence* argument.
#
# The `IO` class raises on this method, but some subclasses, notable
# `IO::FileDescriptor` and `IO::Memory` implement it.
#
# Returns `self`.
#
# ```
# File.write("testfile", "abc")
#
# file = File.new("testfile")
# file.gets(3) # => "abc"
# file.seek(1, IO::Seek::Set)
# file.gets(2) # => "bc"
# file.seek(-1, IO::Seek::Current)
# file.gets(1) # => "c"
# ```
def seek(offset, whence : Seek = Seek::Set)
raise Error.new "Unable to seek"
end

# Returns the current position (in bytes) in this `IO`.
#
# The `IO` class raises on this method, but some subclasses, notable
# `IO::FileDescriptor` and `IO::Memory` implement it.
#
# ```
# File.write("testfile", "hello")
#
# file = File.new("testfile")
# file.pos # => 0
# file.gets(2) # => "he"
# file.pos # => 2
# ```
def pos
raise Error.new "Unable to pos"
end

# Sets the current position (in bytes) in this `IO`.
#
# The `IO` class raises on this method, but some subclasses, notable
# `IO::FileDescriptor` and `IO::Memory` implement it.
#
# ```
# File.write("testfile", "hello")
#
# file = File.new("testfile")
# file.pos = 3
# file.gets_to_end # => "lo"
# ```
def pos=(value)
raise Error.new "Unable to pos="
end

# Same as `pos`.
def tell
pos
end

# Yields an `IO` to read a section inside this IO.
#
# The `IO` class raises on this method, but some subclasses, notable
# `File` and `IO::Memory` implement it.
#
# Mutliple sections can be read concurrently.
def read_at(offset, bytesize, &block)
raise Error.new "Unable to read_at"
end

# Copy all contents from *src* to *dst*.
#
# ```
Expand Down
4 changes: 1 addition & 3 deletions src/io/argf.cr
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# :nodoc:
class IO::ARGF
include IO

class IO::ARGF < IO
@path : String?
@current_io : IO?

Expand Down
4 changes: 1 addition & 3 deletions src/io/buffered.cr
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
# The `IO::Buffered` mixin enhances the `IO` module with input/output buffering.
# The `IO::Buffered` mixin enhances an `IO` with input/output buffering.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WhyIO::Buffered should be a module and IO::Delimited a class?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could make IO::Buffered a class too, but then you'd have to do like in Java:

io = File.open("some_file")
io = IO::Buffered.new(io)
io.gets

otherwise things will be very inefficient. That's why in Ruby buffering is embedded in these kind of IOs because it's almost always slow otherwise.

We could have IO::Buffering as a module and IO::Buffered as a class, though, or something like that.

Still, maybe a discussion for another issue :-)

#
# The buffering behaviour can be turned on/off with the `#sync=` method.
#
# Additionally, several methods, like `#gets`, are implemented in a more
# efficient way.
module IO::Buffered
include IO

BUFFER_SIZE = 8192

@in_buffer_rem = Bytes.empty
Expand Down
2 changes: 1 addition & 1 deletion src/io/console.cr
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
require "termios"

class IO::FileDescriptor
class IO::FileDescriptor < IO
# Turn off character echoing for the duration of the given block.
# This will prevent displaying back to the user what they enter on the terminal.
# Only call this when this IO is a TTY, such as a not redirected stdin.
Expand Down
Loading