to-result 0.0.2 → 0.0.3
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 +4 -4
- data/Gemfile +14 -0
- data/Gemfile.lock +30 -0
- data/LICENSE +21 -0
- data/README.md +134 -0
- data/bin/console +15 -0
- data/lib/{to_result.rb → to-result.rb} +15 -1
- data/tests/support/fake_logger.rb +5 -0
- data/tests/to_result_test.rb +68 -0
- data/to-result.gemspec +17 -0
- metadata +11 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 842070fb5be7a4388055318b3956091d4869885c76d745c0dd01ba87cde415e5
|
4
|
+
data.tar.gz: 0a7c4e93685409f3f96479df8482255e501a88db53523c55d2f5f171255f58a3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c16327c7a1420c8c1b711d98ffd59de246a3221c98dd6a3bae2cf3650c731c558ccbb02effccdd25d6f9f99e0097d030d751cf8c1dac40569b97dd95b4845817
|
7
|
+
data.tar.gz: 23d78aac99bc431eb9b47aafe9aca8dde5b95615766a60305ce016638a3c26e74ac632f5c19bf9c913a5ce70a2ba14d187dc3aea99f8c5876155db4ad9cbed34
|
data/Gemfile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source 'https://rubygems.org'
|
4
|
+
|
5
|
+
# Specify your gem's dependencies in pulsarcli.gemspec
|
6
|
+
gemspec
|
7
|
+
|
8
|
+
gem 'dry-monads', '~> 1.4'
|
9
|
+
|
10
|
+
gem 'byebug', '~> 11.1'
|
11
|
+
|
12
|
+
gem 'minitest', '~> 5.16'
|
13
|
+
|
14
|
+
gem 'mocha', '~> 1.15'
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
to-result (0.0.3)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
byebug (11.1.3)
|
10
|
+
concurrent-ruby (1.1.10)
|
11
|
+
dry-core (0.8.1)
|
12
|
+
concurrent-ruby (~> 1.0)
|
13
|
+
dry-monads (1.4.0)
|
14
|
+
concurrent-ruby (~> 1.0)
|
15
|
+
dry-core (~> 0.7)
|
16
|
+
minitest (5.16.3)
|
17
|
+
mocha (1.15.0)
|
18
|
+
|
19
|
+
PLATFORMS
|
20
|
+
ruby
|
21
|
+
|
22
|
+
DEPENDENCIES
|
23
|
+
byebug (~> 11.1)
|
24
|
+
dry-monads (~> 1.4)
|
25
|
+
minitest (~> 5.16)
|
26
|
+
mocha (~> 1.15)
|
27
|
+
to-result!
|
28
|
+
|
29
|
+
BUNDLED WITH
|
30
|
+
2.3.15
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2022 Christian
|
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,134 @@
|
|
1
|
+
|
2
|
+
# ToResult
|
3
|
+
|
4
|
+
ToResult is a wrapper built over `dry-monads` to make the `Do Notation`, `Result` and `Try` concepts more handy and consistent to use, in particular to implement the **Railway Pattern**.
|
5
|
+
|
6
|
+
## Why I created ToResult
|
7
|
+
|
8
|
+
`dry-monads` is full of edge cases that require to write boilerplate code everytime I want a method to return a `Success` or `Failure`, for example:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
def my_method
|
12
|
+
Success(another_method.call)
|
13
|
+
rescue StandardError => e
|
14
|
+
Failure(e)
|
15
|
+
end
|
16
|
+
```
|
17
|
+
|
18
|
+
so I started using `Try`, that makes the code easier to read and faster to write:
|
19
|
+
```ruby
|
20
|
+
def my_method
|
21
|
+
Try do
|
22
|
+
another_method.call
|
23
|
+
end.to_result
|
24
|
+
end
|
25
|
+
```
|
26
|
+
|
27
|
+
but I feel like `to_result` is not really visible at the end of the code and if you forget to write it (as always happens to me) your application blows up.
|
28
|
+
|
29
|
+
But this is not the bigget problem, bear with me.
|
30
|
+
|
31
|
+
One of the biggest problem is that we cannot use the `Do Notation` inside a `Try` block:
|
32
|
+
```ruby
|
33
|
+
# this will return a Failure(Dry::Monads::Do::Halt)
|
34
|
+
def my_method
|
35
|
+
Try do
|
36
|
+
yield Failure('error code')
|
37
|
+
end.to_result
|
38
|
+
end
|
39
|
+
```
|
40
|
+
|
41
|
+
and you cannot even use `yield` and `rescue` in the same method:
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
# this will return a Failure(Dry::Monads::Do::Halt)
|
45
|
+
def my_method
|
46
|
+
yield Failure('error code')
|
47
|
+
rescue StandardError => e
|
48
|
+
# e is an instance of Dry::Monads::Do::Halt
|
49
|
+
Failure(e)
|
50
|
+
end
|
51
|
+
```
|
52
|
+
|
53
|
+
because they will raise a `Dry::Monads::Do::Halt` exception and the original exception will be forever lost if we do not "unbox" the exception with `e.result`.
|
54
|
+
|
55
|
+
## Installation
|
56
|
+
|
57
|
+
To install with bundler:
|
58
|
+
```bash
|
59
|
+
bundle add to-result
|
60
|
+
```
|
61
|
+
or with `gem`:
|
62
|
+
```bash
|
63
|
+
gem install to-result
|
64
|
+
```
|
65
|
+
|
66
|
+
## Usage
|
67
|
+
|
68
|
+
To use it with instances of a class, just include it
|
69
|
+
```ruby
|
70
|
+
require 'to_result'
|
71
|
+
|
72
|
+
class MyClass
|
73
|
+
include ToResultMixin
|
74
|
+
|
75
|
+
def my_method
|
76
|
+
ToResult do
|
77
|
+
whatever_method.call
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
```
|
82
|
+
|
83
|
+
or if you want to use it with Singleton Classes:
|
84
|
+
```ruby
|
85
|
+
require 'to_result'
|
86
|
+
|
87
|
+
class MyClass
|
88
|
+
extend ToResultMixin
|
89
|
+
|
90
|
+
class << self
|
91
|
+
def my_method
|
92
|
+
ToResult do
|
93
|
+
whatever_method.call
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
```
|
99
|
+
|
100
|
+
now you can always use `ToResult` all the time you wanted to use `Success`, `Failure` or `Try` but with a more convenient interface and consistent behaviour.
|
101
|
+
|
102
|
+
Look at this:
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
ToResult { raise StandardError.new('error code') }
|
106
|
+
# returns Failure(StandardError('error code'))
|
107
|
+
|
108
|
+
ToResult { yield Success('hello!') }
|
109
|
+
# returns Success('hello!')
|
110
|
+
|
111
|
+
ToResult { yield Failure('error code') }
|
112
|
+
# returns Failure('error code')
|
113
|
+
|
114
|
+
ToResult { yield Failure(StandardError.new('error code')) }
|
115
|
+
# returns Failure(StandardError('error code'))
|
116
|
+
|
117
|
+
ToResult([YourCustomError]) { yield Failure(YourCustomError.new('error code')) }
|
118
|
+
# returns Failure(YourCustomError('error code'))
|
119
|
+
|
120
|
+
ToResult([ArgumentError]) { yield Failure(YourCustomError.new('error code')) }
|
121
|
+
# raises YourCustomError('error code')
|
122
|
+
```
|
123
|
+
|
124
|
+
## Roadmap
|
125
|
+
I'm already planning to implement some useful features:
|
126
|
+
- [x] write more examples/documentation/tests
|
127
|
+
- [ ] configurable error logging when an exception is catched inside `DoResult`
|
128
|
+
e.g. sending the log to Airbrake or whathever service you are using
|
129
|
+
- [ ] transform/process the catched error
|
130
|
+
- [ ] any other suggestion would be appreciated 😁
|
131
|
+
|
132
|
+
## Authors
|
133
|
+
|
134
|
+
- [@a-chris](https://www.github.com/a-chris)
|
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'to-result'
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
+
# require "pry"
|
12
|
+
# Pry.start
|
13
|
+
|
14
|
+
require 'irb'
|
15
|
+
IRB.start(__FILE__)
|
@@ -3,6 +3,17 @@ require 'dry/monads'
|
|
3
3
|
module ToResultMixin
|
4
4
|
include Dry::Monads[:do, :result, :try]
|
5
5
|
|
6
|
+
Configuration = Struct.new(:on_error)
|
7
|
+
|
8
|
+
@@configuration = Configuration.new(on_error: nil)
|
9
|
+
|
10
|
+
#
|
11
|
+
# Allow to override the @@configuration fields
|
12
|
+
#
|
13
|
+
def self.configure
|
14
|
+
yield @@configuration
|
15
|
+
end
|
16
|
+
|
6
17
|
#
|
7
18
|
# ToResult executes a block of code and returns Success or Failure.
|
8
19
|
# All exceptions inherited from StandardError are catched and
|
@@ -20,8 +31,11 @@ module ToResultMixin
|
|
20
31
|
Proc.new do
|
21
32
|
f.call
|
22
33
|
rescue Dry::Monads::Do::Halt => e
|
23
|
-
|
34
|
+
error = e.result
|
35
|
+
@@configuration.on_error.call(error) if @@configuration.on_error.respond_to?(:call)
|
36
|
+
return error
|
24
37
|
rescue *exceptions => e
|
38
|
+
@@configuration.on_error.call(e) if @@configuration.on_error.respond_to?(:call)
|
25
39
|
raise e
|
26
40
|
end
|
27
41
|
).to_result
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'mocha/minitest'
|
3
|
+
require 'byebug'
|
4
|
+
|
5
|
+
require './lib/to-result'
|
6
|
+
require './tests/support/fake_logger'
|
7
|
+
|
8
|
+
class ToResultTest < Minitest::Test
|
9
|
+
include ToResultMixin
|
10
|
+
|
11
|
+
def setup
|
12
|
+
super
|
13
|
+
@value = 'hello world!'
|
14
|
+
end
|
15
|
+
|
16
|
+
def teardown
|
17
|
+
super
|
18
|
+
|
19
|
+
# reset the configuration after each test
|
20
|
+
ToResultMixin.configure { |c| c = {} }
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_string
|
24
|
+
assert ToResult { @value } == Success(@value)
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_success
|
28
|
+
expected = Success(@value)
|
29
|
+
assert ToResult { expected } == Success(expected)
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_exception
|
33
|
+
expected = StandardError.new(@value)
|
34
|
+
assert ToResult { raise expected } == Failure(expected)
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_exception_included_in_exceptions_list
|
38
|
+
expected = ArgumentError.new(@value)
|
39
|
+
assert ToResult([ArgumentError]) { raise expected } == Failure(expected)
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_exception_not_included_in_exceptions_list
|
43
|
+
expected = NameError.new(@value)
|
44
|
+
assert_raises(NameError) { ToResult([ArgumentError]) { raise expected } }
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_yield_failure
|
48
|
+
expected = Failure(@value)
|
49
|
+
# this will raise a Dry::Monads::Do::Halt exception
|
50
|
+
assert ToResult { yield expected } == expected
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_yield_failure_exception
|
54
|
+
expected = Failure(StandardError.new(@value))
|
55
|
+
# this will raise a Dry::Monads::Do::Halt exception
|
56
|
+
assert ToResult { yield expected } == expected
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_on_error
|
60
|
+
FakeLogger.expects(:log_error).once
|
61
|
+
|
62
|
+
ToResultMixin.configure do |c|
|
63
|
+
c.on_error = Proc.new { FakeLogger.log_error }
|
64
|
+
end
|
65
|
+
|
66
|
+
ToResult { raise StandardError.new(@value) }
|
67
|
+
end
|
68
|
+
end
|
data/to-result.gemspec
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = 'to-result'
|
8
|
+
s.version = '0.0.3'
|
9
|
+
s.summary = 'A wrapper over dry-monads to offer a handy and consistent way to implement the Railway pattern.'
|
10
|
+
s.description = 'A wrapper over dry-monads to offer a handy and consistent way to implement the Railway pattern.'
|
11
|
+
s.authors = ['Christian Toscano']
|
12
|
+
s.homepage = 'https://github.com/a-chris/to-result'
|
13
|
+
s.license = 'MIT'
|
14
|
+
|
15
|
+
s.require_paths = ['lib']
|
16
|
+
s.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR).reject { |f| (f == '.gitignore') || f =~ /^examples/ }
|
17
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: to-result
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Christian Toscano
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-10-08 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: A wrapper over dry-monads to offer a handy and consistent way to implement
|
14
14
|
the Railway pattern.
|
@@ -17,7 +17,15 @@ executables: []
|
|
17
17
|
extensions: []
|
18
18
|
extra_rdoc_files: []
|
19
19
|
files:
|
20
|
-
-
|
20
|
+
- Gemfile
|
21
|
+
- Gemfile.lock
|
22
|
+
- LICENSE
|
23
|
+
- README.md
|
24
|
+
- bin/console
|
25
|
+
- lib/to-result.rb
|
26
|
+
- tests/support/fake_logger.rb
|
27
|
+
- tests/to_result_test.rb
|
28
|
+
- to-result.gemspec
|
21
29
|
homepage: https://github.com/a-chris/to-result
|
22
30
|
licenses:
|
23
31
|
- MIT
|