fear 1.2.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +27 -0
  3. data/.github/workflows/rubocop.yml +2 -2
  4. data/.github/workflows/spec.yml +1 -1
  5. data/CHANGELOG.md +13 -0
  6. data/Gemfile.lock +53 -56
  7. data/README.md +54 -186
  8. data/Rakefile +0 -21
  9. data/examples/pattern_extracting.rb +4 -4
  10. data/fear.gemspec +2 -4
  11. data/lib/fear/either.rb +8 -4
  12. data/lib/fear/either_api.rb +2 -0
  13. data/lib/fear/either_pattern_match.rb +7 -8
  14. data/lib/fear/failure.rb +0 -9
  15. data/lib/fear/failure_pattern_match.rb +2 -0
  16. data/lib/fear/for_api.rb +2 -0
  17. data/lib/fear/future.rb +12 -20
  18. data/lib/fear/future_api.rb +13 -2
  19. data/lib/fear/left.rb +0 -9
  20. data/lib/fear/none.rb +7 -9
  21. data/lib/fear/option.rb +5 -1
  22. data/lib/fear/option_api.rb +2 -0
  23. data/lib/fear/option_pattern_match.rb +6 -4
  24. data/lib/fear/partial_function/empty.rb +2 -0
  25. data/lib/fear/partial_function/guard.rb +4 -4
  26. data/lib/fear/partial_function/or_else.rb +2 -0
  27. data/lib/fear/partial_function.rb +9 -8
  28. data/lib/fear/pattern_match.rb +0 -10
  29. data/lib/fear/pattern_matching_api.rb +3 -28
  30. data/lib/fear/promise.rb +3 -9
  31. data/lib/fear/right.rb +0 -10
  32. data/lib/fear/right_biased.rb +1 -1
  33. data/lib/fear/right_pattern_match.rb +2 -0
  34. data/lib/fear/some.rb +7 -10
  35. data/lib/fear/struct.rb +3 -14
  36. data/lib/fear/success.rb +0 -9
  37. data/lib/fear/success_pattern_match.rb +2 -0
  38. data/lib/fear/try.rb +6 -2
  39. data/lib/fear/try_api.rb +2 -0
  40. data/lib/fear/try_pattern_match.rb +7 -8
  41. data/lib/fear/utils.rb +0 -3
  42. data/lib/fear/version.rb +1 -1
  43. data/lib/fear.rb +8 -42
  44. data/spec/fear/awaitable_spec.rb +2 -0
  45. data/spec/fear/either_spec.rb +26 -0
  46. data/spec/fear/failure_spec.rb +8 -23
  47. data/spec/fear/for/mixin_spec.rb +15 -0
  48. data/spec/fear/future_spec.rb +17 -2
  49. data/spec/fear/guard_spec.rb +110 -0
  50. data/spec/fear/left_spec.rb +7 -22
  51. data/spec/fear/none_spec.rb +11 -17
  52. data/spec/fear/option_spec.rb +15 -1
  53. data/spec/fear/partial_function/any_spec.rb +25 -0
  54. data/spec/fear/partial_function_spec.rb +2 -24
  55. data/spec/fear/pattern_match_spec.rb +0 -34
  56. data/spec/fear/promise_spec.rb +4 -6
  57. data/spec/fear/right_spec.rb +0 -22
  58. data/spec/fear/some_spec.rb +10 -22
  59. data/spec/fear/success_spec.rb +0 -22
  60. data/spec/fear/try/mixin_spec.rb +14 -0
  61. data/spec/fear/try_api_spec.rb +23 -0
  62. data/spec/struct_spec.rb +1 -33
  63. metadata +18 -80
  64. data/examples/pattern_extracting_ruby2.7.rb +0 -15
  65. data/lib/fear/extractor/anonymous_array_splat_matcher.rb +0 -10
  66. data/lib/fear/extractor/any_matcher.rb +0 -17
  67. data/lib/fear/extractor/array_head_matcher.rb +0 -36
  68. data/lib/fear/extractor/array_matcher.rb +0 -40
  69. data/lib/fear/extractor/array_splat_matcher.rb +0 -16
  70. data/lib/fear/extractor/empty_list_matcher.rb +0 -20
  71. data/lib/fear/extractor/extractor_matcher.rb +0 -44
  72. data/lib/fear/extractor/grammar.rb +0 -203
  73. data/lib/fear/extractor/grammar.treetop +0 -129
  74. data/lib/fear/extractor/identifier_matcher.rb +0 -18
  75. data/lib/fear/extractor/matcher/and.rb +0 -38
  76. data/lib/fear/extractor/matcher.rb +0 -53
  77. data/lib/fear/extractor/named_array_splat_matcher.rb +0 -17
  78. data/lib/fear/extractor/pattern.rb +0 -58
  79. data/lib/fear/extractor/typed_identifier_matcher.rb +0 -26
  80. data/lib/fear/extractor/value_matcher.rb +0 -19
  81. data/lib/fear/extractor.rb +0 -112
  82. data/lib/fear/extractor_api.rb +0 -35
  83. data/spec/fear/extractor/array_matcher_spec.rb +0 -230
  84. data/spec/fear/extractor/extractor_matcher_spec.rb +0 -153
  85. data/spec/fear/extractor/grammar_array_spec.rb +0 -25
  86. data/spec/fear/extractor/identified_matcher_spec.rb +0 -49
  87. data/spec/fear/extractor/identifier_matcher_spec.rb +0 -68
  88. data/spec/fear/extractor/pattern_spec.rb +0 -34
  89. data/spec/fear/extractor/typed_identifier_matcher_spec.rb +0 -64
  90. data/spec/fear/extractor/value_matcher_number_spec.rb +0 -79
  91. data/spec/fear/extractor/value_matcher_string_spec.rb +0 -88
  92. data/spec/fear/extractor/value_matcher_symbol_spec.rb +0 -71
  93. data/spec/fear/extractor_api_spec.rb +0 -115
  94. data/spec/fear/extractor_spec.rb +0 -61
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 27594ab99bc8a361057d1222bd4e70aa49f5c8535c95f622f338ab17e216cd31
4
- data.tar.gz: dfbd5982e2da247cdb74edb7a218023195c4806d56af786098e294165bf345ec
3
+ metadata.gz: df922b21ff9dcd7919c83d864ed99d7cc6cdd1618fac27f98f768c8cc921e569
4
+ data.tar.gz: 600968bd95a016565e58b782a877ea76b73eec925f1d637539b866acea6313fd
5
5
  SHA512:
6
- metadata.gz: 4c880fdc1a916306cd222bb4d63526aa3b9ab19eadb095df1ea2aef4343e443015b8067628a34a0b436dc5d994f39fd8810b03dbb05adc7d6df975eb1943bee7
7
- data.tar.gz: f9e9c441b5d208b65cbd678ae38412d404879834e1ca3ac6636140aa9b53bbb6c1ddf88b613c52d74cb6b09c481a0983c5a3e903c19ee5f98ca94161eb541ba9
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: push
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: 2.6
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
@@ -7,7 +7,7 @@ jobs:
7
7
  runs-on: ubuntu-latest
8
8
  strategy:
9
9
  matrix:
10
- ruby: [ '2.5.x', '2.6.x', '2.7.x' ]
10
+ ruby: ['2.7.x' , '3.0.x', '3.1.x']
11
11
  name: ${{ matrix.ruby }}
12
12
 
13
13
  steps:
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 (1.2.0)
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.1)
20
- benchmark-ips (2.8.3)
21
- concurrent-ruby (1.1.7)
22
- diff-lcs (1.4.4)
23
- docile (1.3.2)
24
- dry-configurable (0.11.6)
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.4, >= 0.4.7)
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.4.9)
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.6)
32
+ dry-logic (1.1.0)
36
33
  concurrent-ruby (~> 1.0)
37
- dry-core (~> 0.2)
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.2.0)
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.4.0)
41
+ dry-types (1.5.1)
46
42
  concurrent-ruby (~> 1.0)
47
43
  dry-container (~> 0.3)
48
- dry-core (~> 0.4, >= 0.4.4)
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
- lru_redux (1.1.0)
58
- parallel (1.20.0)
59
- parser (2.7.2.0)
52
+ json (2.6.2)
53
+ parallel (1.22.1)
54
+ parser (3.1.2.0)
60
55
  ast (~> 2.4.1)
61
- polyglot (0.3.5)
62
- rainbow (3.0.0)
63
- rake (13.0.1)
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.4)
67
- rspec (3.10.0)
68
- rspec-core (~> 3.10.0)
69
- rspec-expectations (~> 3.10.0)
70
- rspec-mocks (~> 3.10.0)
71
- rspec-core (3.10.0)
72
- rspec-support (~> 3.10.0)
73
- rspec-expectations (3.10.0)
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.10.0)
76
- rspec-mocks (3.10.0)
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.10.0)
79
- rspec-support (3.10.0)
80
- rubocop (1.0.0)
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 (>= 2.7.1.5)
77
+ parser (>= 3.1.0.0)
83
78
  rainbow (>= 2.2.2, < 4.0)
84
- regexp_parser (>= 1.8)
85
- rexml
86
- rubocop-ast (>= 0.6.0)
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, < 2.0)
89
- rubocop-ast (1.1.1)
90
- parser (>= 2.7.1.5)
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.10.1)
94
- ruby_coding_standard (0.3.0)
95
- rubocop (~> 1.0.0)
96
- simplecov (0.19.1)
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
- treetop (1.6.11)
102
- polyglot (~> 0.3)
103
- unicode-display_width (1.7.0)
104
- yard (0.9.25)
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.0.0)
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.0.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]).match do |m|
55
- m.some { |name| name.strip.upcase }
56
- m.none { 'No name value' }
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, include `Fear::Option::Mixin` to use `Option()`, `Some()` and `None()` methods:
73
+ Alternatively, you can use camel-case factory methods `Fear::Option()`, `Fear::Some()` and `Fear::None` methods:
72
74
 
73
75
  ```ruby
74
- include Fear::Option::Mixin
76
+ Fear::Option(42) #=> #<Fear::Some get=42>
77
+ Fear::Option(nil) #=> #<Fear::None>
75
78
 
76
- Option(42) #=> #<Fear::Some get=42>
77
- Option(nil) #=> #<Fear::None>
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.success(21)
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.match |m|
232
- m.success do |result|
233
- puts "Result of #{dividend.get} / #{divisor.get} is: #{result}"
234
- end
235
-
236
- m.failure(ZeroDivisionError) do
237
- puts "Division by zero is not allowed"
238
- end
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 `Fear::Try::Mixin` to use `Try()` method:
273
+ Alternatively, include you can use camel-case factory method `Fear::Try()`:
261
274
 
262
275
  ```ruby
263
- include Fear::Try::Mixin
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(21)
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(21)
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/Option))
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.match do |m|
467
- m.right do |x|
468
- "You passed me the Int: #{x}, which I will increment. #{x} + 1 = #{x+1}"
469
- end
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, include `Fear::Either::Mixin` to use `Left()`, and `Right()` methods:
489
+ Alternatively, you can use camel-case factory methods `Fear::Left()`, and `Fear::Right()`:
482
490
 
483
491
  ```ruby
484
- include Fear::Either::Mixin
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(21)
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 = Fear.matcher do |m|
8
- m.xcase("User(_, name, true)") do |name:|
7
+ matcher = proc do |value|
8
+ case value
9
+ in User(admin: true, name:)
9
10
  puts "Hi #{name}, you are welcome"
10
- end
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.0.0"
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"