streamingly 0.0.8 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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