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.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +30 -4
  3. data/.travis.yml +2 -3
  4. data/Appraisals +5 -9
  5. data/CHANGELOG.md +9 -0
  6. data/Gemfile +2 -0
  7. data/LICENSE.txt +1 -1
  8. data/README.md +255 -85
  9. data/Rakefile +393 -0
  10. data/fear.gemspec +13 -6
  11. data/gemfiles/dry_equalizer_0.1.0.gemfile +1 -0
  12. data/gemfiles/dry_equalizer_0.1.0.gemfile.lock +31 -27
  13. data/gemfiles/dry_equalizer_0.2.1.gemfile +1 -0
  14. data/gemfiles/dry_equalizer_0.2.1.gemfile.lock +31 -27
  15. data/lib/fear/either.rb +49 -14
  16. data/lib/fear/either_pattern_match.rb +48 -0
  17. data/lib/fear/empty_partial_function.rb +36 -0
  18. data/lib/fear/failure.rb +5 -4
  19. data/lib/fear/failure_pattern_match.rb +8 -0
  20. data/lib/fear/for.rb +46 -51
  21. data/lib/fear/left.rb +7 -1
  22. data/lib/fear/left_pattern_match.rb +9 -0
  23. data/lib/fear/none.rb +37 -2
  24. data/lib/fear/none_pattern_match.rb +12 -0
  25. data/lib/fear/option.rb +65 -31
  26. data/lib/fear/option_pattern_match.rb +45 -0
  27. data/lib/fear/partial_function/and_then.rb +48 -0
  28. data/lib/fear/partial_function/any.rb +26 -0
  29. data/lib/fear/partial_function/combined.rb +51 -0
  30. data/lib/fear/partial_function/empty.rb +6 -0
  31. data/lib/fear/partial_function/guard/and.rb +36 -0
  32. data/lib/fear/partial_function/guard/and3.rb +39 -0
  33. data/lib/fear/partial_function/guard/or.rb +36 -0
  34. data/lib/fear/partial_function/guard.rb +90 -0
  35. data/lib/fear/partial_function/lifted.rb +20 -0
  36. data/lib/fear/partial_function/or_else.rb +62 -0
  37. data/lib/fear/partial_function.rb +171 -0
  38. data/lib/fear/partial_function_class.rb +26 -0
  39. data/lib/fear/pattern_match.rb +102 -0
  40. data/lib/fear/pattern_matching_api.rb +110 -0
  41. data/lib/fear/right.rb +7 -1
  42. data/lib/fear/right_biased.rb +2 -12
  43. data/lib/fear/right_pattern_match.rb +9 -0
  44. data/lib/fear/some.rb +5 -2
  45. data/lib/fear/some_pattern_match.rb +11 -0
  46. data/lib/fear/success.rb +5 -4
  47. data/lib/fear/success_pattern_match.rb +10 -0
  48. data/lib/fear/try.rb +56 -16
  49. data/lib/fear/try_pattern_match.rb +28 -0
  50. data/lib/fear/utils.rb +24 -14
  51. data/lib/fear/version.rb +1 -1
  52. data/lib/fear.rb +21 -4
  53. data/spec/fear/either_pattern_match_spec.rb +37 -0
  54. data/spec/fear/failure_spec.rb +41 -3
  55. data/spec/fear/for_spec.rb +17 -29
  56. data/spec/fear/guard_spec.rb +101 -0
  57. data/spec/fear/left_spec.rb +38 -0
  58. data/spec/fear/none_spec.rb +80 -0
  59. data/spec/fear/option_pattern_match_spec.rb +35 -0
  60. data/spec/fear/partial_function/empty_spec.rb +36 -0
  61. data/spec/fear/partial_function_and_then_spec.rb +145 -0
  62. data/spec/fear/partial_function_composition_spec.rb +80 -0
  63. data/spec/fear/partial_function_or_else_spec.rb +274 -0
  64. data/spec/fear/partial_function_spec.rb +165 -0
  65. data/spec/fear/pattern_match_spec.rb +59 -0
  66. data/spec/fear/right_biased/left.rb +1 -6
  67. data/spec/fear/right_biased/right.rb +0 -5
  68. data/spec/fear/right_spec.rb +38 -0
  69. data/spec/fear/some_spec.rb +37 -0
  70. data/spec/fear/success_spec.rb +41 -4
  71. data/spec/fear/try_pattern_match_spec.rb +37 -0
  72. metadata +97 -23
  73. 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
- extend self
4
+ UNDEFINED = Object.new.freeze
5
5
 
6
- def assert_arg_or_block!(method_name, *args)
7
- unless block_given? ^ !args.empty?
8
- fail ArgumentError, "##{method_name} accepts either one argument or block"
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
- def assert_type!(value, *types)
13
- if types.none? { |type| value.is_a?(type) }
14
- fail TypeError, "expected `#{value.inspect}` to be of #{types.join(', ')} class"
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
- def return_or_call_proc(value)
19
- if value.respond_to?(:call)
20
- value.call
21
- else
22
- value
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
@@ -1,3 +1,3 @@
1
1
  module Fear
2
- VERSION = '0.10.0'.freeze
2
+ VERSION = '0.11.0'.freeze
3
3
  end
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 :None, 'fear/none'
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
@@ -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 { fail 'unexpected error' } }
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 { fail 'unexpected error' } }
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 { fail 'unexpected error' } }
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
@@ -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(a: Some(2)) { a * 2 }
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(a: None()) { a * 2 }
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(a: [1, 2], b: [2, 3], c: [3, 4]) do
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(a: first, b: second, c: third) do
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) { -> { Some(2) } }
83
- let(:second) { -> { Some(3) } }
84
- let(:third) { -> { Some(4) } }
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) { -> { None() } }
91
- let(:second) { -> { fail 'kaboom' } }
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) { -> { None() } }
102
- let(:third) { -> { fail 'kaboom' } }
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(a: first, b: second, c: third) do
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) { -> { Some(3) } }
106
+ let(:second) { ->(a) { a.map { |x| x * 2 } } }
107
+ let(:third) { proc { Some(3) } }
120
108
 
121
- it { is_expected.to eq(Some(6)) }
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
@@ -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
@@ -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