Class: CouchRest::StreamRowParser

Inherits:
Object
  • Object
show all
Defined in:
lib/couchrest/helper/stream_row_parser.rb

Overview

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.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(mode = :array) ⇒ StreamRowParser

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.


29
30
31
32
33
34
35
36
37
38
39
# 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

Instance Attribute Details

#headerObject

String containing the fields provided before and after the rows.



17
18
19
# File 'lib/couchrest/helper/stream_row_parser.rb', line 17

def header
  @header
end

#row_levelObject (readonly)

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.



21
22
23
# File 'lib/couchrest/helper/stream_row_parser.rb', line 21

def row_level
  @row_level
end

Instance Method Details

#parse(segment, &block) ⇒ Object



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# 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