resultr 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +7 -0
- data/.rubocop.yml +8 -0
- data/.travis.yml +7 -0
- data/Gemfile +2 -0
- data/LICENSE +21 -0
- data/README.md +169 -0
- data/Rakefile +12 -0
- data/lib/resultr.rb +16 -0
- data/lib/resultr/expectation_error.rb +5 -0
- data/lib/resultr/result.rb +50 -0
- data/lib/resultr/result_proxy.rb +41 -0
- data/lib/resultr/version.rb +5 -0
- data/resultr.gemspec +30 -0
- metadata +102 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 1651637e66e98a6a7001ee31ec00314f2a64c42ddb1520d20d4f407271b383ab
|
|
4
|
+
data.tar.gz: 4aca80aa9cc55ec641d255a3c05984ecea4f8cdc75a334179a20f813900c9f74
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 1843722ea0e26a7a67c8b693c613e567f269a12fb62a765452bab23e7a54a414753a76886dc539a39d545688d824f06a85e8d1914364248a833aade06104276c
|
|
7
|
+
data.tar.gz: 31ec642597a4a10651b25234b321e1518e184fef7eb43e290f4e23a7462157a0940a9d10d3dd8de857536e2ecfa41da51aa8775d0ad298b4d3b2b07a20936440
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2018 Leonardo Camelo
|
|
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,169 @@
|
|
|
1
|
+
# Resultr
|
|
2
|
+
|
|
3
|
+
[](https://travis-ci.org/leocamelo/resultr)
|
|
4
|
+
[](https://codeclimate.com/github/leocamelo/resultr/maintainability)
|
|
5
|
+
|
|
6
|
+
Ruby beautiful results.
|
|
7
|
+
|
|
8
|
+
Resultr provides a simple interface to work with computation results, highly
|
|
9
|
+
inspired by [Rust results](https://doc.rust-lang.org/std/result) for handling
|
|
10
|
+
errors on function returns.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
Add Resultr to your `Gemfile`:
|
|
15
|
+
|
|
16
|
+
```ruby
|
|
17
|
+
gem 'resultr'
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
and run `bundle install` from your shell.
|
|
21
|
+
|
|
22
|
+
## Getting Started
|
|
23
|
+
|
|
24
|
+
### Good and bad results
|
|
25
|
+
|
|
26
|
+
Resultr works with two kinds of results, good results (`Resultr.ok`) and bad
|
|
27
|
+
results (`Resultr.err`). You can store any kind of data on any kind of result.
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
good_result = Resultr.ok(42)
|
|
31
|
+
# => <Resultr::Result @kind=:ok @value=42>
|
|
32
|
+
|
|
33
|
+
good_result.ok?
|
|
34
|
+
# => true
|
|
35
|
+
|
|
36
|
+
good_result.value
|
|
37
|
+
# => 42
|
|
38
|
+
|
|
39
|
+
bad_result = Resultr.err('foo')
|
|
40
|
+
# => <Resultr::Result @kind=:err @value="foo">
|
|
41
|
+
|
|
42
|
+
bad_result.err?
|
|
43
|
+
# => true
|
|
44
|
+
|
|
45
|
+
bad_result.reason
|
|
46
|
+
# => "foo"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
*`#reason` is an alias of `#value`, but is a common practice
|
|
50
|
+
to use `#value` for good results and `#reason` for bad results.*
|
|
51
|
+
|
|
52
|
+
### Chaining results
|
|
53
|
+
|
|
54
|
+
You can chain results with `#and_then` and `#or_else` methods,
|
|
55
|
+
both will receive a block using result's value / reason and returns
|
|
56
|
+
the block return or itself, depending on result kind.
|
|
57
|
+
|
|
58
|
+
```ruby
|
|
59
|
+
def shout(word)
|
|
60
|
+
if word == 'marco'
|
|
61
|
+
Resultr.ok('polo')
|
|
62
|
+
else
|
|
63
|
+
Resultr.err('unknown word')
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# =========================================
|
|
68
|
+
# Using #and_then to chaining good results.
|
|
69
|
+
# =========================================
|
|
70
|
+
|
|
71
|
+
shout('marco').and_then { |value| "#{value}!" }
|
|
72
|
+
# => "polo!"
|
|
73
|
+
|
|
74
|
+
shout('foo').and_then { |value| "#{value}!" }
|
|
75
|
+
# => <Resultr::Result @kind=:err @value="unknown word">
|
|
76
|
+
|
|
77
|
+
# =======================================
|
|
78
|
+
# Using #or_else to chaining bad results.
|
|
79
|
+
# =======================================
|
|
80
|
+
|
|
81
|
+
shout('marco').or_else { |reason| "#{reason}, try 'marco'" }
|
|
82
|
+
# => <Resultr::Result @kind=:ok @value="polo">
|
|
83
|
+
|
|
84
|
+
shout('foo').or_else { |reason| "#{reason}, try 'marco'" }
|
|
85
|
+
# => "unknown word, try 'marco'"
|
|
86
|
+
|
|
87
|
+
# ================================================
|
|
88
|
+
# Using both to chaining the two kinds of results.
|
|
89
|
+
# ================================================
|
|
90
|
+
|
|
91
|
+
shout('marco').and_then { |v| "#{v}!" }.or_else { |r| "#{r}, try 'marco'" }
|
|
92
|
+
# => "polo!"
|
|
93
|
+
|
|
94
|
+
shout('foo').and_then { |v| "#{v}!" }.or_else { |r| "#{r}, try 'marco'" }
|
|
95
|
+
# => "unknown word, try 'marco'"
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Elegant result branching
|
|
99
|
+
|
|
100
|
+
Resultr provides a sugar syntax for branching results by kind,
|
|
101
|
+
its called `#thus` and works like a flavor of case statement.
|
|
102
|
+
|
|
103
|
+
```ruby
|
|
104
|
+
def get_posts
|
|
105
|
+
response = get('/posts')
|
|
106
|
+
|
|
107
|
+
if response.status == 200
|
|
108
|
+
Resultr.ok(response.body)
|
|
109
|
+
else
|
|
110
|
+
Resultr.err(response.body)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# ==================================
|
|
115
|
+
# Using #thus for branching actions.
|
|
116
|
+
# ==================================
|
|
117
|
+
|
|
118
|
+
get_posts.thus do |result|
|
|
119
|
+
result.ok do |value|
|
|
120
|
+
render json: { posts: value }
|
|
121
|
+
end
|
|
122
|
+
result.err do |reason|
|
|
123
|
+
render json: { error: reason }
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
# => ["{\"posts\":[{\"title\":\"The Free Lunch Is Over\"}]}"]
|
|
127
|
+
|
|
128
|
+
# =====================================
|
|
129
|
+
# Using #thus for branching assignment.
|
|
130
|
+
# =====================================
|
|
131
|
+
|
|
132
|
+
posts = get_posts.thus do |result|
|
|
133
|
+
result.ok # if you omit the block, it returns value
|
|
134
|
+
result.err { |_reason| [{ title: 'The Placeholder Post' }] }
|
|
135
|
+
end
|
|
136
|
+
# => [{ title: "The Free Lunch Is Over" }]
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Raising exceptions for bad results
|
|
140
|
+
|
|
141
|
+
If you don't want to handle possible result errors, you can use
|
|
142
|
+
`#expect!`, that returns the result value when successful or raises
|
|
143
|
+
an exception with the error reason, you can also provides a custom
|
|
144
|
+
message for the exception.
|
|
145
|
+
|
|
146
|
+
```ruby
|
|
147
|
+
def write_on(file, text)
|
|
148
|
+
if File.exist?(file)
|
|
149
|
+
File.open(file, 'w') { |f| f.write(text) }
|
|
150
|
+
Resultr.ok(text)
|
|
151
|
+
else
|
|
152
|
+
Resultr.err('File not found')
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
write_on('diary.txt', 'Dear diary').expect!
|
|
157
|
+
# => "Dear diary"
|
|
158
|
+
|
|
159
|
+
write_on('wrong.txt', 'Dear diary').expect!
|
|
160
|
+
# => Resultr::ExpectationError: File not found
|
|
161
|
+
|
|
162
|
+
write_on('wrong.txt', 'Dear diary').expect!('Failed to write text')
|
|
163
|
+
# => Resultr::ExpectationError: Failed to write text
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## License
|
|
167
|
+
|
|
168
|
+
Resultr is freely distributable under the
|
|
169
|
+
[MIT license](https://github.com/leocamelo/resultr/blob/master/LICENSE)
|
data/Rakefile
ADDED
data/lib/resultr.rb
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'resultr/version'
|
|
4
|
+
require 'resultr/expectation_error'
|
|
5
|
+
require 'resultr/result'
|
|
6
|
+
require 'resultr/result_proxy'
|
|
7
|
+
|
|
8
|
+
module Resultr
|
|
9
|
+
def self.ok(value)
|
|
10
|
+
::Resultr::Result.new(:ok, value)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.err(reason)
|
|
14
|
+
::Resultr::Result.new(:err, reason)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Resultr
|
|
4
|
+
class Result
|
|
5
|
+
attr_reader :value
|
|
6
|
+
alias reason value
|
|
7
|
+
|
|
8
|
+
def initialize(kind, value)
|
|
9
|
+
@kind = kind
|
|
10
|
+
@value = value
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def ok?
|
|
14
|
+
@kind == :ok
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def err?
|
|
18
|
+
@kind == :err
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def and_then
|
|
22
|
+
if ok?
|
|
23
|
+
yield @value
|
|
24
|
+
else
|
|
25
|
+
self
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def or_else
|
|
30
|
+
if err?
|
|
31
|
+
yield @value
|
|
32
|
+
else
|
|
33
|
+
self
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def expect!(message = @value)
|
|
38
|
+
if ok?
|
|
39
|
+
@value
|
|
40
|
+
else
|
|
41
|
+
raise ::Resultr::ExpectationError, message
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def thus
|
|
46
|
+
result_proxy = ::Resultr::ResultProxy.new(self)
|
|
47
|
+
yield result_proxy
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Resultr
|
|
4
|
+
class ResultProxy
|
|
5
|
+
def initialize(result)
|
|
6
|
+
@result = result
|
|
7
|
+
@resolved = false
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def ok
|
|
11
|
+
resolve_result_if :ok? do |value|
|
|
12
|
+
if block_given?
|
|
13
|
+
yield value
|
|
14
|
+
else
|
|
15
|
+
value
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def err
|
|
21
|
+
resolve_result_if :err? do |reason|
|
|
22
|
+
if block_given?
|
|
23
|
+
yield reason
|
|
24
|
+
else
|
|
25
|
+
reason
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def resolve_result_if(condition)
|
|
33
|
+
if @resolved
|
|
34
|
+
@result.value
|
|
35
|
+
elsif @result.send(condition)
|
|
36
|
+
@resolved = true
|
|
37
|
+
yield @result.value
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
data/resultr.gemspec
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
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 'resultr/version'
|
|
6
|
+
|
|
7
|
+
Gem::Specification.new do |spec|
|
|
8
|
+
spec.name = 'resultr'
|
|
9
|
+
spec.version = Resultr::VERSION
|
|
10
|
+
spec.authors = ['@leocamelo']
|
|
11
|
+
spec.email = ['leonardocamelo.nave@gmail.com']
|
|
12
|
+
spec.summary = 'Ruby beatiful results'
|
|
13
|
+
spec.homepage = 'https://github.com/leocamelo/resultr'
|
|
14
|
+
spec.license = 'MIT'
|
|
15
|
+
spec.require_paths = ['lib']
|
|
16
|
+
|
|
17
|
+
spec.description = <<~DESCRIPTION
|
|
18
|
+
Resultr provides a simple interface to work with
|
|
19
|
+
computation results, highly inspired by Rust results
|
|
20
|
+
for handling errors on function returns.
|
|
21
|
+
DESCRIPTION
|
|
22
|
+
|
|
23
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
|
24
|
+
f.match(%r{^(test)/})
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
spec.add_development_dependency 'bundler', '~> 1.16'
|
|
28
|
+
spec.add_development_dependency 'minitest', '~> 5.0'
|
|
29
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
|
30
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: resultr
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- "@leocamelo"
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2018-02-02 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: bundler
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '1.16'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '1.16'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: minitest
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '5.0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '5.0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: rake
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '10.0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '10.0'
|
|
55
|
+
description: |
|
|
56
|
+
Resultr provides a simple interface to work with
|
|
57
|
+
computation results, highly inspired by Rust results
|
|
58
|
+
for handling errors on function returns.
|
|
59
|
+
email:
|
|
60
|
+
- leonardocamelo.nave@gmail.com
|
|
61
|
+
executables: []
|
|
62
|
+
extensions: []
|
|
63
|
+
extra_rdoc_files: []
|
|
64
|
+
files:
|
|
65
|
+
- ".gitignore"
|
|
66
|
+
- ".rubocop.yml"
|
|
67
|
+
- ".travis.yml"
|
|
68
|
+
- Gemfile
|
|
69
|
+
- LICENSE
|
|
70
|
+
- README.md
|
|
71
|
+
- Rakefile
|
|
72
|
+
- lib/resultr.rb
|
|
73
|
+
- lib/resultr/expectation_error.rb
|
|
74
|
+
- lib/resultr/result.rb
|
|
75
|
+
- lib/resultr/result_proxy.rb
|
|
76
|
+
- lib/resultr/version.rb
|
|
77
|
+
- resultr.gemspec
|
|
78
|
+
homepage: https://github.com/leocamelo/resultr
|
|
79
|
+
licenses:
|
|
80
|
+
- MIT
|
|
81
|
+
metadata: {}
|
|
82
|
+
post_install_message:
|
|
83
|
+
rdoc_options: []
|
|
84
|
+
require_paths:
|
|
85
|
+
- lib
|
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
87
|
+
requirements:
|
|
88
|
+
- - ">="
|
|
89
|
+
- !ruby/object:Gem::Version
|
|
90
|
+
version: '0'
|
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - ">="
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '0'
|
|
96
|
+
requirements: []
|
|
97
|
+
rubyforge_project:
|
|
98
|
+
rubygems_version: 2.7.3
|
|
99
|
+
signing_key:
|
|
100
|
+
specification_version: 4
|
|
101
|
+
summary: Ruby beatiful results
|
|
102
|
+
test_files: []
|