fear 1.2.0 → 2.0.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/.github/dependabot.yml +27 -0
- data/.github/workflows/rubocop.yml +2 -2
- data/.github/workflows/spec.yml +1 -1
- data/CHANGELOG.md +13 -0
- data/Gemfile.lock +53 -56
- data/README.md +54 -186
- data/Rakefile +0 -21
- data/examples/pattern_extracting.rb +4 -4
- data/fear.gemspec +2 -4
- data/lib/fear/either.rb +8 -4
- data/lib/fear/either_api.rb +2 -0
- data/lib/fear/either_pattern_match.rb +7 -8
- data/lib/fear/failure.rb +0 -9
- data/lib/fear/failure_pattern_match.rb +2 -0
- data/lib/fear/for_api.rb +2 -0
- data/lib/fear/future.rb +12 -20
- data/lib/fear/future_api.rb +13 -2
- data/lib/fear/left.rb +0 -9
- data/lib/fear/none.rb +7 -9
- data/lib/fear/option.rb +5 -1
- data/lib/fear/option_api.rb +2 -0
- data/lib/fear/option_pattern_match.rb +6 -4
- data/lib/fear/partial_function/empty.rb +2 -0
- data/lib/fear/partial_function/guard.rb +4 -4
- data/lib/fear/partial_function/or_else.rb +2 -0
- data/lib/fear/partial_function.rb +9 -8
- data/lib/fear/pattern_match.rb +0 -10
- data/lib/fear/pattern_matching_api.rb +3 -28
- data/lib/fear/promise.rb +3 -9
- data/lib/fear/right.rb +0 -10
- data/lib/fear/right_biased.rb +1 -1
- data/lib/fear/right_pattern_match.rb +2 -0
- data/lib/fear/some.rb +7 -10
- data/lib/fear/struct.rb +3 -14
- data/lib/fear/success.rb +0 -9
- data/lib/fear/success_pattern_match.rb +2 -0
- data/lib/fear/try.rb +6 -2
- data/lib/fear/try_api.rb +2 -0
- data/lib/fear/try_pattern_match.rb +7 -8
- data/lib/fear/utils.rb +0 -3
- data/lib/fear/version.rb +1 -1
- data/lib/fear.rb +8 -42
- data/spec/fear/awaitable_spec.rb +2 -0
- data/spec/fear/either_spec.rb +26 -0
- data/spec/fear/failure_spec.rb +8 -23
- data/spec/fear/for/mixin_spec.rb +15 -0
- data/spec/fear/future_spec.rb +17 -2
- data/spec/fear/guard_spec.rb +110 -0
- data/spec/fear/left_spec.rb +7 -22
- data/spec/fear/none_spec.rb +11 -17
- data/spec/fear/option_spec.rb +15 -1
- data/spec/fear/partial_function/any_spec.rb +25 -0
- data/spec/fear/partial_function_spec.rb +2 -24
- data/spec/fear/pattern_match_spec.rb +0 -34
- data/spec/fear/promise_spec.rb +4 -6
- data/spec/fear/right_spec.rb +0 -22
- data/spec/fear/some_spec.rb +10 -22
- data/spec/fear/success_spec.rb +0 -22
- data/spec/fear/try/mixin_spec.rb +14 -0
- data/spec/fear/try_api_spec.rb +23 -0
- data/spec/struct_spec.rb +1 -33
- metadata +18 -80
- data/examples/pattern_extracting_ruby2.7.rb +0 -15
- data/lib/fear/extractor/anonymous_array_splat_matcher.rb +0 -10
- data/lib/fear/extractor/any_matcher.rb +0 -17
- data/lib/fear/extractor/array_head_matcher.rb +0 -36
- data/lib/fear/extractor/array_matcher.rb +0 -40
- data/lib/fear/extractor/array_splat_matcher.rb +0 -16
- data/lib/fear/extractor/empty_list_matcher.rb +0 -20
- data/lib/fear/extractor/extractor_matcher.rb +0 -44
- data/lib/fear/extractor/grammar.rb +0 -203
- data/lib/fear/extractor/grammar.treetop +0 -129
- data/lib/fear/extractor/identifier_matcher.rb +0 -18
- data/lib/fear/extractor/matcher/and.rb +0 -38
- data/lib/fear/extractor/matcher.rb +0 -53
- data/lib/fear/extractor/named_array_splat_matcher.rb +0 -17
- data/lib/fear/extractor/pattern.rb +0 -58
- data/lib/fear/extractor/typed_identifier_matcher.rb +0 -26
- data/lib/fear/extractor/value_matcher.rb +0 -19
- data/lib/fear/extractor.rb +0 -112
- data/lib/fear/extractor_api.rb +0 -35
- data/spec/fear/extractor/array_matcher_spec.rb +0 -230
- data/spec/fear/extractor/extractor_matcher_spec.rb +0 -153
- data/spec/fear/extractor/grammar_array_spec.rb +0 -25
- data/spec/fear/extractor/identified_matcher_spec.rb +0 -49
- data/spec/fear/extractor/identifier_matcher_spec.rb +0 -68
- data/spec/fear/extractor/pattern_spec.rb +0 -34
- data/spec/fear/extractor/typed_identifier_matcher_spec.rb +0 -64
- data/spec/fear/extractor/value_matcher_number_spec.rb +0 -79
- data/spec/fear/extractor/value_matcher_string_spec.rb +0 -88
- data/spec/fear/extractor/value_matcher_symbol_spec.rb +0 -71
- data/spec/fear/extractor_api_spec.rb +0 -115
- data/spec/fear/extractor_spec.rb +0 -61
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: df922b21ff9dcd7919c83d864ed99d7cc6cdd1618fac27f98f768c8cc921e569
|
4
|
+
data.tar.gz: 600968bd95a016565e58b782a877ea76b73eec925f1d637539b866acea6313fd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 99b517ac8c12f8071cba9324a68b0161ff4d3cc23e9230cf686b766c084500a9e0541f66b174b8bdebf063b1307adae9ac56806895aa5c8fabbad3c6c2b2a666
|
7
|
+
data.tar.gz: 0d91fec31fd44c59dbdb373bc230cd0d227a41b091ce3188fab59424d9ee86e7b7f3256f5b471a477dc96e5028d2bfa263bdc57b45cb6e4de3147dd71ef12d87
|
@@ -0,0 +1,27 @@
|
|
1
|
+
version: 2
|
2
|
+
updates:
|
3
|
+
- package-ecosystem: bundler
|
4
|
+
directory: "/"
|
5
|
+
schedule:
|
6
|
+
interval: daily
|
7
|
+
time: "02:00"
|
8
|
+
open-pull-requests-limit: 10
|
9
|
+
ignore:
|
10
|
+
- dependency-name: dry-matcher
|
11
|
+
versions:
|
12
|
+
- "> 0.8.0"
|
13
|
+
- dependency-name: dry-monads
|
14
|
+
versions:
|
15
|
+
- "> 1.2.0"
|
16
|
+
- dependency-name: irb
|
17
|
+
versions:
|
18
|
+
- "> 1.1.0"
|
19
|
+
- dependency-name: rubocop-rspec
|
20
|
+
versions:
|
21
|
+
- "> 1.34.0"
|
22
|
+
- dependency-name: dry-types
|
23
|
+
versions:
|
24
|
+
- 1.5.0
|
25
|
+
- dependency-name: concurrent-ruby
|
26
|
+
versions:
|
27
|
+
- 1.1.8
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# pulled from repo
|
2
2
|
name: "Rubocop"
|
3
3
|
|
4
|
-
on:
|
4
|
+
on: pull_request
|
5
5
|
|
6
6
|
jobs:
|
7
7
|
rubocop:
|
@@ -18,7 +18,7 @@ jobs:
|
|
18
18
|
- name: Set up Ruby
|
19
19
|
uses: ruby/setup-ruby@v1
|
20
20
|
with:
|
21
|
-
ruby-version:
|
21
|
+
ruby-version: 3.1
|
22
22
|
|
23
23
|
# This step is not necessary if you add the gem to your Gemfile
|
24
24
|
- name: Install Code Scanning integration
|
data/.github/workflows/spec.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
## 2.0.0
|
2
|
+
|
3
|
+
* Added `Fear::Option#present?` and `Fear::Option#empty?` methods ([@Lokideos][])
|
4
|
+
* Start testing against Ruby 3.1.0 ([@bolshakov][])
|
5
|
+
* Minimal supported Ruby version is 2.7.0 ([@bolshakov][])
|
6
|
+
* Make future and promises compatible with Ruby 3.0 method syntax. ([@bolshakov][])
|
7
|
+
* Get rid off autoload in favor of require. Autoload appeared to be not thread-safe. ([@bolshakov][])
|
8
|
+
* All monads are shareable with Ractors ([@bolshakov][])
|
9
|
+
* Drop pattern extraction support (`Fear.xcase`). ([@bolshakov][])
|
10
|
+
* Add top-level factory methods: `Fear::Some`, `Fear::Option`, `Fear::Right`, `Fear::Left` ([@bolshakov][])
|
11
|
+
`Fear::Try`, `Fear::Success`, and `Fear::Failure`. ([@bolshakov][])
|
12
|
+
|
1
13
|
## 1.2.0
|
2
14
|
|
3
15
|
* Implement `Fear::Option#zip` and `Fear::Future#zip` with block argument ([@bolshakov][])
|
@@ -56,3 +68,4 @@
|
|
56
68
|
* Added `Either#or_else` and `Option#or_else`. `Try#or_else` may accept only block ([@bolshakov][])
|
57
69
|
|
58
70
|
[@bolshakov]: https://github.com/bolshakov
|
71
|
+
[@Lokideos]: https://github.com/Lokideos
|
data/Gemfile.lock
CHANGED
@@ -8,45 +8,40 @@ GIT
|
|
8
8
|
PATH
|
9
9
|
remote: .
|
10
10
|
specs:
|
11
|
-
fear (
|
12
|
-
lru_redux
|
13
|
-
treetop
|
11
|
+
fear (2.0.0)
|
14
12
|
|
15
13
|
GEM
|
16
14
|
remote: https://rubygems.org/
|
17
15
|
specs:
|
18
16
|
any (0.1.0)
|
19
|
-
ast (2.4.
|
20
|
-
benchmark-ips (2.
|
21
|
-
concurrent-ruby (1.1.
|
22
|
-
diff-lcs (1.
|
23
|
-
docile (1.3.
|
24
|
-
dry-configurable (0.
|
17
|
+
ast (2.4.2)
|
18
|
+
benchmark-ips (2.10.0)
|
19
|
+
concurrent-ruby (1.1.10)
|
20
|
+
diff-lcs (1.5.0)
|
21
|
+
docile (1.3.4)
|
22
|
+
dry-configurable (0.12.1)
|
25
23
|
concurrent-ruby (~> 1.0)
|
26
|
-
dry-core (~> 0.
|
27
|
-
dry-equalizer (~> 0.2)
|
24
|
+
dry-core (~> 0.5, >= 0.5.0)
|
28
25
|
dry-container (0.7.2)
|
29
26
|
concurrent-ruby (~> 1.0)
|
30
27
|
dry-configurable (~> 0.1, >= 0.1.3)
|
31
|
-
dry-core (0.
|
28
|
+
dry-core (0.5.0)
|
32
29
|
concurrent-ruby (~> 1.0)
|
33
30
|
dry-equalizer (0.3.0)
|
34
31
|
dry-inflector (0.2.0)
|
35
|
-
dry-logic (1.0
|
32
|
+
dry-logic (1.1.0)
|
36
33
|
concurrent-ruby (~> 1.0)
|
37
|
-
dry-core (~> 0.
|
38
|
-
dry-equalizer (~> 0.2)
|
34
|
+
dry-core (~> 0.5, >= 0.5)
|
39
35
|
dry-matcher (0.8.0)
|
40
36
|
dry-core (>= 0.4.7)
|
41
|
-
dry-monads (1.
|
37
|
+
dry-monads (1.3.5)
|
42
38
|
concurrent-ruby (~> 1.0)
|
43
39
|
dry-core (~> 0.4, >= 0.4.4)
|
44
40
|
dry-equalizer
|
45
|
-
dry-types (1.
|
41
|
+
dry-types (1.5.1)
|
46
42
|
concurrent-ruby (~> 1.0)
|
47
43
|
dry-container (~> 0.3)
|
48
|
-
dry-core (~> 0.
|
49
|
-
dry-equalizer (~> 0.3)
|
44
|
+
dry-core (~> 0.5, >= 0.5)
|
50
45
|
dry-inflector (~> 0.1, >= 0.1.2)
|
51
46
|
dry-logic (~> 1.0, >= 1.0.2)
|
52
47
|
fear-rspec (0.3.0)
|
@@ -54,54 +49,56 @@ GEM
|
|
54
49
|
rspec (~> 3.0)
|
55
50
|
irb (1.1.0)
|
56
51
|
reline (>= 0.0.1)
|
57
|
-
|
58
|
-
parallel (1.
|
59
|
-
parser (
|
52
|
+
json (2.6.2)
|
53
|
+
parallel (1.22.1)
|
54
|
+
parser (3.1.2.0)
|
60
55
|
ast (~> 2.4.1)
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
regexp_parser (1.8.2)
|
56
|
+
rainbow (3.1.1)
|
57
|
+
rake (13.0.6)
|
58
|
+
regexp_parser (2.5.0)
|
65
59
|
reline (0.0.7)
|
66
|
-
rexml (3.2.
|
67
|
-
rspec (3.
|
68
|
-
rspec-core (~> 3.
|
69
|
-
rspec-expectations (~> 3.
|
70
|
-
rspec-mocks (~> 3.
|
71
|
-
rspec-core (3.
|
72
|
-
rspec-support (~> 3.
|
73
|
-
rspec-expectations (3.
|
60
|
+
rexml (3.2.5)
|
61
|
+
rspec (3.11.0)
|
62
|
+
rspec-core (~> 3.11.0)
|
63
|
+
rspec-expectations (~> 3.11.0)
|
64
|
+
rspec-mocks (~> 3.11.0)
|
65
|
+
rspec-core (3.11.0)
|
66
|
+
rspec-support (~> 3.11.0)
|
67
|
+
rspec-expectations (3.11.0)
|
74
68
|
diff-lcs (>= 1.2.0, < 2.0)
|
75
|
-
rspec-support (~> 3.
|
76
|
-
rspec-mocks (3.
|
69
|
+
rspec-support (~> 3.11.0)
|
70
|
+
rspec-mocks (3.11.0)
|
77
71
|
diff-lcs (>= 1.2.0, < 2.0)
|
78
|
-
rspec-support (~> 3.
|
79
|
-
rspec-support (3.
|
80
|
-
rubocop (1.
|
72
|
+
rspec-support (~> 3.11.0)
|
73
|
+
rspec-support (3.11.0)
|
74
|
+
rubocop (1.32.0)
|
75
|
+
json (~> 2.3)
|
81
76
|
parallel (~> 1.10)
|
82
|
-
parser (>=
|
77
|
+
parser (>= 3.1.0.0)
|
83
78
|
rainbow (>= 2.2.2, < 4.0)
|
84
|
-
regexp_parser (>= 1.8)
|
85
|
-
rexml
|
86
|
-
rubocop-ast (>=
|
79
|
+
regexp_parser (>= 1.8, < 3.0)
|
80
|
+
rexml (>= 3.2.5, < 4.0)
|
81
|
+
rubocop-ast (>= 1.19.1, < 2.0)
|
87
82
|
ruby-progressbar (~> 1.7)
|
88
|
-
unicode-display_width (>= 1.4.0, <
|
89
|
-
rubocop-ast (1.
|
90
|
-
parser (>=
|
83
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
84
|
+
rubocop-ast (1.19.1)
|
85
|
+
parser (>= 3.1.1.0)
|
91
86
|
rubocop-rspec (1.34.0)
|
92
87
|
rubocop (>= 0.60.0)
|
93
|
-
ruby-progressbar (1.
|
94
|
-
ruby_coding_standard (0.
|
95
|
-
rubocop (~> 1.
|
96
|
-
simplecov (0.
|
88
|
+
ruby-progressbar (1.11.0)
|
89
|
+
ruby_coding_standard (0.4.0)
|
90
|
+
rubocop (~> 1.32.0)
|
91
|
+
simplecov (0.21.2)
|
97
92
|
docile (~> 1.1)
|
98
93
|
simplecov-html (~> 0.11)
|
94
|
+
simplecov_json_formatter (~> 0.1)
|
99
95
|
simplecov-html (0.12.3)
|
100
96
|
simplecov-lcov (0.8.0)
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
yard (0.9.
|
97
|
+
simplecov_json_formatter (0.1.2)
|
98
|
+
unicode-display_width (2.2.0)
|
99
|
+
webrick (1.7.0)
|
100
|
+
yard (0.9.28)
|
101
|
+
webrick (~> 1.7.0)
|
105
102
|
|
106
103
|
PLATFORMS
|
107
104
|
ruby
|
@@ -119,7 +116,7 @@ DEPENDENCIES
|
|
119
116
|
qo!
|
120
117
|
rake (~> 13.0)
|
121
118
|
rspec (~> 3.1)
|
122
|
-
rubocop (= 1.
|
119
|
+
rubocop (= 1.32.0)
|
123
120
|
rubocop-rspec (= 1.34.0)
|
124
121
|
ruby_coding_standard
|
125
122
|
simplecov
|
@@ -127,4 +124,4 @@ DEPENDENCIES
|
|
127
124
|
yard
|
128
125
|
|
129
126
|
BUNDLED WITH
|
130
|
-
2.
|
127
|
+
2.1.4
|
data/README.md
CHANGED
@@ -40,7 +40,7 @@ Represents optional (nullable) values. Instances of `Option` are either an insta
|
|
40
40
|
The most idiomatic way to use an `Option` instance is to treat it as a collection
|
41
41
|
|
42
42
|
```ruby
|
43
|
-
name = Fear.option(params[:name])
|
43
|
+
name = Fear.option(params[:name])
|
44
44
|
upper = name.map(&:strip).select { |n| n.length != 0 }.map(&:upcase)
|
45
45
|
puts upper.get_or_else('')
|
46
46
|
```
|
@@ -51,9 +51,11 @@ having to check for the existence of a value.
|
|
51
51
|
A less-idiomatic way to use `Option` values is via pattern matching
|
52
52
|
|
53
53
|
```ruby
|
54
|
-
Fear.option(params[:name])
|
55
|
-
|
56
|
-
|
54
|
+
case Fear.option(params[:name])
|
55
|
+
in Fear::Some(name)
|
56
|
+
name.strip.upcase
|
57
|
+
in Fear::None
|
58
|
+
'No name value'
|
57
59
|
end
|
58
60
|
```
|
59
61
|
|
@@ -68,17 +70,15 @@ else
|
|
68
70
|
end
|
69
71
|
```
|
70
72
|
|
71
|
-
Alternatively,
|
73
|
+
Alternatively, you can use camel-case factory methods `Fear::Option()`, `Fear::Some()` and `Fear::None` methods:
|
72
74
|
|
73
75
|
```ruby
|
74
|
-
|
76
|
+
Fear::Option(42) #=> #<Fear::Some get=42>
|
77
|
+
Fear::Option(nil) #=> #<Fear::None>
|
75
78
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
Some(42) #=> #<Fear::Some get=42>
|
80
|
-
Some(nil) #=> #<Fear::Some get=nil>
|
81
|
-
None() #=> #<Fear::None>
|
79
|
+
Fear::Some(42) #=> #<Fear::Some get=42>
|
80
|
+
Fear::Some(nil) #=> #<Fear::Some get=nil>
|
81
|
+
Fear::None #=> #<Fear::None>
|
82
82
|
```
|
83
83
|
|
84
84
|
#### Option#get_or_else
|
@@ -156,7 +156,7 @@ Returns self if it is nonempty and applying the predicate to this `Option`'s val
|
|
156
156
|
return `None`.
|
157
157
|
|
158
158
|
```ruby
|
159
|
-
Fear.some(42).select { |v| v > 40 } #=> Fear.
|
159
|
+
Fear.some(42).select { |v| v > 40 } #=> Fear.some(42)
|
160
160
|
Fear.some(42).select { |v| v < 40 } #=> None
|
161
161
|
Fear.none.select { |v| v < 40 } #=> None
|
162
162
|
```
|
@@ -196,6 +196,24 @@ Fear.some(42).empty? #=> false
|
|
196
196
|
Fear.none.empty? #=> true
|
197
197
|
```
|
198
198
|
|
199
|
+
#### Option#blank?
|
200
|
+
|
201
|
+
Returns `true` if the `Option` is `None`, `false` otherwise.
|
202
|
+
|
203
|
+
```ruby
|
204
|
+
Fear.some(42).blank? #=> false
|
205
|
+
Fear.none.blank? #=> true
|
206
|
+
```
|
207
|
+
|
208
|
+
#### Option#present?
|
209
|
+
|
210
|
+
Returns `false` if the `Option` is `None`, `true` otherwise.
|
211
|
+
|
212
|
+
```ruby
|
213
|
+
Fear.some(42).present? #=> true
|
214
|
+
Fear.none.present? #=> false
|
215
|
+
```
|
216
|
+
|
199
217
|
#### Option#zip
|
200
218
|
|
201
219
|
Returns a `Fear::Some` formed from this Option and another Option by combining the corresponding elements in a pair.
|
@@ -228,19 +246,14 @@ dividend = Fear.try { Integer(params[:dividend]) }
|
|
228
246
|
divisor = Fear.try { Integer(params[:divisor]) }
|
229
247
|
problem = dividend.flat_map { |x| divisor.map { |y| x / y } }
|
230
248
|
|
231
|
-
problem
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
m.failure do |exception|
|
241
|
-
puts "You entered something wrong. Try again"
|
242
|
-
puts "Info from the exception: #{exception.message}"
|
243
|
-
end
|
249
|
+
case problem
|
250
|
+
in Fear::Success(result)
|
251
|
+
puts "Result of #{dividend.get} / #{divisor.get} is: #{result}"
|
252
|
+
in Fear::Failure(ZeroDivisionError)
|
253
|
+
puts "Division by zero is not allowed"
|
254
|
+
in Fear::Failure(exception)
|
255
|
+
puts "You entered something wrong. Try again"
|
256
|
+
puts "Info from the exception: #{exception.message}"
|
244
257
|
end
|
245
258
|
```
|
246
259
|
|
@@ -257,13 +270,11 @@ type of default behavior in the case of failure.
|
|
257
270
|
*NOTE*: Only non-fatal exceptions are caught by the combinators on `Try`.
|
258
271
|
Serious system errors, on the other hand, will be thrown.
|
259
272
|
|
260
|
-
Alternatively, include
|
273
|
+
Alternatively, include you can use camel-case factory method `Fear::Try()`:
|
261
274
|
|
262
275
|
```ruby
|
263
|
-
|
264
|
-
|
265
|
-
Try { 4/0 } #=> #<Fear::Failure exception=...>
|
266
|
-
Try { 4/2 } #=> #<Fear::Success value=2>
|
276
|
+
Fear::Try { 4/0 } #=> #<Fear::Failure exception=...>
|
277
|
+
Fear::Try { 4/2 } #=> #<Fear::Success value=2>
|
267
278
|
```
|
268
279
|
|
269
280
|
#### Try#get_or_else
|
@@ -318,7 +329,7 @@ Fear.failure(ArgumentError.new).flat_map { |v| Fear.success(v/2) } #=> Fear.fail
|
|
318
329
|
Returns an `Some` containing the `Success` value or a `None` if this is a `Failure`.
|
319
330
|
|
320
331
|
```ruby
|
321
|
-
Fear.success(42).to_option #=> Fear.some(
|
332
|
+
Fear.success(42).to_option #=> Fear.some(42)
|
322
333
|
Fear.failure(ArgumentError.new).to_option #=> None
|
323
334
|
```
|
324
335
|
|
@@ -379,7 +390,7 @@ Converts this to a `Failure` if the predicate is not satisfied.
|
|
379
390
|
|
380
391
|
```ruby
|
381
392
|
Fear.success(42).select { |v| v > 40 }
|
382
|
-
#=> Fear.success(
|
393
|
+
#=> Fear.success(42)
|
383
394
|
Fear.success(42).select { |v| v < 40 }
|
384
395
|
#=> Fear.failure(Fear::NoSuchElementError.new("Predicate does not hold for 42"))
|
385
396
|
Fear.failure(ArgumentError.new).select { |v| v < 40 }
|
@@ -441,7 +452,7 @@ Fear.success(42).to_either #=> Fear.right(42)
|
|
441
452
|
Fear.failure(ArgumentError.new).to_either #=> Fear.left(ArgumentError.new)
|
442
453
|
```
|
443
454
|
|
444
|
-
### Either ([API Documentation](https://www.rubydoc.info/github/bolshakov/fear/master/Fear/
|
455
|
+
### Either ([API Documentation](https://www.rubydoc.info/github/bolshakov/fear/master/Fear/Either))
|
445
456
|
|
446
457
|
Represents a value of one of two possible types (a disjoint union.)
|
447
458
|
An instance of `Either` is either an instance of `Left` or `Right`.
|
@@ -463,14 +474,11 @@ rescue ArgumentError
|
|
463
474
|
Fear.left(in)
|
464
475
|
end
|
465
476
|
|
466
|
-
result
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
m.left do |x|
|
472
|
-
"You passed me the String: #{x}"
|
473
|
-
end
|
477
|
+
case result
|
478
|
+
in Fear::Right(x)
|
479
|
+
"You passed me the Int: #{x}, which I will increment. #{x} + 1 = #{x+1}"
|
480
|
+
in Fear::Left(x)
|
481
|
+
"You passed me the String: #{x}"
|
474
482
|
end
|
475
483
|
```
|
476
484
|
|
@@ -478,13 +486,11 @@ Either is right-biased, which means that `Right` is assumed to be the default ca
|
|
478
486
|
operate on. If it is `Left`, operations like `#map`, `#flat_map`, ... return the `Left` value
|
479
487
|
unchanged.
|
480
488
|
|
481
|
-
Alternatively,
|
489
|
+
Alternatively, you can use camel-case factory methods `Fear::Left()`, and `Fear::Right()`:
|
482
490
|
|
483
491
|
```ruby
|
484
|
-
|
485
|
-
|
486
|
-
Left(42) #=> #<Fear::Left value=42>
|
487
|
-
Right(42) #=> #<Fear::Right value=42>
|
492
|
+
Fear::Left(42) #=> #<Fear::Left value=42>
|
493
|
+
Fear::Right(42) #=> #<Fear::Right value=42>
|
488
494
|
```
|
489
495
|
|
490
496
|
#### Either#get_or_else
|
@@ -551,7 +557,7 @@ Fear.left('undefined').flat_map { |v| Fear.right(v/2) } #=> Fear.left('undefined
|
|
551
557
|
Returns an `Some` containing the `Right` value or a `None` if this is a `Left`.
|
552
558
|
|
553
559
|
```ruby
|
554
|
-
Fear.right(42).to_option #=> Fear.some(
|
560
|
+
Fear.right(42).to_option #=> Fear.some(42)
|
555
561
|
Fear.left('undefined').to_option #=> Fear::None
|
556
562
|
```
|
557
563
|
|
@@ -963,144 +969,6 @@ matcher.(42) #=> "42 is a number"
|
|
963
969
|
matcher.(10..20) #=> "10..20 is a Range"
|
964
970
|
```
|
965
971
|
|
966
|
-
#### Pattern extraction
|
967
|
-
|
968
|
-
It's possible to use special syntax to match against an object and extract a variable form this object.
|
969
|
-
To perform such extraction, `#xcase` method should be used. The following example should give you a sense
|
970
|
-
how extraction works.
|
971
|
-
|
972
|
-
```ruby
|
973
|
-
matcher = Fear.matcher do |m|
|
974
|
-
m.xcase('[1, *tail]') { |tail:| tail }
|
975
|
-
end
|
976
|
-
```
|
977
|
-
|
978
|
-
It matches only on an array starting from `1` integer, and captures its tail:
|
979
|
-
|
980
|
-
```ruby
|
981
|
-
matcher.([1,2,3]) #=> [2,3]
|
982
|
-
matcher.([2,3]) #=> raises MatchError
|
983
|
-
```
|
984
|
-
|
985
|
-
If you want to match against any value, use `_`
|
986
|
-
|
987
|
-
```ruby
|
988
|
-
matcher = Fear.matcher do |m|
|
989
|
-
m.xcase('[1, _, 3]') { .. }
|
990
|
-
end
|
991
|
-
```
|
992
|
-
|
993
|
-
It matches against `[1, 2, 3]`, `[1, 'foo', 3]`, but not `[1, 2]`. It's also possible to capture several variables
|
994
|
-
at the same time. Tho following example describes an array starting from `1`, and captures second and third elements.
|
995
|
-
|
996
|
-
|
997
|
-
```ruby
|
998
|
-
matcher = Fear.matcher do |m|
|
999
|
-
m.xcase('[1, second, third]') { |second:, third: |.. }
|
1000
|
-
end
|
1001
|
-
```
|
1002
|
-
|
1003
|
-
Matching on deeper structures is possible as well:
|
1004
|
-
|
1005
|
-
```ruby
|
1006
|
-
matcher = Fear.matcher do |m|
|
1007
|
-
m.xcase('[["status", first_status], 4, *tail]') { |first_status:, tail: |.. }
|
1008
|
-
end
|
1009
|
-
```
|
1010
|
-
|
1011
|
-
If you want to capture variable of specific type, there is a type matcher for that case:
|
1012
|
-
|
1013
|
-
```ruby
|
1014
|
-
matcher = Fear.matcher do |m|
|
1015
|
-
m.xcase('[head : String, 2, *]') { |head: | head }
|
1016
|
-
end
|
1017
|
-
matcher.(['foo', 2]) #=> 'foo'
|
1018
|
-
matcher.(['foo', 3]) #=> MatchError
|
1019
|
-
matcher.([1, 2]) #=> MatchError
|
1020
|
-
```
|
1021
|
-
|
1022
|
-
You can extract variables from more complex objects. Fear packed with extractors for monads and `Date` object:
|
1023
|
-
|
1024
|
-
```ruby
|
1025
|
-
Fear.matcher do |m|
|
1026
|
-
m.xcase('Date(year, 2, 29)', ->(year:) { year < 2000 }) do |year:|
|
1027
|
-
"#{year} is a leap year before Millennium"
|
1028
|
-
end
|
1029
|
-
|
1030
|
-
m.xcase('Date(year, 2, 29)') do |year:|
|
1031
|
-
"#{year} is a leap year after Millennium"
|
1032
|
-
end
|
1033
|
-
|
1034
|
-
m.case(Date) do |date|
|
1035
|
-
"#{date.year} is not a leap year"
|
1036
|
-
end
|
1037
|
-
end
|
1038
|
-
```
|
1039
|
-
|
1040
|
-
This matcher extracts values from date object and match against them at the same time
|
1041
|
-
|
1042
|
-
```ruby
|
1043
|
-
matcher.(Date.new(1996,02,29)) #=> "1996 is a leap year before Millennium"
|
1044
|
-
matcher.(Date.new(2004,02,29)) #=> "1996 is a leap year after Millennium"
|
1045
|
-
matcher.(Date.new(2003,01,24)) #=> "2003 is not a leap year"
|
1046
|
-
```
|
1047
|
-
|
1048
|
-
Nothing tricky here. The extractor object takes an object and tries to give back the arguments. It's like
|
1049
|
-
constructor, but instead of construction an object, it deconstructs it.
|
1050
|
-
|
1051
|
-
An argument of an extractor may be also a pattern or even introduce a new variable.
|
1052
|
-
|
1053
|
-
```ruby
|
1054
|
-
matcher = Fear.matcher do |m|
|
1055
|
-
m.xcase('Some([status : Integer, body : String])') do |status:, body:|
|
1056
|
-
"#{body.bytesize} bytes received with code #{status}"
|
1057
|
-
end
|
1058
|
-
end
|
1059
|
-
|
1060
|
-
matcher.(Fear.some([200, 'hello'])) #=> "5 bytes received with code 200"
|
1061
|
-
matcher.(Fear.some(['hello', 200])) #=> MatchError
|
1062
|
-
```
|
1063
|
-
|
1064
|
-
You can provide extractors for you own classes
|
1065
|
-
|
1066
|
-
```ruby
|
1067
|
-
Fear.register_extractor(User, Fear.case(User) { |user| [user.id, user.email] }.lift)
|
1068
|
-
# is the same as
|
1069
|
-
Fear.register_extractor(User, proc do |user|
|
1070
|
-
if user.is_a?(User)
|
1071
|
-
Fear.some([user.id, user.email])
|
1072
|
-
else
|
1073
|
-
Fear.none
|
1074
|
-
end
|
1075
|
-
end)
|
1076
|
-
```
|
1077
|
-
|
1078
|
-
Now extracting user's id and email is possible:
|
1079
|
-
|
1080
|
-
|
1081
|
-
```ruby
|
1082
|
-
Fear.match(user) do |m|
|
1083
|
-
m.xcase('User(id, email)') { |id:, email:| }
|
1084
|
-
end
|
1085
|
-
```
|
1086
|
-
|
1087
|
-
Note, registered extractor should return either array of arguments, or boolean.
|
1088
|
-
|
1089
|
-
#### Extracting struct
|
1090
|
-
|
1091
|
-
There is predefined `Struct` extractor:
|
1092
|
-
|
1093
|
-
```ruby
|
1094
|
-
Envelope = Struct.new(:id, :receiver, :sender, :message)
|
1095
|
-
|
1096
|
-
Fear.matcher do |m|
|
1097
|
-
m.xcase('envelope @ Envelope(id, _, sender, _)') do |id:, sender:, envelope:|
|
1098
|
-
acknowledge(id, sender)
|
1099
|
-
process(acknowledge)
|
1100
|
-
end
|
1101
|
-
end
|
1102
|
-
```
|
1103
|
-
|
1104
972
|
#### How to debug pattern extractors?
|
1105
973
|
|
1106
974
|
You can build pattern manually and ask for failure reason:
|
data/Rakefile
CHANGED
@@ -198,27 +198,6 @@ namespace :perf do
|
|
198
198
|
end
|
199
199
|
end
|
200
200
|
|
201
|
-
task :qo_vs_fear_pattern_extraction do
|
202
|
-
User = Struct.new(:id, :name)
|
203
|
-
user = User.new(42, "Jane")
|
204
|
-
|
205
|
-
Benchmark.ips do |x|
|
206
|
-
x.report("Qo") do
|
207
|
-
Qo.case(user, destructure: true) do |m|
|
208
|
-
m.when(User) { |id, name| [id, name] }
|
209
|
-
end
|
210
|
-
end
|
211
|
-
|
212
|
-
x.report("Fear") do
|
213
|
-
Fear.match(user) do |m|
|
214
|
-
m.xcase("User(id, name)") { |id:, name:| [id, name] }
|
215
|
-
end
|
216
|
-
end
|
217
|
-
|
218
|
-
x.compare!
|
219
|
-
end
|
220
|
-
end
|
221
|
-
|
222
201
|
task :dry_vs_qo_vs_fear_try do
|
223
202
|
module ExhaustivePatternMatch
|
224
203
|
def initialize(*)
|
@@ -4,11 +4,11 @@ require "fear"
|
|
4
4
|
|
5
5
|
User = Struct.new(:id, :name, :admin)
|
6
6
|
|
7
|
-
matcher =
|
8
|
-
|
7
|
+
matcher = proc do |value|
|
8
|
+
case value
|
9
|
+
in User(admin: true, name:)
|
9
10
|
puts "Hi #{name}, you are welcome"
|
10
|
-
|
11
|
-
m.xcase("User(_, _, false)") do
|
11
|
+
in User(admin: false)
|
12
12
|
puts "Only admins are allowed here"
|
13
13
|
end
|
14
14
|
end
|
data/fear.gemspec
CHANGED
@@ -19,9 +19,7 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.executables = spec.files.grep(%r{^bin\/}) { |f| File.basename(f) }
|
20
20
|
spec.test_files = spec.files.grep(%r{^spec\/})
|
21
21
|
spec.require_paths = ["lib"]
|
22
|
-
|
23
|
-
spec.add_runtime_dependency "lru_redux"
|
24
|
-
spec.add_runtime_dependency "treetop"
|
22
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.6.0")
|
25
23
|
|
26
24
|
spec.add_development_dependency "benchmark-ips"
|
27
25
|
spec.add_development_dependency "bundler"
|
@@ -32,7 +30,7 @@ Gem::Specification.new do |spec|
|
|
32
30
|
spec.add_development_dependency "rake", "~> 13.0"
|
33
31
|
spec.add_development_dependency "rspec", "~> 3.1"
|
34
32
|
spec.add_development_dependency "rubocop-rspec", "1.34.0"
|
35
|
-
spec.add_development_dependency "rubocop", "1.
|
33
|
+
spec.add_development_dependency "rubocop", "1.32.0"
|
36
34
|
spec.add_development_dependency "ruby_coding_standard"
|
37
35
|
spec.add_development_dependency "yard"
|
38
36
|
spec.add_development_dependency "dry-types"
|