streamingly 0.0.8 → 0.1.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.
- checksums.yaml +4 -4
- data/lib/streamingly/reducer.rb +14 -1
- data/lib/streamingly/serde.rb +2 -0
- data/lib/streamingly/version.rb +1 -1
- data/spec/streamingly/reducer_spec.rb +156 -1
- data/streamingly.gemspec +1 -0
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 58821c716d9d1b756cfca76758fe86c538017a4d
|
4
|
+
data.tar.gz: b85d8a2398ab33ba1a08fa2770af84d0b2b0ebee
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 75cc3e16e31928a46816eb3dce6248e129a21ba5124716bcb258de9c01e75b9a7650befc0602d78e469110f5f00800fd22a7a7afba4ba0a9e528cd964a4bbb07
|
7
|
+
data.tar.gz: d65f1bfd7b08897bb985674276a1a098185583123115941bc2d2f845225ae7e8f22a892ff0e12878ed9e4382aeb5e3369bd76bef0f2ab71d582e0198d771949a
|
data/lib/streamingly/reducer.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
module Streamingly
|
2
2
|
class Reducer
|
3
3
|
|
4
|
-
def initialize(accumulator_class, accumulator_options=nil)
|
4
|
+
def initialize(accumulator_class, accumulator_options = nil)
|
5
5
|
@accumulator_class = accumulator_class
|
6
6
|
@accumulator_options = accumulator_options
|
7
|
+
@error_callback_defined = @accumulator_class.method_defined?(:on_error)
|
7
8
|
end
|
8
9
|
|
9
10
|
def reduce_over(enumerator)
|
@@ -16,12 +17,16 @@ module Streamingly
|
|
16
17
|
flush.each do |out|
|
17
18
|
yield out
|
18
19
|
end
|
20
|
+
|
19
21
|
end
|
20
22
|
|
21
23
|
private
|
22
24
|
|
23
25
|
def flush
|
24
26
|
@accumulator ? @accumulator.flush : []
|
27
|
+
rescue StandardError => error
|
28
|
+
on_error(error, {})
|
29
|
+
[]
|
25
30
|
end
|
26
31
|
|
27
32
|
def reduce(line)
|
@@ -40,6 +45,14 @@ module Streamingly
|
|
40
45
|
@accumulator.apply_value(value)
|
41
46
|
|
42
47
|
results || []
|
48
|
+
rescue StandardError => error
|
49
|
+
on_error(error, { :line => line })
|
50
|
+
[]
|
51
|
+
end
|
52
|
+
|
53
|
+
def on_error(error, error_context)
|
54
|
+
raise error unless @error_callback_defined
|
55
|
+
@accumulator.on_error(error, error_context)
|
43
56
|
end
|
44
57
|
|
45
58
|
def new_accumulator(key)
|
data/lib/streamingly/serde.rb
CHANGED
data/lib/streamingly/version.rb
CHANGED
@@ -1,8 +1,44 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
+
class TestAccumulator
|
4
|
+
attr_reader :applied_values, :raised_errors, :error_contexts
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@applied_values = []
|
8
|
+
@raised_errors = []
|
9
|
+
@error_contexts = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def apply_value(value)
|
13
|
+
@applied_values << value
|
14
|
+
end
|
15
|
+
|
16
|
+
def flush
|
17
|
+
[]
|
18
|
+
end
|
19
|
+
|
20
|
+
def on_error(error, error_context)
|
21
|
+
@raised_errors << error
|
22
|
+
@error_contexts << error_context
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class RaisesOnSingleCharAccumulator < TestAccumulator
|
27
|
+
def apply_value(value)
|
28
|
+
super(value)
|
29
|
+
raise ArgumentError, "Must not be a single character" if value.size == 1
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class RaisesOnFlushAccumulator < TestAccumulator
|
34
|
+
def flush
|
35
|
+
raise RuntimeError, "Cannot flush when ya only have two pairs"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
3
39
|
describe Streamingly::Reducer do
|
4
40
|
|
5
|
-
let(:accumulator_class) { double }
|
41
|
+
let(:accumulator_class) { double(:accumulator_class, :method_defined? => true) }
|
6
42
|
subject { described_class.new(accumulator_class) }
|
7
43
|
|
8
44
|
describe "#reduce_over" do
|
@@ -85,6 +121,125 @@ describe Streamingly::Reducer do
|
|
85
121
|
end
|
86
122
|
end
|
87
123
|
|
124
|
+
context "given a record which will cause an exception" do
|
125
|
+
let(:key) { 'key1' }
|
126
|
+
let(:value1) { "a" }
|
127
|
+
let(:value2) { "abc" }
|
128
|
+
let(:records) {
|
129
|
+
[
|
130
|
+
[key, value1].join("\t"),
|
131
|
+
[key, value2].join("\t")
|
132
|
+
]
|
133
|
+
}
|
134
|
+
|
135
|
+
let(:accumulator) { RaisesOnSingleCharAccumulator.new }
|
136
|
+
|
137
|
+
context "with error callback specified" do
|
138
|
+
before do
|
139
|
+
accumulator_class.stub(:new).with(key) { accumulator }
|
140
|
+
end
|
141
|
+
|
142
|
+
it "keeps processing after error applying value" do
|
143
|
+
subject.reduce_over(records)
|
144
|
+
|
145
|
+
expect(accumulator.applied_values).to eq([value1, value2])
|
146
|
+
end
|
147
|
+
|
148
|
+
it "calls supplied error callback with correct context" do
|
149
|
+
subject.reduce_over(records)
|
150
|
+
|
151
|
+
expect(accumulator.raised_errors.size).to eq(1)
|
152
|
+
raised_error = accumulator.raised_errors[0]
|
153
|
+
expect(raised_error.class).to eq(ArgumentError)
|
154
|
+
|
155
|
+
expect(accumulator.error_contexts.size).to eq(1)
|
156
|
+
error_context = accumulator.error_contexts[0]
|
157
|
+
expect(error_context[:line]).to eq([key, value1].join("\t"))
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
context "without error callback specified" do
|
162
|
+
let(:accumulator_class) { double(:accumulator_class, :method_defined? => false) }
|
163
|
+
subject { described_class.new(accumulator_class) }
|
164
|
+
|
165
|
+
before do
|
166
|
+
accumulator_class.stub(:new).with(key) { accumulator }
|
167
|
+
end
|
168
|
+
|
169
|
+
it "stops processing after error applying value" do
|
170
|
+
expect{ subject.reduce_over(records) }.to raise_error(ArgumentError)
|
171
|
+
|
172
|
+
expect(accumulator.applied_values).to eq([value1])
|
173
|
+
expect(accumulator.raised_errors.size).to eq(0)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
context "given accumulator which fails on flush" do
|
179
|
+
let(:key1) { 'key1' }
|
180
|
+
let(:key2) { 'key2' }
|
181
|
+
let(:value1) { 'asdf' }
|
182
|
+
let(:value2) { 'qwerty' }
|
183
|
+
|
184
|
+
let(:records) {
|
185
|
+
[
|
186
|
+
[key1, value1].join("\t"),
|
187
|
+
[key2, value2].join("\t")
|
188
|
+
]
|
189
|
+
}
|
190
|
+
|
191
|
+
let(:accumulator1) { RaisesOnFlushAccumulator.new }
|
192
|
+
let(:accumulator2) { TestAccumulator.new }
|
193
|
+
|
194
|
+
context "with error callback specified" do
|
195
|
+
before do
|
196
|
+
accumulator_class.stub(:new).with(key1) { accumulator1 }
|
197
|
+
accumulator_class.stub(:new).with(key2) { accumulator2 }
|
198
|
+
end
|
199
|
+
|
200
|
+
it "keeps processing after error applying value" do
|
201
|
+
subject.reduce_over(records)
|
202
|
+
|
203
|
+
expect(accumulator1.applied_values).to eq([value1])
|
204
|
+
|
205
|
+
expect(accumulator2.applied_values).to eq([value2])
|
206
|
+
end
|
207
|
+
|
208
|
+
it "calls supplied error callback" do
|
209
|
+
subject.reduce_over(records)
|
210
|
+
|
211
|
+
expect(accumulator1.raised_errors.size).to eq(1)
|
212
|
+
raised_error = accumulator1.raised_errors[0]
|
213
|
+
expect(raised_error.class).to eq(RuntimeError)
|
214
|
+
|
215
|
+
expect(accumulator1.error_contexts.size).to eq(1)
|
216
|
+
expect(accumulator1.error_contexts[0]).to be_empty
|
217
|
+
|
218
|
+
expect(accumulator2.raised_errors.size).to eq(0)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
context "without error callback specified" do
|
223
|
+
let(:accumulator_class) { double(:accumulator_class, :method_defined? => false) }
|
224
|
+
subject { described_class.new(accumulator_class) }
|
225
|
+
|
226
|
+
before do
|
227
|
+
accumulator_class.stub(:new).with(key1) { accumulator1 }
|
228
|
+
accumulator_class.stub(:new).with(key2) { accumulator2 }
|
229
|
+
end
|
230
|
+
|
231
|
+
it "stops processing after error applying value" do
|
232
|
+
expect{ subject.reduce_over(records) }.to raise_error(RuntimeError)
|
233
|
+
|
234
|
+
expect(accumulator1.applied_values).to eq([value1])
|
235
|
+
expect(accumulator1.raised_errors.size).to eq(0)
|
236
|
+
|
237
|
+
expect(accumulator2.applied_values).to eq([])
|
238
|
+
expect(accumulator2.raised_errors.size).to eq(0)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
88
243
|
context "when supplied with accumulator options" do
|
89
244
|
let(:accumulator_options) { { foo: 'bar' } }
|
90
245
|
subject { described_class.new(accumulator_class, accumulator_options) }
|
data/streamingly.gemspec
CHANGED
@@ -19,6 +19,7 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
21
|
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "pry"
|
22
23
|
spec.add_development_dependency "rake"
|
23
24
|
spec.add_development_dependency "rspec", "~> 2.11"
|
24
25
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: streamingly
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Gillooly
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-11-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: pry
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: rake
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|