fear 0.10.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
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,59 @@
1
+ RSpec.describe Fear::PatternMatch do
2
+ include Fear::Option::Mixin
3
+
4
+ context 'else at the end' do
5
+ let(:matcher) do
6
+ described_class.new do |m|
7
+ m.case(Integer) { |x| "#{x} is int" }
8
+ m.case(String) { |x| "#{x} is str" }
9
+ m.else { |x| "#{x} is something else" }
10
+ end
11
+ end
12
+
13
+ context 'Integer' do
14
+ subject { matcher.call(4) }
15
+
16
+ it { is_expected.to eq('4 is int') }
17
+ end
18
+
19
+ context 'String' do
20
+ subject { matcher.call('4') }
21
+
22
+ it { is_expected.to eq('4 is str') }
23
+ end
24
+
25
+ context 'Symbol' do
26
+ subject { matcher.call(:a) }
27
+
28
+ it { is_expected.to eq('a is something else') }
29
+ end
30
+ end
31
+
32
+ context 'else before other branches' do
33
+ subject { matcher.call(4) }
34
+
35
+ let(:matcher) do
36
+ described_class.new do |m|
37
+ m.else { |x| "#{x} is something else" }
38
+ m.case(Integer) { |x| "#{x} is int" }
39
+ end
40
+ end
41
+
42
+ it { is_expected.to eq('4 is something else') }
43
+ end
44
+
45
+ context 'several else branches' do
46
+ subject { matcher.call(4) }
47
+
48
+ let(:matcher) do
49
+ described_class.new do |m|
50
+ m.else { |x| "#{x} else 1" }
51
+ m.else { |x| "#{x} else 2" }
52
+ end
53
+ end
54
+
55
+ it 'first one wins' do
56
+ is_expected.to eq('4 else 1')
57
+ end
58
+ end
59
+ end
@@ -66,14 +66,9 @@ RSpec.shared_examples Fear::RightBiased::Left do
66
66
  end
67
67
  end
68
68
 
69
- describe '#to_a' do
70
- subject { left.to_a }
71
- it { is_expected.to eq([]) }
72
- end
73
-
74
69
  describe '#to_option' do
75
70
  subject { left.to_option }
76
- it { is_expected.to eq(Fear::None.new) }
71
+ it { is_expected.to eq(Fear::None) }
77
72
  end
78
73
 
79
74
  describe '#any?' do
@@ -93,11 +93,6 @@ RSpec.shared_examples Fear::RightBiased::Right do
93
93
  end
94
94
  end
95
95
 
96
- describe '#to_a' do
97
- subject { right.to_a }
98
- it { is_expected.to eq(['value']) }
99
- end
100
-
101
96
  describe '#to_option' do
102
97
  subject { right.to_option }
103
98
  it { is_expected.to eq(Fear::Some.new('value')) }
@@ -114,4 +114,42 @@ RSpec.describe Fear::Right do
114
114
  it { is_expected.to eq(either) }
115
115
  end
116
116
  end
117
+
118
+ describe '#match' do
119
+ context 'matched' do
120
+ subject do
121
+ right.match do |m|
122
+ m.right(->(x) { x.length < 2 }) { |x| "Right: #{x}" }
123
+ m.right(->(x) { x.length > 2 }) { |x| "Right: #{x}" }
124
+ m.left(->(x) { x.length > 2 }) { |x| "Left: #{x}" }
125
+ end
126
+ end
127
+
128
+ it { is_expected.to eq('Right: value') }
129
+ end
130
+
131
+ context 'nothing matched and no else given' do
132
+ subject do
133
+ proc do
134
+ right.match do |m|
135
+ m.right(->(x) { x.length < 2 }) { |x| "Right: #{x}" }
136
+ m.left { |_| 'noop' }
137
+ end
138
+ end
139
+ end
140
+
141
+ it { is_expected.to raise_error(Fear::MatchError) }
142
+ end
143
+
144
+ context 'nothing matched and else given' do
145
+ subject do
146
+ right.match do |m|
147
+ m.right(->(x) { x.length < 2 }) { |x| "Right: #{x}" }
148
+ m.else { :default }
149
+ end
150
+ end
151
+
152
+ it { is_expected.to eq(:default) }
153
+ end
154
+ end
117
155
  end
@@ -49,4 +49,41 @@ RSpec.describe Fear::Some do
49
49
  subject { some.empty? }
50
50
  it { is_expected.to eq(false) }
51
51
  end
52
+
53
+ describe '#match' do
54
+ context 'matched' do
55
+ subject do
56
+ some.match do |m|
57
+ m.some(->(x) { x > 2 }) { |x| x * 2 }
58
+ m.none { 'noop' }
59
+ end
60
+ end
61
+
62
+ it { is_expected.to eq(84) }
63
+ end
64
+
65
+ context 'nothing matched and no else given' do
66
+ subject do
67
+ proc do
68
+ some.match do |m|
69
+ m.some(->(x) { x < 2 }) { |x| x * 2 }
70
+ m.none { 'noop' }
71
+ end
72
+ end
73
+ end
74
+
75
+ it { is_expected.to raise_error(Fear::MatchError) }
76
+ end
77
+
78
+ context 'nothing matched and else given' do
79
+ subject do
80
+ some.match do |m|
81
+ m.none { |x| x * 2 }
82
+ m.else { :default }
83
+ end
84
+ end
85
+
86
+ it { is_expected.to eq(:default) }
87
+ end
88
+ end
52
89
  end
@@ -5,14 +5,14 @@ RSpec.describe Fear::Success do
5
5
  let(:right) { success }
6
6
 
7
7
  describe '#map', 'block fails' do
8
- subject(:map) { right.map { fail 'unexpected error' } }
8
+ subject(:map) { right.map { raise 'unexpected error' } }
9
9
 
10
10
  it { is_expected.to be_kind_of(Fear::Failure) }
11
11
  it { expect { map.get }.to raise_error(RuntimeError, 'unexpected error') }
12
12
  end
13
13
 
14
14
  describe '#flat_map', 'block fails' do
15
- subject(:flat_map) { right.flat_map { fail 'unexpected error' } }
15
+ subject(:flat_map) { right.flat_map { raise 'unexpected error' } }
16
16
 
17
17
  it { is_expected.to be_kind_of(Fear::Failure) }
18
18
  it { expect { flat_map.get }.to raise_error(RuntimeError, 'unexpected error') }
@@ -66,12 +66,12 @@ RSpec.describe Fear::Success do
66
66
  end
67
67
 
68
68
  context 'predicate does not hold for value' do
69
- subject { proc { success.select { |v| v != 'value' }.get } }
69
+ subject { proc { success.select { |v| v != 'value' }.get } } # rubocop: disable Style/InverseMethods
70
70
  it { is_expected.to raise_error(Fear::NoSuchElementError, 'Predicate does not hold for `value`') }
71
71
  end
72
72
 
73
73
  context 'predicate fails with error' do
74
- subject { proc { success.select { fail 'foo' }.get } }
74
+ subject { proc { success.select { raise 'foo' }.get } }
75
75
  it { is_expected.to raise_error(RuntimeError, 'foo') }
76
76
  end
77
77
  end
@@ -90,4 +90,41 @@ RSpec.describe Fear::Success do
90
90
  subject { success.to_either }
91
91
  it { is_expected.to eq(Right('value')) }
92
92
  end
93
+
94
+ describe '#match' do
95
+ context 'matched' do
96
+ subject do
97
+ success.match do |m|
98
+ m.success(->(x) { x.length > 2 }) { |x| x * 2 }
99
+ m.failure { 'noop' }
100
+ end
101
+ end
102
+
103
+ it { is_expected.to eq('valuevalue') }
104
+ end
105
+
106
+ context 'nothing matched and no else given' do
107
+ subject do
108
+ proc do
109
+ success.match do |m|
110
+ m.success(->(x) { x.length < 2 }) { |x| x * 2 }
111
+ m.failure { 'noop' }
112
+ end
113
+ end
114
+ end
115
+
116
+ it { is_expected.to raise_error(Fear::MatchError) }
117
+ end
118
+
119
+ context 'nothing matched and else given' do
120
+ subject do
121
+ success.match do |m|
122
+ m.failure { |x| x * 2 }
123
+ m.else { :default }
124
+ end
125
+ end
126
+
127
+ it { is_expected.to eq(:default) }
128
+ end
129
+ end
93
130
  end
@@ -0,0 +1,37 @@
1
+ RSpec.describe Fear::TryPatternMatch do
2
+ include Fear::Try::Mixin
3
+
4
+ context 'Success' do
5
+ let(:matcher) do
6
+ described_class.new do |m|
7
+ m.success(:even?) { |x| "#{x} is even" }
8
+ m.success(:odd?) { |x| "#{x} is odd" }
9
+ end
10
+ end
11
+
12
+ it do
13
+ expect(matcher.call(Success(4))).to eq('4 is even')
14
+ expect(matcher.call(Success(3))).to eq('3 is odd')
15
+ expect do
16
+ matcher.call(Failure(RuntimeError.new))
17
+ end.to raise_error(Fear::MatchError)
18
+ end
19
+ end
20
+
21
+ context 'Failure' do
22
+ let(:matcher) do
23
+ described_class.new do |m|
24
+ m.failure(RuntimeError) { |x| "#{x} is first" }
25
+ m.failure(StandardError) { |x| "#{x} is second" }
26
+ end
27
+ end
28
+
29
+ it do
30
+ expect(matcher.call(Failure(RuntimeError.new))).to eq('RuntimeError is first')
31
+ expect(matcher.call(Failure(StandardError.new))).to eq('StandardError is second')
32
+ expect do
33
+ matcher.call(Success(44))
34
+ end.to raise_error(Fear::MatchError)
35
+ end
36
+ end
37
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fear
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.0
4
+ version: 0.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tema Bolshakov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-02-16 00:00:00.000000000 Z
11
+ date: 2019-03-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-equalizer
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: benchmark-ips
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: bundler
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -52,6 +66,34 @@ dependencies:
52
66
  - - ">="
53
67
  - !ruby/object:Gem::Version
54
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: dry-matcher
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: qo
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
55
97
  - !ruby/object:Gem::Dependency
56
98
  name: rake
57
99
  requirement: !ruby/object:Gem::Requirement
@@ -86,44 +128,30 @@ dependencies:
86
128
  requirements:
87
129
  - - '='
88
130
  - !ruby/object:Gem::Version
89
- version: 0.47.1
131
+ version: 0.65.0
90
132
  type: :development
91
133
  prerelease: false
92
134
  version_requirements: !ruby/object:Gem::Requirement
93
135
  requirements:
94
136
  - - '='
95
137
  - !ruby/object:Gem::Version
96
- version: 0.47.1
138
+ version: 0.65.0
97
139
  - !ruby/object:Gem::Dependency
98
140
  name: rubocop-rspec
99
141
  requirement: !ruby/object:Gem::Requirement
100
142
  requirements:
101
143
  - - '='
102
144
  - !ruby/object:Gem::Version
103
- version: 1.13.0
145
+ version: 1.32.0
104
146
  type: :development
105
147
  prerelease: false
106
148
  version_requirements: !ruby/object:Gem::Requirement
107
149
  requirements:
108
150
  - - '='
109
151
  - !ruby/object:Gem::Version
110
- version: 1.13.0
111
- - !ruby/object:Gem::Dependency
112
- name: simplecov
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - ">="
116
- - !ruby/object:Gem::Version
117
- version: '0'
118
- type: :development
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - ">="
123
- - !ruby/object:Gem::Version
124
- version: '0'
152
+ version: 1.32.0
125
153
  - !ruby/object:Gem::Dependency
126
- name: spbtv_code_style
154
+ name: yard
127
155
  requirement: !ruby/object:Gem::Requirement
128
156
  requirements:
129
157
  - - ">="
@@ -162,37 +190,73 @@ files:
162
190
  - lib/fear.rb
163
191
  - lib/fear/done.rb
164
192
  - lib/fear/either.rb
193
+ - lib/fear/either_pattern_match.rb
194
+ - lib/fear/empty_partial_function.rb
165
195
  - lib/fear/failure.rb
196
+ - lib/fear/failure_pattern_match.rb
166
197
  - lib/fear/for.rb
167
- - lib/fear/for/evaluation_context.rb
168
198
  - lib/fear/left.rb
199
+ - lib/fear/left_pattern_match.rb
169
200
  - lib/fear/none.rb
201
+ - lib/fear/none_pattern_match.rb
170
202
  - lib/fear/option.rb
203
+ - lib/fear/option_pattern_match.rb
204
+ - lib/fear/partial_function.rb
205
+ - lib/fear/partial_function/and_then.rb
206
+ - lib/fear/partial_function/any.rb
207
+ - lib/fear/partial_function/combined.rb
208
+ - lib/fear/partial_function/empty.rb
209
+ - lib/fear/partial_function/guard.rb
210
+ - lib/fear/partial_function/guard/and.rb
211
+ - lib/fear/partial_function/guard/and3.rb
212
+ - lib/fear/partial_function/guard/or.rb
213
+ - lib/fear/partial_function/lifted.rb
214
+ - lib/fear/partial_function/or_else.rb
215
+ - lib/fear/partial_function_class.rb
216
+ - lib/fear/pattern_match.rb
217
+ - lib/fear/pattern_matching_api.rb
171
218
  - lib/fear/right.rb
172
219
  - lib/fear/right_biased.rb
220
+ - lib/fear/right_pattern_match.rb
173
221
  - lib/fear/some.rb
222
+ - lib/fear/some_pattern_match.rb
174
223
  - lib/fear/success.rb
224
+ - lib/fear/success_pattern_match.rb
175
225
  - lib/fear/try.rb
226
+ - lib/fear/try_pattern_match.rb
176
227
  - lib/fear/utils.rb
177
228
  - lib/fear/version.rb
178
229
  - spec/fear/done_spec.rb
230
+ - spec/fear/either_pattern_match_spec.rb
179
231
  - spec/fear/failure_spec.rb
180
232
  - spec/fear/for_spec.rb
233
+ - spec/fear/guard_spec.rb
181
234
  - spec/fear/left_spec.rb
182
235
  - spec/fear/none_spec.rb
236
+ - spec/fear/option_pattern_match_spec.rb
183
237
  - spec/fear/option_spec.rb
238
+ - spec/fear/partial_function/empty_spec.rb
239
+ - spec/fear/partial_function_and_then_spec.rb
240
+ - spec/fear/partial_function_composition_spec.rb
241
+ - spec/fear/partial_function_or_else_spec.rb
242
+ - spec/fear/partial_function_spec.rb
243
+ - spec/fear/pattern_match_spec.rb
184
244
  - spec/fear/right_biased/left.rb
185
245
  - spec/fear/right_biased/right.rb
186
246
  - spec/fear/right_spec.rb
187
247
  - spec/fear/some_spec.rb
188
248
  - spec/fear/success_spec.rb
249
+ - spec/fear/try_pattern_match_spec.rb
189
250
  - spec/fear/utils_spec.rb
190
251
  - spec/spec_helper.rb
191
252
  homepage: https://github.com/bolshakov/fear
192
253
  licenses:
193
254
  - MIT
194
255
  metadata: {}
195
- post_install_message:
256
+ post_install_message: |2
257
+ Fear v0.11.0 introduces backwards-incompatible changes.
258
+ Please see https://github.com/bolshakov/fear/blob/master/CHANGELOG.md#0110 for details.
259
+ Successfully installed fear-0.11.0
196
260
  rdoc_options: []
197
261
  require_paths:
198
262
  - lib
@@ -214,15 +278,25 @@ specification_version: 4
214
278
  summary: "%q{Ruby port of some Scala's monads.}"
215
279
  test_files:
216
280
  - spec/fear/done_spec.rb
281
+ - spec/fear/either_pattern_match_spec.rb
217
282
  - spec/fear/failure_spec.rb
218
283
  - spec/fear/for_spec.rb
284
+ - spec/fear/guard_spec.rb
219
285
  - spec/fear/left_spec.rb
220
286
  - spec/fear/none_spec.rb
287
+ - spec/fear/option_pattern_match_spec.rb
221
288
  - spec/fear/option_spec.rb
289
+ - spec/fear/partial_function/empty_spec.rb
290
+ - spec/fear/partial_function_and_then_spec.rb
291
+ - spec/fear/partial_function_composition_spec.rb
292
+ - spec/fear/partial_function_or_else_spec.rb
293
+ - spec/fear/partial_function_spec.rb
294
+ - spec/fear/pattern_match_spec.rb
222
295
  - spec/fear/right_biased/left.rb
223
296
  - spec/fear/right_biased/right.rb
224
297
  - spec/fear/right_spec.rb
225
298
  - spec/fear/some_spec.rb
226
299
  - spec/fear/success_spec.rb
300
+ - spec/fear/try_pattern_match_spec.rb
227
301
  - spec/fear/utils_spec.rb
228
302
  - spec/spec_helper.rb
@@ -1,91 +0,0 @@
1
- module Fear
2
- # This class provides syntactic sugar for composition of
3
- # multiple monadic operations. It supports two such
4
- # operations - +flat_map+ and +map+. Any class providing them
5
- # is supported by +For+.
6
- #
7
- # For(a: Some(2), b: Some(3)) { a * b } #=> Some(6)
8
- #
9
- # If one of operands is None, the result is None
10
- #
11
- # For(a: Some(2), b: None()) { a * b } #=> None()
12
- # For(a: None(), b: Some(2)) { a * b } #=> None()
13
- #
14
- # Lets look at first example:
15
- #
16
- # For(a: Some(2), b: Some(3)) { a * b }
17
- #
18
- # would be translated to:
19
- #
20
- # Some(2).flat_map do |a|
21
- # Some(3).map do |b|
22
- # a * b
23
- # end
24
- # end
25
- #
26
- # It works with arrays as well
27
- #
28
- # For(a: [1, 2], b: [2, 3], c: [3, 4]) { a * b * c }
29
- # #=> [6, 8, 9, 12, 12, 16, 18, 24]
30
- #
31
- # would be translated to:
32
- #
33
- # [1, 2].flat_map do |a|
34
- # [2, 3].flat_map do |b|
35
- # [3, 4].map do |c|
36
- # a * b * c
37
- # end
38
- # end
39
- # end
40
- #
41
- # If you pass lambda as a variable value, it would be evaluated
42
- # only on demand.
43
- #
44
- # For(a: -> { None() }, b: -> { fail 'kaboom' } ) { a * b }
45
- # #=> None()
46
- #
47
- # It does not fail since `b` is not evaluated.
48
- # You can mix and match lambdas and variables.
49
- #
50
- # For(a: -> None(), b: -> { fail 'kaboom' } ) { a * b }
51
- # #=> None()
52
- #
53
- class For
54
- # Context of block evaluation. It respond to passed locals.
55
- #
56
- # @example
57
- # context = EvaluationContext.new
58
- # context.assign(:foo, 'bar')
59
- # context.foo #=> 'bar'
60
- #
61
- class EvaluationContext < BasicObject
62
- include ::Fear::Option::Mixin
63
- include ::Fear::Either::Mixin
64
- include ::Fear::Try::Mixin
65
-
66
- def initialize(outer_context)
67
- @assigns = {}
68
- @outer_context = outer_context
69
- end
70
-
71
- def __assign__(name, value)
72
- @assigns[name] = value
73
- end
74
-
75
- def method_missing(name, *args, &block)
76
- if @assigns.include?(name) && args.empty? && block.nil?
77
- @assigns[name]
78
- elsif @outer_context.respond_to?(name, true)
79
- @outer_context.__send__(name, *args, &block)
80
- else
81
- super
82
- end
83
- end
84
-
85
- def respond_to_missing?(name, _)
86
- @assigns.key?(name) && args.empty? && block.nil?
87
- end
88
- end
89
- private_constant(:EvaluationContext)
90
- end
91
- end