rescuer 0.0.1 → 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: 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