json-write-stream 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a397871c57d6b78327b37cac6e9848b212b4a2d6
4
+ data.tar.gz: 2a0437147cacd67a906a1c1d34b266001ae1f2fc
5
+ SHA512:
6
+ metadata.gz: 6af47c318296dd8bfc6bc981fcb420e7ba9b42905cd08ac459668dae13c588cc67bc1159e00f98c06d740304d6c1c6b3c27a36b50457ebef9951254430665e20
7
+ data.tar.gz: 619674607994da2fe54e97608869ae70ebf81a5f9e9100f0e2771c7dddae56af537f262f4a997221a31d28c7609f1523351b9d7a50d5e6a978954ed562b8c4b4
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ group :development, :test do
6
+ gem 'pry'
7
+ gem 'pry-nav'
8
+ gem 'rake'
9
+ end
10
+
11
+ group :test do
12
+ gem 'rspec'
13
+ gem 'rr'
14
+ end
@@ -0,0 +1,3 @@
1
+ == 1.0.0
2
+
3
+ * Birthday!
@@ -0,0 +1,113 @@
1
+ json-write-stream
2
+ =================
3
+
4
+ An easy, streaming way to generate JSON.
5
+
6
+ ## Installation
7
+
8
+ `gem install json-write-stream`
9
+
10
+ ## Usage
11
+
12
+ ```ruby
13
+ require 'json-write-stream'
14
+ ```
15
+
16
+ ### Examples for the Impatient
17
+
18
+ There are two types of JSON write stream: one that uses blocks and `yield` to delimit arrays and objects, and one that's purely stateful. Here are two examples that produce the same output:
19
+
20
+ Yielding:
21
+
22
+ ```ruby
23
+ stream = StringIO.new
24
+ JsonWriteStream.from_stream(stream) do |writer|
25
+ writer.write_object do |obj_writer|
26
+ obj_writer.write_key_value('foo', 'bar')
27
+ obj_writer.write_array('baz') do |arr_writer|
28
+ arr_writer.write_element('goo')
29
+ end
30
+ end
31
+ end
32
+ ```
33
+
34
+ Stateful:
35
+
36
+ ```ruby
37
+ stream = StringIO.new
38
+ writer = JsonWriteStream.from_stream(stream)
39
+ writer.write_object
40
+ writer.write_key_value('foo', 'bar')
41
+ writer.write_array('baz')
42
+ writer.write_element('goo')
43
+ writer.close # automatically adds closing punctuation for all nested types
44
+ ```
45
+
46
+ Output:
47
+
48
+ ```ruby
49
+ stream.string # => {"foo":"bar","baz":["goo"]}
50
+ ```
51
+
52
+ ### Yielding Writers
53
+
54
+ As far as yielding writers go, the example above contains everything you need. The stream will be automatically closed when the outermost block terminates.
55
+
56
+ ### Stateful Writers
57
+
58
+ Stateful writers have a number of additional methods:
59
+
60
+ ```ruby
61
+ stream = StringIO.new
62
+ writer = JsonWriteStream.from_stream(stream)
63
+ writer.write_object
64
+
65
+ writer.in_object? # => true, currently writing an object
66
+ writer.in_array? # => false, not currently writing an array
67
+ writer.eos? # => false, the stream is open and the outermost object hasn't been closed yet
68
+
69
+ writer.close_object # explicitly close the current object
70
+ writer.eos? # => true, the outermost object has been closed
71
+
72
+ writer.write_array # => raises JsonWriteStream::EndOfStreamError
73
+ writer.close_array # => raises JsonWriteStream::NotInArrayError
74
+
75
+ writer.closed? # => false, the stream is still open
76
+ writer.close # close the stream
77
+ writer.closed? # => true, the stream has been closed
78
+ ```
79
+
80
+ ### Writing to a File
81
+
82
+ JsonWriteStream also supports streaming to a file via the `open` method:
83
+
84
+ Yielding:
85
+
86
+ ```ruby
87
+ JsonWriteStream.open('path/to/file.json') do |writer|
88
+ writer.write_object do |obj_writer|
89
+ ...
90
+ end
91
+ end
92
+ ```
93
+
94
+ Stateful:
95
+
96
+ ```ruby
97
+ writer = JsonWriteStream.open('path/to/file.json')
98
+ writer.write_object
99
+ ...
100
+ writer.close
101
+ ```
102
+
103
+ ## Requirements
104
+
105
+ No external requirements.
106
+
107
+ ## Running Tests
108
+
109
+ `bundle exec rake` should do the trick. Alternatively you can run `bundle exec rspec`, which does the same thing.
110
+
111
+ ## Authors
112
+
113
+ * Cameron C. Dutro: http://github.com/camertron
@@ -0,0 +1,18 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'rubygems' unless ENV['NO_RUBYGEMS']
4
+
5
+ require 'bundler'
6
+ require 'rspec/core/rake_task'
7
+ require 'rubygems/package_task'
8
+
9
+ require './lib/json-write-stream'
10
+
11
+ Bundler::GemHelper.install_tasks
12
+
13
+ task :default => :spec
14
+
15
+ desc 'Run specs'
16
+ RSpec::Core::RakeTask.new do |t|
17
+ t.pattern = './spec/**/*_spec.rb'
18
+ end
@@ -0,0 +1,20 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), 'lib')
2
+ require 'json-write-stream/version'
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "json-write-stream"
6
+ s.version = ::JsonWriteStream::VERSION
7
+ s.authors = ["Cameron Dutro"]
8
+ s.email = ["camertron@gmail.com"]
9
+ s.homepage = "http://github.com/camertron"
10
+
11
+ s.description = s.summary = "An easy, streaming way to generate JSON."
12
+
13
+ s.add_dependency 'json_pure', '~> 1.8.0'
14
+
15
+ s.platform = Gem::Platform::RUBY
16
+ s.has_rdoc = true
17
+
18
+ s.require_path = 'lib'
19
+ s.files = Dir["{lib,spec}/**/*", "Gemfile", "History.txt", "README.md", "Rakefile", "json-write-stream.gemspec"]
20
+ end
@@ -0,0 +1,29 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'json'
4
+ require 'json-write-stream/yielding'
5
+ require 'json-write-stream/stateful'
6
+
7
+ class JsonWriteStream
8
+ class << self
9
+ def from_stream(stream)
10
+ if block_given?
11
+ yield writer = YieldingWriter.new(stream)
12
+ writer.close
13
+ else
14
+ StatefulWriter.new(stream)
15
+ end
16
+ end
17
+
18
+ def open(file)
19
+ handle = File.open(file, 'w')
20
+
21
+ if block_given?
22
+ yield writer = YieldingWriter.new(handle)
23
+ writer.close
24
+ else
25
+ StatefulWriter.new(handle)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,203 @@
1
+ # encoding: UTF-8
2
+
3
+ class JsonWriteStream
4
+ class NotInObjectError < StandardError; end
5
+ class NotInArrayError < StandardError; end
6
+ class EndOfStreamError < StandardError; end
7
+
8
+ class StatefulWriter
9
+ attr_reader :stream, :index, :stack, :closed
10
+ alias :closed? :closed
11
+
12
+ def initialize(stream)
13
+ @stream = stream
14
+ @index = 0
15
+ @stack = []
16
+ @closed = false
17
+ after_initialize
18
+ end
19
+
20
+ def after_initialize
21
+ end
22
+
23
+ def write_object(*args)
24
+ check_eos
25
+ current.write_object(*args) if current
26
+ stack.push(StatefulObjectWriter.new(stream))
27
+ end
28
+
29
+ def write_array(*args)
30
+ check_eos
31
+ current.write_array(*args) if current
32
+ stack.push(StatefulArrayWriter.new(stream))
33
+ end
34
+
35
+ def write_key_value(*args)
36
+ check_eos
37
+ current.write_key_value(*args)
38
+ end
39
+
40
+ def write_element(*args)
41
+ check_eos
42
+ current.write_element(*args)
43
+ end
44
+
45
+ def close_object
46
+ if in_object?
47
+ stack.pop.close
48
+ current.increment if current
49
+ increment
50
+ else
51
+ raise NotInObjectError, 'not currently writing an object.'
52
+ end
53
+ end
54
+
55
+ def close_array
56
+ if in_array?
57
+ stack.pop.close
58
+ current.increment if current
59
+ increment
60
+ else
61
+ raise NotInArrayError, 'not currently writing an array.'
62
+ end
63
+ end
64
+
65
+ def close
66
+ until stack.empty?
67
+ if in_object?
68
+ close_object
69
+ else
70
+ close_array
71
+ end
72
+ end
73
+
74
+ stream.close
75
+ @closed = true
76
+ nil
77
+ end
78
+
79
+ def in_object?
80
+ current ? current.is_object? : false
81
+ end
82
+
83
+ def in_array?
84
+ current ? current.is_array? : false
85
+ end
86
+
87
+ def eos?
88
+ (stack.size == 0 && index > 0) || closed?
89
+ end
90
+
91
+ protected
92
+
93
+ def check_eos
94
+ if eos?
95
+ raise EndOfStreamError, 'end of stream.'
96
+ end
97
+ end
98
+
99
+ def current
100
+ stack.last
101
+ end
102
+
103
+ def escape(str)
104
+ JSON.generate([str])[1..-2]
105
+ end
106
+
107
+ def write_comma
108
+ stream.write(',') if index > 0
109
+ end
110
+
111
+ def increment
112
+ @index += 1
113
+ end
114
+ end
115
+
116
+ class StatefulObjectWriter < StatefulWriter
117
+ def after_initialize
118
+ stream.write('{')
119
+ end
120
+
121
+ # prep work (array is written afterwards)
122
+ def write_array(key)
123
+ write_comma
124
+ increment
125
+ write_key(key)
126
+ stream.write(':')
127
+ end
128
+
129
+ # prep work (object is written afterwards)
130
+ def write_object(key)
131
+ write_comma
132
+ increment
133
+ write_key(key)
134
+ stream.write(':')
135
+ end
136
+
137
+ def write_key_value(key, value)
138
+ write_comma
139
+ increment
140
+ write_key(key)
141
+ stream.write(":#{escape(value)}")
142
+ end
143
+
144
+ def close
145
+ stream.write('}')
146
+ end
147
+
148
+ def is_object?
149
+ true
150
+ end
151
+
152
+ def is_array?
153
+ false
154
+ end
155
+
156
+ private
157
+
158
+ def write_key(key)
159
+ case key
160
+ when String
161
+ stream.write(escape(key))
162
+ else
163
+ raise ArgumentError, "'#{key}' must be a string"
164
+ end
165
+ end
166
+ end
167
+
168
+ class StatefulArrayWriter < StatefulWriter
169
+ def after_initialize
170
+ stream.write('[')
171
+ end
172
+
173
+ def write_element(element)
174
+ write_comma
175
+ increment
176
+ stream.write(escape(element))
177
+ end
178
+
179
+ # prep work
180
+ def write_array
181
+ write_comma
182
+ increment
183
+ end
184
+
185
+ # prep work
186
+ def write_object
187
+ write_comma
188
+ increment
189
+ end
190
+
191
+ def close
192
+ stream.write(']')
193
+ end
194
+
195
+ def is_object?
196
+ false
197
+ end
198
+
199
+ def is_array?
200
+ true
201
+ end
202
+ end
203
+ end
@@ -0,0 +1,5 @@
1
+ # encoding: UTF-8
2
+
3
+ class JsonWriteStream
4
+ VERSION = "1.0.0"
5
+ end
@@ -0,0 +1,109 @@
1
+ # encoding: UTF-8
2
+
3
+ class JsonWriteStream
4
+ class YieldingWriter
5
+ attr_reader :stream, :index
6
+
7
+ def initialize(stream)
8
+ @stream = stream
9
+ @index = 0
10
+ after_initialize
11
+ end
12
+
13
+ def after_initialize
14
+ end
15
+
16
+ def write_object(comma_written = false)
17
+ unless comma_written
18
+ write_comma
19
+ increment
20
+ end
21
+
22
+ yield writer = YieldingObjectWriter.new(stream)
23
+ writer.close
24
+ end
25
+
26
+ def write_array(comma_written = false)
27
+ unless comma_written
28
+ write_comma
29
+ increment
30
+ end
31
+
32
+ yield writer = YieldingArrayWriter.new(stream)
33
+ writer.close
34
+ end
35
+
36
+ def close
37
+ stream.close
38
+ end
39
+
40
+ protected
41
+
42
+ def escape(str)
43
+ JSON.generate([str])[1..-2]
44
+ end
45
+
46
+ def write_comma
47
+ stream.write(',') if index > 0
48
+ end
49
+
50
+ def increment
51
+ @index += 1
52
+ end
53
+ end
54
+
55
+ class YieldingObjectWriter < YieldingWriter
56
+ def after_initialize
57
+ stream.write('{')
58
+ end
59
+
60
+ def write_array(key)
61
+ write_comma
62
+ increment
63
+ write_key(key)
64
+ stream.write(':')
65
+ super(true)
66
+ end
67
+
68
+ def write_object(key)
69
+ write_comma
70
+ increment
71
+ write_key(key)
72
+ stream.write(':')
73
+ super(true)
74
+ end
75
+
76
+ def write_key_value(key, value)
77
+ write_comma
78
+ increment
79
+ write_key(key)
80
+ stream.write(":#{escape(value)}")
81
+ end
82
+
83
+ def close
84
+ stream.write('}')
85
+ end
86
+
87
+ private
88
+
89
+ def write_key(key)
90
+ stream.write(escape(key.to_s))
91
+ end
92
+ end
93
+
94
+ class YieldingArrayWriter < YieldingWriter
95
+ def after_initialize
96
+ stream.write('[')
97
+ end
98
+
99
+ def write_element(element)
100
+ write_comma
101
+ increment
102
+ stream.write(escape(element))
103
+ end
104
+
105
+ def close
106
+ stream.write(']')
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,44 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'spec_helper'
4
+ require 'tempfile'
5
+
6
+ describe JsonWriteStream do
7
+ let(:yielding_writer) { JsonWriteStream::YieldingWriter }
8
+ let(:stateful_writer) { JsonWriteStream::StatefulWriter }
9
+ let(:stream_writer) { JsonWriteStream }
10
+ let(:tempfile) { Tempfile.new('temp') }
11
+ let(:stream) { StringIO.new }
12
+
13
+ describe '#from_stream' do
14
+ it 'yields a yielding stream if given a block' do
15
+ stream_writer.from_stream(stream) do |writer|
16
+ expect(writer).to be_a(yielding_writer)
17
+ expect(writer.stream).to equal(stream)
18
+ end
19
+ end
20
+
21
+ it 'returns a stateful writer if not given a block' do
22
+ writer = stream_writer.from_stream(stream)
23
+ expect(writer).to be_a(stateful_writer)
24
+ expect(writer.stream).to equal(stream)
25
+ end
26
+ end
27
+
28
+ describe '#open' do
29
+ it 'opens a file and yields a yielding stream if given a block' do
30
+ mock.proxy(File).open(tempfile, 'w')
31
+ stream_writer.open(tempfile) do |writer|
32
+ expect(writer).to be_a(yielding_writer)
33
+ expect(writer.stream.path).to eq(tempfile.path)
34
+ end
35
+ end
36
+
37
+ it 'opens a file and returns a stateful writer if not given a block' do
38
+ mock.proxy(File).open(tempfile, 'w')
39
+ writer = stream_writer.open(tempfile)
40
+ expect(writer).to be_a(stateful_writer)
41
+ expect(writer.stream.path).to eq(tempfile.path)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,30 @@
1
+ # encoding: UTF-8
2
+
3
+ shared_examples 'a json stream' do
4
+ it 'handles a simple array' do
5
+ check_roundtrip(['abc'])
6
+ end
7
+
8
+ it 'handles a simple object' do
9
+ check_roundtrip({ 'foo' => 'bar' })
10
+ end
11
+
12
+ it 'handles one level of array nesting' do
13
+ check_roundtrip([['def'],'abc'])
14
+ check_roundtrip(['abc',['def']])
15
+ end
16
+
17
+ it 'handles one level of object nesting' do
18
+ check_roundtrip({ 'foo' => { 'bar' => 'baz' } })
19
+ end
20
+
21
+ it 'handles one level of mixed nesting' do
22
+ check_roundtrip({ 'foo' => ['bar', 'baz'] })
23
+ check_roundtrip([{ 'foo' => 'bar' }])
24
+ end
25
+
26
+ it 'handles multiple levels of mixed nesting' do
27
+ check_roundtrip({'foo' => ['bar', { 'baz' => 'moo', 'gaz' => ['doo'] }, 'kal'], 'jim' => ['jill', ['john']] })
28
+ check_roundtrip(['foo', { 'bar' => 'baz', 'moo' => ['gaz', ['jim', ['jill']], 'jam'] }])
29
+ end
30
+ end
@@ -0,0 +1,159 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'rspec'
4
+ require 'json-write-stream'
5
+ require 'shared_examples'
6
+ require 'pry-nav'
7
+
8
+ RSpec.configure do |config|
9
+ config.mock_with :rr
10
+ end
11
+
12
+ class RoundtripChecker
13
+ class << self
14
+ include RSpec::Matchers
15
+
16
+ def check_roundtrip(obj)
17
+ stream = StringIO.new
18
+ writer = create_writer(stream)
19
+ serialize(obj, writer)
20
+ new_obj = JSON.parse(stream.string)
21
+ compare(obj, new_obj)
22
+ end
23
+
24
+ private
25
+
26
+ def compare(old_obj, new_obj)
27
+ expect(old_obj.class).to equal(new_obj.class)
28
+
29
+ case old_obj
30
+ when Hash
31
+ expect(old_obj.keys).to eq(new_obj.keys)
32
+
33
+ old_obj.each_pair do |key, old_val|
34
+ compare(old_val, new_obj[key])
35
+ end
36
+ when Array
37
+ old_obj.each_with_index do |old_element, idx|
38
+ compare(old_element, new_obj[idx])
39
+ end
40
+ else
41
+ expect(old_obj).to eq(new_obj)
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ class YieldingRoundtripChecker < RoundtripChecker
48
+ class << self
49
+ protected
50
+
51
+ def create_writer(stream)
52
+ JsonWriteStream::YieldingWriter.new(stream)
53
+ end
54
+
55
+ def serialize(obj, writer)
56
+ case obj
57
+ when Hash
58
+ writer.write_object do |object_writer|
59
+ serialize_object(obj, object_writer)
60
+ end
61
+ when Array
62
+ writer.write_array do |array_writer|
63
+ serialize_array(obj, array_writer)
64
+ end
65
+ end
66
+ end
67
+
68
+ def serialize_object(obj, writer)
69
+ obj.each_pair do |key, val|
70
+ case val
71
+ when Hash
72
+ writer.write_object(key) do |object_writer|
73
+ serialize_object(val, object_writer)
74
+ end
75
+ when Array
76
+ writer.write_array(key) do |array_writer|
77
+ serialize_array(val, array_writer)
78
+ end
79
+ else
80
+ writer.write_key_value(key, val)
81
+ end
82
+ end
83
+ end
84
+
85
+ def serialize_array(obj, writer)
86
+ obj.each do |element|
87
+ case element
88
+ when Hash
89
+ writer.write_object do |object_writer|
90
+ serialize_object(element, object_writer)
91
+ end
92
+ when Array
93
+ writer.write_array do |array_writer|
94
+ serialize_array(element, array_writer)
95
+ end
96
+ else
97
+ writer.write_element(element)
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+
104
+ class StatefulRoundtripChecker < RoundtripChecker
105
+ class << self
106
+ protected
107
+
108
+ def create_writer(stream)
109
+ JsonWriteStream::StatefulWriter.new(stream)
110
+ end
111
+
112
+ def serialize(obj, writer)
113
+ case obj
114
+ when Hash
115
+ writer.write_object
116
+ serialize_object(obj, writer)
117
+ writer.close_object
118
+ when Array
119
+ writer.write_array
120
+ serialize_array(obj, writer)
121
+ writer.close_array
122
+ end
123
+ end
124
+
125
+ def serialize_object(obj, writer)
126
+ obj.each_pair do |key, val|
127
+ case val
128
+ when Hash
129
+ writer.write_object(key)
130
+ serialize_object(val, writer)
131
+ writer.close_object
132
+ when Array
133
+ writer.write_array(key)
134
+ serialize_array(val, writer)
135
+ writer.close_array
136
+ else
137
+ writer.write_key_value(key, val)
138
+ end
139
+ end
140
+ end
141
+
142
+ def serialize_array(obj, writer)
143
+ obj.each do |element|
144
+ case element
145
+ when Hash
146
+ writer.write_object
147
+ serialize_object(element, writer)
148
+ writer.close_object
149
+ when Array
150
+ writer.write_array
151
+ serialize_array(element, writer)
152
+ writer.close_array
153
+ else
154
+ writer.write_element(element)
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,131 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe JsonWriteStream::YieldingWriter do
6
+ let(:stream) { StringIO.new }
7
+ let(:stream_writer) { JsonWriteStream::StatefulWriter.new(stream) }
8
+
9
+ def check_roundtrip(obj)
10
+ StatefulRoundtripChecker.check_roundtrip(obj)
11
+ end
12
+
13
+ it_behaves_like 'a json stream'
14
+
15
+ describe '#close' do
16
+ it 'unwinds the stack, adds appropriate closing punctuation for each unclosed item, and closes the stream' do
17
+ stream_writer.write_array
18
+ stream_writer.write_element('abc')
19
+ stream_writer.write_object
20
+ stream_writer.write_key_value('def', 'ghi')
21
+ stream_writer.close
22
+
23
+ expect(stream.string).to eq('["abc",{"def":"ghi"}]')
24
+ expect(stream_writer).to be_closed
25
+ expect(stream).to be_closed
26
+ end
27
+ end
28
+
29
+ describe '#closed?' do
30
+ it 'returns false if the stream is still open' do
31
+ expect(stream_writer).to_not be_closed
32
+ end
33
+
34
+ it 'returns true if the stream is closed' do
35
+ stream_writer.close
36
+ expect(stream_writer).to be_closed
37
+ end
38
+ end
39
+
40
+ describe '#in_object?' do
41
+ it 'returns true if the writer is currently writing an object' do
42
+ stream_writer.write_object
43
+ expect(stream_writer).to be_in_object
44
+ end
45
+
46
+ it 'returns false if the writer is not currently writing an object' do
47
+ expect(stream_writer).to_not be_in_object
48
+ stream_writer.write_array
49
+ expect(stream_writer).to_not be_in_object
50
+ end
51
+ end
52
+
53
+ describe '#in_array?' do
54
+ it 'returns true if the writer is currently writing an array' do
55
+ stream_writer.write_array
56
+ expect(stream_writer).to be_in_array
57
+ end
58
+
59
+ it 'returns false if the writer is not currently writing an array' do
60
+ expect(stream_writer).to_not be_in_array
61
+ stream_writer.write_object
62
+ expect(stream_writer).to_not be_in_array
63
+ end
64
+ end
65
+
66
+ describe '#eos?' do
67
+ it 'returns false if nothing has been written yet' do
68
+ expect(stream_writer).to_not be_eos
69
+ end
70
+
71
+ it 'returns false if the writer is in the middle of writing' do
72
+ stream_writer.write_object
73
+ expect(stream_writer).to_not be_eos
74
+ end
75
+
76
+ it "returns true if the writer has finished it's top-level" do
77
+ stream_writer.write_object
78
+ stream_writer.close_object
79
+ expect(stream_writer).to be_eos
80
+ end
81
+
82
+ it 'returns true if the writer is closed' do
83
+ stream_writer.close
84
+ expect(stream_writer).to be_eos
85
+ end
86
+ end
87
+
88
+ describe '#close_object' do
89
+ it 'raises an error if an object is not currently being written' do
90
+ stream_writer.write_array
91
+ expect(-> { stream_writer.close_object }).to raise_error(JsonWriteStream::NotInObjectError)
92
+ end
93
+ end
94
+
95
+ describe '#close_array' do
96
+ it 'raises an error if an array is not currently being written' do
97
+ stream_writer.write_object
98
+ expect(-> { stream_writer.close_array }).to raise_error(JsonWriteStream::NotInArrayError)
99
+ end
100
+ end
101
+
102
+ context 'with a closed stream writer' do
103
+ before(:each) do
104
+ stream_writer.close
105
+ end
106
+
107
+ describe '#write_object' do
108
+ it 'raises an error if eos' do
109
+ expect(-> { stream_writer.write_object }).to raise_error(JsonWriteStream::EndOfStreamError)
110
+ end
111
+ end
112
+
113
+ describe '#write_array' do
114
+ it 'raises an error if eos' do
115
+ expect(-> { stream_writer.write_object }).to raise_error(JsonWriteStream::EndOfStreamError)
116
+ end
117
+ end
118
+
119
+ describe '#write_key_value' do
120
+ it 'raises an error if eos' do
121
+ expect(-> { stream_writer.write_key_value('abc', 'def') }).to raise_error(JsonWriteStream::EndOfStreamError)
122
+ end
123
+ end
124
+
125
+ describe '#write_element' do
126
+ it 'raises an error if eos' do
127
+ expect(-> { stream_writer.write_element('foo') }).to raise_error(JsonWriteStream::EndOfStreamError)
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,40 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe JsonWriteStream::YieldingWriter do
6
+ let(:stream) { StringIO.new }
7
+ let(:stream_writer) { JsonWriteStream::YieldingWriter.new(stream) }
8
+
9
+ def check_roundtrip(obj)
10
+ YieldingRoundtripChecker.check_roundtrip(obj)
11
+ end
12
+
13
+ it_behaves_like 'a json stream'
14
+
15
+ describe '#write_key_value' do
16
+ it 'converts all keys to strings' do
17
+ stream_writer.write_object do |object_writer|
18
+ object_writer.write_key_value(123, 'abc')
19
+ end
20
+
21
+ expect(stream.string).to eq('{"123":"abc"}')
22
+ end
23
+
24
+ it 'supports non-string values' do
25
+ stream_writer.write_object do |object_writer|
26
+ object_writer.write_key_value('abc', 123)
27
+ object_writer.write_key_value('def', true)
28
+ end
29
+
30
+ expect(stream.string).to eq('{"abc":123,"def":true}')
31
+ end
32
+ end
33
+
34
+ describe '#close' do
35
+ it 'closes the underlying stream' do
36
+ stream_writer.close
37
+ expect(stream).to be_closed
38
+ end
39
+ end
40
+ end
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: json-write-stream
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Cameron Dutro
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-08-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: json_pure
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 1.8.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 1.8.0
27
+ description: An easy, streaming way to generate JSON.
28
+ email:
29
+ - camertron@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - Gemfile
35
+ - History.txt
36
+ - README.md
37
+ - Rakefile
38
+ - json-write-stream.gemspec
39
+ - lib/json-write-stream.rb
40
+ - lib/json-write-stream/stateful.rb
41
+ - lib/json-write-stream/version.rb
42
+ - lib/json-write-stream/yielding.rb
43
+ - spec/json-write-stream_spec.rb
44
+ - spec/shared_examples.rb
45
+ - spec/spec_helper.rb
46
+ - spec/stateful_spec.rb
47
+ - spec/yielding_spec.rb
48
+ homepage: http://github.com/camertron
49
+ licenses: []
50
+ metadata: {}
51
+ post_install_message:
52
+ rdoc_options: []
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - '>='
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - '>='
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ requirements: []
66
+ rubyforge_project:
67
+ rubygems_version: 2.2.2
68
+ signing_key:
69
+ specification_version: 4
70
+ summary: An easy, streaming way to generate JSON.
71
+ test_files: []