Skip to content

Commit 9bccd7c

Browse files
authored
Merge pull request #10 from greensync/simplify-parser
Simplify parser
2 parents 2d1a0d0 + 7ff3dc5 commit 9bccd7c

3 files changed

Lines changed: 37 additions & 28 deletions

File tree

lib/json_sequence/parser.rb

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -18,44 +18,44 @@ def parse(chunk, &block)
1818
# Takes a String buffer to parse and returns String containing any
1919
# text remaining to parse when more data is available.
2020
def do_parse(buffer)
21-
records = buffer.split(RS, -1) # -1 stops suppression of trailing null fields
22-
23-
records.each_with_index do |record, i|
24-
# RFC7464 2.1 Multiple consecutive RS octets do not denote empty
25-
# sequence elements between them and can be ignored.
26-
next if record == ''
27-
28-
# Try to decode the record
29-
begin
30-
value = MultiJson.load(record)
31-
result, remaining = handle_parsed(record, value, is_last_record: i == records.size - 1)
32-
rescue MultiJson::ParseError => err
33-
result, remaining = handle_err(record, err, is_last_record: i == records.size - 1)
34-
end
35-
36-
return remaining if result.nil?
37-
yield result
38-
end
21+
# RFC7464 2.1 Multiple consecutive RS octets do not denote empty
22+
# sequence elements between them and can be ignored.
23+
records = buffer.split(RS).reject(&:empty?)
24+
25+
# Every record except the last is guaranteed to be completed
26+
records[0...-1].each { |record| yield decode_record(record) }
27+
28+
last_result = decode_record(records.last)
29+
30+
# If we have an incomplete record and run out of valid json early, return it to the buffer
31+
return records.last if !buffer.end_with?(RS) && partial_result?(last_result)
32+
yield last_result
3933

4034
''
4135
end
4236

43-
def handle_parsed(record, value, is_last_record:)
37+
def decode_record(record)
38+
value = MultiJson.load(record)
39+
return JsonSequence::Result::MaybeTruncated.new(value) if truncated?(record, value)
40+
41+
JsonSequence::Result::Json.new(value)
42+
rescue MultiJson::ParseError => e
43+
JsonSequence::Result::ParseError.new(record, e)
44+
end
45+
46+
def truncated?(record, value)
4447
case value
4548
when Numeric, TrueClass, FalseClass, NilClass
4649
# Check for truncation, if record was parsed but doesn't end in
4750
# whitespace it may be truncated
48-
if record !~ /\s$/
49-
return is_last_record ? [nil, record] : [JsonSequence::Result::MaybeTruncated.new(value), '']
50-
end
51+
record !~ /\s$/
52+
else
53+
false
5154
end
52-
53-
[JsonSequence::Result::Json.new(value), '']
5455
end
5556

56-
def handle_err(record, err, is_last_record:)
57-
# Last record, might be incomplete, stash for later
58-
is_last_record ? [nil, record] : [JsonSequence::Result::ParseError.new(record, err), '']
57+
def partial_result?(result)
58+
!result.is_a?(JsonSequence::Result::Json)
5959
end
6060
end
6161
end

lib/json_sequence/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module JsonSequence
2-
VERSION = "0.2.0"
2+
VERSION = "0.2.1"
33
end

spec/json_sequence/parser_spec.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,15 @@
6666
)
6767
end
6868

69+
it 'process records when ending with a RS token' do
70+
expect { |b| parser.parse(%|\x1E123\x0A\x1E|, &b) }.to yield_successive_args(
71+
JsonSequence::Result::Json.new(123),
72+
)
73+
expect { |b| parser.parse(%|123\x0A|, &b) }.to yield_successive_args(
74+
JsonSequence::Result::Json.new(123),
75+
)
76+
end
77+
6978
it 'parses formatted json' do
7079
expect { |b| parser.parse(%|\x1E{"some": "json",\n"more": 1,\n"even more": []}\x0A|, &b) }.to yield_successive_args(
7180
JsonSequence::Result::Json.new('some' => 'json', 'more' => 1, 'even more' => [])

0 commit comments

Comments
 (0)