rescuer 0.0.1 → 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: cb1cf739c264df062124c8278d07ae5c5045b82c
4
- data.tar.gz: 9202313bc83a069afc537d62a6c64074d969b8f0
3
+ metadata.gz: 4faa4200edef84a1567bb09eccfbbe6e1418867b
4
+ data.tar.gz: d7a26758830d969a3f828da1573164c1150b700e
5
5
  SHA512:
6
- metadata.gz: e2092f82656031cce05dd6eee1a75d0b8c6a49fee76a649c2456528ab054ec910b8467bd3e42d3846a01d695358de487cfcbe44e370ff5347a60516bb0bcb235
7
- data.tar.gz: 7d61b9f169eb50a91a2cdaf87a03d40b3ad3166ad2ff4a603e7d705eb3726410a590d10a62a5dbcc1ec60281479d928b7cf705233d0f03cb0103da59a7eb7673
6
+ metadata.gz: 32557bb01bd39b5b179bd00b9e168ad00142e3ba9e8a1280000976a210c959d3f0d73daf5794bd6be25bc9f89b214b7558e3f4720d803bc70c4af2a1cae0e5b1
7
+ data.tar.gz: 4e3fde18020cf6a940653e62a6d0d78d51c02f913b5e4da10dbc24abf823031b78d9c399e0f9d07a6f2d6219fc3a8f7cde99f5884cd27b5a278b813429a93b8e
data/.gitignore CHANGED
@@ -2,6 +2,7 @@
2
2
  *.rbc
3
3
  /.config
4
4
  /.idea
5
+ /*.iml
5
6
  /coverage/
6
7
  /InstalledFiles
7
8
  /pkg/
data/README.md CHANGED
@@ -5,4 +5,29 @@
5
5
  [![Code Climate](https://codeclimate.com/github/ms-ati/rescuer.png)](https://codeclimate.com/github/ms-ati/rescuer)
6
6
  [![Coverage Status](https://coveralls.io/repos/ms-ati/rescuer/badge.png)](https://coveralls.io/r/ms-ati/rescuer)
7
7
 
8
- A functional abstraction of rescuing exceptions inspired by Scala's Try class
8
+ A functional abstraction of rescuing exceptions inspired by Scala's [Try][1] class (here's an [explanation][2]).
9
+
10
+ [1]: http://www.scala-lang.org/api/2.10.3/index.html#scala.util.Try
11
+ [2]: http://danielwestheide.com/blog/2012/12/26/the-neophytes-guide-to-scala-part-6-error-handling-with-try.html
12
+
13
+ ## Usage
14
+
15
+ ```ruby
16
+ denominators = [0, 1]
17
+
18
+ fractions = denominators.map { |d| Rescuer.new { 1 / d } }
19
+ #=> [#<struct Rescuer::Failure exception=#<ZeroDivisionError: divided by 0>, exceptions_to_rescue=nil>,
20
+ # #<struct Rescuer::Success value=1, exceptions_to_rescue=nil>]
21
+ ```
22
+
23
+ More coming soon...
24
+
25
+ ## TODO
26
+
27
+ 1. Add any missing test to ensure exception rescuing matches all the same cases in Scala's `Try` class
28
+ 1. Come up with output for `#to_s` and `#inspect` that I like
29
+ 1. Document using yard
30
+ 1. Release 0.9.0 and solicit comments
31
+ 1. Incorporate suggested changes
32
+ 1. Release 1.0.0
33
+ 1. Potentially incorporate as a depedendency into [Rumonade](https://github.com/ms-ati/rumonade)
@@ -1,16 +1,165 @@
1
1
  require 'rescuer/version'
2
2
 
3
3
  module Rescuer
4
- DEFAULT_EXCEPTIONS = [StandardError]
4
+ DEFAULT_EXCEPTIONS_TO_RESCUE = [StandardError]
5
5
 
6
6
  def new(*exceptions)
7
- exceptions = DEFAULT_EXCEPTIONS if exceptions.nil? || exceptions.empty?
8
- Success.new(yield)
9
- rescue *exceptions => ex
10
- Failure.new(ex)
7
+ raise ArgumentError, 'no block given' unless block_given?
8
+
9
+ passed = exceptions && exceptions.flatten.compact
10
+ to_rescue, to_pass =
11
+ if passed.nil? || passed.empty? then [DEFAULT_EXCEPTIONS_TO_RESCUE, nil] else [passed, passed] end
12
+
13
+ begin
14
+ value = yield
15
+ Success.new(value, to_pass)
16
+ rescue *to_rescue => error
17
+ Failure.new(error, to_pass)
18
+ end
11
19
  end
12
20
  module_function :new
13
21
 
14
- Success = Struct.new(:value)
15
- Failure = Struct.new(:cause)
22
+ Success = Struct.new(:value, :exceptions_to_rescue) do
23
+ def initialize(value, exceptions_to_rescue = nil)
24
+ super(value, exceptions_to_rescue)
25
+ freeze
26
+ end
27
+
28
+ def success?
29
+ true
30
+ end
31
+
32
+ def failure?
33
+ false
34
+ end
35
+
36
+ def get
37
+ value
38
+ end
39
+
40
+ def get_or_else(_)
41
+ value
42
+ end
43
+
44
+ def or_else(_)
45
+ self
46
+ end
47
+
48
+ def failed
49
+ Failure.new(TypeError.new('Success is not a Failure'))
50
+ end
51
+
52
+ def flat_map
53
+ Rescuer.new(exceptions_to_rescue) { yield value }.flatten(1)
54
+ end
55
+
56
+ def map
57
+ flat_map do |v|
58
+ new_value = yield v
59
+ Success.new(new_value)
60
+ end
61
+ end
62
+
63
+ def each
64
+ yield value
65
+ self
66
+ end
67
+
68
+ def select
69
+ flat_map do |v|
70
+ predicate = yield v
71
+ if predicate then self else Failure.new(IndexError.new("predicate does not hold for #{v}")) end
72
+ end
73
+ end
74
+ alias_method :find_all, :select
75
+
76
+ def transform(f_success, f_failure)
77
+ flat_map { |v| f_success.call(v) }
78
+ end
79
+
80
+ def recover
81
+ self
82
+ end
83
+
84
+ def recover_with
85
+ self
86
+ end
87
+
88
+ def flatten(depth = nil)
89
+ raise ArgumentError, 'invalid depth' unless depth.nil? || (depth.is_a?(Integer) && depth >= 0)
90
+ if depth && depth.zero?
91
+ self
92
+ else
93
+ case value
94
+ when Success, Failure then value.flatten(depth && depth - 1)
95
+ else self
96
+ end
97
+ end
98
+ end
99
+ end
100
+
101
+ Failure = Struct.new(:exception, :exceptions_to_rescue) do
102
+ def initialize(exception, exceptions_to_rescue = nil)
103
+ super(exception, exceptions_to_rescue)
104
+ raise ArgumentError, 'not an exception' unless exception.is_a? Exception
105
+ freeze
106
+ end
107
+
108
+ def success?
109
+ false
110
+ end
111
+
112
+ def failure?
113
+ true
114
+ end
115
+
116
+ def get
117
+ raise exception
118
+ end
119
+
120
+ def get_or_else(default)
121
+ default
122
+ end
123
+
124
+ def or_else(other)
125
+ other
126
+ end
127
+
128
+ def failed
129
+ Success.new(exception)
130
+ end
131
+
132
+ def flat_map
133
+ self
134
+ end
135
+
136
+ def map
137
+ self
138
+ end
139
+
140
+ def each
141
+ self
142
+ end
143
+
144
+ def select
145
+ self
146
+ end
147
+ alias_method :find_all, :select
148
+
149
+ def transform(f_success, f_failure)
150
+ failed.flat_map { |e| f_failure.call(e) }
151
+ end
152
+
153
+ def recover
154
+ failed.map { |e| yield e }
155
+ end
156
+
157
+ def recover_with
158
+ failed.flat_map { |e| yield e }
159
+ end
160
+
161
+ def flatten(depth = nil)
162
+ self
163
+ end
164
+ end
16
165
  end
@@ -1,4 +1,4 @@
1
1
  module Rescuer
2
2
  # The current version of this gem
3
- VERSION = '0.0.1'
3
+ VERSION = '0.1.0'
4
4
  end
@@ -3,6 +3,9 @@ require 'rescuer'
3
3
 
4
4
  describe Rescuer do
5
5
 
6
+ ##
7
+ # Construct indirectly by wrapping a block which may raise an exception
8
+ ##
6
9
  describe '.new' do
7
10
 
8
11
  context 'when block does *not* raise' do
@@ -27,26 +30,476 @@ describe Rescuer do
27
30
  context 'when raise NoMemoryError' do
28
31
  let(:e) { NoMemoryError.new }
29
32
  subject { Rescuer.new(Exception) { raise e } }
30
- it { is_expected.to eq(Rescuer::Failure.new(e)) }
33
+ it { is_expected.to eq(Rescuer::Failure.new(e, [Exception])) }
31
34
  end
32
35
  end
33
36
 
34
37
  context 'when block raises and two subclasses of Exception are arguments' do
35
- exceptions = [NoMemoryError, SyntaxError]
38
+ let(:exceptions) { [NoMemoryError, SyntaxError] }
36
39
 
37
40
  context 'when raise StandardError' do
38
41
  subject { lambda { Rescuer.new(exceptions) { raise StandardError } } }
39
42
  it { is_expected.to raise_error(StandardError) }
40
43
  end
41
44
 
42
- context 'when raise NoMemoryError' do
43
- let(:e) { NoMemoryError.new }
44
- subject { Rescuer.new(Exception) { raise e } }
45
- it { is_expected.to eq(Rescuer::Failure.new(e)) }
45
+ context 'when raise SyntaxError' do
46
+ let(:e) { SyntaxError.new }
47
+ subject { Rescuer.new(exceptions) { raise e } }
48
+ it { is_expected.to eq(Rescuer::Failure.new(e, exceptions)) }
49
+ end
50
+
51
+ end
52
+
53
+ context 'when no block given' do
54
+ subject { lambda { Rescuer.new } }
55
+ it { is_expected.to raise_error(ArgumentError, 'no block given') }
56
+ end
57
+
58
+ end
59
+
60
+ ##
61
+ # Construct directly as Success
62
+ ##
63
+ describe Rescuer::Success do
64
+ context 'when given any object' do
65
+ let(:the_value) { 42 }
66
+ let(:a_success) { Rescuer::Success.new(the_value) }
67
+
68
+ describe '.new' do
69
+ subject { a_success }
70
+ it { is_expected.to be_frozen }
71
+ it { is_expected.to be_success }
72
+ it { is_expected.not_to be_failure }
73
+ it { is_expected.to respond_to :value }
74
+ it { is_expected.not_to respond_to :exception }
75
+ it { is_expected.to respond_to :exceptions_to_rescue }
76
+ end
77
+
78
+ describe '#==, #eql?, #equal?, #hash' do
79
+ subject { a_success }
80
+
81
+ context 'when same object' do
82
+ let(:another) { a_success }
83
+ it { is_expected.to eq another }
84
+ it { is_expected.to eql another }
85
+ it { is_expected.to equal another }
86
+ it { expect(subject.hash).to eq another.hash }
87
+ end
88
+
89
+ context 'when same class and equal values' do
90
+ let(:another) { Rescuer::Success.new(the_value) }
91
+ it { is_expected.to eq another }
92
+ it { is_expected.to eql another }
93
+ it { is_expected.not_to equal another } # not the same object!
94
+ it { expect(subject.hash).to eq another.hash }
95
+ end
96
+
97
+ context 'when same class and different values' do
98
+ let(:another) { Rescuer::Success.new('a different value') }
99
+ it { is_expected.not_to eq another }
100
+ it { is_expected.not_to eql another }
101
+ it { is_expected.not_to equal another }
102
+ it { expect(subject.hash).not_to eq another.hash }
103
+ end
104
+
105
+ context 'when different class and same values' do
106
+ let(:another_class) { Struct.new(*a_success.members) }
107
+ let(:another) { another_class.new(*a_success.values) }
108
+ it { is_expected.not_to eq another }
109
+ it { is_expected.not_to eql another }
110
+ it { is_expected.not_to equal another }
111
+ #it { expect(subject.hash).not_to eq another.hash } -- fails on rbx?
112
+ end
113
+ end
114
+
115
+ describe '#value' do
116
+ subject { a_success.value }
117
+ it { is_expected.to be the_value}
118
+ end
119
+
120
+ describe '#exceptions_to_rescue' do
121
+ context 'when not given' do
122
+ subject { a_success.exceptions_to_rescue }
123
+ it { is_expected.to be_nil }
124
+ end
125
+
126
+ context 'when given' do
127
+ let(:ex_to_rescue) { [ArgumentError, TypeError] }
128
+ subject { Rescuer::Success.new(the_value, ex_to_rescue).exceptions_to_rescue }
129
+ it { is_expected.to be ex_to_rescue }
130
+ end
131
+ end
132
+
133
+ describe '#get' do
134
+ subject { a_success.get }
135
+ it { is_expected.to be the_value }
136
+ end
137
+
138
+ describe '#get_or_else' do
139
+ subject { a_success.get_or_else(123) }
140
+ it { is_expected.to be the_value }
141
+ end
142
+
143
+ describe '#or_else' do
144
+ let(:another) { Rescuer::Success.new(123) }
145
+ subject { a_success.or_else(another) }
146
+ it { is_expected.to be a_success }
147
+ end
148
+
149
+ describe '#failed' do
150
+ subject { a_success.failed }
151
+ it { is_expected.to be_instance_of Rescuer::Failure }
152
+ it { expect(subject.exception).to eq TypeError.new('Success is not a Failure') }
153
+ end
154
+
155
+ describe '#each' do
156
+ context 'when block does *not* raise' do
157
+ let(:acc) { arr = []; a_success.each { |v| arr << v }; arr }
158
+ subject { dummy = []; a_success.each { |v| dummy << v } }
159
+ it { is_expected.to be a_success }
160
+ it { expect(acc).to eq [the_value] }
161
+ end
162
+
163
+ context 'when block raises' do
164
+ let(:the_error) { StandardError.new('a standard error') }
165
+ subject { lambda { a_success.each { |_| raise the_error } } }
166
+ it { is_expected.to raise_error(the_error) }
167
+ end
168
+ end
169
+
170
+ describe '#map' do
171
+ let(:to_rescue) { [ArgumentError] }
172
+ let(:a_success_rescuing_ae) { Rescuer::Success.new(the_value, to_rescue) }
173
+
174
+ context 'when block returns a value' do
175
+ subject { a_success.map { |v| v + 1 } }
176
+ it { is_expected.to eq Rescuer::Success.new(the_value + 1) }
177
+ end
178
+
179
+ context 'when block raises an exception that is to be rescued' do
180
+ let(:the_error) { ArgumentError.new('an error to be rescued') }
181
+ subject { a_success_rescuing_ae.map { |_| raise the_error } }
182
+ it { is_expected.to eq Rescuer::Failure.new(the_error, to_rescue) }
183
+ end
184
+
185
+ context 'when block raises an exception that is *not* to be rescued' do
186
+ let(:the_error) { StandardError.new('a standard error') }
187
+ subject { lambda { a_success_rescuing_ae.map { |_| raise the_error } } }
188
+ it { is_expected.to raise_error(the_error) }
189
+ end
190
+ end
191
+
192
+ describe '#flat_map' do
193
+ let(:to_rescue) { [ArgumentError] }
194
+ let(:a_success_rescuing_ae) { Rescuer::Success.new(the_value, to_rescue) }
195
+
196
+ context 'when block returns a Success' do
197
+ subject { a_success.flat_map { |v| Rescuer::Success.new(v + 1) } }
198
+ it { is_expected.to eq Rescuer::Success.new(the_value + 1) }
199
+ end
200
+
201
+ context 'when block raises an exception that is to be rescued' do
202
+ let(:the_error) { ArgumentError.new('an error to be rescued') }
203
+ subject { a_success_rescuing_ae.flat_map { |_| raise the_error } }
204
+ it { is_expected.to eq Rescuer::Failure.new(the_error, to_rescue) }
205
+ end
206
+
207
+ context 'when block raises an exception that is *not* to be rescued' do
208
+ let(:the_error) { StandardError.new('a standard error') }
209
+ subject { lambda { a_success_rescuing_ae.flat_map { |_| raise the_error } } }
210
+ it { is_expected.to raise_error(the_error) }
211
+ end
212
+ end
213
+
214
+ shared_examples 'select methods' do
215
+ context 'when predicate returns true' do
216
+ subject { select_method { |v| v == the_value } }
217
+ it { is_expected.to be a_success }
218
+ end
219
+
220
+ context 'when predicate returns false' do
221
+ let(:the_error) { IndexError.new('predicate does not hold for 42') }
222
+ subject { select_method { |v| v != the_value } }
223
+ it { is_expected.to eq Rescuer::Failure.new(the_error) }
224
+ end
225
+
226
+ context 'when predicate raises an exception that is to be rescued' do
227
+ let(:the_error) { StandardError.new('a standard error') }
228
+ subject { select_method { |_| raise the_error } }
229
+ it { is_expected.to eq Rescuer::Failure.new(the_error) }
230
+ end
231
+
232
+ context 'when predicate raises an exception that is *not* to be rescued' do
233
+ subject { lambda { select_method { |_| raise NoMemoryError } } }
234
+ it { is_expected.to raise_error(NoMemoryError) }
235
+ end
236
+ end
237
+
238
+ describe '#select' do
239
+ def select_method; a_success.select { |v| yield v }; end
240
+ it_behaves_like 'select methods'
241
+ end
242
+
243
+ describe '#find_all' do
244
+ def select_method; a_success.find_all { |v| yield v }; end
245
+ it_behaves_like 'select methods'
246
+ end
247
+
248
+ describe '#transform' do
249
+ subject { a_success.transform(lambda { |v| Rescuer::Success.new(v + 1) },
250
+ lambda { |e| Rescuer::Success.new(e.message.length) }) }
251
+ it { is_expected.to eq Rescuer::Success.new(the_value + 1) }
252
+ end
253
+
254
+ describe '#recover' do
255
+ subject { a_success.recover { |e| e.message.length } }
256
+ it { is_expected.to be a_success }
257
+ end
258
+
259
+ describe '#recover_with' do
260
+ subject { a_success.recover_with { |e| Rescuer::Success.new(e.message.length) } }
261
+ it { is_expected.to be a_success }
262
+ end
263
+
264
+ describe '#flatten' do
265
+ let(:nested_once) { Rescuer::Success.new(a_success) }
266
+ let(:nested_twice) { Rescuer::Success.new(nested_once) }
267
+ let(:nested_fail) { Rescuer::Success.new(Rescuer::Failure.new(StandardError.new('a standard error'))) }
268
+
269
+ context 'given an invalid depth' do
270
+ let(:string_depth) { lambda { a_success.flatten('hey!') } }
271
+ let(:negative_depth) { lambda { a_success.flatten(-1) } }
272
+ let(:float_depth) { lambda { a_success.flatten(1.23) } }
273
+ it { expect(string_depth).to raise_error(ArgumentError, 'invalid depth') }
274
+ it { expect(negative_depth).to raise_error(ArgumentError, 'invalid depth') }
275
+ it { expect(float_depth).to raise_error(ArgumentError, 'invalid depth') }
276
+ end
277
+
278
+ context 'when *not* nested' do
279
+ context 'given depth is nil' do
280
+ subject { a_success.flatten }
281
+ it { is_expected.to be a_success }
282
+ end
283
+
284
+ context 'given depth = 1' do
285
+ subject { a_success.flatten(1) }
286
+ it { is_expected.to be a_success }
287
+ end
288
+ end
289
+
290
+ context 'when nested success containing success' do
291
+ context 'given depth is nil' do
292
+ subject { nested_once.flatten }
293
+ it { is_expected.to be a_success }
294
+ end
295
+
296
+ context 'given depth = 1' do
297
+ subject { nested_once.flatten(1) }
298
+ it { is_expected.to be a_success }
299
+ end
300
+ end
301
+
302
+ context 'when nested success containing success containing success' do
303
+ context 'given depth is nil' do
304
+ subject { nested_twice.flatten }
305
+ it { is_expected.to be a_success }
306
+ end
307
+
308
+ context 'given depth = 1' do
309
+ subject { nested_twice.flatten(1) }
310
+ it { is_expected.to be nested_once }
311
+ end
312
+ end
313
+
314
+ context 'when nested success containing failure' do
315
+ context 'given depth is nil' do
316
+ subject { nested_fail.flatten }
317
+ it { is_expected.to be nested_fail.value }
318
+ end
319
+
320
+ context 'given depth = 1' do
321
+ subject { nested_fail.flatten(1) }
322
+ it { is_expected.to be nested_fail.value }
323
+ end
324
+ end
325
+ end
326
+ end
327
+ end
328
+
329
+ ##
330
+ # Construct directly as Failure
331
+ ##
332
+ describe Rescuer::Failure do
333
+ context 'when given an exception' do
334
+ let(:the_error) { StandardError.new('a standard error') }
335
+ let(:a_failure) { Rescuer::Failure.new(the_error) }
336
+
337
+ describe '.new' do
338
+ subject { a_failure }
339
+ it { is_expected.to be_frozen }
340
+ it { is_expected.not_to be_success }
341
+ it { is_expected.to be_failure }
342
+ it { is_expected.not_to respond_to :value }
343
+ it { is_expected.to respond_to :exception }
344
+ it { is_expected.to respond_to :exceptions_to_rescue }
345
+ end
346
+
347
+ describe '#==, #eql?, #equal?, #hash' do
348
+ subject { a_failure }
349
+
350
+ context 'when same object' do
351
+ let(:another) { a_failure }
352
+ it { is_expected.to eq another }
353
+ it { is_expected.to eql another }
354
+ it { is_expected.to equal another }
355
+ it { expect(subject.hash).to eq another.hash }
356
+ end
357
+
358
+ context 'when same class and equal values' do
359
+ let(:another) { Rescuer::Failure.new(the_error) }
360
+ it { is_expected.to eq another }
361
+ it { is_expected.to eql another }
362
+ it { is_expected.not_to equal another } # not the same object!
363
+ it { expect(subject.hash).to eq another.hash }
364
+ end
365
+
366
+ context 'when same class and different values' do
367
+ let(:another) { Rescuer::Failure.new(StandardError.new('a different error')) }
368
+ it { is_expected.not_to eq another }
369
+ it { is_expected.not_to eql another }
370
+ it { is_expected.not_to equal another }
371
+ it { expect(subject.hash).not_to eq another.hash }
372
+ end
373
+
374
+ context 'when different class and same values' do
375
+ let(:another_class) { Struct.new(*a_failure.members) }
376
+ let(:another) { another_class.new(*a_failure.values) }
377
+ it { is_expected.not_to eq another }
378
+ it { is_expected.not_to eql another }
379
+ it { is_expected.not_to equal another }
380
+ #it { expect(subject.hash).not_to eq another.hash } -- fails on rbx?
381
+ end
382
+ end
383
+
384
+ describe '#exception' do
385
+ subject { a_failure.exception }
386
+ it { is_expected.to be the_error }
387
+ end
388
+
389
+ describe '#exceptions_to_rescue' do
390
+ context 'when not given' do
391
+ subject { a_failure.exceptions_to_rescue }
392
+ it { is_expected.to be_nil }
393
+ end
394
+
395
+ context 'when given' do
396
+ let(:ex_to_rescue) { [ArgumentError, TypeError] }
397
+ subject { Rescuer::Failure.new(the_error, ex_to_rescue).exceptions_to_rescue }
398
+ it { is_expected.to be ex_to_rescue }
399
+ end
400
+ end
401
+
402
+ describe '#get' do
403
+ subject { lambda { a_failure.get } }
404
+ it { is_expected.to raise_error(the_error) }
46
405
  end
47
406
 
407
+ describe '#get_or_else' do
408
+ subject { a_failure.get_or_else(123) }
409
+ it { is_expected.to be 123 }
410
+ end
411
+
412
+ describe '#or_else' do
413
+ let(:another) { Rescuer::Success.new(123) }
414
+ subject { a_failure.or_else(another) }
415
+ it { is_expected.to be another }
416
+ end
417
+
418
+ describe '#failed' do
419
+ subject { a_failure.failed }
420
+ it { is_expected.to be_instance_of Rescuer::Success }
421
+ it { expect(subject.value).to be the_error }
422
+ end
423
+
424
+ describe '#each' do
425
+ let(:acc) { arr = []; a_failure.each { |v| arr << v }; arr }
426
+ subject { dummy = []; a_failure.each { |v| dummy << v } }
427
+ it { is_expected.to be a_failure }
428
+ it { expect(acc).to eq [] }
429
+ end
430
+
431
+ describe '#map' do
432
+ subject { a_failure.map { |v| v + 1 } }
433
+ it { is_expected.to be a_failure }
434
+ end
435
+
436
+ describe '#flat_map' do
437
+ subject { a_failure.flat_map { |v| Rescuer::Success.new(v + 1) } }
438
+ it { is_expected.to be a_failure }
439
+ end
440
+
441
+ shared_examples 'select methods' do
442
+ context 'when predicate returns true' do
443
+ subject { select_method { |v| v == the_value } }
444
+ it { is_expected.to be a_failure }
445
+ end
446
+
447
+ context 'when predicate returns false' do
448
+ subject { select_method { |v| v != the_value } }
449
+ it { is_expected.to be a_failure }
450
+ end
451
+
452
+ context 'when predicate raises any exception' do
453
+ subject { select_method { |_| raise the_error } }
454
+ it { is_expected.to be a_failure }
455
+ end
456
+ end
457
+
458
+ describe '#select' do
459
+ def select_method; a_failure.select { |v| yield v }; end
460
+ it_behaves_like 'select methods'
461
+ end
462
+
463
+ describe '#find_all' do
464
+ def select_method; a_failure.find_all { |v| yield v }; end
465
+ it_behaves_like 'select methods'
466
+ end
467
+
468
+ describe '#transform' do
469
+ subject { a_failure.transform(lambda { |v| Rescuer::Success.new(v + 1) },
470
+ lambda { |e| Rescuer::Success.new(e.message.length) }) }
471
+ it { is_expected.to eq Rescuer::Success.new(the_error.message.length) }
472
+ end
473
+
474
+ describe '#recover' do
475
+ subject { a_failure.recover { |e| e.message.length } }
476
+ it { is_expected.to eq Rescuer::Success.new(the_error.message.length) }
477
+ end
478
+
479
+ describe '#recover_with' do
480
+ subject { a_failure.recover_with { |e| Rescuer::Success.new(e.message.length) } }
481
+ it { is_expected.to eq Rescuer::Success.new(the_error.message.length) }
482
+ end
483
+
484
+ describe '#flatten' do
485
+ context 'given depth is nil' do
486
+ subject { a_failure.flatten }
487
+ it { is_expected.to be a_failure }
488
+ end
489
+
490
+ context 'given depth = 1' do
491
+ subject { a_failure.flatten(1) }
492
+ it { is_expected.to be a_failure }
493
+ end
494
+ end
48
495
  end
49
496
 
497
+ context 'when given a *non*-exception' do
498
+ describe '.new' do
499
+ subject { lambda { Rescuer::Failure.new(42) } }
500
+ it { is_expected.to raise_error(ArgumentError, 'not an exception') }
501
+ end
502
+ end
50
503
  end
51
504
 
52
505
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rescuer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marc Siegel
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-16 00:00:00.000000000 Z
11
+ date: 2014-04-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake