sorbet-result 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -0
- data/Gemfile.lock +14 -12
- data/README.md +17 -17
- data/lib/sorbet-result.rb +8 -5
- data/lib/{t → typed}/failure.rb +7 -8
- data/lib/{t → typed}/nil_payload_error.rb +2 -2
- data/lib/{t → typed}/no_payload_on_failure_error.rb +2 -2
- data/lib/{t → typed}/result.rb +8 -8
- data/lib/{t → typed}/success.rb +7 -8
- data/sorbet/rbi/gems/minitest@5.18.0.rbi +662 -0
- data/sorbet/rbi/gems/{parallel@1.22.1.rbi → parallel@1.23.0.rbi} +75 -79
- data/sorbet/rbi/gems/rbi@0.0.16.rbi +5 -5
- data/sorbet/rbi/gems/{regexp_parser@2.7.0.rbi → regexp_parser@2.8.0.rbi} +617 -449
- data/sorbet/rbi/gems/spoom@1.2.1.rbi +14 -14
- data/sorbet/rbi/gems/tapioca@0.11.5.rbi +6 -6
- data/sorbet/rbi/gems/yard-sorbet@0.8.1.rbi +1 -1
- data/sorbet/rbi/gems/zeitwerk@2.6.7.rbi +966 -0
- data/sorbet/tapioca/require.rb +5 -2
- metadata +28 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 445c79091954105691cf4c5d0f0084ca69ea9bee925bd8c87374b1e93d7d9811
|
4
|
+
data.tar.gz: b5e200826decd0aa68b3efe53c39cedfb34b1d3ac8807eff1af7f4ae89c36e8b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f4a9faca56fe08cc9c8338df268ba3d203ccfcc0ce292a63c7cbdd6e8f1532c86833b56002ef2544659834af6f0b06eb0a799dbc37cadf88a6034ae09a856f6b
|
7
|
+
data.tar.gz: 3fd929e0dc36870f908eb0c2111def18d0dc33dfd90700869cf611ddb362ecb6f0323cb6acdc9d789a07031576851593323eb47ce73bc7a987a2932f82c20010
|
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## [0.2.0] - 2023-04-21
|
10
|
+
|
11
|
+
### Changed
|
12
|
+
|
13
|
+
- *Breaking* Updated all `T::` modules to `Typed::`. This allows the Sorbet project freedom to explore these constants in the future.
|
14
|
+
- Pulled in Zeitwerk for autoloading.
|
15
|
+
|
16
|
+
## [0.1.1] - 2023-04-17
|
17
|
+
|
18
|
+
### Changed
|
19
|
+
|
20
|
+
- Updated `T::Result` to be an abstract class, instead of an interface module.
|
21
|
+
|
22
|
+
### Fixed
|
23
|
+
|
24
|
+
- `bin/console` now requires the correct file.
|
25
|
+
|
9
26
|
## [0.1.0] - 2023-04-17
|
10
27
|
|
11
28
|
### Added
|
data/Gemfile.lock
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
sorbet-result (0.
|
5
|
-
sorbet-runtime
|
4
|
+
sorbet-result (0.2.0)
|
5
|
+
sorbet-runtime (~> 0.5)
|
6
|
+
zeitwerk (~> 2.6)
|
6
7
|
|
7
8
|
GEM
|
8
9
|
remote: https://rubygems.org/
|
@@ -18,7 +19,7 @@ GEM
|
|
18
19
|
json (2.6.3)
|
19
20
|
minitest (5.18.0)
|
20
21
|
netrc (0.11.0)
|
21
|
-
parallel (1.
|
22
|
+
parallel (1.23.0)
|
22
23
|
parser (3.2.2.0)
|
23
24
|
ast (~> 2.4.1)
|
24
25
|
rainbow (3.1.1)
|
@@ -28,7 +29,7 @@ GEM
|
|
28
29
|
parser (>= 2.6.4.0)
|
29
30
|
sorbet-runtime (>= 0.5.9204)
|
30
31
|
unparser
|
31
|
-
regexp_parser (2.
|
32
|
+
regexp_parser (2.8.0)
|
32
33
|
reline (0.3.3)
|
33
34
|
io-console (~> 0.5)
|
34
35
|
rexml (3.2.5)
|
@@ -51,14 +52,14 @@ GEM
|
|
51
52
|
rubocop-sorbet (0.7.0)
|
52
53
|
rubocop (>= 0.90.0)
|
53
54
|
ruby-progressbar (1.13.0)
|
54
|
-
sorbet (0.5.
|
55
|
-
sorbet-static (= 0.5.
|
56
|
-
sorbet-runtime (0.5.
|
57
|
-
sorbet-static (0.5.
|
58
|
-
sorbet-static (0.5.
|
59
|
-
sorbet-static-and-runtime (0.5.
|
60
|
-
sorbet (= 0.5.
|
61
|
-
sorbet-runtime (= 0.5.
|
55
|
+
sorbet (0.5.10792)
|
56
|
+
sorbet-static (= 0.5.10792)
|
57
|
+
sorbet-runtime (0.5.10792)
|
58
|
+
sorbet-static (0.5.10792-universal-darwin-22)
|
59
|
+
sorbet-static (0.5.10792-x86_64-linux)
|
60
|
+
sorbet-static-and-runtime (0.5.10792)
|
61
|
+
sorbet (= 0.5.10792)
|
62
|
+
sorbet-runtime (= 0.5.10792)
|
62
63
|
spoom (1.2.1)
|
63
64
|
sorbet (>= 0.5.10187)
|
64
65
|
sorbet-runtime (>= 0.5.9204)
|
@@ -81,6 +82,7 @@ GEM
|
|
81
82
|
yard-sorbet (0.8.1)
|
82
83
|
sorbet-runtime (>= 0.5)
|
83
84
|
yard (>= 0.9)
|
85
|
+
zeitwerk (2.6.7)
|
84
86
|
|
85
87
|
PLATFORMS
|
86
88
|
arm64-darwin-22
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# Typed::Result, Typed::Success and Typed::Failure
|
2
2
|
|
3
3
|
A simple, strongly-typed monad for modeling results, helping you implement [Railway Oriented Programming concepts](https://blog.logrocket.com/what-is-railway-oriented-programming/) in Ruby. Helps alleviate error-driven development, all the while boosting your confidence with Sorbet static checks.
|
4
4
|
|
@@ -14,50 +14,50 @@ If bundler is not being used to manage dependencies, install the gem by executin
|
|
14
14
|
|
15
15
|
## Usage
|
16
16
|
|
17
|
-
Using a basic Result in your methods is as simple as indicating something could return a `
|
17
|
+
Using a basic Result in your methods is as simple as indicating something could return a `Typed::Result`.
|
18
18
|
|
19
19
|
```ruby
|
20
|
-
sig { params(resource_id: Integer).returns(
|
20
|
+
sig { params(resource_id: Integer).returns(Typed::Result) }
|
21
21
|
def call_api(resource_id)
|
22
22
|
# something bad happened
|
23
|
-
return
|
23
|
+
return Typed::Failure.new
|
24
24
|
|
25
25
|
# something other bad thing happened
|
26
|
-
return
|
26
|
+
return Typed::Failure.new
|
27
27
|
|
28
28
|
# Success!
|
29
|
-
|
29
|
+
Typed::Success.new
|
30
30
|
end
|
31
31
|
```
|
32
32
|
|
33
33
|
Generally, it's nice to have a payload with results, and it's nice to have more information on failures. We can indicate what types these are in our signatures for better static checks. Note that payloads and errors can be _any_ type.
|
34
34
|
|
35
35
|
```ruby
|
36
|
-
sig { params(resource_id: Integer).returns(
|
36
|
+
sig { params(resource_id: Integer).returns(Typed::Result[Float, String]) }
|
37
37
|
def call_api(resource_id)
|
38
38
|
# something bad happened
|
39
|
-
return
|
39
|
+
return Typed::Failure.new(error: "I couldn't do it!")
|
40
40
|
|
41
41
|
# something other bad thing happened
|
42
|
-
return
|
42
|
+
return Typed::Failure.new(error: "I couldn't do it for another reason!")
|
43
43
|
|
44
44
|
# Success!
|
45
|
-
|
45
|
+
Typed::Success.new(payload: 1.12)
|
46
46
|
end
|
47
47
|
```
|
48
48
|
|
49
49
|
*Note:* We use Sorbet's generics for payload and error typing. The generic payload and error types are erased at runtime, so you won't get a type error at runtime if you violate the generic types. These types will help you statically so be sure to run `srb tc` on your project.
|
50
50
|
|
51
|
-
Further, if another part of your program needs the Result, it can depend on _only_ `
|
51
|
+
Further, if another part of your program needs the Result, it can depend on _only_ `Typed::Success`es (or `Typed::Failure`s if you're doing something with those results).
|
52
52
|
|
53
53
|
```ruby
|
54
|
-
sig { params(success_result:
|
54
|
+
sig { params(success_result: Typed::Success).void }
|
55
55
|
def do_something_with_resource(success_result)
|
56
56
|
...
|
57
57
|
end
|
58
58
|
```
|
59
59
|
|
60
|
-
Finally, there are a few methods you can use on both `
|
60
|
+
Finally, there are a few methods you can use on both `Typed::Result` types.
|
61
61
|
|
62
62
|
```ruby
|
63
63
|
result = call_api(1)
|
@@ -99,16 +99,16 @@ This has several downsides; primarily, this required your caller to _know_ what
|
|
99
99
|
Railway Oriented Programming, which comes from the functional programming community, alleviates this by _expecting_ the failure states and making them a part of the normal flow of a method. Most errors are recoverable; at the very least we can message them back to the user in some way. We should inform the caller with a Result that could be either a Success or Failure, and allow them continue or take an "off-ramp" with a failure message. If we embrace this style of programming, our `call_api` method turns into this:
|
100
100
|
|
101
101
|
```ruby
|
102
|
-
sig { params(resource_id: Integer).returns(
|
102
|
+
sig { params(resource_id: Integer).returns(Typed::Result[Float, String]) }
|
103
103
|
def call_api(resource_id)
|
104
104
|
# something bad happened
|
105
|
-
return
|
105
|
+
return Typed::Failure.new(error: "I couldn't do it!")
|
106
106
|
|
107
107
|
# something other bad thing happened
|
108
|
-
return
|
108
|
+
return Typed::Failure.new(error: "I couldn't do it for another reason!")
|
109
109
|
|
110
110
|
# Success!
|
111
|
-
|
111
|
+
Typed::Success.new(payload: 1.12)
|
112
112
|
end
|
113
113
|
```
|
114
114
|
|
data/lib/sorbet-result.rb
CHANGED
@@ -4,8 +4,11 @@
|
|
4
4
|
# frozen_string_literal: true
|
5
5
|
|
6
6
|
require "sorbet-runtime"
|
7
|
-
require "
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
require "zeitwerk"
|
8
|
+
|
9
|
+
# Sorbet-aware namespace to super-charge your projects
|
10
|
+
module Typed; end
|
11
|
+
|
12
|
+
loader = Zeitwerk::Loader.new
|
13
|
+
loader.push_dir("#{__dir__}/typed", namespace: Typed)
|
14
|
+
loader.setup
|
data/lib/{t → typed}/failure.rb
RENAMED
@@ -1,13 +1,11 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
module
|
4
|
+
module Typed
|
5
5
|
# Represents a failed result. Contains error information but no payload.
|
6
|
-
class Failure
|
7
|
-
extend Sig
|
8
|
-
extend Generic
|
9
|
-
|
10
|
-
include Result
|
6
|
+
class Failure < Result
|
7
|
+
extend T::Sig
|
8
|
+
extend T::Generic
|
11
9
|
|
12
10
|
Payload = type_member
|
13
11
|
Error = type_member
|
@@ -18,14 +16,15 @@ module T
|
|
18
16
|
sig { params(error: T.nilable(Error)).void }
|
19
17
|
def initialize(error: nil)
|
20
18
|
@error = error
|
19
|
+
super()
|
21
20
|
end
|
22
21
|
|
23
|
-
sig { override.returns(Boolean) }
|
22
|
+
sig { override.returns(T::Boolean) }
|
24
23
|
def success?
|
25
24
|
false
|
26
25
|
end
|
27
26
|
|
28
|
-
sig { override.returns(Boolean) }
|
27
|
+
sig { override.returns(T::Boolean) }
|
29
28
|
def failure?
|
30
29
|
true
|
31
30
|
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
module
|
4
|
+
module Typed
|
5
5
|
# Error when user attempts to unwrap payload for Success Result without payload.
|
6
6
|
class NilPayloadError < StandardError
|
7
|
-
extend Sig
|
7
|
+
extend T::Sig
|
8
8
|
|
9
9
|
sig { void }
|
10
10
|
def initialize
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
module
|
4
|
+
module Typed
|
5
5
|
# Error when user attempts to access payload from a Failure Result.
|
6
6
|
class NoPayloadOnFailureError < StandardError
|
7
|
-
extend Sig
|
7
|
+
extend T::Sig
|
8
8
|
|
9
9
|
sig { void }
|
10
10
|
def initialize
|
data/lib/{t → typed}/result.rb
RENAMED
@@ -1,22 +1,22 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
module
|
4
|
+
module Typed
|
5
5
|
# A monad representing either a success or a failure. Contains payload and error information as well.
|
6
|
-
|
7
|
-
extend Sig
|
8
|
-
extend Helpers
|
9
|
-
extend Generic
|
6
|
+
class Result
|
7
|
+
extend T::Sig
|
8
|
+
extend T::Helpers
|
9
|
+
extend T::Generic
|
10
10
|
|
11
|
-
|
11
|
+
abstract!
|
12
12
|
|
13
13
|
Payload = type_member
|
14
14
|
Error = type_member
|
15
15
|
|
16
|
-
sig { abstract.returns(Boolean) }
|
16
|
+
sig { abstract.returns(T::Boolean) }
|
17
17
|
def success?; end
|
18
18
|
|
19
|
-
sig { abstract.returns(Boolean) }
|
19
|
+
sig { abstract.returns(T::Boolean) }
|
20
20
|
def failure?; end
|
21
21
|
|
22
22
|
sig { abstract.returns(T.nilable(Payload)) }
|
data/lib/{t → typed}/success.rb
RENAMED
@@ -1,13 +1,11 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
module
|
4
|
+
module Typed
|
5
5
|
# Represents a successful result. Contains a payload and no error information.
|
6
|
-
class Success
|
7
|
-
extend Sig
|
8
|
-
extend Generic
|
9
|
-
|
10
|
-
include Result
|
6
|
+
class Success < Result
|
7
|
+
extend T::Sig
|
8
|
+
extend T::Generic
|
11
9
|
|
12
10
|
Payload = type_member
|
13
11
|
Error = type_member
|
@@ -18,14 +16,15 @@ module T
|
|
18
16
|
sig { params(payload: T.nilable(Payload)).void }
|
19
17
|
def initialize(payload: nil)
|
20
18
|
@payload = payload
|
19
|
+
super()
|
21
20
|
end
|
22
21
|
|
23
|
-
sig { override.returns(Boolean) }
|
22
|
+
sig { override.returns(T::Boolean) }
|
24
23
|
def success?
|
25
24
|
true
|
26
25
|
end
|
27
26
|
|
28
|
-
sig { override.returns(Boolean) }
|
27
|
+
sig { override.returns(T::Boolean) }
|
29
28
|
def failure?
|
30
29
|
false
|
31
30
|
end
|