lib/io_streams/stream.rb in iostreams-1.0.0.beta7 vs lib/io_streams/stream.rb in iostreams-1.0.0
- old
+ new
@@ -1,27 +1,27 @@
module IOStreams
class Stream
attr_reader :io_stream
- attr_writer :streams
+ attr_writer :builder
def initialize(io_stream)
raise(ArgumentError, 'io_stream cannot be nil') if io_stream.nil?
raise(ArgumentError, "io_stream must not be a string: #{io_stream.inspect}") if io_stream.is_a?(String)
@io_stream = io_stream
- @streams = nil
+ @builder = nil
end
# Ignore the filename and use only the supplied streams.
#
# See #option to set an option for one of the streams included based on the file name extensions.
#
# Example:
#
# IOStreams.path('tempfile2527').stream(:zip).stream(:pgp, passphrase: 'receiver_passphrase').reader(&:read)
def stream(stream, **options)
- streams.stream(stream, **options)
+ builder.stream(stream, **options)
self
end
# Set the options for an element within the stream for this file.
# If the relevant stream is not found for this file it is ignored.
@@ -32,49 +32,118 @@
# # In this case the file is not pgp so the `passphrase` option is ignored.
# IOStreams.path('keep_safe.enc').option(:pgp, passphrase: 'receiver_passphrase').reader(&:read)
#
# IOStreams.path(output_file_name).option(:pgp, passphrase: 'receiver_passphrase').reader(&:read)
def option(stream, **options)
- streams.option(stream, **options)
+ builder.option(stream, **options)
self
end
# Adds the options for the specified stream as an option,
# but if streams have already been added it is instead added as a stream.
def option_or_stream(stream, **options)
- streams.option_or_stream(stream, **options)
+ builder.option_or_stream(stream, **options)
self
end
# Return the options already set for either a stream or option.
def setting(stream)
- streams.setting(stream)
+ builder.setting(stream)
self
end
# Returns [Hash<Symbol:Hash>] the pipeline of streams
# with their options that will be applied when the reader or writer is invoked.
def pipeline
- streams.pipeline
+ builder.pipeline
end
+ # Iterate over a file / stream returning one line at a time.
+ #
+ # Example: Read a line at a time
+ # IOStreams.path("file.txt").each(:line) do |line|
+ # puts line
+ # end
+ #
+ # Example: Read a line at a time with custom options
+ # IOStreams.path("file.csv").each(:line, embedded_within: '"') do |line|
+ # puts line
+ # end
+ #
+ # Example: Read a row at a time
+ # IOStreams.path("file.csv").each(:array) do |array|
+ # p array
+ # end
+ #
+ # Example: Read a record at a time
+ # IOStreams.path("file.csv").each(:hash) do |hash|
+ # p hash
+ # end
+ #
+ # Notes:
+ # - Embedded lines (within double quotes) will be skipped if
+ # 1. The file name contains .csv
+ # 2. Or the embedded_within argument is set
+ def each(mode = :line, **args, &block)
+ raise(ArgumentError, "Invalid mode: #{mode.inspect}") if mode == :stream
+
+ # return enum_for __method__ unless block_given?
+ reader(mode, **args) { |stream| stream.each(&block) }
+ end
+
# Returns a Reader for reading a file / stream
- def reader(&block)
- streams.reader(io_stream, &block)
+ def reader(mode = :stream, **args, &block)
+ case mode
+ when :stream
+ stream_reader(&block)
+ when :line
+ line_reader(**args, &block)
+ when :array
+ row_reader(**args, &block)
+ when :hash
+ record_reader(**args, &block)
+ else
+ raise(ArgumentError, "Invalid mode: #{mode.inspect}")
+ end
end
# Read an entire file into memory.
#
# Notes:
# - Use with caution since large files can cause a denial of service since
# this method will load the entire file into memory.
- # - Recommend using instead `#reader`, `#each_line`, or `#each_record` to read a
- # block into memory at a time.
+ # - Recommend using instead `#reader` to read a block into memory at a time.
def read(*args)
reader { |stream| stream.read(*args) }
end
+ # Returns a Writer for writing to a file / stream
+ def writer(mode = :stream, **args, &block)
+ case mode
+ when :stream
+ stream_writer(&block)
+ when :line
+ line_writer(**args, &block)
+ when :array
+ row_writer(**args, &block)
+ when :hash
+ record_writer(**args, &block)
+ else
+ raise(ArgumentError, "Invalid mode: #{mode.inspect}")
+ end
+ end
+
+ # Write entire string to file.
+ #
+ # Notes:
+ # - Use with caution since preparing large amounts of data in memory can cause a denial of service
+ # since all the data for the file needs to be resident in memory before writing.
+ # - Recommend using instead `#writer` to write a block of memory at a time.
+ def write(data)
+ writer { |stream| stream.write(data) }
+ end
+
# Copy from another stream, path, file_name or IO instance.
#
# Parameters:
# stream [IOStreams::Path|String<file_name>|IO]
# The stream to read from.
@@ -111,113 +180,23 @@
def copy_to(target, convert: true)
target = IOStreams.path(target) unless target.is_a?(Stream)
target.copy_from(self, convert: convert)
end
- # Iterate over a file / stream returning one line at a time.
- # Embedded lines (within double quotes) will be skipped if
- # 1. The file name contains .csv
- # 2. Or the embedded_within argument is set
- #
- # Example: Supply custom options
- # IOStreams.each_line(file_name, embedded_within: '"') do |line|
- # puts line
- # end
- #
- def each_line(**args, &block)
- # return enum_for __method__ unless block_given?
- line_reader(**args) { |line_stream| line_stream.each(&block) }
- end
-
- # Iterate over a file / stream returning one line at a time.
- # Embedded lines (within double quotes) will be skipped if
- # 1. The file name contains .csv
- # 2. Or the embedded_within argument is set
- #
- # Example: Supply custom options
- # IOStreams.each_row(file_name, embedded_within: '"') do |line|
- # puts line
- # end
- #
- def each_row(**args, &block)
- row_reader(**args) { |row_stream| row_stream.each(&block) }
- end
-
- # Returns [Hash] of every record in a file or stream with support for headers.
- def each_record(**args, &block)
- record_reader(**args) { |record_stream| record_stream.each(&block) }
- end
-
- # Iterate over a file / stream returning each record/line one at a time.
- # It will apply the embedded_within argument if the file or input_stream contain .csv in its name.
- def line_reader(embedded_within: nil, **args)
- embedded_within = '"' if embedded_within.nil? && streams.file_name&.include?('.csv')
-
- reader { |io| yield IOStreams::Line::Reader.new(io, embedded_within: embedded_within, **args) }
- end
-
- # Iterate over a file / stream returning each line as an array, one at a time.
- def row_reader(delimiter: nil, embedded_within: nil, **args)
- line_reader(delimiter: delimiter, embedded_within: embedded_within) do |io|
- yield IOStreams::Row::Reader.new(io, **args)
- end
- end
-
- # Iterate over a file / stream returning each line as a hash, one at a time.
- def record_reader(delimiter: nil, embedded_within: nil, **args)
- line_reader(delimiter: delimiter, embedded_within: embedded_within) do |io|
- yield IOStreams::Record::Reader.new(io, **args)
- end
- end
-
- # Returns a Writer for writing to a file / stream
- def writer(&block)
- streams.writer(io_stream, &block)
- end
-
- # Write entire string to file.
- #
- # Notes:
- # - Use with caution since preparing large amounts of data in memory can cause a denial of service
- # since all the data for the file needs to be resident in memory before writing.
- # - Recommend using instead `#writer`, `#line_writer`, or `#row_writer` to write a
- # block of memory at a time.
- def write(data)
- writer { |stream| stream.write(data) }
- end
-
- def line_writer(**args, &block)
- return block.call(io_stream) if io_stream && io_stream.is_a?(IOStreams::Line::Writer)
-
- writer { |io| IOStreams::Line::Writer.stream(io, **args, &block) }
- end
-
- def row_writer(delimiter: $/, **args, &block)
- return block.call(io_stream) if io_stream && io_stream.is_a?(IOStreams::Row::Writer)
-
- line_writer(delimiter: delimiter) { |io| IOStreams::Row::Writer.stream(io, **args, &block) }
- end
-
- def record_writer(delimiter: $/, **args, &block)
- return block.call(io_stream) if io_stream && io_stream.is_a?(IOStreams::Record::Writer)
-
- line_writer(delimiter: delimiter) { |io| IOStreams::Record::Writer.stream(io, **args, &block) }
- end
-
# Set/get the original file_name
def file_name(file_name = :none)
if file_name == :none
- streams.file_name
+ builder.file_name
else
- streams.file_name = file_name
+ builder.file_name = file_name
self
end
end
# Set/get the original file_name
def file_name=(file_name)
- streams.file_name = file_name
+ builder.file_name = file_name
end
# Returns [String] the last component of this path.
# Returns `nil` if no `file_name` was set.
#
@@ -228,11 +207,11 @@
#
# IOStreams.path("/home/gumby/work/ruby.rb").basename #=> "ruby.rb"
# IOStreams.path("/home/gumby/work/ruby.rb").basename(".rb") #=> "ruby"
# IOStreams.path("/home/gumby/work/ruby.rb").basename(".*") #=> "ruby"
def basename(suffix = nil)
- file_name = streams.file_name
+ file_name = builder.file_name
return unless file_name
suffix.nil? ? ::File.basename(file_name) : ::File.basename(file_name, suffix)
end
@@ -246,11 +225,11 @@
# IOStreams.path(".a/b/d/test.rb").dirname #=> ".a/b/d"
# IOStreams.path("foo.").dirname #=> "."
# IOStreams.path("test").dirname #=> "."
# IOStreams.path(".profile").dirname #=> "."
def dirname
- file_name = streams.file_name
+ file_name = builder.file_name
::File.dirname(file_name) if file_name
end
# Returns [String] the extension for this file including the last period.
# Returns `nil` if no `file_name` was set.
@@ -266,11 +245,11 @@
# IOStreams.path("foo.").extname #=> ""
# IOStreams.path("test").extname #=> ""
# IOStreams.path(".profile").extname #=> ""
# IOStreams.path(".profile.sh").extname #=> ".sh"
def extname
- file_name = streams.file_name
+ file_name = builder.file_name
::File.extname(file_name) if file_name
end
# Returns [String] the extension for this file _without_ the last period.
# Returns `nil` if no `file_name` was set.
@@ -291,10 +270,56 @@
extname&.sub(/^\./, '')
end
private
- def streams
- @streams ||= IOStreams::Streams.new
+ def builder
+ @builder ||= IOStreams::Builder.new
+ end
+
+ def stream_reader(&block)
+ builder.reader(io_stream, &block)
+ end
+
+ def line_reader(embedded_within: nil, **args)
+ embedded_within = '"' if embedded_within.nil? && builder.file_name&.include?('.csv')
+
+ stream_reader { |io| yield IOStreams::Line::Reader.new(io, embedded_within: embedded_within, **args) }
+ end
+
+ # Iterate over a file / stream returning each line as an array, one at a time.
+ def row_reader(delimiter: nil, embedded_within: nil, **args)
+ line_reader(delimiter: delimiter, embedded_within: embedded_within) do |io|
+ yield IOStreams::Row::Reader.new(io, **args)
+ end
+ end
+
+ # Iterate over a file / stream returning each line as a hash, one at a time.
+ def record_reader(delimiter: nil, embedded_within: nil, **args)
+ line_reader(delimiter: delimiter, embedded_within: embedded_within) do |io|
+ yield IOStreams::Record::Reader.new(io, **args)
+ end
+ end
+
+ def stream_writer(&block)
+ builder.writer(io_stream, &block)
+ end
+
+ def line_writer(**args, &block)
+ return block.call(io_stream) if io_stream && io_stream.is_a?(IOStreams::Line::Writer)
+
+ writer { |io| IOStreams::Line::Writer.stream(io, **args, &block) }
+ end
+
+ def row_writer(delimiter: $/, **args, &block)
+ return block.call(io_stream) if io_stream && io_stream.is_a?(IOStreams::Row::Writer)
+
+ line_writer(delimiter: delimiter) { |io| IOStreams::Row::Writer.stream(io, **args, &block) }
+ end
+
+ def record_writer(delimiter: $/, **args, &block)
+ return block.call(io_stream) if io_stream && io_stream.is_a?(IOStreams::Record::Writer)
+
+ line_writer(delimiter: delimiter) { |io| IOStreams::Record::Writer.stream(io, **args, &block) }
end
end
end