rresult 0.1.8
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.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.rubocop.yml +14 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +157 -0
- data/lib/result/core_ext/object.rb +14 -0
- data/lib/result/version.rb +5 -0
- data/lib/rresult.rb +133 -0
- data/result.gemspec +25 -0
- metadata +67 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 4a5e5a6db001e80dd33d18496f00c02befb0edce12a780a4dd0e88955e88c551
|
|
4
|
+
data.tar.gz: c99e0b23767416fad14acab2464639c5f9e9975ebeb81a81aab8128d26d09b41
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 6f8f60e83598d99163750914bc2511d4f0a7685f3a3a1bbd151c094a7c542c7253544c08e1d017faf810ca2dabf3058458626050852c3af3b1d572b8be62892d
|
|
7
|
+
data.tar.gz: 034e6cfdb48c1def1adaab8ce44b8cc3bd0ed21f85a1ebb9863048bd28d3db97d49e094f861d7c74cb438b0369260f9f5b85239e838fc1c9b4bf7eb7646b8076
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 Thomas Villa
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# Result-rb
|
|
2
|
+
|
|
3
|
+
A ruby port of the `Rust` result-monad `Result`.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add this line to your application's `Gemfile`:
|
|
8
|
+
````ruby
|
|
9
|
+
gem 'rresult'
|
|
10
|
+
`````
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
The Result can be an `Ok`, to wrap successfull values, or an `Err`, to wrap something that went wrong.
|
|
15
|
+
|
|
16
|
+
An `Err` has a description, this should be a `Symbol`, and a value, which can be any `Object`
|
|
17
|
+
|
|
18
|
+
### Constructors
|
|
19
|
+
|
|
20
|
+
Regular class-constructors:
|
|
21
|
+
````ruby
|
|
22
|
+
ok_result = Result::ok(42)
|
|
23
|
+
|
|
24
|
+
err_result = Result::err(description: :not_the_answer_to_anything, value: 41)
|
|
25
|
+
````
|
|
26
|
+
|
|
27
|
+
`Object` is monkey-patched to provide convenience-constructors
|
|
28
|
+
````ruby
|
|
29
|
+
ok_result = Ok(42)
|
|
30
|
+
|
|
31
|
+
err_result = Err(:not_the_answer_to_anything, 41)
|
|
32
|
+
````
|
|
33
|
+
|
|
34
|
+
### Some inspectors
|
|
35
|
+
|
|
36
|
+
Check if the `Result` is `Ok` or `Err`
|
|
37
|
+
````ruby
|
|
38
|
+
Ok(42).ok? == true
|
|
39
|
+
Ok(42).is_ok == true
|
|
40
|
+
|
|
41
|
+
Err(:not_the_answer_to_anything, 41).err? == true
|
|
42
|
+
Err(:not_the_answer_to_anything, 41).is_err == true
|
|
43
|
+
````
|
|
44
|
+
|
|
45
|
+
Check the wrapped value inside:
|
|
46
|
+
````ruby
|
|
47
|
+
Ok(42).contains? 42 == true
|
|
48
|
+
Ok(42).contains 42 == true
|
|
49
|
+
|
|
50
|
+
Err(:not_the_answer_to_anything, 41).contains_err?(:not_the_answer_to_anything, 42) == true
|
|
51
|
+
Err(:not_the_answer_to_anything, 41).contains_err(:not_the_answer_to_anything, 42) == true
|
|
52
|
+
````
|
|
53
|
+
|
|
54
|
+
### Accessors
|
|
55
|
+
Get the wrapped error elements with `#err_description` and `#err_value`.
|
|
56
|
+
These return `nil` for an `Ok`.
|
|
57
|
+
````ruby
|
|
58
|
+
Ok(42).err_description == nil
|
|
59
|
+
Ok(42).err_value == nil
|
|
60
|
+
Err(:not_the_answer_to_anything, 41).err_description == :not_the_answer_to_anything
|
|
61
|
+
Err(:not_the_answer_to_anything, 41).err_value == 41
|
|
62
|
+
````
|
|
63
|
+
|
|
64
|
+
### Unwrappers
|
|
65
|
+
`#unwrap` return the wrapped value of an `Ok`, but raises a `RuntimeError` for an `Err`
|
|
66
|
+
````ruby
|
|
67
|
+
Ok(42).unwrap == 42
|
|
68
|
+
Err(:wrong_answer, 41).unwrap # raises RuntimeError
|
|
69
|
+
````
|
|
70
|
+
|
|
71
|
+
`#unwrap_err` returns an `Array` with the description and value of an `Err`, but raises a `RuntimeError` for an `Ok`
|
|
72
|
+
````ruby
|
|
73
|
+
Ok(42).unwrap # raises RuntimeError
|
|
74
|
+
Err(:wrong_answer, 41).unwrap_err == [:wrong_answer, 41]
|
|
75
|
+
````
|
|
76
|
+
|
|
77
|
+
`#unwrap_or` unwraps an `Ok` or returns the provided default if `Err`
|
|
78
|
+
````ruby
|
|
79
|
+
Ok(42).unwrap_or(-1) == 42
|
|
80
|
+
Err(:not_the_answer_to_anything, 41).unwrap_or(-1) == -1
|
|
81
|
+
````
|
|
82
|
+
|
|
83
|
+
`#unwrap_or_else` unwraps an `Ok` or returns the provided default if `Err` as result of the block.
|
|
84
|
+
The block takes the error description and the error value
|
|
85
|
+
````ruby
|
|
86
|
+
Ok(42).unwrap_or_else { |descr, val| val / 2 } == 42
|
|
87
|
+
Err(:not_the_answer_to_anything, 41).unwrap_or_else{ |descr, val| val / 2 } == 20
|
|
88
|
+
````
|
|
89
|
+
|
|
90
|
+
### Map
|
|
91
|
+
`#map` allows to apply a block to the wrapped value, if its `Ok`. It does not touch an `Err`
|
|
92
|
+
````ruby
|
|
93
|
+
Ok(42).map { |val| val * 2 } == Ok(84)
|
|
94
|
+
|
|
95
|
+
Err(:not_the_answer_to_anything, 41).map { |val| val * 2 } == Err(:not_the_answer_to_anything, 41)
|
|
96
|
+
````
|
|
97
|
+
|
|
98
|
+
`#map_or` unwraps the `Result` and returns a default value, in case of an `Err`
|
|
99
|
+
````ruby
|
|
100
|
+
Ok(42).map_or(-1) { |val| val * 2 } == 84
|
|
101
|
+
|
|
102
|
+
Err(:not_the_answer_to_anything, 41).map_or(-1) { |val| val * 2 } == -1
|
|
103
|
+
````
|
|
104
|
+
|
|
105
|
+
`#map_or_else` unwraps the `Result` like `#map_or`, but take 2 lambda's to specify the mapping of both `Ok` and `Err`
|
|
106
|
+
````ruby
|
|
107
|
+
map = ->(ok_val) { ok_val * 2 }
|
|
108
|
+
or_else = ->(_desc, err_val) { err_val / 2 }
|
|
109
|
+
|
|
110
|
+
Ok(42).map_or_else(map: map, or_else: or_else) == 84
|
|
111
|
+
Err(:not_the_answer_to_anything, 41).map_or_else(map: map, or_else: or_else) == 20
|
|
112
|
+
````
|
|
113
|
+
|
|
114
|
+
`#map_err` maps an `Err` with a provided block. That block should return an `Array` with the new description and value
|
|
115
|
+
````ruby
|
|
116
|
+
Ok(42).map_err { |descr, err_val| val * 2 } == Ok(84)
|
|
117
|
+
|
|
118
|
+
Err(:not_the_answer_to_anything, 41).map { |val| val * 2 } == Err(:not_the_answer_to_anything, 41)
|
|
119
|
+
````
|
|
120
|
+
|
|
121
|
+
### Logical AND combinator
|
|
122
|
+
|
|
123
|
+
`#and` replaces the value of the `Result` by that of that argument, only if the `Result` is an `Ok`
|
|
124
|
+
````ruby
|
|
125
|
+
some_ok_res = Ok(-1)
|
|
126
|
+
some_err_res = Err(:mistake, 'you did something wrong')
|
|
127
|
+
|
|
128
|
+
Ok(42).and(some_ok_res) == some_ok_res
|
|
129
|
+
Ok(42).and(some_err_res) == some_ok_res
|
|
130
|
+
Err(:not_the_answer_to_anything, 41).and(some_ok_res) == Err(:not_the_answer_to_anything, 41)
|
|
131
|
+
Err(:not_the_answer_to_anything, 41).and(some_err_res) == Err(:not_the_answer_to_anything, 41)
|
|
132
|
+
````
|
|
133
|
+
|
|
134
|
+
`#and_then` applies a logical AND with the result of a block
|
|
135
|
+
````ruby
|
|
136
|
+
Ok(42).and_then { |val| Ok(val * 2) }).to be == Ok(84)
|
|
137
|
+
Ok(42).and_then { |val| Err(:wrong_answer, 41) } == Err(:wrong_answer, 41)
|
|
138
|
+
````
|
|
139
|
+
|
|
140
|
+
### Logical OR combinator
|
|
141
|
+
|
|
142
|
+
`#or` replaces the value of the `Result` by that of that argument, only if the `Result` is an `Err`
|
|
143
|
+
````ruby
|
|
144
|
+
some_ok_res = Ok(-1)
|
|
145
|
+
some_err_res = Err(:mistake, 'you did something wrong')
|
|
146
|
+
|
|
147
|
+
Ok(42).or(some_ok_res) == Ok(42)
|
|
148
|
+
Ok(42).and(some_err_res) == Ok(42)
|
|
149
|
+
Err(:not_the_answer_to_anything, 41).or(some_ok_res) == some_ok_res
|
|
150
|
+
Err(:not_the_answer_to_anything, 41).or(some_err_res) == some_err_res
|
|
151
|
+
````
|
|
152
|
+
|
|
153
|
+
`#or_else` applies a logical OR with the result of a block. The block provides takes the description and value of the `Err`
|
|
154
|
+
````ruby
|
|
155
|
+
Err(:wrong_answer, 41).or_else { |descr, val| Ok(val / 2) }).to be == Ok(20)
|
|
156
|
+
Err(:wrong_answer, 41).or_else { |descr, val| Err(:not_corrected, -1) } == Err(:not_corrected, -1)
|
|
157
|
+
````
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Convenience-constructors for `Result`
|
|
4
|
+
class Object
|
|
5
|
+
# rubocop:disable Naming/MethodName
|
|
6
|
+
def Ok(value)
|
|
7
|
+
Result.ok(value)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def Err(description, value)
|
|
11
|
+
Result.err(description: description, value: value)
|
|
12
|
+
end
|
|
13
|
+
# rubocop:enable Naming/MethodName
|
|
14
|
+
end
|
data/lib/rresult.rb
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Result can hold a success value in an `Ok` or and error in an `Err`
|
|
4
|
+
class Result
|
|
5
|
+
attr_reader :ok, :err_description, :err_value
|
|
6
|
+
|
|
7
|
+
def initialize(ok_val, err_description, err_value, type)
|
|
8
|
+
@ok, @err_description, @err_value, @type = ok_val, err_description, err_value, type
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.ok(value)
|
|
12
|
+
new(value, nil, nil, :ok)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.err(description:, value:)
|
|
16
|
+
raise ArgumentError, 'The error description should be a symbol' unless description.is_a? Symbol
|
|
17
|
+
|
|
18
|
+
new(nil, description, value, :err)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def ok?
|
|
22
|
+
@type == :ok
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def err?
|
|
26
|
+
@type == :err
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def contains?(val)
|
|
30
|
+
@ok == val
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def contains_err?(description, value)
|
|
34
|
+
@err_description == description && @err_value == value
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def map
|
|
38
|
+
return self if err?
|
|
39
|
+
|
|
40
|
+
Result.ok(yield @ok)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def map_or(val)
|
|
44
|
+
err? ? val : yield(@ok)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def map_or_else(map:, or_else:)
|
|
48
|
+
err? ? or_else.call(@err_description, @err_value) : map.call(@ok)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def map_err
|
|
52
|
+
return self if ok?
|
|
53
|
+
|
|
54
|
+
descr, val = yield @err_description, @err_value
|
|
55
|
+
Result.err(description: descr, value: val)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def and(other_res)
|
|
59
|
+
err? ? self : other_res
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def and_then
|
|
63
|
+
return self if err?
|
|
64
|
+
|
|
65
|
+
new_res = yield @ok
|
|
66
|
+
raise "The provided block should return a Result, not a #{new_res.class}" unless new_res.is_a? Result
|
|
67
|
+
|
|
68
|
+
self.and new_res
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def or(other_res)
|
|
72
|
+
ok? ? self : other_res
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def or_else
|
|
76
|
+
return self if ok?
|
|
77
|
+
|
|
78
|
+
new_res = yield @err_description, @err_value
|
|
79
|
+
raise "The provided block should return a Result, not a #{new_res.class}" unless new_res.is_a? Result
|
|
80
|
+
|
|
81
|
+
self.or new_res
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def unwrap_or(ok_value)
|
|
85
|
+
err? ? ok_value : @ok
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def unwrap_or_else
|
|
89
|
+
err? ? yield(@err_description, @err_value) : @ok
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def unwrap
|
|
93
|
+
raise unwrap_runtime_error_msg if err?
|
|
94
|
+
|
|
95
|
+
@ok
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def unwrap_err
|
|
99
|
+
raise unwrap_err_runtime_error_msg if ok?
|
|
100
|
+
|
|
101
|
+
[@err_description, @err_value]
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def ==(other)
|
|
105
|
+
@ok == other.ok && @err_description == other.err_description && @err_value == err_value
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def ===(other)
|
|
109
|
+
self == other
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def inspect
|
|
113
|
+
ok? ? "Result::Ok(#{@ok.inspect})" : "Result::Err(#{@err_description.inspect}, #{@err_value.inspect})"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
private
|
|
117
|
+
|
|
118
|
+
def unwrap_runtime_error_msg
|
|
119
|
+
"\nCan't unwrap a Result::Err\n #{@err_description}\n #{@err_value}"
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def unwrap_err_runtime_error_msg
|
|
123
|
+
"\nCan't unwrap_err a Result::Ok\n #{@ok}"
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
private_class_method :new
|
|
127
|
+
alias is_ok ok?
|
|
128
|
+
alias is_err err?
|
|
129
|
+
alias contains contains?
|
|
130
|
+
alias contains_err contains_err?
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
require_relative './result/core_ext/object'
|
data/result.gemspec
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
|
+
require 'result/version'
|
|
6
|
+
|
|
7
|
+
Gem::Specification.new do |s|
|
|
8
|
+
s.name = 'rresult'
|
|
9
|
+
s.version = Result::VERSION
|
|
10
|
+
s.authors = ['Thomas Villa']
|
|
11
|
+
s.email = ['thomvil87@gmail.com']
|
|
12
|
+
|
|
13
|
+
s.summary = 'A ruby port of the Rust Result enum.'
|
|
14
|
+
s.description = "A ruby port of the Rust Result enum. 🦀\n Documentation will be added later. For now visit https://gitlab/thomvil/result-rb for usage"
|
|
15
|
+
s.homepage = 'http://gitlab.com/thomvil/result-rb'
|
|
16
|
+
s.license = 'MIT'
|
|
17
|
+
|
|
18
|
+
s.files = `git ls-files -z`.split("\x0")
|
|
19
|
+
.reject { |f| f.match?(%r{^(test|spec|features)/}) || f.match?(/\.code-workspace$/) }
|
|
20
|
+
|
|
21
|
+
s.require_paths = ['lib']
|
|
22
|
+
s.required_ruby_version = '>= 3.0'
|
|
23
|
+
|
|
24
|
+
s.add_development_dependency 'rspec', '~> 3.10'
|
|
25
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rresult
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.8
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Thomas Villa
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2021-01-10 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rspec
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '3.10'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '3.10'
|
|
27
|
+
description: "A ruby port of the Rust Result enum. \U0001F980\n Documentation will
|
|
28
|
+
be added later. For now visit https://gitlab/thomvil/result-rb for usage"
|
|
29
|
+
email:
|
|
30
|
+
- thomvil87@gmail.com
|
|
31
|
+
executables: []
|
|
32
|
+
extensions: []
|
|
33
|
+
extra_rdoc_files: []
|
|
34
|
+
files:
|
|
35
|
+
- ".gitignore"
|
|
36
|
+
- ".rubocop.yml"
|
|
37
|
+
- Gemfile
|
|
38
|
+
- LICENSE.txt
|
|
39
|
+
- README.md
|
|
40
|
+
- lib/result/core_ext/object.rb
|
|
41
|
+
- lib/result/version.rb
|
|
42
|
+
- lib/rresult.rb
|
|
43
|
+
- result.gemspec
|
|
44
|
+
homepage: http://gitlab.com/thomvil/result-rb
|
|
45
|
+
licenses:
|
|
46
|
+
- MIT
|
|
47
|
+
metadata: {}
|
|
48
|
+
post_install_message:
|
|
49
|
+
rdoc_options: []
|
|
50
|
+
require_paths:
|
|
51
|
+
- lib
|
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
53
|
+
requirements:
|
|
54
|
+
- - ">="
|
|
55
|
+
- !ruby/object:Gem::Version
|
|
56
|
+
version: '3.0'
|
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
requirements: []
|
|
63
|
+
rubygems_version: 3.2.4
|
|
64
|
+
signing_key:
|
|
65
|
+
specification_version: 4
|
|
66
|
+
summary: A ruby port of the Rust Result enum.
|
|
67
|
+
test_files: []
|