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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f507114d8202daadc401f6de1d18054035a7c557
4
- data.tar.gz: 4cab496f8fb9940b12aca0eab8735ef00344e124
3
+ metadata.gz: 58821c716d9d1b756cfca76758fe86c538017a4d
4
+ data.tar.gz: b85d8a2398ab33ba1a08fa2770af84d0b2b0ebee
5
5
  SHA512:
6
- metadata.gz: 7a33cace50cfb7ebf35723738474b0026f415228366df9385b63abc645f0c046017c174aa633fd2bf1953f358ec4c31be6d3577741c7bb83c4286abeb0002d91
7
- data.tar.gz: a3f0251f7359e1ada162f7cf5378ecdd458fae05eb3bff8dde27e1d9f26e0cc68210de99e01ce25313555cbec1c58050fcb4476bcb76a73a8013a7d50a31546a
6
+ metadata.gz: 75cc3e16e31928a46816eb3dce6248e129a21ba5124716bcb258de9c01e75b9a7650befc0602d78e469110f5f00800fd22a7a7afba4ba0a9e528cd964a4bbb07
7
+ data.tar.gz: d65f1bfd7b08897bb985674276a1a098185583123115941bc2d2f845225ae7e8f22a892ff0e12878ed9e4382aeb5e3369bd76bef0f2ab71d582e0198d771949a
@@ -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)
@@ -8,6 +8,8 @@ module Streamingly
8
8
  case record
9
9
  when String
10
10
  record
11
+ when Streamingly::KV
12
+ record.to_s
11
13
  when Struct
12
14
  tokens = *record.map { |token|
13
15
  case token
@@ -1,3 +1,3 @@
1
1
  module Streamingly
2
- VERSION = "0.0.8"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -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.8
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-07-08 00:00:00.000000000 Z
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