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.
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"