class Stash::Sword::SequenceIO

A read-only `IO`-like that concatenates a sequence of strings or IOs.

Attributes

index[R]
input[R]
inputs[R]

Public Class Methods

new(inputs) click to toggle source

Creates a new {SequenceIO} concatenating the specified input sources. Strings are wrapped internally as `StringIO`.

@param inputs [Enumerable<String, IO>] an array of strings and/or IOs to

concatenate
# File lib/stash/sword/sequence_io.rb, line 13
def initialize(inputs)
  inputs  = [inputs] unless inputs.respond_to?(:[]) && inputs.respond_to?(:map)
  @inputs = to_ios(inputs)
  binmode if any_binmode(@inputs)
  @index = 0
  @input = @inputs[index] unless inputs.empty?
end

Public Instance Methods

binmode() click to toggle source
# File lib/stash/sword/sequence_io.rb, line 36
def binmode
  return self if binmode?
  inputs.each do |input|
    input.binmode if input.respond_to?(:binmode)
  end
  @binmode = true
  self
end
binmode?() click to toggle source
# File lib/stash/sword/sequence_io.rb, line 45
def binmode?
  @binmode
end
close() click to toggle source
# File lib/stash/sword/sequence_io.rb, line 49
def close
  next_input! until input.nil?
end
closed?() click to toggle source
# File lib/stash/sword/sequence_io.rb, line 53
def closed?
  input.nil? && index >= inputs.length
end
read(length = nil, outbuf = nil) click to toggle source
# File lib/stash/sword/sequence_io.rb, line 28
def read(length = nil, outbuf = nil)
  # use <= instead of == to get around https://github.com/bbatsov/rubocop/issues/3131
  return nil if size <= 0
  outbuf = outbuf ? outbuf.clear : ''
  length ? read_segment(length, outbuf) : read_fully(outbuf)
  outbuf
end
size() click to toggle source
# File lib/stash/sword/sequence_io.rb, line 21
def size
  @size ||= inputs.inject(0) do |sum, input|
    raise "input #{input} does not respond to :size" unless input.respond_to?(:size)
    sum + input.size
  end
end

Private Instance Methods

any_binmode(ios) click to toggle source
# File lib/stash/sword/sequence_io.rb, line 97
def any_binmode(ios)
  ios.each do |io|
    return true if io.respond_to?(:binmode?) && io.binmode?
  end
  false
end
next_input!() click to toggle source

TODO: Array.pop! or something

# File lib/stash/sword/sequence_io.rb, line 85
def next_input!
  input.close if input && input.respond_to?(:close)
  @index += 1
  @input = index < inputs.length ? inputs[index] : nil
end
read_fully(buffer) click to toggle source
# File lib/stash/sword/sequence_io.rb, line 63
def read_fully(buffer)
  until input.nil?
    buffer << input.read(nil)
    next_input!
  end
end
read_segment(length, buffer) click to toggle source
# File lib/stash/sword/sequence_io.rb, line 70
def read_segment(length, buffer)
  return unless input && length > 0

  remaining = length
  if (result = input.read(length))
    buffer << result
    remaining = length - result.length
  end
  return unless remaining > 0

  next_input!
  read_segment(remaining, buffer)
end
to_ios(inputs) click to toggle source
# File lib/stash/sword/sequence_io.rb, line 91
def to_ios(inputs)
  inputs.map do |input|
    input.respond_to?(:read) ? input : StringIO.new(input.to_s)
  end
end