fear 0.11.0 → 1.0.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.
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