fear 0.10.0 → 0.11.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 +4 -4
- data/.rubocop.yml +30 -4
- data/.travis.yml +2 -3
- data/Appraisals +5 -9
- data/CHANGELOG.md +9 -0
- data/Gemfile +2 -0
- data/LICENSE.txt +1 -1
- data/README.md +255 -85
- data/Rakefile +393 -0
- data/fear.gemspec +13 -6
- data/gemfiles/dry_equalizer_0.1.0.gemfile +1 -0
- data/gemfiles/dry_equalizer_0.1.0.gemfile.lock +31 -27
- data/gemfiles/dry_equalizer_0.2.1.gemfile +1 -0
- data/gemfiles/dry_equalizer_0.2.1.gemfile.lock +31 -27
- data/lib/fear/either.rb +49 -14
- data/lib/fear/either_pattern_match.rb +48 -0
- data/lib/fear/empty_partial_function.rb +36 -0
- data/lib/fear/failure.rb +5 -4
- data/lib/fear/failure_pattern_match.rb +8 -0
- data/lib/fear/for.rb +46 -51
- data/lib/fear/left.rb +7 -1
- data/lib/fear/left_pattern_match.rb +9 -0
- data/lib/fear/none.rb +37 -2
- data/lib/fear/none_pattern_match.rb +12 -0
- data/lib/fear/option.rb +65 -31
- data/lib/fear/option_pattern_match.rb +45 -0
- data/lib/fear/partial_function/and_then.rb +48 -0
- data/lib/fear/partial_function/any.rb +26 -0
- data/lib/fear/partial_function/combined.rb +51 -0
- data/lib/fear/partial_function/empty.rb +6 -0
- data/lib/fear/partial_function/guard/and.rb +36 -0
- data/lib/fear/partial_function/guard/and3.rb +39 -0
- data/lib/fear/partial_function/guard/or.rb +36 -0
- data/lib/fear/partial_function/guard.rb +90 -0
- data/lib/fear/partial_function/lifted.rb +20 -0
- data/lib/fear/partial_function/or_else.rb +62 -0
- data/lib/fear/partial_function.rb +171 -0
- data/lib/fear/partial_function_class.rb +26 -0
- data/lib/fear/pattern_match.rb +102 -0
- data/lib/fear/pattern_matching_api.rb +110 -0
- data/lib/fear/right.rb +7 -1
- data/lib/fear/right_biased.rb +2 -12
- data/lib/fear/right_pattern_match.rb +9 -0
- data/lib/fear/some.rb +5 -2
- data/lib/fear/some_pattern_match.rb +11 -0
- data/lib/fear/success.rb +5 -4
- data/lib/fear/success_pattern_match.rb +10 -0
- data/lib/fear/try.rb +56 -16
- data/lib/fear/try_pattern_match.rb +28 -0
- data/lib/fear/utils.rb +24 -14
- data/lib/fear/version.rb +1 -1
- data/lib/fear.rb +21 -4
- data/spec/fear/either_pattern_match_spec.rb +37 -0
- data/spec/fear/failure_spec.rb +41 -3
- data/spec/fear/for_spec.rb +17 -29
- data/spec/fear/guard_spec.rb +101 -0
- data/spec/fear/left_spec.rb +38 -0
- data/spec/fear/none_spec.rb +80 -0
- data/spec/fear/option_pattern_match_spec.rb +35 -0
- data/spec/fear/partial_function/empty_spec.rb +36 -0
- data/spec/fear/partial_function_and_then_spec.rb +145 -0
- data/spec/fear/partial_function_composition_spec.rb +80 -0
- data/spec/fear/partial_function_or_else_spec.rb +274 -0
- data/spec/fear/partial_function_spec.rb +165 -0
- data/spec/fear/pattern_match_spec.rb +59 -0
- data/spec/fear/right_biased/left.rb +1 -6
- data/spec/fear/right_biased/right.rb +0 -5
- data/spec/fear/right_spec.rb +38 -0
- data/spec/fear/some_spec.rb +37 -0
- data/spec/fear/success_spec.rb +41 -4
- data/spec/fear/try_pattern_match_spec.rb +37 -0
- metadata +97 -23
- data/lib/fear/for/evaluation_context.rb +0 -91
@@ -0,0 +1,28 @@
|
|
1
|
+
module Fear
|
2
|
+
# Try pattern matcher
|
3
|
+
#
|
4
|
+
# @note it has two optimized subclasses +Fear::SuccessPatternMatch+ and +Fear::FailurePatternMatch+
|
5
|
+
# @api private
|
6
|
+
class TryPatternMatch < Fear::PatternMatch
|
7
|
+
SUCCESS_EXTRACTOR = :get.to_proc
|
8
|
+
FAILURE_EXTRACTOR = :exception.to_proc
|
9
|
+
|
10
|
+
# Match against +Fear::Success+
|
11
|
+
#
|
12
|
+
# @param conditions [<#==>]
|
13
|
+
# @return [Fear::TryPatternMatch]
|
14
|
+
def success(*conditions, &effect)
|
15
|
+
branch = Fear.case(Fear::Success, &SUCCESS_EXTRACTOR).and_then(Fear.case(*conditions, &effect))
|
16
|
+
or_else(branch)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Match against +Fear::Failure+
|
20
|
+
#
|
21
|
+
# @param conditions [<#==>]
|
22
|
+
# @return [Fear::TryPatternMatch]
|
23
|
+
def failure(*conditions, &effect)
|
24
|
+
branch = Fear.case(Fear::Failure, &FAILURE_EXTRACTOR).and_then(Fear.case(*conditions, &effect))
|
25
|
+
or_else(branch)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/fear/utils.rb
CHANGED
@@ -1,25 +1,35 @@
|
|
1
1
|
module Fear
|
2
2
|
# @private
|
3
3
|
module Utils
|
4
|
-
|
4
|
+
UNDEFINED = Object.new.freeze
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
class << self
|
7
|
+
def assert_arg_or_block!(method_name, *args)
|
8
|
+
unless block_given? ^ !args.empty?
|
9
|
+
raise ArgumentError, "##{method_name} accepts either one argument or block"
|
10
|
+
end
|
9
11
|
end
|
10
|
-
end
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
13
|
+
def with_block_or_argument(method_name, arg = UNDEFINED, block = nil)
|
14
|
+
if block.nil? ^ arg.equal?(UNDEFINED)
|
15
|
+
yield(block || arg)
|
16
|
+
else
|
17
|
+
raise ArgumentError, "#{method_name} accepts either block or partial function"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def assert_type!(value, *types)
|
22
|
+
if types.none? { |type| value.is_a?(type) }
|
23
|
+
raise TypeError, "expected `#{value.inspect}` to be of #{types.join(', ')} class"
|
24
|
+
end
|
15
25
|
end
|
16
|
-
end
|
17
26
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
27
|
+
def return_or_call_proc(value)
|
28
|
+
if value.respond_to?(:call)
|
29
|
+
value.call
|
30
|
+
else
|
31
|
+
value
|
32
|
+
end
|
23
33
|
end
|
24
34
|
end
|
25
35
|
end
|
data/lib/fear/version.rb
CHANGED
data/lib/fear.rb
CHANGED
@@ -1,27 +1,44 @@
|
|
1
1
|
require 'dry-equalizer'
|
2
2
|
require 'fear/version'
|
3
|
+
require 'fear/pattern_matching_api'
|
3
4
|
|
4
5
|
module Fear
|
5
6
|
Error = Class.new(StandardError)
|
6
|
-
IllegalStateException = Class.new(Error)
|
7
7
|
NoSuchElementError = Class.new(Error)
|
8
|
+
MatchError = Class.new(Error)
|
9
|
+
extend PatternMatchingApi
|
10
|
+
|
11
|
+
autoload :EmptyPartialFunction, 'fear/empty_partial_function'
|
12
|
+
autoload :PartialFunction, 'fear/partial_function'
|
13
|
+
autoload :PartialFunctionClass, 'fear/partial_function_class'
|
14
|
+
autoload :PatternMatch, 'fear/pattern_match'
|
8
15
|
|
9
16
|
autoload :Done, 'fear/done'
|
10
17
|
autoload :For, 'fear/for'
|
11
18
|
autoload :RightBiased, 'fear/right_biased'
|
12
19
|
autoload :Utils, 'fear/utils'
|
13
20
|
|
21
|
+
autoload :None, 'fear/none'
|
22
|
+
autoload :NoneClass, 'fear/none'
|
23
|
+
autoload :NonePatternMatch, 'fear/none_pattern_match'
|
14
24
|
autoload :Option, 'fear/option'
|
25
|
+
autoload :OptionPatternMatch, 'fear/option_pattern_match'
|
15
26
|
autoload :Some, 'fear/some'
|
16
|
-
autoload :
|
27
|
+
autoload :SomePatternMatch, 'fear/some_pattern_match'
|
17
28
|
|
18
|
-
autoload :Try, 'fear/try'
|
19
|
-
autoload :Success, 'fear/success'
|
20
29
|
autoload :Failure, 'fear/failure'
|
30
|
+
autoload :FailurePatternMatch, 'fear/failure_pattern_match'
|
31
|
+
autoload :Success, 'fear/success'
|
32
|
+
autoload :SuccessPatternMatch, 'fear/success_pattern_match'
|
33
|
+
autoload :Try, 'fear/try'
|
34
|
+
autoload :TryPatternMatch, 'fear/try_pattern_match'
|
21
35
|
|
22
36
|
autoload :Either, 'fear/either'
|
37
|
+
autoload :EitherPatternMatch, 'fear/either_pattern_match'
|
23
38
|
autoload :Left, 'fear/left'
|
39
|
+
autoload :LeftPatternMatch, 'fear/left_pattern_match'
|
24
40
|
autoload :Right, 'fear/right'
|
41
|
+
autoload :RightPatternMatch, 'fear/right_pattern_match'
|
25
42
|
|
26
43
|
module Mixin
|
27
44
|
include Either::Mixin
|
@@ -0,0 +1,37 @@
|
|
1
|
+
RSpec.describe Fear::EitherPatternMatch do
|
2
|
+
include Fear::Either::Mixin
|
3
|
+
|
4
|
+
context 'Right' do
|
5
|
+
let(:matcher) do
|
6
|
+
described_class.new do |m|
|
7
|
+
m.right(:even?) { |x| "#{x} is even" }
|
8
|
+
m.right(:odd?) { |x| "#{x} is odd" }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
it do
|
13
|
+
expect(matcher.call(Right(4))).to eq('4 is even')
|
14
|
+
expect(matcher.call(Right(3))).to eq('3 is odd')
|
15
|
+
expect do
|
16
|
+
matcher.call(Left(44))
|
17
|
+
end.to raise_error(Fear::MatchError)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'Left' do
|
22
|
+
let(:matcher) do
|
23
|
+
described_class.new do |m|
|
24
|
+
m.left(:even?) { |x| "#{x} is even" }
|
25
|
+
m.left(:odd?) { |x| "#{x} is odd" }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it do
|
30
|
+
expect(matcher.call(Left(4))).to eq('4 is even')
|
31
|
+
expect(matcher.call(Left(3))).to eq('3 is odd')
|
32
|
+
expect do
|
33
|
+
matcher.call(Right(44))
|
34
|
+
end.to raise_error(Fear::MatchError)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/spec/fear/failure_spec.rb
CHANGED
@@ -33,7 +33,7 @@ RSpec.describe Fear::Failure do
|
|
33
33
|
end
|
34
34
|
|
35
35
|
context 'default fails with error' do
|
36
|
-
subject(:or_else) { failure.or_else {
|
36
|
+
subject(:or_else) { failure.or_else { raise 'unexpected error' } }
|
37
37
|
it { is_expected.to be_kind_of(described_class) }
|
38
38
|
it { expect { or_else.get }.to raise_error(RuntimeError, 'unexpected error') }
|
39
39
|
end
|
@@ -63,7 +63,7 @@ RSpec.describe Fear::Failure do
|
|
63
63
|
end
|
64
64
|
|
65
65
|
context 'block fails' do
|
66
|
-
subject(:recover_with) { failure.recover_with {
|
66
|
+
subject(:recover_with) { failure.recover_with { raise 'unexpected error' } }
|
67
67
|
|
68
68
|
it { is_expected.to be_kind_of(described_class) }
|
69
69
|
it { expect { recover_with.get }.to raise_error(RuntimeError, 'unexpected error') }
|
@@ -80,7 +80,7 @@ RSpec.describe Fear::Failure do
|
|
80
80
|
end
|
81
81
|
|
82
82
|
context 'block fails' do
|
83
|
-
subject(:recover) { failure.recover {
|
83
|
+
subject(:recover) { failure.recover { raise 'unexpected error' } }
|
84
84
|
|
85
85
|
it { is_expected.to be_kind_of(described_class) }
|
86
86
|
it { expect { recover.get }.to raise_error(RuntimeError, 'unexpected error') }
|
@@ -115,4 +115,42 @@ RSpec.describe Fear::Failure do
|
|
115
115
|
it { is_expected.to eq(false) }
|
116
116
|
end
|
117
117
|
end
|
118
|
+
|
119
|
+
describe '#match' do
|
120
|
+
context 'matched' do
|
121
|
+
subject do
|
122
|
+
failure.match do |m|
|
123
|
+
m.failure(->(x) { x.message.length < 2 }) { |x| "Error: #{x}" }
|
124
|
+
m.failure(->(x) { x.message.length > 2 }) { |x| "Error: #{x}" }
|
125
|
+
m.success(->(x) { x.length > 2 }) { |x| "Success: #{x}" }
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
it { is_expected.to eq('Error: error') }
|
130
|
+
end
|
131
|
+
|
132
|
+
context 'nothing matched and no else given' do
|
133
|
+
subject do
|
134
|
+
proc do
|
135
|
+
failure.match do |m|
|
136
|
+
m.failure(->(x) { x.message.length < 2 }) { |x| "Error: #{x}" }
|
137
|
+
m.success { 'noop' }
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
it { is_expected.to raise_error(Fear::MatchError) }
|
143
|
+
end
|
144
|
+
|
145
|
+
context 'nothing matched and else given' do
|
146
|
+
subject do
|
147
|
+
failure.match do |m|
|
148
|
+
m.failure(->(x) { x.message.length < 2 }) { |x| "Error: #{x}" }
|
149
|
+
m.else { :default }
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
it { is_expected.to eq(:default) }
|
154
|
+
end
|
155
|
+
end
|
118
156
|
end
|
data/spec/fear/for_spec.rb
CHANGED
@@ -2,7 +2,7 @@ RSpec.describe Fear::For do
|
|
2
2
|
context 'unary' do
|
3
3
|
context 'Some' do
|
4
4
|
subject do
|
5
|
-
For(
|
5
|
+
For(Some(2)) { |a| a * 2 }
|
6
6
|
end
|
7
7
|
|
8
8
|
it { is_expected.to eq(Some(4)) }
|
@@ -10,38 +10,26 @@ RSpec.describe Fear::For do
|
|
10
10
|
|
11
11
|
context 'None' do
|
12
12
|
subject do
|
13
|
-
For(
|
13
|
+
For(None()) { |a| a * 2 }
|
14
14
|
end
|
15
15
|
|
16
16
|
it { is_expected.to eq(None()) }
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
context 'access method from outer scope' do
|
21
|
-
def two
|
22
|
-
2
|
23
|
-
end
|
24
|
-
private :two
|
25
|
-
|
26
|
-
subject do
|
27
|
-
For(a: Some(2)) { a * two }
|
28
|
-
end
|
29
|
-
|
30
|
-
it { is_expected.to eq(Some(4)) }
|
31
|
-
end
|
32
|
-
|
33
20
|
context 'arrays' do
|
34
21
|
subject do
|
35
|
-
For(
|
22
|
+
For([1, 2], [2, 3], [3, 4]) do |a, b, c|
|
36
23
|
a * b * c
|
37
24
|
end
|
38
25
|
end
|
26
|
+
|
39
27
|
it { is_expected.to eq([6, 8, 9, 12, 12, 16, 18, 24]) }
|
40
28
|
end
|
41
29
|
|
42
30
|
context 'ternary' do
|
43
31
|
subject do
|
44
|
-
For(
|
32
|
+
For(first, second, third) do |a, b, c|
|
45
33
|
a * b * c
|
46
34
|
end
|
47
35
|
end
|
@@ -79,17 +67,17 @@ RSpec.describe Fear::For do
|
|
79
67
|
end
|
80
68
|
|
81
69
|
context 'all Same in lambdas' do
|
82
|
-
let(:first) {
|
83
|
-
let(:second) {
|
84
|
-
let(:third) {
|
70
|
+
let(:first) { proc { Some(2) } }
|
71
|
+
let(:second) { proc { Some(3) } }
|
72
|
+
let(:third) { proc { Some(4) } }
|
85
73
|
|
86
74
|
it { is_expected.to eq(Some(24)) }
|
87
75
|
end
|
88
76
|
|
89
77
|
context 'first is None in lambda, second is failure in lambda' do
|
90
|
-
let(:first) {
|
91
|
-
let(:second) {
|
92
|
-
let(:third) {
|
78
|
+
let(:first) { proc { None() } }
|
79
|
+
let(:second) { proc { raise 'kaboom' } }
|
80
|
+
let(:third) { proc {} }
|
93
81
|
|
94
82
|
it 'returns None without evaluating second and third' do
|
95
83
|
is_expected.to eq(None())
|
@@ -98,8 +86,8 @@ RSpec.describe Fear::For do
|
|
98
86
|
|
99
87
|
context 'second is None in lambda, third is failure in lambda' do
|
100
88
|
let(:first) { Some(2) }
|
101
|
-
let(:second) {
|
102
|
-
let(:third) {
|
89
|
+
let(:second) { proc { None() } }
|
90
|
+
let(:third) { proc { raise 'kaboom' } }
|
103
91
|
|
104
92
|
it 'returns None without evaluating third' do
|
105
93
|
is_expected.to eq(None())
|
@@ -109,15 +97,15 @@ RSpec.describe Fear::For do
|
|
109
97
|
|
110
98
|
context 'refer to previous variable from lambda' do
|
111
99
|
subject do
|
112
|
-
For(
|
100
|
+
For(first, second, third) do |_, b, c|
|
113
101
|
b * c
|
114
102
|
end
|
115
103
|
end
|
116
104
|
|
117
105
|
let(:first) { Some(Some(2)) }
|
118
|
-
let(:second) { -> { a } }
|
119
|
-
let(:third) {
|
106
|
+
let(:second) { ->(a) { a.map { |x| x * 2 } } }
|
107
|
+
let(:third) { proc { Some(3) } }
|
120
108
|
|
121
|
-
it { is_expected.to eq(Some(
|
109
|
+
it { is_expected.to eq(Some(12)) }
|
122
110
|
end
|
123
111
|
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
RSpec.describe Fear::PartialFunction::Guard do
|
2
|
+
context 'Class' do
|
3
|
+
context 'match' do
|
4
|
+
subject { Fear::PartialFunction::Guard.new(Integer) === 4 }
|
5
|
+
|
6
|
+
it { is_expected.to eq(true) }
|
7
|
+
end
|
8
|
+
|
9
|
+
context 'not match' do
|
10
|
+
subject { Fear::PartialFunction::Guard.new(Integer) === '4' }
|
11
|
+
|
12
|
+
it { is_expected.to eq(false) }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'Symbol' do
|
17
|
+
context 'match' do
|
18
|
+
subject { Fear::PartialFunction::Guard.new(:even?) === 4 }
|
19
|
+
|
20
|
+
it { is_expected.to eq(true) }
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'not match' do
|
24
|
+
subject { Fear::PartialFunction::Guard.new(:even?) === 3 }
|
25
|
+
|
26
|
+
it { is_expected.to eq(false) }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'Proc' do
|
31
|
+
context 'match' do
|
32
|
+
subject { Fear::PartialFunction::Guard.new(->(x) { x.even? }) === 4 }
|
33
|
+
|
34
|
+
it { is_expected.to eq(true) }
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'not match' do
|
38
|
+
subject { Fear::PartialFunction::Guard.new(->(x) { x.even? }) === 3 }
|
39
|
+
|
40
|
+
it { is_expected.to eq(false) }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '.and' do
|
45
|
+
context 'match' do
|
46
|
+
subject { guard === 4 }
|
47
|
+
let(:guard) { Fear::PartialFunction::Guard.and([Integer, :even?, ->(x) { x.even? }]) }
|
48
|
+
|
49
|
+
it { is_expected.to eq(true) }
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'not match' do
|
53
|
+
subject { guard === 3 }
|
54
|
+
let(:guard) { Fear::PartialFunction::Guard.and([Integer, :even?, ->(x) { x.even? }]) }
|
55
|
+
|
56
|
+
it { is_expected.to eq(false) }
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'empty array' do
|
60
|
+
subject { guard === 4 }
|
61
|
+
let(:guard) { Fear::PartialFunction::Guard.and([]) }
|
62
|
+
|
63
|
+
it 'matches any values' do
|
64
|
+
is_expected.to eq(true)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'short circuit' do
|
69
|
+
let(:guard) { Fear::PartialFunction::Guard.and([first, second, third]) }
|
70
|
+
let(:first) { ->(_) { false } }
|
71
|
+
let(:second) { ->(_) { raise } }
|
72
|
+
let(:third) { ->(_) { raise } }
|
73
|
+
|
74
|
+
it 'does not call the second and the third' do
|
75
|
+
expect { guard === 4 }.not_to raise_error
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe '.or' do
|
81
|
+
let(:guard) { Fear::PartialFunction::Guard.or(['F', Integer]) }
|
82
|
+
|
83
|
+
context 'match second' do
|
84
|
+
subject { guard === 4 }
|
85
|
+
|
86
|
+
it { is_expected.to eq(true) }
|
87
|
+
end
|
88
|
+
|
89
|
+
context 'match first' do
|
90
|
+
subject { guard === 'F' }
|
91
|
+
|
92
|
+
it { is_expected.to eq(true) }
|
93
|
+
end
|
94
|
+
|
95
|
+
context 'not match' do
|
96
|
+
subject { guard === 'A&' }
|
97
|
+
|
98
|
+
it { is_expected.to eq(false) }
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
data/spec/fear/left_spec.rb
CHANGED
@@ -141,4 +141,42 @@ RSpec.describe Fear::Left do
|
|
141
141
|
it { is_expected.to eq(false) }
|
142
142
|
end
|
143
143
|
end
|
144
|
+
|
145
|
+
describe '#match' do
|
146
|
+
context 'matched' do
|
147
|
+
subject do
|
148
|
+
left.match do |m|
|
149
|
+
m.left(->(x) { x.length < 2 }) { |x| "Left: #{x}" }
|
150
|
+
m.left(->(x) { x.length > 2 }) { |x| "Left: #{x}" }
|
151
|
+
m.right(->(x) { x.length > 2 }) { |x| "Right: #{x}" }
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
it { is_expected.to eq('Left: value') }
|
156
|
+
end
|
157
|
+
|
158
|
+
context 'nothing matched and no else given' do
|
159
|
+
subject do
|
160
|
+
proc do
|
161
|
+
left.match do |m|
|
162
|
+
m.left(->(x) { x.length < 2 }) { |x| "Left: #{x}" }
|
163
|
+
m.right { |_| 'noop' }
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
it { is_expected.to raise_error(Fear::MatchError) }
|
169
|
+
end
|
170
|
+
|
171
|
+
context 'nothing matched and else given' do
|
172
|
+
subject do
|
173
|
+
left.match do |m|
|
174
|
+
m.left(->(x) { x.length < 2 }) { |x| "Left: #{x}" }
|
175
|
+
m.else { :default }
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
it { is_expected.to eq(:default) }
|
180
|
+
end
|
181
|
+
end
|
144
182
|
end
|
data/spec/fear/none_spec.rb
CHANGED
@@ -46,4 +46,84 @@ RSpec.describe Fear::None do
|
|
46
46
|
is_expected.to eq(None())
|
47
47
|
end
|
48
48
|
end
|
49
|
+
|
50
|
+
describe '.new' do
|
51
|
+
subject { Fear::None.class.new }
|
52
|
+
|
53
|
+
it { is_expected.to eq(Fear::None) }
|
54
|
+
end
|
55
|
+
|
56
|
+
describe '.inherited' do
|
57
|
+
subject { -> { Class.new(Fear::None.class) } }
|
58
|
+
|
59
|
+
it { is_expected.to raise_error }
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '#to_s' do
|
63
|
+
subject { none.to_s }
|
64
|
+
|
65
|
+
it { is_expected.to eq('Fear::None') }
|
66
|
+
end
|
67
|
+
|
68
|
+
describe '#inspect' do
|
69
|
+
subject { none.inspect }
|
70
|
+
|
71
|
+
it { is_expected.to eq('Fear::None') }
|
72
|
+
end
|
73
|
+
|
74
|
+
describe '#===' do
|
75
|
+
context 'None' do
|
76
|
+
subject { Fear::None === none }
|
77
|
+
|
78
|
+
it { is_expected.to eq(true) }
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'Fear::Some' do
|
82
|
+
subject { Fear::None === Some(4) }
|
83
|
+
|
84
|
+
it { is_expected.to eq(false) }
|
85
|
+
end
|
86
|
+
|
87
|
+
context 'Integer' do
|
88
|
+
subject { Fear::None === 4 }
|
89
|
+
|
90
|
+
it { is_expected.to eq(false) }
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe '#match' do
|
95
|
+
context 'matched' do
|
96
|
+
subject do
|
97
|
+
none.match do |m|
|
98
|
+
m.some { |x| x * 2 }
|
99
|
+
m.none { 'noop' }
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
it { is_expected.to eq('noop') }
|
104
|
+
end
|
105
|
+
|
106
|
+
context 'nothing matched and no else given' do
|
107
|
+
subject do
|
108
|
+
proc do
|
109
|
+
none.match do |m|
|
110
|
+
m.some { |x| x * 2 }
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
it { is_expected.to raise_error(Fear::MatchError) }
|
116
|
+
end
|
117
|
+
|
118
|
+
context 'nothing matched and else given' do
|
119
|
+
subject do
|
120
|
+
none.match do |m|
|
121
|
+
m.some { |x| x * 2 }
|
122
|
+
m.else { :default }
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
it { is_expected.to eq(:default) }
|
127
|
+
end
|
128
|
+
end
|
49
129
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
RSpec.describe Fear::OptionPatternMatch do
|
2
|
+
include Fear::Option::Mixin
|
3
|
+
|
4
|
+
context 'Some' do
|
5
|
+
let(:matcher) do
|
6
|
+
described_class.new do |m|
|
7
|
+
m.some(:even?) { |x| "#{x} is even" }
|
8
|
+
m.some(:odd?) { |x| "#{x} is odd" }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
it do
|
13
|
+
expect(matcher.call(Some(4))).to eq('4 is even')
|
14
|
+
expect(matcher.call(Some(3))).to eq('3 is odd')
|
15
|
+
expect do
|
16
|
+
matcher.call(None())
|
17
|
+
end.to raise_error(Fear::MatchError)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'None' do
|
22
|
+
let(:matcher) do
|
23
|
+
described_class.new do |m|
|
24
|
+
m.none { 'nil' }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
it do
|
29
|
+
expect(matcher.call(None())).to eq('nil')
|
30
|
+
expect do
|
31
|
+
matcher.call(Some(3))
|
32
|
+
end.to raise_error(Fear::MatchError)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
RSpec.describe Fear::PartialFunction::EMPTY do
|
2
|
+
describe '#defined?' do
|
3
|
+
subject { described_class.defined_at?(42) }
|
4
|
+
|
5
|
+
it { is_expected.to be(false) }
|
6
|
+
end
|
7
|
+
|
8
|
+
describe '#call' do
|
9
|
+
subject { -> { described_class.call(42) } }
|
10
|
+
|
11
|
+
it { is_expected.to raise_error(Fear::MatchError, 'partial function not defined at: 42') }
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#call_or_else' do
|
15
|
+
subject { described_class.call_or_else(42, &default) }
|
16
|
+
let(:default) { ->(x) { "default: #{x}" } }
|
17
|
+
|
18
|
+
it { is_expected.to eq('default: 42') }
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#and_then' do
|
22
|
+
subject { described_class.and_then { |_x| 'then' } }
|
23
|
+
|
24
|
+
it { is_expected.to eq(described_class) }
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#or_else' do
|
28
|
+
subject { described_class.or_else(other) }
|
29
|
+
|
30
|
+
let(:other) { Fear.case(proc { true }) { 'other' } }
|
31
|
+
|
32
|
+
it { is_expected.to eq(other) }
|
33
|
+
end
|
34
|
+
|
35
|
+
it { is_expected.to be_kind_of(Fear::PartialFunction) }
|
36
|
+
end
|