fear 0.9.0 → 1.2.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 (155) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/rubocop.yml +39 -0
  3. data/.github/workflows/spec.yml +42 -0
  4. data/.gitignore +0 -1
  5. data/.rubocop.yml +4 -12
  6. data/.simplecov +17 -0
  7. data/CHANGELOG.md +40 -0
  8. data/Gemfile +5 -2
  9. data/Gemfile.lock +130 -0
  10. data/LICENSE.txt +1 -1
  11. data/README.md +1293 -97
  12. data/Rakefile +369 -1
  13. data/benchmarks/README.md +1 -0
  14. data/benchmarks/dry_do_vs_fear_for.txt +11 -0
  15. data/benchmarks/dry_some_fmap_vs_fear_some_map.txt +11 -0
  16. data/benchmarks/factorial.txt +16 -0
  17. data/benchmarks/fear_gaurd_and1_vs_new.txt +13 -0
  18. data/benchmarks/fear_gaurd_and2_vs_and.txt +13 -0
  19. data/benchmarks/fear_gaurd_and3_vs_and_and.txt +13 -0
  20. data/benchmarks/fear_pattern_extracting_with_vs_without_cache.txt +11 -0
  21. data/benchmarks/fear_pattern_matching_construction_vs_execution.txt +13 -0
  22. data/benchmarks/pattern_matching_dry_vs_qo_vs_fear_try.txt +14 -0
  23. data/benchmarks/pattern_matching_qo_vs_fear_pattern_extraction.txt +11 -0
  24. data/benchmarks/pattern_matching_qo_vs_fear_try_execution.txt +11 -0
  25. data/examples/pattern_extracting.rb +17 -0
  26. data/examples/pattern_extracting_ruby2.7.rb +15 -0
  27. data/examples/pattern_matching_binary_tree_set.rb +101 -0
  28. data/examples/pattern_matching_number_in_words.rb +60 -0
  29. data/fear.gemspec +34 -23
  30. data/lib/dry/types/fear.rb +8 -0
  31. data/lib/dry/types/fear/option.rb +125 -0
  32. data/lib/fear.rb +65 -15
  33. data/lib/fear/await.rb +33 -0
  34. data/lib/fear/awaitable.rb +28 -0
  35. data/lib/fear/either.rb +131 -71
  36. data/lib/fear/either_api.rb +23 -0
  37. data/lib/fear/either_pattern_match.rb +53 -0
  38. data/lib/fear/empty_partial_function.rb +38 -0
  39. data/lib/fear/extractor.rb +112 -0
  40. data/lib/fear/extractor/anonymous_array_splat_matcher.rb +10 -0
  41. data/lib/fear/extractor/any_matcher.rb +17 -0
  42. data/lib/fear/extractor/array_head_matcher.rb +36 -0
  43. data/lib/fear/extractor/array_matcher.rb +40 -0
  44. data/lib/fear/extractor/array_splat_matcher.rb +16 -0
  45. data/lib/fear/extractor/empty_list_matcher.rb +20 -0
  46. data/lib/fear/extractor/extractor_matcher.rb +44 -0
  47. data/lib/fear/extractor/grammar.rb +203 -0
  48. data/lib/fear/extractor/grammar.treetop +129 -0
  49. data/lib/fear/extractor/identifier_matcher.rb +18 -0
  50. data/lib/fear/extractor/matcher.rb +53 -0
  51. data/lib/fear/extractor/matcher/and.rb +38 -0
  52. data/lib/fear/extractor/named_array_splat_matcher.rb +17 -0
  53. data/lib/fear/extractor/pattern.rb +58 -0
  54. data/lib/fear/extractor/typed_identifier_matcher.rb +26 -0
  55. data/lib/fear/extractor/value_matcher.rb +19 -0
  56. data/lib/fear/extractor_api.rb +35 -0
  57. data/lib/fear/failure.rb +46 -14
  58. data/lib/fear/failure_pattern_match.rb +10 -0
  59. data/lib/fear/for.rb +37 -95
  60. data/lib/fear/for_api.rb +68 -0
  61. data/lib/fear/future.rb +497 -0
  62. data/lib/fear/future_api.rb +21 -0
  63. data/lib/fear/left.rb +19 -2
  64. data/lib/fear/left_pattern_match.rb +11 -0
  65. data/lib/fear/none.rb +67 -3
  66. data/lib/fear/none_pattern_match.rb +14 -0
  67. data/lib/fear/option.rb +120 -56
  68. data/lib/fear/option_api.rb +40 -0
  69. data/lib/fear/option_pattern_match.rb +48 -0
  70. data/lib/fear/partial_function.rb +176 -0
  71. data/lib/fear/partial_function/and_then.rb +50 -0
  72. data/lib/fear/partial_function/any.rb +28 -0
  73. data/lib/fear/partial_function/combined.rb +53 -0
  74. data/lib/fear/partial_function/empty.rb +10 -0
  75. data/lib/fear/partial_function/guard.rb +80 -0
  76. data/lib/fear/partial_function/guard/and.rb +38 -0
  77. data/lib/fear/partial_function/guard/and3.rb +41 -0
  78. data/lib/fear/partial_function/guard/or.rb +38 -0
  79. data/lib/fear/partial_function/lifted.rb +23 -0
  80. data/lib/fear/partial_function/or_else.rb +64 -0
  81. data/lib/fear/partial_function_class.rb +38 -0
  82. data/lib/fear/pattern_match.rb +114 -0
  83. data/lib/fear/pattern_matching_api.rb +137 -0
  84. data/lib/fear/promise.rb +95 -0
  85. data/lib/fear/right.rb +20 -2
  86. data/lib/fear/right_biased.rb +6 -14
  87. data/lib/fear/right_pattern_match.rb +11 -0
  88. data/lib/fear/some.rb +55 -3
  89. data/lib/fear/some_pattern_match.rb +13 -0
  90. data/lib/fear/struct.rb +248 -0
  91. data/lib/fear/success.rb +35 -5
  92. data/lib/fear/success_pattern_match.rb +12 -0
  93. data/lib/fear/try.rb +136 -79
  94. data/lib/fear/try_api.rb +33 -0
  95. data/lib/fear/try_pattern_match.rb +33 -0
  96. data/lib/fear/unit.rb +32 -0
  97. data/lib/fear/utils.rb +39 -14
  98. data/lib/fear/version.rb +4 -1
  99. data/spec/dry/types/fear/option/constrained_spec.rb +22 -0
  100. data/spec/dry/types/fear/option/core_spec.rb +77 -0
  101. data/spec/dry/types/fear/option/default_spec.rb +21 -0
  102. data/spec/dry/types/fear/option/hash_spec.rb +58 -0
  103. data/spec/dry/types/fear/option/option_spec.rb +97 -0
  104. data/spec/fear/awaitable_spec.rb +17 -0
  105. data/spec/fear/done_spec.rb +8 -6
  106. data/spec/fear/either/mixin_spec.rb +17 -0
  107. data/spec/fear/either_pattern_match_spec.rb +37 -0
  108. data/spec/fear/either_pattern_matching_spec.rb +28 -0
  109. data/spec/fear/extractor/array_matcher_spec.rb +230 -0
  110. data/spec/fear/extractor/extractor_matcher_spec.rb +153 -0
  111. data/spec/fear/extractor/grammar_array_spec.rb +25 -0
  112. data/spec/fear/extractor/identified_matcher_spec.rb +49 -0
  113. data/spec/fear/extractor/identifier_matcher_spec.rb +68 -0
  114. data/spec/fear/extractor/pattern_spec.rb +34 -0
  115. data/spec/fear/extractor/typed_identifier_matcher_spec.rb +64 -0
  116. data/spec/fear/extractor/value_matcher_number_spec.rb +79 -0
  117. data/spec/fear/extractor/value_matcher_string_spec.rb +88 -0
  118. data/spec/fear/extractor/value_matcher_symbol_spec.rb +71 -0
  119. data/spec/fear/extractor_api_spec.rb +115 -0
  120. data/spec/fear/extractor_spec.rb +61 -0
  121. data/spec/fear/failure_spec.rb +145 -45
  122. data/spec/fear/for_spec.rb +57 -67
  123. data/spec/fear/future_spec.rb +691 -0
  124. data/spec/fear/guard_spec.rb +103 -0
  125. data/spec/fear/left_spec.rb +112 -46
  126. data/spec/fear/none_spec.rb +114 -16
  127. data/spec/fear/option/mixin_spec.rb +39 -0
  128. data/spec/fear/option_pattern_match_spec.rb +35 -0
  129. data/spec/fear/option_pattern_matching_spec.rb +34 -0
  130. data/spec/fear/option_spec.rb +121 -8
  131. data/spec/fear/partial_function/empty_spec.rb +38 -0
  132. data/spec/fear/partial_function_and_then_spec.rb +147 -0
  133. data/spec/fear/partial_function_composition_spec.rb +82 -0
  134. data/spec/fear/partial_function_or_else_spec.rb +276 -0
  135. data/spec/fear/partial_function_spec.rb +239 -0
  136. data/spec/fear/pattern_match_spec.rb +93 -0
  137. data/spec/fear/pattern_matching_api_spec.rb +31 -0
  138. data/spec/fear/promise_spec.rb +96 -0
  139. data/spec/fear/right_biased/left.rb +29 -32
  140. data/spec/fear/right_biased/right.rb +51 -54
  141. data/spec/fear/right_spec.rb +109 -41
  142. data/spec/fear/some_spec.rb +80 -15
  143. data/spec/fear/success_spec.rb +99 -32
  144. data/spec/fear/try/mixin_spec.rb +19 -0
  145. data/spec/fear/try_pattern_match_spec.rb +37 -0
  146. data/spec/fear/try_pattern_matching_spec.rb +34 -0
  147. data/spec/fear/utils_spec.rb +16 -14
  148. data/spec/spec_helper.rb +13 -7
  149. data/spec/struct_pattern_matching_spec.rb +36 -0
  150. data/spec/struct_spec.rb +226 -0
  151. data/spec/support/dry_types.rb +6 -0
  152. metadata +320 -29
  153. data/.travis.yml +0 -9
  154. data/lib/fear/done.rb +0 -22
  155. data/lib/fear/for/evaluation_context.rb +0 -91
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Fear::ExtractorApi do
4
+ def assert(value)
5
+ expect(value).to eq(true)
6
+ end
7
+
8
+ def assert_not(value)
9
+ expect(value).not_to eq(true)
10
+ end
11
+
12
+ def assert_invalid_syntax
13
+ expect do
14
+ yield
15
+ end.to raise_error(Fear::PatternSyntaxError)
16
+ end
17
+
18
+ def assert_valid_syntax
19
+ expect { yield }.not_to raise_error
20
+ end
21
+
22
+ specify "Array" do
23
+ assert(Fear["[]"] === [])
24
+ assert_not(Fear["[]"] === [1])
25
+ assert(Fear["[1]"] === [1])
26
+ assert_not(Fear["[1]"] === [1, 2])
27
+ assert_not(Fear["[1]"] === [2])
28
+ assert(Fear["[1, 2]"] === [1, 2])
29
+ assert_not(Fear["[1, 2]"] === [1, 3])
30
+ assert_not(Fear["[1, 2]"] === [1, 2, 4])
31
+ assert_not(Fear["[1, 2]"] === [1])
32
+ assert(Fear["[*]"] === [])
33
+ assert(Fear["[*]"] === [1, 2])
34
+ assert_not(Fear["[1, *]"] === [])
35
+ assert(Fear["[1, *]"] === [1])
36
+ assert(Fear["[1, *]"] === [1, 2])
37
+ assert(Fear["[1, *]"] === [1, 2, 3])
38
+ assert(Fear["[[1]]"] === [[1]])
39
+ assert(Fear["[[1],2]"] === [[1], 2])
40
+ assert(Fear["[[1],*]"] === [[1], 2])
41
+ assert(Fear["[[*],*]"] === [[1], 2])
42
+ assert_invalid_syntax { Fear["[*, 2]"] }
43
+ assert_invalid_syntax { Fear["[*, ]"] }
44
+ assert_invalid_syntax { Fear["[1, *, ]"] }
45
+ assert_invalid_syntax { Fear["[1, *, 2]"] }
46
+ assert_invalid_syntax { Fear["[1, *, *]"] }
47
+ assert_invalid_syntax { Fear["[*, *]"] }
48
+ assert(Fear["[a, b]"] === [1, 2])
49
+ assert_not(Fear["[a, b, c]"] === [1, 2])
50
+ assert(Fear["[a, b, _]"] === [1, 2, 3])
51
+ assert(Fear["[a, b, *c]"] === [1, 2])
52
+ assert_not(Fear["[a, b, c, *d]"] === [1, 2])
53
+ end
54
+
55
+ specify "String" do
56
+ assert(Fear['"foo"'] === "foo")
57
+ assert(Fear['"f\"oo"'] === 'f"oo')
58
+ assert_not(Fear['"foo"'] === "bar")
59
+ assert(Fear["'foo'"] === "foo")
60
+ assert_not(Fear["'foo'"] === "bar")
61
+ end
62
+
63
+ specify "Symbol" do
64
+ assert(Fear[':"foo"'] === :foo)
65
+ assert(Fear[":'foo'"] === :foo)
66
+ assert(Fear[":foo"] === :foo)
67
+ assert_not(Fear[":foo"] === :bar)
68
+ end
69
+
70
+ specify "Boolean" do
71
+ assert(Fear["true"] === true)
72
+ assert(Fear["false"] === false)
73
+ assert_not(Fear["true"] === false)
74
+ assert_not(Fear["false"] === true)
75
+ end
76
+
77
+ specify "Nil" do
78
+ assert(Fear["nil"] === nil) # rubocop:disable Style/NilComparison
79
+ assert_not(Fear["nil"] === 42)
80
+ end
81
+
82
+ specify "_" do
83
+ assert(Fear["_"] === nil) # rubocop:disable Style/NilComparison
84
+ assert(Fear["_"] === true)
85
+ assert(Fear["_"] === false)
86
+ assert(Fear["_"] === 42)
87
+ assert(Fear["_"] === "foo")
88
+ assert(Fear["_"] === [42])
89
+ end
90
+
91
+ specify "type matching" do
92
+ class Foo
93
+ class Bar
94
+ end
95
+ end
96
+
97
+ assert(Fear["Integer"] === 3)
98
+ assert_not(Fear["Integer"] === "3")
99
+ assert(Fear["Numeric"] === 3)
100
+ assert(Fear["Foo::Bar"] === Foo::Bar.new)
101
+ assert(Fear["var : Integer"] === 3)
102
+ assert_not(Fear["var : Integer"] === "3")
103
+ end
104
+
105
+ specify "capture matcher" do
106
+ assert(Fear["array @ [head : Integer, *tail]"] === [1, 2])
107
+ assert_not(Fear["array @ [head : Integer, *tail]"] === ["1", 2])
108
+ end
109
+
110
+ specify "extractor" do
111
+ assert_valid_syntax { Fear["Foo(a, b : Integer)"] }
112
+ assert(Fear["Fear::Some(a : Integer)"] === Fear.some(42))
113
+ assert_not(Fear["Fear::Some(a : Integer)"] === Fear.some("foo"))
114
+ end
115
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Fear::Extractor do
4
+ describe ".register_extractor" do
5
+ Foo = ::Struct.new(:v1, :v2)
6
+ let(:matcher) do
7
+ Fear.matcher do |m|
8
+ m.case(Fear["Foo(43, second : Integer)"]) { |second| "43 and #{second}" }
9
+ m.case(Fear["Foo(42, second : Integer)"]) { |second| "42 and #{second}" }
10
+ m.else { "no match" }
11
+ end
12
+ end
13
+
14
+ let(:extractor) do
15
+ Fear.case(Foo) { |foo| [foo.v1, foo.v2] }.lift
16
+ end
17
+
18
+ context "extractor not registered" do
19
+ it "raise Fear::Extractor::ExtractorNotFound" do
20
+ expect do
21
+ described_class.find_extractor("UnknownExtractor")
22
+ end.to raise_error(Fear::Extractor::ExtractorNotFound)
23
+ end
24
+ end
25
+
26
+ context "register by name" do
27
+ let(:extractor) { ->(*) { Fear.some("matched") } }
28
+
29
+ before do
30
+ described_class.register_extractor(
31
+ "ExtractorRegisteredByName",
32
+ "ExtractorRegisteredByName2",
33
+ extractor,
34
+ )
35
+ end
36
+
37
+ it "returns extractor" do
38
+ expect(described_class.find_extractor("ExtractorRegisteredByName")).to eq(extractor)
39
+ expect(described_class.find_extractor("ExtractorRegisteredByName2")).to eq(extractor)
40
+ end
41
+ end
42
+
43
+ context "register by class" do
44
+ let(:extractor) { ->(*) { Fear.some("matched") } }
45
+ ExtractorRegisteredByClass = Class.new
46
+
47
+ before do
48
+ described_class.register_extractor(
49
+ ExtractorRegisteredByClass,
50
+ "ExtractorRegisteredByClass2",
51
+ extractor,
52
+ )
53
+ end
54
+
55
+ it "returns extractor" do
56
+ expect(described_class.find_extractor("ExtractorRegisteredByClass")).to eq(extractor)
57
+ expect(described_class.find_extractor("ExtractorRegisteredByClass2")).to eq(extractor)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -1,118 +1,218 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Fear::Failure do
2
- let(:exception) { RuntimeError.new('error') }
3
- let(:failure) { Failure(exception) }
4
+ let(:exception) { RuntimeError.new("error") }
5
+ let(:failure) { Fear.failure(exception) }
4
6
 
5
7
  it_behaves_like Fear::RightBiased::Left do
6
8
  let(:left) { failure }
7
9
  end
8
10
 
9
- describe '#exception' do
11
+ describe "#exception" do
10
12
  subject { failure.exception }
11
13
  it { is_expected.to eq(exception) }
12
14
  end
13
15
 
14
- describe '#success?' do
16
+ describe "#success?" do
15
17
  subject { failure }
16
18
  it { is_expected.not_to be_success }
17
19
  end
18
20
 
19
- describe '#failure?' do
21
+ describe "#failure?" do
20
22
  subject { failure }
21
23
  it { is_expected.to be_failure }
22
24
  end
23
25
 
24
- describe '#get' do
26
+ describe "#get" do
25
27
  subject { proc { failure.get } }
26
- it { is_expected.to raise_error(RuntimeError, 'error') }
28
+ it { is_expected.to raise_error(RuntimeError, "error") }
27
29
  end
28
30
 
29
- describe '#or_else' do
30
- context 'default does not fail' do
31
- subject { failure.or_else { Fear::Success.new('value') } }
32
- it { is_expected.to eq(Fear::Success.new('value')) }
31
+ describe "#or_else" do
32
+ context "default does not fail" do
33
+ subject { failure.or_else { Fear::Success.new("value") } }
34
+ it { is_expected.to eq(Fear::Success.new("value")) }
33
35
  end
34
36
 
35
- context 'default fails with error' do
36
- subject(:or_else) { failure.or_else { fail 'unexpected error' } }
37
+ context "default fails with error" do
38
+ subject(:or_else) { failure.or_else { raise "unexpected error" } }
37
39
  it { is_expected.to be_kind_of(described_class) }
38
- it { expect { or_else.get }.to raise_error(RuntimeError, 'unexpected error') }
40
+ it { expect { or_else.get }.to raise_error(RuntimeError, "unexpected error") }
39
41
  end
40
42
  end
41
43
 
42
- describe '#flatten' do
44
+ describe "#flatten" do
43
45
  subject { failure.flatten }
44
46
  it { is_expected.to eq(failure) }
45
47
  end
46
48
 
47
- describe '#select' do
48
- subject { failure.select { |v| v == 'value' } }
49
+ describe "#select" do
50
+ subject { failure.select { |v| v == "value" } }
49
51
  it { is_expected.to eq(failure) }
50
52
  end
51
53
 
52
- context '#recover_with' do
53
- context 'block does not fail' do
54
+ context "#recover_with" do
55
+ context "block matches the error and does not fail" do
56
+ subject do
57
+ failure.recover_with do |m|
58
+ m.case(RuntimeError) { |error| Fear.success(error.message) }
59
+ end
60
+ end
61
+
62
+ it "returns result of evaluation of the block against the error" do
63
+ is_expected.to eq(Fear::Success.new("error"))
64
+ end
65
+ end
66
+
67
+ context "block does not match the error" do
54
68
  subject do
55
- failure.recover_with do |error|
56
- Success(error.message)
69
+ failure.recover_with do |m|
70
+ m.case(ZeroDivisionError) { Fear.success(0) }
57
71
  end
58
72
  end
59
73
 
60
- it 'returns result of evaluation of the block against the error' do
61
- is_expected.to eq(Fear::Success.new('error'))
74
+ it "returns the same failure" do
75
+ is_expected.to eq(failure)
62
76
  end
63
77
  end
64
78
 
65
- context 'block fails' do
66
- subject(:recover_with) { failure.recover_with { fail 'unexpected error' } }
79
+ context "block fails" do
80
+ subject(:recover_with) { failure.recover_with { raise "unexpected error" } }
67
81
 
68
82
  it { is_expected.to be_kind_of(described_class) }
69
- it { expect { recover_with.get }.to raise_error(RuntimeError, 'unexpected error') }
83
+ it { expect { recover_with.get }.to raise_error(RuntimeError, "unexpected error") }
70
84
  end
71
85
  end
72
86
 
73
- context '#recover' do
74
- context 'block does not fail' do
75
- subject { failure.recover(&:message) }
87
+ context "#recover" do
88
+ context "block matches the error and does not fail" do
89
+ subject do
90
+ failure.recover do |m|
91
+ m.case(RuntimeError, &:message)
92
+ end
93
+ end
76
94
 
77
- it 'returns Success of evaluation of the block against the error' do
78
- is_expected.to eq(Success('error'))
95
+ it "returns Success of evaluation of the block against the error" do
96
+ is_expected.to eq(Fear.success("error"))
79
97
  end
80
98
  end
81
99
 
82
- context 'block fails' do
83
- subject(:recover) { failure.recover { fail 'unexpected error' } }
100
+ context "block does not match the error" do
101
+ subject do
102
+ failure.recover do |m|
103
+ m.case(ZeroDivisionError, &:message)
104
+ end
105
+ end
106
+
107
+ it "returns the same failure" do
108
+ is_expected.to eq(failure)
109
+ end
110
+ end
111
+
112
+ context "block fails" do
113
+ subject(:recover) do
114
+ failure.recover do
115
+ raise "unexpected error"
116
+ end
117
+ end
84
118
 
85
119
  it { is_expected.to be_kind_of(described_class) }
86
- it { expect { recover.get }.to raise_error(RuntimeError, 'unexpected error') }
120
+ it { expect { recover.get }.to raise_error(RuntimeError, "unexpected error") }
87
121
  end
88
122
  end
89
123
 
90
- describe '#to_either' do
124
+ describe "#to_either" do
91
125
  subject { failure.to_either }
92
- it { is_expected.to eq(Left(exception)) }
126
+ it { is_expected.to eq(Fear.left(exception)) }
93
127
  end
94
128
 
95
- describe '#===' do
129
+ describe "#===" do
96
130
  subject { match === failure }
97
131
 
98
- context 'matches erectly' do
99
- let(:match) { Failure(exception) }
132
+ context "matches erectly" do
133
+ let(:match) { Fear.failure(exception) }
100
134
  it { is_expected.to eq(true) }
101
135
  end
102
136
 
103
- context 'value does not match' do
104
- let(:match) { Failure(ArgumentError.new) }
137
+ context "value does not match" do
138
+ let(:match) { Fear.failure(ArgumentError.new) }
105
139
  it { is_expected.to eq(false) }
106
140
  end
107
141
 
108
- context 'matches by class' do
109
- let(:match) { Failure(RuntimeError) }
142
+ context "matches by class" do
143
+ let(:match) { Fear.failure(RuntimeError) }
110
144
  it { is_expected.to eq(true) }
111
145
  end
112
146
 
113
- context 'does not matches by class' do
114
- let(:match) { Failure(ArgumentError) }
147
+ context "does not matches by class" do
148
+ let(:match) { Fear.failure(ArgumentError) }
115
149
  it { is_expected.to eq(false) }
116
150
  end
117
151
  end
152
+
153
+ describe "#match" do
154
+ context "matched" do
155
+ subject do
156
+ failure.match do |m|
157
+ m.failure(->(x) { x.message.length < 2 }) { |x| "Error: #{x}" }
158
+ m.failure(->(x) { x.message.length > 2 }) { |x| "Error: #{x}" }
159
+ m.success(->(x) { x.length > 2 }) { |x| "Success: #{x}" }
160
+ end
161
+ end
162
+
163
+ it { is_expected.to eq("Error: error") }
164
+ end
165
+
166
+ context "nothing matched and no else given" do
167
+ subject do
168
+ proc do
169
+ failure.match do |m|
170
+ m.failure(->(x) { x.message.length < 2 }) { |x| "Error: #{x}" }
171
+ m.success { "noop" }
172
+ end
173
+ end
174
+ end
175
+
176
+ it { is_expected.to raise_error(Fear::MatchError) }
177
+ end
178
+
179
+ context "nothing matched and else given" do
180
+ subject do
181
+ failure.match do |m|
182
+ m.failure(->(x) { x.message.length < 2 }) { |x| "Error: #{x}" }
183
+ m.else { :default }
184
+ end
185
+ end
186
+
187
+ it { is_expected.to eq(:default) }
188
+ end
189
+ end
190
+
191
+ describe "#to_s" do
192
+ subject { failure.to_s }
193
+
194
+ it { is_expected.to eq("#<Fear::Failure exception=#<RuntimeError: error>>") }
195
+ end
196
+
197
+ describe "pattern matching" do
198
+ subject { Fear.xcase("Failure(v : ArgumentError)") { "matched" }.call_or_else(var) { "nothing" } }
199
+
200
+ context "failure of ArgumentError" do
201
+ let(:var) { Fear.failure(ArgumentError.new) }
202
+
203
+ it { is_expected.to eq("matched") }
204
+ end
205
+
206
+ context "failure of RuntimeError" do
207
+ let(:var) { Fear.failure(RuntimeError.new) }
208
+
209
+ it { is_expected.to eq("nothing") }
210
+ end
211
+
212
+ context "not failure" do
213
+ let(:var) { "42" }
214
+
215
+ it { is_expected.to eq("nothing") }
216
+ end
217
+ end
118
218
  end
@@ -1,123 +1,113 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Fear::For do
2
- context 'unary' do
3
- context 'Some' do
4
+ context "unary" do
5
+ context "Some" do
4
6
  subject do
5
- For(a: Some(2)) { a * 2 }
7
+ Fear.for(Fear.some(2)) { |a| a * 2 }
6
8
  end
7
9
 
8
- it { is_expected.to eq(Some(4)) }
10
+ it { is_expected.to eq(Fear.some(4)) }
9
11
  end
10
12
 
11
- context 'None' do
13
+ context "None" do
12
14
  subject do
13
- For(a: None()) { a * 2 }
15
+ Fear.for(Fear.none) { |a| a * 2 }
14
16
  end
15
17
 
16
- it { is_expected.to eq(None()) }
18
+ it { is_expected.to eq(Fear.none) }
17
19
  end
18
20
  end
19
21
 
20
- context 'access method from outer scope' do
21
- def two
22
- 2
23
- end
24
- private :two
25
-
22
+ context "arrays" do
26
23
  subject do
27
- For(a: Some(2)) { a * two }
28
- end
29
-
30
- it { is_expected.to eq(Some(4)) }
31
- end
32
-
33
- context 'arrays' do
34
- subject do
35
- For(a: [1, 2], b: [2, 3], c: [3, 4]) do
24
+ Fear.for([1, 2], [2, 3], [3, 4]) do |a, b, c|
36
25
  a * b * c
37
26
  end
38
27
  end
28
+
39
29
  it { is_expected.to eq([6, 8, 9, 12, 12, 16, 18, 24]) }
40
30
  end
41
31
 
42
- context 'ternary' do
32
+ context "ternary" do
43
33
  subject do
44
- For(a: first, b: second, c: third) do
34
+ Fear.for(first, second, third) do |a, b, c|
45
35
  a * b * c
46
36
  end
47
37
  end
48
38
 
49
- context 'all Same' do
50
- let(:first) { Some(2) }
51
- let(:second) { Some(3) }
52
- let(:third) { Some(4) }
39
+ context "all Same" do
40
+ let(:first) { Fear.some(2) }
41
+ let(:second) { Fear.some(3) }
42
+ let(:third) { Fear.some(4) }
53
43
 
54
- it { is_expected.to eq(Some(24)) }
44
+ it { is_expected.to eq(Fear.some(24)) }
55
45
  end
56
46
 
57
- context 'first is None' do
58
- let(:first) { None() }
59
- let(:second) { Some(3) }
60
- let(:third) { Some(4) }
47
+ context "first is None" do
48
+ let(:first) { Fear.none }
49
+ let(:second) { Fear.some(3) }
50
+ let(:third) { Fear.some(4) }
61
51
 
62
- it { is_expected.to eq(None()) }
52
+ it { is_expected.to eq(Fear.none) }
63
53
  end
64
54
 
65
- context 'second is None' do
66
- let(:first) { Some(2) }
67
- let(:second) { None() }
68
- let(:third) { Some(4) }
55
+ context "second is None" do
56
+ let(:first) { Fear.some(2) }
57
+ let(:second) { Fear.none }
58
+ let(:third) { Fear.some(4) }
69
59
 
70
- it { is_expected.to eq(None()) }
60
+ it { is_expected.to eq(Fear.none) }
71
61
  end
72
62
 
73
- context 'last is None' do
74
- let(:first) { Some(2) }
75
- let(:second) { Some(3) }
76
- let(:third) { None() }
63
+ context "last is None" do
64
+ let(:first) { Fear.some(2) }
65
+ let(:second) { Fear.some(3) }
66
+ let(:third) { Fear.none }
77
67
 
78
- it { is_expected.to eq(None()) }
68
+ it { is_expected.to eq(Fear.none) }
79
69
  end
80
70
 
81
- context 'all Same in lambdas' do
82
- let(:first) { -> { Some(2) } }
83
- let(:second) { -> { Some(3) } }
84
- let(:third) { -> { Some(4) } }
71
+ context "all Same in lambdas" do
72
+ let(:first) { proc { Fear.some(2) } }
73
+ let(:second) { proc { Fear.some(3) } }
74
+ let(:third) { proc { Fear.some(4) } }
85
75
 
86
- it { is_expected.to eq(Some(24)) }
76
+ it { is_expected.to eq(Fear.some(24)) }
87
77
  end
88
78
 
89
- context 'first is None in lambda, second is failure in lambda' do
90
- let(:first) { -> { None() } }
91
- let(:second) { -> { fail 'kaboom' } }
92
- let(:third) { -> {} }
79
+ context "first is None in lambda, second is failure in lambda" do
80
+ let(:first) { proc { Fear.none } }
81
+ let(:second) { proc { raise "kaboom" } }
82
+ let(:third) { proc {} }
93
83
 
94
- it 'returns None without evaluating second and third' do
95
- is_expected.to eq(None())
84
+ it "returns None without evaluating second and third" do
85
+ is_expected.to eq(Fear.none)
96
86
  end
97
87
  end
98
88
 
99
- context 'second is None in lambda, third is failure in lambda' do
100
- let(:first) { Some(2) }
101
- let(:second) { -> { None() } }
102
- let(:third) { -> { fail 'kaboom' } }
89
+ context "second is None in lambda, third is failure in lambda" do
90
+ let(:first) { Fear.some(2) }
91
+ let(:second) { proc { Fear.none } }
92
+ let(:third) { proc { raise "kaboom" } }
103
93
 
104
- it 'returns None without evaluating third' do
105
- is_expected.to eq(None())
94
+ it "returns None without evaluating third" do
95
+ is_expected.to eq(Fear.none)
106
96
  end
107
97
  end
108
98
  end
109
99
 
110
- context 'refer to previous variable from lambda' do
100
+ context "refer to previous variable from lambda" do
111
101
  subject do
112
- For(a: first, b: second, c: third) do
102
+ Fear.for(first, second, third) do |_, b, c|
113
103
  b * c
114
104
  end
115
105
  end
116
106
 
117
- let(:first) { Some(Some(2)) }
118
- let(:second) { -> { a } }
119
- let(:third) { -> { Some(3) } }
107
+ let(:first) { Fear.some(Fear.some(2)) }
108
+ let(:second) { ->(a) { a.map { |x| x * 2 } } }
109
+ let(:third) { proc { Fear.some(3) } }
120
110
 
121
- it { is_expected.to eq(Some(6)) }
111
+ it { is_expected.to eq(Fear.some(12)) }
122
112
  end
123
113
  end