fear 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
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