fear 0.11.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +0 -1
  3. data/.rubocop.yml +18 -0
  4. data/.travis.yml +0 -3
  5. data/CHANGELOG.md +12 -1
  6. data/Gemfile +1 -0
  7. data/{gemfiles/dry_equalizer_0.2.1.gemfile.lock → Gemfile.lock} +21 -12
  8. data/README.md +594 -241
  9. data/Rakefile +166 -219
  10. data/benchmarks/README.md +1 -0
  11. data/benchmarks/dry_do_vs_fear_for.txt +11 -0
  12. data/benchmarks/dry_some_fmap_vs_fear_some_map.txt +11 -0
  13. data/benchmarks/factorial.txt +16 -0
  14. data/benchmarks/fear_gaurd_and1_vs_new.txt +13 -0
  15. data/benchmarks/fear_gaurd_and2_vs_and.txt +13 -0
  16. data/benchmarks/fear_gaurd_and3_vs_and_and.txt +13 -0
  17. data/benchmarks/fear_pattern_extracting_with_vs_without_cache.txt +11 -0
  18. data/benchmarks/fear_pattern_matching_construction_vs_execution.txt +13 -0
  19. data/benchmarks/pattern_matching_dry_vs_qo_vs_fear_try.txt +14 -0
  20. data/benchmarks/pattern_matching_qo_vs_fear_pattern_extraction.txt +11 -0
  21. data/benchmarks/pattern_matching_qo_vs_fear_try_execution.txt +11 -0
  22. data/examples/pattern_extracting.rb +15 -0
  23. data/examples/pattern_matching_binary_tree_set.rb +96 -0
  24. data/examples/pattern_matching_number_in_words.rb +54 -0
  25. data/fear.gemspec +4 -2
  26. data/lib/fear.rb +21 -4
  27. data/lib/fear/either.rb +77 -59
  28. data/lib/fear/either_api.rb +21 -0
  29. data/lib/fear/empty_partial_function.rb +1 -1
  30. data/lib/fear/extractor.rb +108 -0
  31. data/lib/fear/extractor/anonymous_array_splat_matcher.rb +8 -0
  32. data/lib/fear/extractor/any_matcher.rb +15 -0
  33. data/lib/fear/extractor/array_head_matcher.rb +34 -0
  34. data/lib/fear/extractor/array_matcher.rb +38 -0
  35. data/lib/fear/extractor/array_splat_matcher.rb +14 -0
  36. data/lib/fear/extractor/empty_list_matcher.rb +18 -0
  37. data/lib/fear/extractor/extractor_matcher.rb +42 -0
  38. data/lib/fear/extractor/grammar.rb +201 -0
  39. data/lib/fear/extractor/grammar.treetop +129 -0
  40. data/lib/fear/extractor/identifier_matcher.rb +16 -0
  41. data/lib/fear/extractor/matcher.rb +54 -0
  42. data/lib/fear/extractor/matcher/and.rb +36 -0
  43. data/lib/fear/extractor/named_array_splat_matcher.rb +15 -0
  44. data/lib/fear/extractor/pattern.rb +55 -0
  45. data/lib/fear/extractor/typed_identifier_matcher.rb +24 -0
  46. data/lib/fear/extractor/value_matcher.rb +17 -0
  47. data/lib/fear/extractor_api.rb +33 -0
  48. data/lib/fear/failure.rb +32 -10
  49. data/lib/fear/for.rb +14 -69
  50. data/lib/fear/for_api.rb +66 -0
  51. data/lib/fear/future.rb +414 -0
  52. data/lib/fear/future_api.rb +19 -0
  53. data/lib/fear/left.rb +8 -0
  54. data/lib/fear/none.rb +17 -8
  55. data/lib/fear/option.rb +55 -49
  56. data/lib/fear/option_api.rb +38 -0
  57. data/lib/fear/partial_function.rb +9 -12
  58. data/lib/fear/partial_function/empty.rb +1 -1
  59. data/lib/fear/partial_function/guard.rb +8 -20
  60. data/lib/fear/partial_function/lifted.rb +1 -0
  61. data/lib/fear/partial_function_class.rb +10 -0
  62. data/lib/fear/pattern_match.rb +10 -0
  63. data/lib/fear/pattern_matching_api.rb +35 -11
  64. data/lib/fear/promise.rb +87 -0
  65. data/lib/fear/right.rb +8 -0
  66. data/lib/fear/some.rb +22 -3
  67. data/lib/fear/success.rb +22 -1
  68. data/lib/fear/try.rb +82 -67
  69. data/lib/fear/try_api.rb +31 -0
  70. data/lib/fear/unit.rb +28 -0
  71. data/lib/fear/version.rb +1 -1
  72. data/spec/fear/done_spec.rb +3 -3
  73. data/spec/fear/either/mixin_spec.rb +15 -0
  74. data/spec/fear/either_pattern_match_spec.rb +10 -12
  75. data/spec/fear/extractor/array_matcher_spec.rb +228 -0
  76. data/spec/fear/extractor/extractor_matcher_spec.rb +151 -0
  77. data/spec/fear/extractor/grammar_array_spec.rb +23 -0
  78. data/spec/fear/extractor/identified_matcher_spec.rb +47 -0
  79. data/spec/fear/extractor/identifier_matcher_spec.rb +66 -0
  80. data/spec/fear/extractor/pattern_spec.rb +32 -0
  81. data/spec/fear/extractor/typed_identifier_matcher_spec.rb +62 -0
  82. data/spec/fear/extractor/value_matcher_number_spec.rb +77 -0
  83. data/spec/fear/extractor/value_matcher_string_spec.rb +86 -0
  84. data/spec/fear/extractor/value_matcher_symbol_spec.rb +69 -0
  85. data/spec/fear/extractor_api_spec.rb +113 -0
  86. data/spec/fear/extractor_spec.rb +59 -0
  87. data/spec/fear/failure_spec.rb +73 -13
  88. data/spec/fear/for_spec.rb +35 -35
  89. data/spec/fear/future_spec.rb +466 -0
  90. data/spec/fear/guard_spec.rb +4 -4
  91. data/spec/fear/left_spec.rb +40 -14
  92. data/spec/fear/none_spec.rb +28 -12
  93. data/spec/fear/option/mixin_spec.rb +37 -0
  94. data/spec/fear/option_pattern_match_spec.rb +7 -9
  95. data/spec/fear/partial_function_spec.rb +25 -3
  96. data/spec/fear/pattern_match_spec.rb +33 -1
  97. data/spec/fear/promise_spec.rb +94 -0
  98. data/spec/fear/right_spec.rb +37 -9
  99. data/spec/fear/some_spec.rb +32 -6
  100. data/spec/fear/success_spec.rb +32 -4
  101. data/spec/fear/try/mixin_spec.rb +17 -0
  102. data/spec/fear/try_pattern_match_spec.rb +8 -10
  103. data/spec/spec_helper.rb +1 -1
  104. metadata +115 -20
  105. data/Appraisals +0 -32
  106. data/gemfiles/dry_equalizer_0.1.0.gemfile +0 -8
  107. data/gemfiles/dry_equalizer_0.1.0.gemfile.lock +0 -82
  108. data/gemfiles/dry_equalizer_0.2.1.gemfile +0 -8
  109. data/lib/fear/done.rb +0 -22
  110. data/spec/fear/option_spec.rb +0 -15
@@ -0,0 +1,466 @@
1
+ RSpec.describe Fear::Future do
2
+ let(:value) { 5 }
3
+ let(:error) { StandardError.new('something went wrong') }
4
+
5
+ def future(&block)
6
+ described_class.new(executor: Concurrent::ImmediateExecutor.new, &block)
7
+ end
8
+
9
+ context '#on_complete' do
10
+ it 'run callback with value' do
11
+ expect do |callback|
12
+ future { value }.on_complete(&callback)
13
+ end.to yield_with_args(Fear.success(value))
14
+ end
15
+
16
+ it 'run callback with error' do
17
+ expect do |callback|
18
+ future { raise error }.on_complete(&callback)
19
+ end.to yield_with_args(Fear.failure(error))
20
+ end
21
+ end
22
+
23
+ context '#on_success' do
24
+ it 'run callback if no error' do
25
+ expect do |callback|
26
+ future { value }.on_success(&callback)
27
+ end.to yield_with_args(value)
28
+ end
29
+
30
+ it 'do not run callback if error occurred' do
31
+ expect do |callback|
32
+ future { raise error }.on_success(&callback)
33
+ end.not_to yield_with_args
34
+ end
35
+
36
+ specify 'call all registered callbacks' do
37
+ expect do |second|
38
+ expect do |first|
39
+ future { value }
40
+ .on_success(&first)
41
+ .on_success(&second)
42
+ end.to yield_with_args(value)
43
+ end.to yield_with_args(value)
44
+ end
45
+ end
46
+
47
+ context '#on_failure' do
48
+ it 'do not run callback if no error' do
49
+ expect do |callback|
50
+ future { value }.on_failure(&callback)
51
+ end.not_to yield_with_args
52
+ end
53
+
54
+ it 'run callback if error occurred' do
55
+ expect do |callback|
56
+ future { raise error }.on_failure(&callback)
57
+ end.to yield_with_args(error)
58
+ end
59
+
60
+ specify 'call all registered callbacks' do
61
+ expect do |second|
62
+ expect do |first|
63
+ future { raise error }
64
+ .on_failure(&first)
65
+ .on_failure(&second)
66
+ end.to yield_with_args(error)
67
+ end.to yield_with_args(error)
68
+ end
69
+ end
70
+
71
+ context '#completed?' do
72
+ it 'completed with value' do
73
+ completed_future = future { value }
74
+
75
+ expect(completed_future).to be_completed
76
+ end
77
+
78
+ it 'completed with error' do
79
+ completed_future = future { raise error }
80
+
81
+ expect(completed_future).to be_completed
82
+ end
83
+
84
+ it 'not completed with value' do
85
+ not_completed_future =
86
+ Fear.future do
87
+ sleep 1
88
+ value
89
+ end
90
+
91
+ expect(not_completed_future).not_to be_completed
92
+ end
93
+
94
+ it 'not completed with error' do
95
+ not_completed_future =
96
+ Fear.future do
97
+ sleep 1
98
+ raise error
99
+ end
100
+
101
+ expect(not_completed_future).not_to be_completed
102
+ end
103
+ end
104
+
105
+ context '#value' do
106
+ it 'None if not completed' do
107
+ not_completed_future =
108
+ Fear.future do
109
+ sleep 1
110
+ value
111
+ end
112
+
113
+ future_value = not_completed_future.value
114
+
115
+ expect(future_value).to eq(Fear.none)
116
+ end
117
+
118
+ it 'Some of Success if completed with result' do
119
+ future_value = future { value }.value
120
+
121
+ expect(future_value).to eq Fear.some(Fear.success(value))
122
+ end
123
+
124
+ it 'Some of Failure if completed with error' do
125
+ value = future { raise error }.value
126
+
127
+ expect(value).to eq Fear.some(Fear.failure(error))
128
+ end
129
+ end
130
+
131
+ context '#transform' do
132
+ let(:failure) { ->(e) { e.message } }
133
+ let(:success) { ->(v) { v * 2 } }
134
+
135
+ it 'call first callback if successful' do
136
+ value = future { 2 }.transform(success, failure).value
137
+
138
+ expect(value).to eq Fear.some(Fear.success(4))
139
+ end
140
+
141
+ it 'call second callback if failed' do
142
+ value = future { raise error }.transform(success, failure).value
143
+
144
+ expect(value).to eq Fear.some(Fear.failure('something went wrong'))
145
+ end
146
+ end
147
+
148
+ context '#map' do
149
+ it 'successful result' do
150
+ result = future { value }.map { |r| r * 2 }.value
151
+
152
+ expect(result).to eq Fear.some(Fear.success(10))
153
+ end
154
+
155
+ it 'failed result' do
156
+ result = future { raise error }.map { |r| r * 2 }.value
157
+
158
+ expect(result).to eq Fear.some(Fear.failure(error))
159
+ end
160
+ end
161
+
162
+ context '#flat_map' do
163
+ it 'successful result' do
164
+ result = future { value }.flat_map { |r| future { r * 2 } }.value
165
+
166
+ expect(result).to eq Fear.some(Fear.success(10))
167
+ end
168
+
169
+ it 'failed result' do
170
+ result = future { raise error }.flat_map { |r| future { r * 2 } }.value
171
+
172
+ expect(result).to eq Fear.some(Fear.failure(error))
173
+ end
174
+
175
+ it 'failed callback future' do
176
+ result = future { value }.flat_map { future { raise error } }.value
177
+
178
+ expect(result).to eq Fear.some(Fear.failure(error))
179
+ end
180
+
181
+ it 'failured callback' do
182
+ result = future { value }.flat_map { raise error }.value
183
+
184
+ expect(result).to eq Fear.some(Fear.failure(error))
185
+ end
186
+ end
187
+
188
+ context '#each' do
189
+ it 'successful result' do
190
+ expect do |callback|
191
+ future { value }.each(&callback)
192
+ end.to yield_with_args(value)
193
+ end
194
+
195
+ it 'failed result' do
196
+ expect do |callback|
197
+ future { raise error }.each(&callback)
198
+ end.not_to yield_with_args
199
+ end
200
+ end
201
+
202
+ context '#select' do
203
+ it 'satisfy predicate' do
204
+ value = future { 2 }.select(&:even?).value
205
+
206
+ expect(value).to eq Fear.some(Fear.success(2))
207
+ end
208
+
209
+ it 'does not satisfy predicate' do
210
+ value = future { 3 }.select(&:even?).value
211
+
212
+ expect(value.get.exception).to be_kind_of(Fear::NoSuchElementError)
213
+ end
214
+
215
+ it 'failure' do
216
+ value = future { raise error }.select(&:even?).value
217
+
218
+ expect(value.get.exception).to eq error
219
+ end
220
+ end
221
+
222
+ context '#recover' do
223
+ it 'success' do
224
+ value = future { 2 / 1 }.recover do |m|
225
+ m.case { 0 }
226
+ end.value
227
+
228
+ expect(value).to eq Fear.some(Fear.success(2))
229
+ end
230
+
231
+ it 'failure and error case covered by pattern match' do
232
+ value = future { 2 / 0 }.recover do |m|
233
+ m.case(RuntimeError, &:message)
234
+ m.case(ZeroDivisionError) { 0 }
235
+ end.value
236
+
237
+ expect(value).to eq Fear.some(Fear.success(0))
238
+ end
239
+
240
+ it 'failure and error case not covered by pattern match' do
241
+ value = future { 2 / 0 }.recover do |m|
242
+ m.case(RuntimeError, &:message)
243
+ end.value
244
+
245
+ expect(value.get).to be_kind_of(Fear::Failure)
246
+ expect(value.get.exception).to be_kind_of(ZeroDivisionError)
247
+ end
248
+ end
249
+
250
+ context '#zip' do
251
+ it 'success' do
252
+ this = future { 1 }
253
+ that = future { 2 }
254
+
255
+ value = this.zip(that).value
256
+
257
+ expect(value).to eq Fear.some(Fear.success([1, 2]))
258
+ end
259
+
260
+ it 'self fails' do
261
+ this = future { raise error }
262
+ that = future { 2 }
263
+
264
+ value = this.zip(that).value
265
+
266
+ expect(value).to eq Fear.some(Fear.failure(error))
267
+ end
268
+
269
+ it 'other fails' do
270
+ this = future { 1 }
271
+ that = future { raise error }
272
+
273
+ value = this.zip(that).value
274
+
275
+ expect(value).to eq Fear.some(Fear.failure(error))
276
+ end
277
+
278
+ it 'both fail' do
279
+ this = future { raise error }
280
+ that = future { raise ArgumentError }
281
+
282
+ value = this.zip(that).value
283
+
284
+ expect(value).to eq Fear.some(Fear.failure(error))
285
+ end
286
+ end
287
+
288
+ context '#fallback_to' do
289
+ it 'success' do
290
+ fallback = future { 42 }
291
+ value = future { 2 }.fallback_to(fallback).value
292
+
293
+ expect(value).to eq Fear.some(Fear.success(2))
294
+ end
295
+
296
+ it 'failure' do
297
+ fallback = future { 42 }
298
+ value = future { raise error }.fallback_to(fallback).value
299
+
300
+ expect(value).to eq Fear.some(Fear.success(42))
301
+ end
302
+
303
+ it 'failure with failed fallback' do
304
+ fallback = future { raise ArumentError }
305
+ value = future { raise error }.fallback_to(fallback).value
306
+
307
+ expect(value).to eq Fear.some(Fear.failure(error))
308
+ end
309
+ end
310
+
311
+ context '#and_then' do
312
+ context 'single callback' do
313
+ context 'callback is called' do
314
+ it 'returns result of future' do
315
+ expect do |callback|
316
+ future { 5 }.and_then do |m|
317
+ m.success(&callback)
318
+ end
319
+ end.to yield_with_args(5)
320
+ end
321
+ end
322
+
323
+ context 'future with Success' do
324
+ context 'callback is not failing' do
325
+ it 'returns result of future' do
326
+ value = future { 5 }.and_then {}.value
327
+
328
+ expect(value).to eq Fear.some(Fear.success(5))
329
+ end
330
+ end
331
+
332
+ context 'callback is failing' do
333
+ it 'returns result of future' do
334
+ value = future { 5 }.and_then { raise error }.value
335
+
336
+ expect(value).to eq Fear.some(Fear.success(5))
337
+ end
338
+ end
339
+ end
340
+
341
+ context 'future with Failure' do
342
+ it 'ensure callback is called' do
343
+ expect do |callback|
344
+ future { raise error }.and_then do |m|
345
+ m.failure(&callback)
346
+ end
347
+ end.to yield_with_args(error)
348
+ end
349
+
350
+ context 'callback is not failing' do
351
+ it 'returns result of future' do
352
+ value = future { raise error }.and_then {}.value
353
+
354
+ expect(value).to eq Fear.some(Fear.failure(error))
355
+ end
356
+ end
357
+
358
+ context 'callback is failing' do
359
+ it 'returns result of future' do
360
+ value = future { raise error }.and_then { raise ArgumentError }.value
361
+
362
+ expect(value).to eq Fear.some(Fear.failure(error))
363
+ end
364
+ end
365
+ end
366
+ end
367
+
368
+ context 'multiple callbacks' do
369
+ context 'on Future with Success' do
370
+ it 'ensure callbacks are called' do
371
+ expect do |first|
372
+ expect do |second|
373
+ future { 5 }.and_then { |m| m.success(&first) }.and_then { |m| m.success(&second) }
374
+ end.to yield_with_args(5)
375
+ end.to yield_with_args(5)
376
+ end
377
+
378
+ # rubocop: disable Style/MultilineBlockChain
379
+ it 'ensure callbacks called in specified order' do
380
+ # REVIEW: could not write failing test
381
+ last_called = nil
382
+ Fear.future { 5 }.and_then do
383
+ sleep 1
384
+ expect(last_called).to eq(nil)
385
+ last_called = :first
386
+ end.and_then do
387
+ expect(last_called).to(
388
+ eq(:first), 'second callback called before first'
389
+ )
390
+ last_called = :second
391
+ end
392
+
393
+ sleep 2
394
+
395
+ expect(last_called).to eq(:second)
396
+ end
397
+ # rubocop: enable Style/MultilineBlockChain
398
+
399
+ context 'first callback is not failing' do
400
+ context 'and second callback is not failing' do
401
+ it 'returns result of the Future' do
402
+ value = future { 5 }.and_then {}.and_then {}.value
403
+
404
+ expect(value).to eq Fear.some(Fear.success(5))
405
+ end
406
+ end
407
+
408
+ context 'and second callback is failing' do
409
+ it 'returns result of the Future' do
410
+ value = future { 5 }.and_then {}.and_then do
411
+ raise ArgumentError
412
+ end.value
413
+
414
+ expect(value).to eq Fear.some(Fear.success(5))
415
+ end
416
+ end
417
+ end
418
+
419
+ context 'first callback is failing' do
420
+ it 'calls second callback' do
421
+ expect do |callback|
422
+ future { 5 }.and_then { raise error }.and_then { |m| m.success(&callback) }
423
+ end.to yield_with_args(5)
424
+ end
425
+
426
+ context 'and second callback is not failing' do
427
+ it 'returns result of the Future' do
428
+ value = future { 5 }.and_then { raise error }.and_then {}.value
429
+
430
+ expect(value).to eq Fear.some(Fear.success(5))
431
+ end
432
+ end
433
+
434
+ context 'and second callback is failing' do
435
+ it 'returns result of the Future' do
436
+ value = future { 5 }
437
+ .and_then { raise error }
438
+ .and_then { raise ArgumentError }
439
+ .value
440
+
441
+ expect(value).to eq Fear.some(Fear.success(5))
442
+ end
443
+ end
444
+ end
445
+ end
446
+ end
447
+ end
448
+
449
+ context '.successful' do
450
+ it 'returns already succeed Future' do
451
+ future = described_class.successful(value)
452
+
453
+ future_value = future.value
454
+
455
+ expect(future_value).to eq Fear.some(Fear.success(value))
456
+ end
457
+ end
458
+
459
+ context '.failed' do
460
+ it 'returns already failed Future' do
461
+ value = described_class.failed(error).value
462
+
463
+ expect(value).to eq Fear.some(Fear.failure(error))
464
+ end
465
+ end
466
+ end