json-write-stream 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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: []