# class GxG::IO::StringIO

### Public Class Methods

new(*args) click to toggle source
Calls superclass method

    # File lib/gxg/gxg_io.rb, line 2063
    def initialize(*args)
      # TODO: override methods to operate in cooperative processing event system
      # TODO: provide a REAL external:internal encoding translation aspect (stock version is poorly designed/wrong, it would act *exactly* like a real IO)
      # TODO: optimize this class for memory and call efficiency (see Question at super() call)
      # LATER: GxG::StringIO : create/learn/whatever an optimized any-encoding-to-any-encoding transcoding/normalization matrix : possibly extend Encoding Class? This should include a conversion newline/field_separator/record_separator for each pair of Encodings.
      # Question: is there some possible benefit to using a ByteArray instead of a stock StringIO ?
      params = self.process_parameters(*args)
      unless params[:mode].size > 0
        params[:mode] << :read
        params[:mode] << :write
      end
      if params[:external_encoding] == ::Encoding::ASCII_8BIT
        unless params[:mode].include?(:binary)
          params[:mode] << :binary
        end
      end
      #
      mode_string = ""
      mode_numeric = 0
      @fcntl_mode = ::Fcntl::O_NONBLOCK
      mode_flags = {:binary => ::IO::BINARY, :text => ::IO::TEXT, :read => ::IO::RDONLY, :write => ::IO::WRONLY, :readwrite => ::IO::RDWR, :create => ::IO::CREAT, :overwrite => ::IO::TRUNC, :append => ::IO::APPEND}
      modes = GxG::IO::IO::valid_mode_set({:type => :io, :read => true, :write => true, :text => true, :binary => true})
      the_mode_used = nil
      options = {}
      # :mode
      #Same as mode parameter
      if params[:mode]
        if params[:mode].index(:binary)
          if params[:mode].index(:read)
            if params[:mode].index(:write)
              mode_string = "binary_readwrite"
              mode_numeric = (mode_numeric | mode_flags[:binary] | mode_flags[:readwrite])
              @fcntl_mode = (@fcntl_mode | ::Fcntl::O_RDWR)
            else
              mode_string = "binary_read"
              mode_numeric = (mode_numeric | mode_flags[:binary] | mode_flags[:read])
              @fcntl_mode = (@fcntl_mode | ::Fcntl::O_RDONLY)
            end
          else
            if params[:mode].index(:write)
              mode_string = "binary_write"
              mode_numeric = (mode_numeric | mode_flags[:binary] | mode_flags[:write])
              @fcntl_mode = (@fcntl_mode | ::Fcntl::O_WRONLY)
            end
          end
        else
          if params[:mode].index(:read)
            if params[:mode].index(:write)
              mode_string = "text_readwrite"
              mode_numeric = (mode_numeric | mode_flags[:text] | mode_flags[:readwrite])
              @fcntl_mode = (@fcntl_mode | ::Fcntl::O_RDWR)
            else
              mode_string = "text_read"
              mode_numeric = (mode_numeric | mode_flags[:text] | mode_flags[:read])
              @fcntl_mode = (@fcntl_mode | ::Fcntl::O_RDONLY)
            end
          else
            if params[:mode].index(:write)
              mode_string = "text_write"
              mode_numeric = (mode_numeric | mode_flags[:text] | mode_flags[:write])
              @fcntl_mode = (@fcntl_mode | ::Fcntl::O_WRONLY)
            end
          end
        end
        if (params[:mode].index(:overwrite) || params[:mode].index(:truncate) )
          mode_string << "_overwrite"
          mode_numeric = (mode_numeric | mode_flags[:overwrite])
          @fcntl_mode = (@fcntl_mode | ::Fcntl::O_TRUNC)
        else
          if params[:mode].index(:append)
            mode_string << "_append"
            mode_numeric = (mode_numeric | mode_flags[:append])
            @fcntl_mode = (@fcntl_mode | ::Fcntl::O_APPEND)
          else
            if params[:mode].index(:create)
              mode_string << "_create"
              mode_numeric = (mode_numeric | mode_flags[:create])
              @fcntl_mode = (@fcntl_mode | ::Fcntl::O_CREAT)
            end
          end
        end
        # [:read, :write, :readwrite, :overwrite, :truncate:, :append, :text, :binary]
        modes.to_enum.each do |mode_entry|
          mode_entry[:synonyms].to_enum.each do |the_synonym|
            if the_synonym.is_a?(Symbol)
              if the_synonym == mode_string.to_sym
                the_mode_used = mode_entry
                break
              end
            else
              if the_synonym.is_a?(Numeric)
                if (the_synonym & mode_numeric == mode_numeric)
                  the_mode_used = mode_entry
                  break
                end
              end
            end
          end
          if the_mode_used
            break
          end
          #
        end
        if (the_mode_used)
          options[:mode] = the_mode_used[:if_exist].to_i
        else
          modes_list = []
          modes.to_enum.each do |item|
            modes_list << item[:synonyms]
          end
          raise ArgumentError, "#{params[:mode].inspect} is an invalid :mode, use one of the following: #{modes_list.inspect}"
        end
      end
      #
      mode_numeric = options.delete(:mode)
      #
      @conversion_options = {:external => {}, :internal => {}}
      # init-text clobber work-around : defer :write of init-text until the end.
      # init_text = params[:object].dup
      # params[:object].clear
      # FORNOW: just use default behavior
      super(params[:object],mode_numeric)
      #
      if params[:mode].include?(:binary)
        self.set_encoding(::Encoding::ASCII_8BIT)
      else
        if params[:external_encoding].is_a?(::Encoding)
          self.set_encoding(params.delete(:external_encoding))
          #:2nd internal_encoding parameter and 3rd parameter optional hash ignored
          # if you want the supplied string's encoding - best to simply pass it on the .new method call params.
        else
          self.set_encoding(::Encoding.default_external)
        end
      end
      #
      # if external_encoding is BINARY/ASCII_8BIT - just nullify internal_encoding, and skip external/internal conversion option mapping.
      if params[:mode].include?(:binary)
        @internal_encoding = nil
      else
        if params[:internal_encoding].is_a?(::Encoding)
          @internal_encoding = params.delete(:internal_encoding)
        else
          @internal_encoding = ::Encoding.default_internal
        end
        #
        if @internal_encoding
          if @internal_encoding != self.external_encoding
            if params[:external_conversion]
              # External conversion options will have to be dynamically generated upon transcode_to_external as *any* encoding is possible, not just internal_encoding
              @conversion_options[:external] = ::String::transcode_options(self.external_encoding,@internal_encoding,params[:external_conversion])
            else
              @conversion_options[:external] = ::String::transcode_options(self.external_encoding,@internal_encoding)
            end
            #
            if @internal_encoding == ::Encoding::ASCII_8BIT
              #
              if params[:internal_conversion]
                nl_op = self.newline_option_used(params[:internal_conversion])
                conv_options = {}
                if nl_op
                  conv_options[(nl_op)] = true
                end
                @conversion_options[:internal] = conv_options
              end
            else
              if params[:internal_conversion]
                @conversion_options[:internal] = ::String::transcode_options(@internal_encoding,self.external_encoding,params[:internal_conversion])
              else
                @conversion_options[:internal] = ::String::transcode_options(@internal_encoding,self.external_encoding)
              end
            end
            #
          end
        end
        #
      end
      #
      @external_field_separator = nil
      if params[:external_field_separator]
        self.external_field_separator = params[:external_field_separator]
      end
      @external_record_separator = nil
      if params[:external_record_separator]
        self.external_record_separator = params[:external_record_separator]
      end
      @internal_field_separator = nil
      if params[:internal_field_separator]
        self.external_field_separator = params[:internal_field_separator]
      end
      @internal_record_separator = nil
      if params[:internal_record_separator]
        self.external_field_separator = params[:internal_record_separator]
      end
      #
      # init-text clobber workaround : deferred :write
      #self.write(init_text)
      self
    end

open(the_string="",mode=::IO::RDWR,&block) click to toggle source

    # File lib/gxg/gxg_io.rb, line 1962
    def self.open(the_string="",mode=::IO::RDWR,&block)
      result = nil
      the_io = GxG::IO::StringIO.new(the_string,mode)
      #
      if block.respond_to?(:call)
        result = block.call(the_io)
        the_io.close
      else
        result = the_io
      end
      #
      result
    end

### Public Instance Methods

<<(*args) click to toggle source

    # File lib/gxg/gxg_io.rb, line 2392
    def <<(*args)
      self.write(*args)
      self
    end

binmode() click to toggle source
Calls superclass method `StringIO#binmode`

    # File lib/gxg/gxg_io.rb, line 2276
    def binmode()
      if self.binmode?
        self
      else
        @internal_encoding = nil
        @conversion_options[:internal] = {}
        @conversion_options[:external] = {}
        self.set_encoding(::Encoding::ASCII_8BIT)
        super()
      end
    end

binmode?() click to toggle source

    # File lib/gxg/gxg_io.rb, line 2268
    def binmode?()
      if self.external_encoding == ::Encoding::ASCII_8BIT
        true
      else
        false
      end
    end

close() click to toggle source
Calls superclass method

    # File lib/gxg/gxg_io.rb, line 2493
    def close()
      super()
    end

close_read() click to toggle source
Calls superclass method

    # File lib/gxg/gxg_io.rb, line 2497
    def close_read()
      super()
    end

close_write() click to toggle source
Calls superclass method

    # File lib/gxg/gxg_io.rb, line 2501
    def close_write()
      super()
    end

external_encoding=(*args) click to toggle source

    # File lib/gxg/gxg_io.rb, line 2288
    def external_encoding=(*args)
      #
      unless self.binmode?
        if args[0].is_any?(::Encoding, ::NilClass)
          #
          if args[0] == ::Encoding::ASCII_8BIT
            self.binmode()
          else
            old_data = self.string().dup
            #
            if args[0].is_a?(::Encoding)
              self.set_encoding(args[0])
              if @internal_encoding
                if @internal_encoding != self.external_encoding
                  # preserve newline settings
                  nl_op = self.newline_option_used(:external)
                  options = {}
                  if nl_op
                    options[(nl_op)] = true
                  end
                  @conversion_options[:external] = ::String::transcoding_options(self.external_encoding,@internal_encoding,options)
                end
              end
            else
              self.set_encoding(::Encoding.default_external())
            end
            #
            if self.external_encoding() != old_data.encoding()
              # preserve newline settings
              nl_op = self.newline_option_used(:external)
              options = {}
              if nl_op
                options[(nl_op)] = true
              end
              old_data.transcode!(self.external_encoding(),options)
              #
              self.string.replace(old_data)
            end
            #
          end
          #
        else
          raise ArgumentError, "Expected an Encoding or NilClass, you provided #{args[0].class}"
        end
      end
      #
    end

flags() click to toggle source

Public instance methods

    # File lib/gxg/gxg_io.rb, line 1977
    def flags()
      result = []
      current = self.fcntl(::Fcntl::F_GETFL)
      flags = {}
      if self.binmode?()
        result << :binary
        if self.external_encoding == ::Encoding::ASCII_8BIT
          result << :setenc_by_bom
        end
      else
        result << :text
        result << :wsplit
      end
      if self.tty?
        result << :tty
      end
      #
      #flag[:read] = 0x00000001
      #flag[:write] = 0x00000002
      #flag[:readwrite] = (flag[:read] | flag[:write])
      #flag[:binmode] = 0x00000004
      #flag[:readwrite] = (flag[:read] | flag[:write])
      flags[:read] = ::Fcntl::O_RDONLY
      flags[:write] = ::Fcntl::O_WRONLY
      flags[:readwrite] =::Fcntl::O_RDWR
      #flags[:binary] = 0x00000004
      flags[:sync] = ::File::SYNC # no internal buffering -- The file will be opened for synchronous I/O. No write operation will complete until the data has been physically written to disk.
      flags[:dsync] = ::File::DSYNC # only normal data be synchronized after each write operation, not metadata.
      flags[:rsync] = ::File::RSYNC # the synchronization of read requests as well as write requests. It must be used with one of IO::SYNC or IO::DSYNC
      #flags[:tty] = 0x00000010
      flags[:notcontrol_tty] = ::Fcntl::O_NOCTTY
      flags[:duplex] = 0x00000020
      flags[:append] = ::Fcntl::O_APPEND
      flags[:create] = ::Fcntl::O_CREAT
      flags[:exclusive] = ::Fcntl::O_EXCL
      # flags[:wsplit] = 0x00000200
      flags[:wsplit_initialized] = 0x00000400
      flags[:trunc] = ::Fcntl::O_TRUNC
      #flags[:text] = 0x00001000
      #flags[:setenc_by_bom] = 0x00100000
      flags[:seek_set] = ::File::SEEK_SET
      flags[:seek_current] = ::File::SEEK_CUR
      flags[:seek_end] = ::File::SEEK_END
      # ### file region locking:
      flags[:shared_lock] = ::File::LOCK_SH # for reading
      flags[:exclusive_lock] = ::File::LOCK_EX # for writing (implies blocking)
      flags[:nonblock_lock] = ::File::LOCK_NB # combine with exclusive_lock for immediate non-blocking lock for writing.
      flags[:unlock] = ::File::LOCK_UN
      #
      flags[:nonblocking] = ::Fcntl::O_NONBLOCK
      flags[:ndelay] = ::Fcntl::O_NDELAY
      # unrelated to in-memory IO-like objects
      #        flags[:nofollow] = ::File::NOFOLLOW # Do not follow symlinks.
      #        flags[:noaccesstime] = ::File::NOATIME # Do not update the access time (atime) of the file.
      #        flags[:match_noescape] = ::File::FNM_NOESCAPE #
      #        flags[:match_pathname] = ::File::FNM_PATHNAME #
      #        flags[:match_dotmatch] = ::File::FNM_DOTMATCH #
      #        flags[:match_casefold] = ::File::FNM_CASEFOLD #
      #        flags[:match_systemcase] = ::File::FNM_SYSCASE #
      #
      flags.keys.to_enum.each do |the_flag_key|
        if (current & flags[(the_flag_key)]) == flags[(the_flag_key)]
          case the_flag_key
          when :readwrite
            if result.index(:write)
              result << :read
            else
              if result.index(:read)
                result << :write
              end
            end
          when :wsplit_initialized
            if result.index(:wsplit)
              result[(result.index(:wsplit))] = :wsplit_initialized
            else
              result << :wsplit_initialized
            end
          else
            result << the_flag_key
          end
        end
      end
      #
      result
    end

inspect() click to toggle source

:reopen, :string, :string, :lineno, :lineno, :close_read, :close_write, :closed?, :closed_read?, :closed_write?, :eof, :eof?, :fcntl, :flush, :fsync, :pos, :pos, :rewind, :seek, :sync, :sync, :tell, :each, :each_line, :lines, :each_codepoint, :codepoints, :getc, :ungetc, :ungetbyte, :readchar, :getbyte, :readbyte, :gets, :readline, :readlines, :read, :sysread, :readpartial, :read_nonblock, :write, :, :print, :printf, :putc, :puts, :syswrite, :write_nonblock, :isatty, :tty?, :pid, :fileno, :size, :length, :truncate, :external_encoding, :internal_encoding, :set_encoding

    # File lib/gxg/gxg_io.rb, line 2264
    def inspect()
      ("<#" << "#{self.class}:#{super().split(" ")[0].split(":").last}")
    end

read(*args) click to toggle source
Calls superclass method

    # File lib/gxg/gxg_io.rb, line 2336
    def read(*args)
      # read length bytes from StringIO and optionally route to a buffer object
      if self.closed_read?
        raise IOError, "not open for reading"
      else
        if args[0].is_a?(Numeric)
          # length
          data = super(args[0].to_i)
        else
          # LATER: provide for StringIO buffer size manipulation??? what is default read length??
          data = super()
        end
        if data.is_a?(::String)
          data = self.transcode_to_internal(data)
          # buffer
          if args[1].is_any?(::String, ::IO, ::StringIO, ::GxG::ByteArray)
            # check which classes this can use and in what ways.
            data.to_enum(:chars).each do |the_character|
              args[1] << the_character
            end
            # return buffer object
            args[1]
          else
            # return string
            data
          end
        else
          # return nil
          nil
        end
        #
      end
    end

Also aliased as: read_nonblock, sysread
read_nonblock(*args)
Alias for: read
readpartial(*args) click to toggle source

    # File lib/gxg/gxg_io.rb, line 2397
    def readpartial(*args)
      # args[0] maxlen (Fixnum)
      # args[1] (optional) buffer_object
      # Note: use the same approach to pause/retry as IO.read_nonblock.
      #
      # Reads at most maxlen bytes from the I/O stream. It blocks only if ios has no data immediately available. It doesn’t block if some data available.
      # If the optional outbuf argument is present, it must reference a String, which will receive the data. It raises EOFError on end of file.
      # readpartial is designed for streams such as pipe, socket, tty, etc. It blocks only when no data immediately available.
      # This means that it blocks only when following all conditions hold:
      #   the byte buffer in the IO object is empty.
      #   the content of the stream is empty.
      #   the stream is not reached to EOF.
      # When readpartial blocks, it waits data or EOF on the stream. If some data is reached, readpartial returns with the data. If EOF is reached,
      # readpartial raises EOFError.
      # When readpartial doesn’t blocks, it returns or raises immediately. If the byte buffer is not empty, it returns the data in the buffer.
      # Otherwise if the stream has some content, it returns the data in the stream. Otherwise if the stream is reached to EOF, it raises EOFError.
      #
      if self.closed_read?
        raise IOError, "not open for reading"
      else
        if self.eof?
          raise EOFError, "end of stream reached"
        else
          result = ""
          result.force_encoding(self.internal_encoding)
          data = self.read(*args)
          if data
            # non-nil
            if data.is_a?(::String)
              # 'blocking' or not?
              if (self.eof?() || (data && data.to_s.size > 0))
                # no pseudo-blocking
                result << data
                data = nil
              else
                # pseudo-blocking
                until (self.eof?() || (data && data.to_s.size > 0))
                  data = self.read(*args)
                  # string, buffer, or nil
                  if data
                    #
                    if data.is_a?(::String)
                      if (data && data.to_s.size > 0)
                        result << data
                        data = nil
                        break
                      else
                        if self.eof?()
                          raise EOFError, "end of stream reached"
                        end
                      end
                    else
                      # buffer
                      result = data
                      data = nil
                      break
                    end
                  else
                    if self.eof?()
                      raise EOFError, "end of stream reached"
                    end
                  end
                  #
                  pause
                  #
                end
              end
            else
              # buffer
              result = data
            end
          else
            if self.eof?()
              raise EOFError, "end of stream reached"
            end
          end
          #
          if result
            if result.is_a?(::String)
              if result.bytesize > 0
                result
              else
                nil
              end
            else
              # buffer
              result
            end
          else
            nil
          end
          #
        end
      end
    end

sysread(*args)
Alias for: read
syswrite(data="")
Alias for: write
write(data="") click to toggle source
Calls superclass method

    # File lib/gxg/gxg_io.rb, line 2372
    def write(data="")
      # writes from (self.pos) forward : this will clobber any init string that was passed.
      written = 0
      if self.closed_write?
        raise IOError, "not open for writing"
      else
        if data.is_a?(::String)
          data = self.transcode_to_external(data.dup)
        else
          data = self.transcode_to_external(data)
        end
        if data.size > 0
          written = super(data)
        end
      end
      written
    end

Also aliased as: write_nonblock, syswrite
write_nonblock(data="")
Alias for: write