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,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