class CouchRest::StreamRowParser

CouchRest Stream Row Parser

Will parse a stream containing a standard CouchDB response including rows. Allows each row to be parsed individually, and provided in a block for efficient memory usage.

The StreamRowParser#parse method expects to be called multiple times with segments of data, typcially provided by the Net::HTTPResponse#read_body method.

Data will be cached until usable objects can be extracted in rows and provied in the block.

Attributes

header[RW]

String containing the fields provided before and after the rows.

row_level[R]

The row level at which we expect to receive “rows” of data. Typically this will be 0 for contious feeds, and 1 for most other users.

Public Class Methods

new(mode = :array) click to toggle source

Instantiate a new StreamRowParser with the mode set according to the type of data. The supported modes are:

* `:array` - objects are contianed in a data array, the default.
* `:feed` - each row of the stream is an object, like in continuous changes feeds.
# File lib/couchrest/helper/stream_row_parser.rb, line 29
def initialize(mode = :array)
  @header  = ""
  @data    = ""
  @string  = false
  @escape  = false

  @row_level = mode == :array ? 1 : 0
  @in_rows   = false
  @obj_level = 0
  @obj_close = false
end

Public Instance Methods

parse(segment, &block) click to toggle source
# File lib/couchrest/helper/stream_row_parser.rb, line 41
def parse(segment, &block)
  @in_rows = true if @row_level == 0
  segment.each_char do |c|
    if @string
      # Inside a string, handling escaping and closure
      if @escape
        @escape = false
      else
        if c == '"'
          @string = false
        elsif c == '\\'
          @escape = true
        end
      end
    else
      # Inside an object
      @obj_close = false
      if @obj_level == @row_level && c == "[" # start of rows
        @in_rows = true
      elsif @obj_level == @row_level && c == "]" # end of rows
        @in_rows = false
      elsif c == "{" # object
        @obj_level += 1
      elsif c == "}" # object end
        @obj_level -= 1
        @obj_close = true
      elsif c == '"'
        @string = true
      end
    end

    # Append data
    if @row_level > 0
      if @obj_level == 0 || (@obj_level == @row_level && !@obj_close)
        @header << c unless @in_rows && (c == ',' || c == ' ' || c == "\n") # skip row whitespace
      else
        @data << c
      end
    else
      @data << c
    end

    # Determine if we need to trigger an event
    if @obj_close && @obj_level == @row_level
      block.call(@data)
      @data = ""
    end
  end
end