fear 1.1.0 → 1.2.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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rubocop.yml +39 -0
  3. data/.github/workflows/spec.yml +42 -0
  4. data/.rubocop.yml +1 -1
  5. data/.simplecov +17 -0
  6. data/CHANGELOG.md +12 -3
  7. data/Gemfile +0 -2
  8. data/Gemfile.lock +80 -39
  9. data/README.md +158 -9
  10. data/Rakefile +27 -0
  11. data/examples/pattern_extracting.rb +1 -1
  12. data/examples/pattern_extracting_ruby2.7.rb +15 -0
  13. data/examples/pattern_matching_binary_tree_set.rb +3 -0
  14. data/examples/pattern_matching_number_in_words.rb +2 -0
  15. data/fear.gemspec +7 -8
  16. data/lib/dry/types/fear.rb +8 -0
  17. data/lib/dry/types/fear/option.rb +125 -0
  18. data/lib/fear.rb +9 -0
  19. data/lib/fear/awaitable.rb +2 -2
  20. data/lib/fear/either.rb +5 -0
  21. data/lib/fear/either_pattern_match.rb +3 -0
  22. data/lib/fear/extractor.rb +2 -0
  23. data/lib/fear/extractor/any_matcher.rb +1 -1
  24. data/lib/fear/extractor/array_splat_matcher.rb +1 -1
  25. data/lib/fear/extractor/empty_list_matcher.rb +1 -1
  26. data/lib/fear/extractor/matcher.rb +0 -3
  27. data/lib/fear/extractor/pattern.rb +1 -0
  28. data/lib/fear/extractor/value_matcher.rb +1 -1
  29. data/lib/fear/failure.rb +7 -0
  30. data/lib/fear/future.rb +12 -6
  31. data/lib/fear/future_api.rb +2 -2
  32. data/lib/fear/left.rb +1 -0
  33. data/lib/fear/none.rb +18 -0
  34. data/lib/fear/option.rb +22 -0
  35. data/lib/fear/option_pattern_match.rb +1 -0
  36. data/lib/fear/partial_function.rb +6 -0
  37. data/lib/fear/partial_function/empty.rb +2 -0
  38. data/lib/fear/pattern_matching_api.rb +1 -0
  39. data/lib/fear/right.rb +2 -0
  40. data/lib/fear/some.rb +28 -0
  41. data/lib/fear/struct.rb +13 -0
  42. data/lib/fear/success.rb +6 -0
  43. data/lib/fear/try_pattern_match.rb +3 -0
  44. data/lib/fear/utils.rb +13 -0
  45. data/lib/fear/version.rb +1 -1
  46. data/spec/dry/types/fear/option/constrained_spec.rb +22 -0
  47. data/spec/dry/types/fear/option/core_spec.rb +77 -0
  48. data/spec/dry/types/fear/option/default_spec.rb +21 -0
  49. data/spec/dry/types/fear/option/hash_spec.rb +58 -0
  50. data/spec/dry/types/fear/option/option_spec.rb +97 -0
  51. data/spec/fear/awaitable_spec.rb +17 -0
  52. data/spec/fear/either_pattern_matching_spec.rb +28 -0
  53. data/spec/fear/future_spec.rb +11 -2
  54. data/spec/fear/none_spec.rb +1 -1
  55. data/spec/fear/option_pattern_matching_spec.rb +34 -0
  56. data/spec/fear/option_spec.rb +128 -0
  57. data/spec/fear/partial_function_spec.rb +50 -0
  58. data/spec/fear/pattern_matching_api_spec.rb +31 -0
  59. data/spec/fear/try_pattern_matching_spec.rb +34 -0
  60. data/spec/spec_helper.rb +6 -2
  61. data/spec/struct_pattern_matching_spec.rb +36 -0
  62. data/spec/struct_spec.rb +2 -2
  63. data/spec/support/dry_types.rb +6 -0
  64. metadata +110 -12
  65. data/.travis.yml +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d67c81eee97a98c49a029602c26e6835f8bcf2875971619f173899a8986275ae
4
- data.tar.gz: 345d18874fdfc360f72816757d5cb74acaa4291ee931c38fb19f82ea9cac3e6f
3
+ metadata.gz: 27594ab99bc8a361057d1222bd4e70aa49f5c8535c95f622f338ab17e216cd31
4
+ data.tar.gz: dfbd5982e2da247cdb74edb7a218023195c4806d56af786098e294165bf345ec
5
5
  SHA512:
6
- metadata.gz: 3ae2c56f15d445dc73a56c11e1d035daacabdc4df85e4752fcf8500ce2ae7d3e9dd0aef9ae92a7f1e1a0560ead65e8ac774c4ffe7c6c251fa809fdf9b70cfda3
7
- data.tar.gz: a0458d44ba7e29725f41285a9687f553b7c2227d99399c28e13502505a5b98844ab5ce31f423f20fdd2b1b015c42b56ea2f4b8bc1f3e01672a851f50ffe87b1f
6
+ metadata.gz: 4c880fdc1a916306cd222bb4d63526aa3b9ab19eadb095df1ea2aef4343e443015b8067628a34a0b436dc5d994f39fd8810b03dbb05adc7d6df975eb1943bee7
7
+ data.tar.gz: f9e9c441b5d208b65cbd678ae38412d404879834e1ca3ac6636140aa9b53bbb6c1ddf88b613c52d74cb6b09c481a0983c5a3e903c19ee5f98ca94161eb541ba9
@@ -0,0 +1,39 @@
1
+ # pulled from repo
2
+ name: "Rubocop"
3
+
4
+ on: push
5
+
6
+ jobs:
7
+ rubocop:
8
+ runs-on: ubuntu-latest
9
+ strategy:
10
+ fail-fast: false
11
+
12
+ steps:
13
+ - name: Checkout repository
14
+ uses: actions/checkout@v2
15
+
16
+ # If running on a self-hosted runner, check it meets the requirements
17
+ # listed at https://github.com/ruby/setup-ruby#using-self-hosted-runners
18
+ - name: Set up Ruby
19
+ uses: ruby/setup-ruby@v1
20
+ with:
21
+ ruby-version: 2.6
22
+
23
+ # This step is not necessary if you add the gem to your Gemfile
24
+ - name: Install Code Scanning integration
25
+ run: bundle add code-scanning-rubocop --version 0.4.0 --skip-install
26
+
27
+ - name: Install dependencies
28
+ run: bundle install
29
+
30
+ - name: Rubocop run
31
+ run: |
32
+ bash -c "
33
+ bundle exec rubocop --require code_scanning --format CodeScanning::SarifFormatter -o rubocop.sarif
34
+ [[ $? -ne 2 ]]
35
+ "
36
+ - name: Upload Sarif output
37
+ uses: github/codeql-action/upload-sarif@v1
38
+ with:
39
+ sarif_file: rubocop.sarif
@@ -0,0 +1,42 @@
1
+ name: Spec
2
+
3
+ on: [push]
4
+
5
+ jobs:
6
+ test:
7
+ runs-on: ubuntu-latest
8
+ strategy:
9
+ matrix:
10
+ ruby: [ '2.5.x', '2.6.x', '2.7.x' ]
11
+ name: ${{ matrix.ruby }}
12
+
13
+ steps:
14
+ - uses: actions/checkout@v1
15
+ - name: Set up ruby
16
+ uses: actions/setup-ruby@v1
17
+ with:
18
+ ruby-version: ${{ matrix.ruby }}
19
+ - name: Install dependencies
20
+ run: |
21
+ gem install bundler --force --version=2.0.1
22
+ bundler --version
23
+ bundle install --jobs 4 --retry 3
24
+ - name: Test
25
+ run: bundle exec rspec
26
+ - name: Coveralls
27
+ uses: coverallsapp/github-action@v1.1.2
28
+ with:
29
+ github-token: ${{ secrets.GITHUB_TOKEN }}
30
+ flag-name: ruby-${{ matrix.ruby }}
31
+ parallel: true
32
+
33
+
34
+ finish:
35
+ needs: test
36
+ runs-on: ubuntu-latest
37
+ steps:
38
+ - name: Coveralls Finished
39
+ uses: coverallsapp/github-action@master
40
+ with:
41
+ github-token: ${{ secrets.GITHUB_TOKEN }}
42
+ parallel-finished: true
@@ -2,6 +2,6 @@ inherit_gem:
2
2
  ruby_coding_standard: .rubocop.yml
3
3
 
4
4
  AllCops:
5
- TargetRubyVersion: 2.4
5
+ TargetRubyVersion: 2.7
6
6
  Exclude:
7
7
  - vendor/**/*
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "simplecov-lcov"
4
+
5
+ SimpleCov::Formatter::LcovFormatter.config do |c|
6
+ c.report_with_single_file = true
7
+ c.single_report_path = "coverage/lcov.info"
8
+ end
9
+ SimpleCov.formatters = SimpleCov::Formatter::MultiFormatter.new(
10
+ [
11
+ SimpleCov::Formatter::HTMLFormatter,
12
+ SimpleCov::Formatter::LcovFormatter,
13
+ ],
14
+ )
15
+ SimpleCov.start do
16
+ add_filter "spec/"
17
+ end
@@ -1,8 +1,17 @@
1
+ ## 1.2.0
2
+
3
+ * Implement `Fear::Option#zip` and `Fear::Future#zip` with block argument ([@bolshakov][])
4
+ * Drop ruby 2.4.x support, test against ruby 2.7.x ([@bolshakov][])
5
+ * Implement `Fear::Option#filter_map` ([@bolshakov][])
6
+ * Add dry-types extentions to wrap nullable values. ([@bolshakov][])
7
+ * Implement pattern matching for ruby >= 2.7
8
+ * Deprecate `Fear.xcase`
9
+
1
10
  ## 1.1.0
2
11
 
3
- * Add `Fear::Await.ready` and `Fear::Await.result`.
4
- * Add callback versions with pattern matching `Fear::Future#on_success_match`, `#on_failure_match` and `#on_complete_match`.
5
- * Implement immutable `Fear::Struct`
12
+ * Add `Fear::Await.ready` and `Fear::Await.result`. ([@bolshakov][])
13
+ * Add callback versions with pattern matching `Fear::Future#on_success_match`, `#on_failure_match` and `#on_complete_match`. ([@bolshakov][])
14
+ * Implement immutable `Fear::Struct`. ([@bolshakov][])
6
15
 
7
16
  ## 1.0.0
8
17
 
data/Gemfile CHANGED
@@ -5,7 +5,5 @@ source "https://rubygems.org"
5
5
  # Specify your gem's dependencies in functional.gemspec
6
6
  gemspec
7
7
 
8
- # gem 'codeclimate-test-reporter', group: :test, require: nil
9
-
10
8
  gem "irb"
11
9
  gem "qo", github: "baweaver/qo"
@@ -1,5 +1,5 @@
1
1
  GIT
2
- remote: git://github.com/baweaver/qo.git
2
+ remote: https://github.com/baweaver/qo.git
3
3
  revision: 8951ce899559118eb60321014b43cf4211730bd0
4
4
  specs:
5
5
  qo (0.99.0)
@@ -8,7 +8,7 @@ GIT
8
8
  PATH
9
9
  remote: .
10
10
  specs:
11
- fear (1.1.0)
11
+ fear (1.2.0)
12
12
  lru_redux
13
13
  treetop
14
14
 
@@ -16,56 +16,92 @@ GEM
16
16
  remote: https://rubygems.org/
17
17
  specs:
18
18
  any (0.1.0)
19
- ast (2.4.0)
20
- benchmark-ips (2.7.2)
21
- concurrent-ruby (1.1.5)
22
- diff-lcs (1.3)
23
- dry-core (0.4.7)
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)
24
25
  concurrent-ruby (~> 1.0)
25
- dry-equalizer (0.2.2)
26
- dry-matcher (0.7.0)
26
+ dry-core (~> 0.4, >= 0.4.7)
27
+ dry-equalizer (~> 0.2)
28
+ dry-container (0.7.2)
29
+ concurrent-ruby (~> 1.0)
30
+ dry-configurable (~> 0.1, >= 0.1.3)
31
+ dry-core (0.4.9)
32
+ concurrent-ruby (~> 1.0)
33
+ dry-equalizer (0.3.0)
34
+ dry-inflector (0.2.0)
35
+ dry-logic (1.0.6)
36
+ concurrent-ruby (~> 1.0)
37
+ dry-core (~> 0.2)
38
+ dry-equalizer (~> 0.2)
39
+ dry-matcher (0.8.0)
40
+ dry-core (>= 0.4.7)
27
41
  dry-monads (1.2.0)
28
42
  concurrent-ruby (~> 1.0)
29
43
  dry-core (~> 0.4, >= 0.4.4)
30
44
  dry-equalizer
31
- irb (1.0.0)
32
- jaro_winkler (1.5.3)
45
+ dry-types (1.4.0)
46
+ concurrent-ruby (~> 1.0)
47
+ dry-container (~> 0.3)
48
+ dry-core (~> 0.4, >= 0.4.4)
49
+ dry-equalizer (~> 0.3)
50
+ dry-inflector (~> 0.1, >= 0.1.2)
51
+ dry-logic (~> 1.0, >= 1.0.2)
52
+ fear-rspec (0.3.0)
53
+ fear (>= 1.0.0)
54
+ rspec (~> 3.0)
55
+ irb (1.1.0)
56
+ reline (>= 0.0.1)
33
57
  lru_redux (1.1.0)
34
- parallel (1.17.0)
35
- parser (2.6.3.0)
36
- ast (~> 2.4.0)
58
+ parallel (1.20.0)
59
+ parser (2.7.2.0)
60
+ ast (~> 2.4.1)
37
61
  polyglot (0.3.5)
38
62
  rainbow (3.0.0)
39
- rake (12.3.2)
40
- rspec (3.8.0)
41
- rspec-core (~> 3.8.0)
42
- rspec-expectations (~> 3.8.0)
43
- rspec-mocks (~> 3.8.0)
44
- rspec-core (3.8.0)
45
- rspec-support (~> 3.8.0)
46
- rspec-expectations (3.8.2)
63
+ rake (13.0.1)
64
+ regexp_parser (1.8.2)
65
+ 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)
47
74
  diff-lcs (>= 1.2.0, < 2.0)
48
- rspec-support (~> 3.8.0)
49
- rspec-mocks (3.8.0)
75
+ rspec-support (~> 3.10.0)
76
+ rspec-mocks (3.10.0)
50
77
  diff-lcs (>= 1.2.0, < 2.0)
51
- rspec-support (~> 3.8.0)
52
- rspec-support (3.8.0)
53
- rubocop (0.72.0)
54
- jaro_winkler (~> 1.5.1)
78
+ rspec-support (~> 3.10.0)
79
+ rspec-support (3.10.0)
80
+ rubocop (1.0.0)
55
81
  parallel (~> 1.10)
56
- parser (>= 2.6)
82
+ parser (>= 2.7.1.5)
57
83
  rainbow (>= 2.2.2, < 4.0)
84
+ regexp_parser (>= 1.8)
85
+ rexml
86
+ rubocop-ast (>= 0.6.0)
58
87
  ruby-progressbar (~> 1.7)
59
- unicode-display_width (>= 1.4.0, < 1.7)
60
- rubocop-rspec (1.33.0)
88
+ unicode-display_width (>= 1.4.0, < 2.0)
89
+ rubocop-ast (1.1.1)
90
+ parser (>= 2.7.1.5)
91
+ rubocop-rspec (1.34.0)
61
92
  rubocop (>= 0.60.0)
62
93
  ruby-progressbar (1.10.1)
63
- ruby_coding_standard (0.1.0)
64
- rubocop
65
- treetop (1.6.10)
94
+ ruby_coding_standard (0.3.0)
95
+ rubocop (~> 1.0.0)
96
+ simplecov (0.19.1)
97
+ docile (~> 1.1)
98
+ simplecov-html (~> 0.11)
99
+ simplecov-html (0.12.3)
100
+ simplecov-lcov (0.8.0)
101
+ treetop (1.6.11)
66
102
  polyglot (~> 0.3)
67
- unicode-display_width (1.6.0)
68
- yard (0.9.20)
103
+ unicode-display_width (1.7.0)
104
+ yard (0.9.25)
69
105
 
70
106
  PLATFORMS
71
107
  ruby
@@ -76,14 +112,19 @@ DEPENDENCIES
76
112
  concurrent-ruby
77
113
  dry-matcher
78
114
  dry-monads
115
+ dry-types
79
116
  fear!
117
+ fear-rspec
80
118
  irb
81
119
  qo!
82
- rake (~> 12.3)
120
+ rake (~> 13.0)
83
121
  rspec (~> 3.1)
84
- rubocop-rspec (= 1.33.0)
122
+ rubocop (= 1.0.0)
123
+ rubocop-rspec (= 1.34.0)
85
124
  ruby_coding_standard
125
+ simplecov
126
+ simplecov-lcov
86
127
  yard
87
128
 
88
129
  BUNDLED WITH
89
- 2.0.1
130
+ 2.0.2
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # Fear
2
- [![Build Status](https://travis-ci.org/bolshakov/fear.svg?branch=master)](https://travis-ci.org/bolshakov/fear)
3
2
  [![Gem Version](https://badge.fury.io/rb/fear.svg)](https://badge.fury.io/rb/fear)
3
+ ![Specs](https://github.com/bolshakov/fear/workflows/Spec/badge.svg)
4
+ [![Maintainability](https://api.codeclimate.com/v1/badges/01030620c59e9f40961b/maintainability)](https://codeclimate.com/github/bolshakov/fear/maintainability)
5
+ [![Coverage Status](https://coveralls.io/repos/github/bolshakov/fear/badge.svg?branch=master)](https://coveralls.io/github/bolshakov/fear?branch=master)
4
6
 
5
7
  This gem provides `Option`, `Either`, and `Try` monads implemented an idiomatic way.
6
8
  It is highly inspired by scala's implementation.
@@ -23,11 +25,11 @@ Or install it yourself as:
23
25
 
24
26
  ## Usage
25
27
 
26
- * [Option](#option-documentation)
27
- * [Try](#try-documentation)
28
- * [Either](#either-documentation)
29
- * [Future](#future-documentation)
30
- * [For composition](#for-composition)
28
+ * [Option](#option-api-documentation)
29
+ * [Try](#try-api-documentation)
30
+ * [Either](#either-api-documentation)
31
+ * [Future](#future-api-documentation)
32
+ * [For composition](#for-composition-api-documentation)
31
33
  * [Pattern Matching](#pattern-matching-api-documentation)
32
34
 
33
35
  ### Option ([API Documentation](https://www.rubydoc.info/github/bolshakov/fear/master/Fear/Option))
@@ -153,12 +155,24 @@ Fear.none.any? { |v| v > 10 } #=> false
153
155
  Returns self if it is nonempty and applying the predicate to this `Option`'s value returns `true`. Otherwise,
154
156
  return `None`.
155
157
 
156
- ```ruby
158
+ ```ruby
157
159
  Fear.some(42).select { |v| v > 40 } #=> Fear.success(21)
158
160
  Fear.some(42).select { |v| v < 40 } #=> None
159
161
  Fear.none.select { |v| v < 40 } #=> None
160
162
  ```
161
163
 
164
+ #### Option#filter_map
165
+
166
+ Returns a new `Some` of truthy results (everything except `false` or `nil`) of
167
+ running the block or `None` otherwise.
168
+
169
+ ```ruby
170
+ Fear.some(42).filter_map { |v| v/2 if v.even? } #=> Fear.some(21)
171
+ Fear.some(42).filter_map { |v| v/2 if v.odd? } #=> Fear.none
172
+ Fear.some(42).filter_map { |v| false } #=> Fear.none
173
+ Fear.none.filter_map { |v| v/2 } #=> Fear.none
174
+ ```
175
+
162
176
  #### Option#reject
163
177
 
164
178
  Returns `Some` if applying the predicate to this `Option`'s value returns `false`. Otherwise, return `None`.
@@ -182,6 +196,19 @@ Fear.some(42).empty? #=> false
182
196
  Fear.none.empty? #=> true
183
197
  ```
184
198
 
199
+ #### Option#zip
200
+
201
+ Returns a `Fear::Some` formed from this Option and another Option by combining the corresponding elements in a pair.
202
+ If either of the two options is empty, `Fear::None` is returned.
203
+
204
+ ```ruby
205
+ Fear.some("foo").zip(Fear.some("bar")) #=> Fear.some(["foo", "bar"])
206
+ Fear.some("foo").zip(Fear.some("bar")) { |x, y| x + y } #=> Fear.some("foobar")
207
+ Fear.some("foo").zip(Fear.none) #=> Fear.none
208
+ Fear.none.zip(Fear.some("bar")) #=> Fear.none
209
+
210
+ ```
211
+
185
212
  @see https://github.com/scala/scala/blob/2.11.x/src/library/scala/Option.scala
186
213
 
187
214
 
@@ -625,7 +652,7 @@ Fear.left("flower").join_right #=> Fear.left("flower")
625
652
  Fear.left(Fear.right("flower")).join_right #=> Fear.left(Fear.right("flower"))
626
653
  ```
627
654
 
628
- #### Either#join_right
655
+ #### Either#join_left
629
656
 
630
657
  Joins an `Either` through `Left`. This method requires that the left side of this `Either` is itself an
631
658
  `Either` type. This method, and `join_right`, are analogous to `Option#flatten`
@@ -1171,7 +1198,7 @@ matcher.(Fear.some(40)) #=> 'Nope'
1171
1198
  #### Under the hood
1172
1199
 
1173
1200
  Pattern matcher is a combination of partial functions wrapped into nice DSL. Every partial function
1174
- defined on domain described with guard.
1201
+ defined on domain described with a guard.
1175
1202
 
1176
1203
  ```ruby
1177
1204
  pf = Fear.case(Integer) { |x| x / 2 }
@@ -1233,6 +1260,128 @@ handle.(12) #=> 'bigger than ten'
1233
1260
  handle.('one') #=> 1
1234
1261
  ```
1235
1262
 
1263
+ ### Native pattern-matching
1264
+
1265
+ Starting from ruby 2.7 you can use native pattern matching capabilities:
1266
+
1267
+ ```ruby
1268
+ case Fear.some(42)
1269
+ in Fear::Some(x)
1270
+ x * 2
1271
+ in Fear::None
1272
+ 'none'
1273
+ end #=> 84
1274
+
1275
+ case Fear.some(41)
1276
+ in Fear::Some(x) if x.even?
1277
+ x / 2
1278
+ in Fear::Some(x) if x.odd? && x > 0
1279
+ x * 2
1280
+ in Fear::None
1281
+ 'none'
1282
+ end #=> 82
1283
+
1284
+ case Fear.some(42)
1285
+ in Fear::Some(x) if x.odd?
1286
+ x * 2
1287
+ else
1288
+ 'nothing'
1289
+ end #=> nothing
1290
+ ```
1291
+
1292
+ It's possible to pattern match against Fear::Either and Fear::Try as well:
1293
+
1294
+ ```ruby
1295
+ case either
1296
+ in Fear::Right(Integer | String => x)
1297
+ "integer or string: #{x}"
1298
+ in Fear::Left(String => error_code) if error_code = :not_found
1299
+ 'not found'
1300
+ end
1301
+ ```
1302
+
1303
+ ```ruby
1304
+ case Fear.try { 10 / x }
1305
+ in Fear::Failure(ZeroDivisionError)
1306
+ # ..
1307
+ in Fear::Success(x)
1308
+ # ..
1309
+ end
1310
+ ```
1311
+
1312
+ ### Dry-Types integration
1313
+
1314
+ #### Option
1315
+
1316
+ NOTE: Requires the dry-tyes gem to be loaded.
1317
+
1318
+ Load the `:fear_option` extension in your application.
1319
+
1320
+ ```ruby
1321
+ require 'dry-types'
1322
+ require 'dry/types/fear'
1323
+
1324
+ Dry::Types.load_extensions(:fear_option)
1325
+
1326
+ module Types
1327
+ include Dry.Types()
1328
+ end
1329
+ ```
1330
+
1331
+ Append .option to a Type to return a `Fear::Option` object:
1332
+
1333
+ ```ruby
1334
+ Types::Option::Strict::Integer[nil]
1335
+ #=> Fear.none
1336
+ Types::Option::Coercible::String[nil]
1337
+ #=> Fear.none
1338
+ Types::Option::Strict::Integer[123]
1339
+ #=> Fear.some(123)
1340
+ Types::Option::Strict::String[123]
1341
+ #=> Fear.some(123)
1342
+ Types::Option::Coercible::Float['12.3']
1343
+ #=> Fear.some(12.3)
1344
+ ```
1345
+
1346
+ 'Option' types can also accessed by calling '.option' on a regular type:
1347
+
1348
+ ```ruby
1349
+ Types::Strict::Integer.option # equivalent to Types::Option::Strict::Integer
1350
+ ```
1351
+
1352
+
1353
+ You can define your own optional types:
1354
+
1355
+ ```ruby
1356
+ option_string = Types::Strict::String.option
1357
+ option_string[nil]
1358
+ # => Fear.none
1359
+ option_string[nil].map(&:upcase)
1360
+ # => Fear.none
1361
+ option_string['something']
1362
+ # => Fear.some('something')
1363
+ option_string['something'].map(&:upcase)
1364
+ # => Fear.some('SOMETHING')
1365
+ option_string['something'].map(&:upcase).get_or_else { 'NOTHING' }
1366
+ # => "SOMETHING"
1367
+ ```
1368
+
1369
+ You can use it with dry-struct as well:
1370
+
1371
+ ```ruby
1372
+ class User < Dry::Struct
1373
+ attribute :name, Types::Coercible::String
1374
+ attribute :age, Types::Coercible::Integer.option
1375
+ end
1376
+
1377
+ user = User.new(name: 'Bob', age: nil)
1378
+ user.name #=> "Bob"
1379
+ user.age #=> Fear.none
1380
+
1381
+ user = User.new(name: 'Bob', age: 42)
1382
+ user.age #=> Fear.some(42)
1383
+ ```
1384
+
1236
1385
  ## Testing
1237
1386
 
1238
1387
  To simplify testing, you may use [fear-rspec](https://github.com/bolshakov/fear-rspec) gem. It