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 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
@@ -0,0 +1,5 @@
1
+ .DS_Store
2
+ .bundle
3
+ Gemfile.lock
4
+ *.gem
5
+ vendor
data/.rubocop.yml ADDED
@@ -0,0 +1,14 @@
1
+ AllCops:
2
+ NewCops:
3
+ enable
4
+ TargetRubyVersion: 3
5
+
6
+ Layout/IndentationWidth:
7
+ Width: 4
8
+ Metrics/BlockLength:
9
+ Exclude:
10
+ - spec/result_spec.rb
11
+ Style/ExpandPathArguments:
12
+ Enabled: false
13
+ Style/ParallelAssignment:
14
+ Enabled: false
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec
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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Result
4
+ VERSION = '0.1.8'
5
+ 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: []