mona-result 0.1.2 → 0.3.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 +4 -4
- data/CHANGELOG.md +16 -0
- data/Gemfile.lock +4 -4
- data/README.md +9 -2
- data/Steepfile +1 -4
- data/lib/mona/dict_result/err.rb +14 -0
- data/lib/mona/dict_result/ok.rb +22 -0
- data/lib/mona/dict_result/sequence.rb +33 -0
- data/lib/mona/dict_result.rb +33 -0
- data/lib/mona/err.rb +28 -0
- data/lib/mona/no_match_error.rb +14 -0
- data/lib/mona/ok.rb +24 -0
- data/lib/mona/result.rb +29 -21
- data/lib/mona/result_action.rb +88 -0
- data/lib/mona/result_match.rb +53 -0
- data/lib/mona.rb +31 -0
- data/sig/mona.rbs +141 -0
- metadata +14 -12
- data/lib/mona/result/action.rb +0 -84
- data/lib/mona/result/dict.rb +0 -57
- data/lib/mona/result/err.rb +0 -48
- data/lib/mona/result/error.rb +0 -18
- data/lib/mona/result/match.rb +0 -53
- data/lib/mona/result/ok.rb +0 -42
- data/lib/mona/result/sequence.rb +0 -33
- data/lib/mona/result/version.rb +0 -7
- data/sig/mona/result.rbs +0 -177
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b53dbabf6036e587ba320df3ffde82737a8d692a354d38ea2093d5c1046eedd8
|
4
|
+
data.tar.gz: ffd6a0f736e3407fb4c1cbab320572b399bc4091de6c90644520695162d382d7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cd2b54c044638818a3359239c43adeb631b19254bf94153a99127008c58774d2813ce4aa1363dc92aa107a269585d8fee1739ae98ff53643afdbd446fa5c0549
|
7
|
+
data.tar.gz: d9947684fd66ee1394811c1bf2160215699dd51650ed0b91fd04f22415155fd1af48e85ed35a9a68bfc4283a258172fcd0689929613f574a30afb28e0081703f
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
## [0.3.0] - 2022-08-25
|
2
|
+
|
3
|
+
- Bring utility methods into Mona namespace
|
4
|
+
- Reorganisation to make Result and DictResult interface modules
|
5
|
+
|
6
|
+
## [0.2.0] - 2022-08-23
|
7
|
+
|
8
|
+
- Adds Mona::Resultable module which implements the Result interface
|
9
|
+
- Simplify and improve RBS
|
10
|
+
- Steep checks tests in a stricter fashion
|
11
|
+
- Improve docs, tests, and rubocop
|
12
|
+
|
13
|
+
## [0.1.3] - 2022-08-09
|
14
|
+
|
15
|
+
- Fix CHANGELOG rubygems link
|
16
|
+
|
1
17
|
## [0.1.2] - 2022-08-09
|
2
18
|
|
3
19
|
- Fix gemspec links
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
mona-result (0.
|
4
|
+
mona-result (0.3.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
@@ -21,7 +21,7 @@ GEM
|
|
21
21
|
listen (3.7.1)
|
22
22
|
rb-fsevent (~> 0.10, >= 0.10.3)
|
23
23
|
rb-inotify (~> 0.9, >= 0.9.10)
|
24
|
-
minitest (5.16.
|
24
|
+
minitest (5.16.3)
|
25
25
|
parallel (1.22.1)
|
26
26
|
parser (3.1.2.1)
|
27
27
|
ast (~> 2.4.1)
|
@@ -33,14 +33,14 @@ GEM
|
|
33
33
|
rbs (2.6.0)
|
34
34
|
regexp_parser (2.5.0)
|
35
35
|
rexml (3.2.5)
|
36
|
-
rubocop (1.
|
36
|
+
rubocop (1.35.1)
|
37
37
|
json (~> 2.3)
|
38
38
|
parallel (~> 1.10)
|
39
39
|
parser (>= 3.1.2.1)
|
40
40
|
rainbow (>= 2.2.2, < 4.0)
|
41
41
|
regexp_parser (>= 1.8, < 3.0)
|
42
42
|
rexml (>= 3.2.5, < 4.0)
|
43
|
-
rubocop-ast (>= 1.20.
|
43
|
+
rubocop-ast (>= 1.20.1, < 2.0)
|
44
44
|
ruby-progressbar (~> 1.7)
|
45
45
|
unicode-display_width (>= 1.4.0, < 3.0)
|
46
46
|
rubocop-ast (1.21.0)
|
data/README.md
CHANGED
@@ -24,7 +24,10 @@ success = Result.ok(1) # => #<OK 1>
|
|
24
24
|
success.ok? # => true
|
25
25
|
success.err? # => false
|
26
26
|
success.value # => 1
|
27
|
-
|
27
|
+
|
28
|
+
success.and_tap { puts "it is #{_1}" }
|
29
|
+
.and_then { _1 * 5 }
|
30
|
+
.value_or { :nope }
|
28
31
|
# OUT: it is 1
|
29
32
|
# => 5
|
30
33
|
|
@@ -34,7 +37,11 @@ failure.ok? # => false
|
|
34
37
|
failure.err? # => true
|
35
38
|
failure.failure # => 4
|
36
39
|
failure.reason # => :not_prime
|
37
|
-
|
40
|
+
|
41
|
+
failure.and_tap { puts "it is #{_1}" }
|
42
|
+
.and_then { _1 * 5 }
|
43
|
+
.value_or { :nope }
|
44
|
+
# => :nope
|
38
45
|
|
39
46
|
# Dict with sequence, example using a fictional repository.
|
40
47
|
# #set can take a [Symbol, Symbol] key argument where left is OK key, right is Err key
|
data/Steepfile
CHANGED
@@ -6,18 +6,15 @@ target :lib do
|
|
6
6
|
signature "sig"
|
7
7
|
|
8
8
|
check "lib"
|
9
|
-
|
10
|
-
configure_code_diagnostics(D::Ruby.default)
|
11
9
|
end
|
12
10
|
|
13
11
|
target :test do
|
14
|
-
signature "sig"
|
12
|
+
signature "sig", "test/sig"
|
15
13
|
|
16
14
|
check "test"
|
17
15
|
|
18
16
|
library "minitest", "mutex_m"
|
19
17
|
|
20
|
-
configure_code_diagnostics(D::Ruby.strict)
|
21
18
|
configure_code_diagnostics do |h|
|
22
19
|
h[D::Ruby::UnsupportedSyntax] = :information
|
23
20
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mona
|
4
|
+
module DictResult
|
5
|
+
# OK DictResult
|
6
|
+
class OK < Mona::OK
|
7
|
+
include DictResult
|
8
|
+
|
9
|
+
def set(key, val)
|
10
|
+
key, failure_key = key if key.is_a?(Array)
|
11
|
+
failure_key ||= key
|
12
|
+
|
13
|
+
# @type var meta: Hash[Symbol, untyped]
|
14
|
+
Result[val].either \
|
15
|
+
->(value) { OK.new to_h.merge(key => value) },
|
16
|
+
->(failure, reason, **meta) { Err.new to_h.merge(failure_key => failure), reason, **meta, key: failure_key }
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_h = @value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mona
|
4
|
+
module DictResult
|
5
|
+
# Sequence.call { ... } allows monadic 'do' notation for DictResult, where #set-ing the first failure skips the
|
6
|
+
# remainder of the block and returns the DictResult
|
7
|
+
class Sequence
|
8
|
+
def self.call(result = DictResult::EMPTY, &) = new(result).call(&)
|
9
|
+
|
10
|
+
def initialize(result = DictResult::EMPTY)
|
11
|
+
@result = result
|
12
|
+
@throw = Object.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def call
|
16
|
+
catch(@throw) do
|
17
|
+
yield self
|
18
|
+
@result
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def set(key, value)
|
23
|
+
@result = @result.set(key, value)
|
24
|
+
ensure
|
25
|
+
throw @throw, @result if @result.err?
|
26
|
+
end
|
27
|
+
|
28
|
+
def get(key) = @result.get(key)
|
29
|
+
|
30
|
+
def key?(key) = @result.key?(key)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "dict_result/ok"
|
4
|
+
require_relative "dict_result/err"
|
5
|
+
require_relative "dict_result/sequence"
|
6
|
+
|
7
|
+
module Mona
|
8
|
+
# Represents a dictionary of results, it is successful if all results are successful, and a failure if one
|
9
|
+
# is a failure. A DictResult can only contain one failure.
|
10
|
+
module DictResult
|
11
|
+
include Result
|
12
|
+
|
13
|
+
# factory method that returns DictResult::OK or DictResult::Err
|
14
|
+
def self.[](initial = {}, &block)
|
15
|
+
result = EMPTY
|
16
|
+
initial.each { |k, v| result = result.set(k, v) }
|
17
|
+
result = result.sequence(&block) if block
|
18
|
+
result
|
19
|
+
end
|
20
|
+
|
21
|
+
def key?(key) = to_h.key?(key)
|
22
|
+
|
23
|
+
def get(key) = to_h.fetch(key)
|
24
|
+
|
25
|
+
def set(_key, _val) = raise(NotImplementedError, "implement #to_h")
|
26
|
+
|
27
|
+
def to_h = raise(NotImplementedError, "implement #to_h")
|
28
|
+
|
29
|
+
def sequence(&) = Sequence.new(self).call(&)
|
30
|
+
|
31
|
+
EMPTY = DictResult::OK.new({}).freeze
|
32
|
+
end
|
33
|
+
end
|
data/lib/mona/err.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mona
|
4
|
+
# An Err (failure) result, with optional reason, and metadata
|
5
|
+
class Err
|
6
|
+
include Result
|
7
|
+
|
8
|
+
def self.[](failure, reason = nil, **meta) = new(failure, reason, **meta)
|
9
|
+
|
10
|
+
def initialize(failure, reason, **meta)
|
11
|
+
raise ArgumentError, "meta can't contain :reason or err: key" if meta.key?(:reason) || meta.key?(:err)
|
12
|
+
|
13
|
+
@failure = failure
|
14
|
+
@reason = reason
|
15
|
+
@meta = meta
|
16
|
+
end
|
17
|
+
|
18
|
+
# @dynamic failure, reason, meta
|
19
|
+
attr_reader :failure, :reason, :meta
|
20
|
+
|
21
|
+
def either(_ok, err) = err.call(@failure, @reason, **@meta)
|
22
|
+
|
23
|
+
def inspect = "Err(#{[failure, *reason, *meta.map { "#{_1}: #{_2}" }].join(", ")})"
|
24
|
+
|
25
|
+
# @dynamic to_s
|
26
|
+
alias to_s inspect
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mona
|
4
|
+
# raised by ResultMatch.call when no match is found
|
5
|
+
class NoMatchError < Mona::Error
|
6
|
+
# @dynamic result
|
7
|
+
attr_reader :result
|
8
|
+
|
9
|
+
def initialize(result)
|
10
|
+
@result = result
|
11
|
+
super("No match found for #{@result}")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/mona/ok.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mona
|
4
|
+
# A Successful (OK) result
|
5
|
+
class OK
|
6
|
+
include Result
|
7
|
+
|
8
|
+
def self.[](value) = new(value)
|
9
|
+
|
10
|
+
def initialize(value)
|
11
|
+
@value = value
|
12
|
+
end
|
13
|
+
|
14
|
+
# @dynamic value
|
15
|
+
attr_reader :value
|
16
|
+
|
17
|
+
def either(ok, _err) = ok.call(@value)
|
18
|
+
|
19
|
+
def inspect = "OK(#{@value})"
|
20
|
+
|
21
|
+
# @dynamic to_s
|
22
|
+
alias to_s inspect
|
23
|
+
end
|
24
|
+
end
|
data/lib/mona/result.rb
CHANGED
@@ -1,37 +1,45 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "
|
4
|
-
require_relative "result/error"
|
3
|
+
require_relative "../mona"
|
5
4
|
|
6
5
|
module Mona
|
7
|
-
#
|
8
|
-
#
|
9
|
-
# @author Ian White
|
10
|
-
# @since 0.1.0
|
6
|
+
# Provides the result interface, including class must implement #either(on_ok, on_err)
|
11
7
|
module Result
|
12
|
-
|
13
|
-
autoload :Err, "mona/result/err.rb"
|
14
|
-
autoload :Match, "mona/result/match.rb"
|
15
|
-
autoload :Dict, "mona/result/dict.rb"
|
16
|
-
autoload :Sequence, "mona/result/sequence.rb"
|
17
|
-
autoload :Action, "mona/result/action.rb"
|
8
|
+
VERSION = "0.3.0"
|
18
9
|
|
19
|
-
def self.[](obj) = obj.
|
10
|
+
def self.[](obj) = obj.is_a?(Result) ? obj : OK[obj]
|
20
11
|
|
21
|
-
|
12
|
+
def either(_ok, _err) = raise(NotImplementedError, "implement #either")
|
22
13
|
|
23
|
-
def
|
14
|
+
def value_or(&block) = either -> { _1 }, ->(*_args) { block.call }
|
24
15
|
|
25
|
-
def ok(
|
16
|
+
def ok? = either ->(_) { true }, ->(*_args) { false }
|
26
17
|
|
27
|
-
def err
|
18
|
+
def err? = either ->(_) { false }, ->(*_args) { true }
|
28
19
|
|
29
|
-
def
|
20
|
+
def ok(&block) = either block, ->(*_args) {}
|
30
21
|
|
31
|
-
def
|
22
|
+
def err(&block) = either ->(_) {}, block
|
32
23
|
|
33
|
-
def
|
24
|
+
def and_tap(&block) = tap { either block, ->(*_args) {} }
|
34
25
|
|
35
|
-
def
|
26
|
+
def and_then(&block) = either -> { Result[block.call _1] }, ->(*_args) { self }
|
27
|
+
|
28
|
+
def or_else(&block)
|
29
|
+
# @type var meta: Hash[Symbol, untyped]
|
30
|
+
either ->(_) { self }, ->(failure, reason, **meta) { Result[block.call failure, reason, **meta] }
|
31
|
+
end
|
32
|
+
|
33
|
+
def deconstruct
|
34
|
+
# @type var meta: Hash[Symbol, untyped]
|
35
|
+
either ->(value) { [:ok, value] }, ->(failure, reason, **meta) { [:err, failure, reason, meta] }
|
36
|
+
end
|
37
|
+
|
38
|
+
def deconstruct_keys(_keys = nil)
|
39
|
+
# @type var meta: Hash[Symbol, untyped]
|
40
|
+
either ->(ok) { { ok: } }, ->(err, reason, **meta) { meta.merge(err:, reason:) }
|
41
|
+
end
|
42
|
+
|
43
|
+
def ==(other) = deconstruct == other.deconstruct
|
36
44
|
end
|
37
45
|
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mona
|
4
|
+
# mixin for objects that return Result::Dict results
|
5
|
+
#
|
6
|
+
# define #perform to do the work, use #set to add results to the Result::Dict, the first failure will abort the
|
7
|
+
# rest of the #perform method and the result will be returned. This works like monadic 'do' notation.
|
8
|
+
#
|
9
|
+
# After a result is set at a key, it can be accessed via its key name as a method.
|
10
|
+
#
|
11
|
+
# use the Action with #call
|
12
|
+
#
|
13
|
+
# example:
|
14
|
+
#
|
15
|
+
# class UpdateUser
|
16
|
+
# inlcude Mona::ResultAction
|
17
|
+
# include Auditing # for example, adds #audit(model, input) method
|
18
|
+
#
|
19
|
+
# def perform(user_id, attributes)
|
20
|
+
# set :user, UserRepo.find(user_id) # if find is Err, the block exits with failure of :user
|
21
|
+
# set :input, UserInput.valid(attributes) # likewise if valid is Err, the block exits
|
22
|
+
# set [:user, :input], UserRepo.update(user.id, input) # note that #input and #user are available methods
|
23
|
+
# # if update is Err it is set on the :input key
|
24
|
+
# audit(user, input) # this only runs if all of the above set successful results
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# UpdateUser.new.call(user_id, attributes) # => Result
|
29
|
+
module ResultAction
|
30
|
+
# You can create an Ephemeral Action as follows:
|
31
|
+
#
|
32
|
+
# Example:
|
33
|
+
# compute = Mona::ResultAction::Ephemeral.new do |x, y|
|
34
|
+
# set :numerator, x
|
35
|
+
# puts "set numerator: #{numerator}"
|
36
|
+
# set :denominator, y.zero? ? Result.failure(y, :zero) : y
|
37
|
+
# puts "set denominator: #{denominator}"
|
38
|
+
# set :answer, numerator / denominator
|
39
|
+
# puts "answer: #{answer}"
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# > compute.call(10,2)
|
43
|
+
# set numerator: 10
|
44
|
+
# set denominator: 2
|
45
|
+
# answer: 5
|
46
|
+
# => #<Result success: {:numerator=>10, :denominator=>2, :answer=>5}>
|
47
|
+
#
|
48
|
+
# > compute.call(10,0)
|
49
|
+
# set numerator: 10
|
50
|
+
# => #<Result failure: {:numerator=>10, :denominator=>0}, error: {:error=>:zero, :on=>:denominator}>
|
51
|
+
#
|
52
|
+
# Mona::ResultAction() is a shortcut for this
|
53
|
+
class Ephemeral
|
54
|
+
include ResultAction
|
55
|
+
|
56
|
+
def initialize(&perform)
|
57
|
+
@perform = perform
|
58
|
+
end
|
59
|
+
|
60
|
+
def perform(*args, **kwargs) = instance_exec(*args, **kwargs, &@perform)
|
61
|
+
end
|
62
|
+
|
63
|
+
def call(*args, **kwargs)
|
64
|
+
@sequence = DictResult::Sequence.new
|
65
|
+
@sequence.call { |_sequence| perform(*args, **kwargs) }
|
66
|
+
ensure
|
67
|
+
remove_instance_variable :@sequence
|
68
|
+
end
|
69
|
+
|
70
|
+
def perform(*args, **kwargs) = raise(NotImplementedError, "implement `perform'")
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def get(key) = @sequence.get(key)
|
75
|
+
|
76
|
+
def set(key, value) = @sequence.set(key, value)
|
77
|
+
|
78
|
+
def key?(key) = @sequence.key?(key)
|
79
|
+
|
80
|
+
def respond_to_missing?(key, _include_private = false) = key?(key)
|
81
|
+
|
82
|
+
def method_missing(key, *args)
|
83
|
+
return get(key) if args.empty? && key?(key)
|
84
|
+
|
85
|
+
raise NoMethodError, "no method `#{key}' for #{self}"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mona
|
4
|
+
# Use ResultMatch.call to respond to result success or failure
|
5
|
+
#
|
6
|
+
# ResultMatch.call(result) do |r|
|
7
|
+
# r.ok { |value| ... }
|
8
|
+
# r.err(reason, **meta) { |failure, reason, **meta| ... }
|
9
|
+
# r.err(**meta) { |failure, reason, **meta| ... }
|
10
|
+
# r.err(reason) { |failure, reason, **meta| ... }
|
11
|
+
# r.err { |failure, reason, **meta| ... }
|
12
|
+
# end
|
13
|
+
class ResultMatch
|
14
|
+
def self.call(result, &) = new(result).call(&)
|
15
|
+
|
16
|
+
def initialize(result)
|
17
|
+
@throw = Object.new
|
18
|
+
@result = result
|
19
|
+
end
|
20
|
+
|
21
|
+
def call
|
22
|
+
catch @throw do
|
23
|
+
yield self
|
24
|
+
raise NoMatchError, @result
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def ok
|
29
|
+
@result.and_then do |value|
|
30
|
+
throw @throw, yield(value)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def err(match_reason = nil, **match_meta)
|
35
|
+
@result.or_else do |failure, reason, **meta|
|
36
|
+
# @type var meta: Hash[Symbol, untyped]
|
37
|
+
if match_reason?(match_reason, reason) && match_meta?(match_meta, meta)
|
38
|
+
throw @throw, yield(failure, reason, **meta)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def match_reason?(match_reason, reason)
|
46
|
+
match_reason.nil? || match_reason === reason # rubocop:disable Style/CaseEquality
|
47
|
+
end
|
48
|
+
|
49
|
+
def match_meta?(match_meta, meta)
|
50
|
+
match_meta.all? { |key, val| val.nil? || val === meta[key] } # rubocop:disable Style/CaseEquality
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/mona.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Mona top level module contains utility methods
|
4
|
+
module Mona
|
5
|
+
autoload :OK, "mona/ok.rb"
|
6
|
+
autoload :Err, "mona/err.rb"
|
7
|
+
autoload :DictResult, "mona/dict_result.rb"
|
8
|
+
autoload :ResultMatch, "mona/result_match.rb"
|
9
|
+
autoload :ResultAction, "mona/result_action.rb"
|
10
|
+
autoload :NoMatchError, "mona/no_match_error.rb"
|
11
|
+
|
12
|
+
class Error < StandardError; end
|
13
|
+
|
14
|
+
module_function
|
15
|
+
|
16
|
+
# @dynamic self.result, self.ok, self.err, self.dict_result, self.on_result, self.on_ok, self.result_action
|
17
|
+
|
18
|
+
def result(obj) = obj.is_a?(Result) ? obj : OK[obj]
|
19
|
+
|
20
|
+
def ok(value) = OK[value]
|
21
|
+
|
22
|
+
def err(failure, reason = nil, **meta) = Err[failure, reason, **meta]
|
23
|
+
|
24
|
+
def dict_result(initial = {}, &) = DictResult[initial, &]
|
25
|
+
|
26
|
+
def on_result(result, &) = ResultMatch.call(result, &)
|
27
|
+
|
28
|
+
def on_ok(result, &) = ResultMatch.call(result) { _1.ok(&) }
|
29
|
+
|
30
|
+
def result_action(&) = ResultAction::Ephemeral.new(&)
|
31
|
+
end
|
data/sig/mona.rbs
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
module Mona
|
2
|
+
Result::VERSION: String
|
3
|
+
|
4
|
+
type dict = Hash[Symbol, untyped]
|
5
|
+
type result = Result[untyped, untyped]
|
6
|
+
type dict_result = DictResult[untyped]
|
7
|
+
|
8
|
+
def self?.result: (untyped) -> result
|
9
|
+
def self?.ok: [T] (T) -> OK[T]
|
10
|
+
def self?.err: [T, ReasonT] (T, ?ReasonT?, **untyped) -> Err[T, ReasonT?]
|
11
|
+
def self?.dict_result: (?dict) ?{ (DictResult::Sequence) -> void } -> dict_result
|
12
|
+
def self?.on_result: (result) { (ResultMatch) -> void } -> untyped
|
13
|
+
def self?.on_ok: (result) { (untyped) -> void } -> untyped
|
14
|
+
def self?.result_action: { (*untyped) -> void } -> ResultAction::Ephemeral
|
15
|
+
|
16
|
+
module Result[T, ReasonT]
|
17
|
+
def self.[]: (untyped) -> result
|
18
|
+
|
19
|
+
def either: [OKR, ErrR] (^(T) -> OKR, ^(T, ?ReasonT?, **untyped) -> ErrR) -> (OKR | ErrR)
|
20
|
+
def ok?: -> bool
|
21
|
+
def err?: -> bool
|
22
|
+
def ok: [R] () { (T) -> R } -> (R | nil)
|
23
|
+
def err: [R] () { (T, ?ReasonT?, **untyped) -> R } -> (R | nil)
|
24
|
+
def value_or: [R] { () -> R } -> (R | T)
|
25
|
+
def and_then: [R] () { (T) -> R } -> result
|
26
|
+
def and_tap: () { (T) -> void } -> self
|
27
|
+
def or_else: [R] () { (T, ?ReasonT?, **untyped) -> R } -> result
|
28
|
+
def deconstruct: -> Array[untyped]
|
29
|
+
def deconstruct_keys: (?Array[Symbol]?) -> dict
|
30
|
+
end
|
31
|
+
|
32
|
+
class OK[T]
|
33
|
+
include Result[T, nil]
|
34
|
+
|
35
|
+
attr_reader value: T
|
36
|
+
|
37
|
+
def self.[]: [T] (T) -> OK[T]
|
38
|
+
|
39
|
+
def initialize: (T) -> void
|
40
|
+
def either: [OKR] (^(T) -> OKR, ^(T, ?untyped?, **untyped) -> void) -> OKR
|
41
|
+
def inspect: -> String
|
42
|
+
alias to_s inspect
|
43
|
+
end
|
44
|
+
|
45
|
+
class Err[T, ReasonT]
|
46
|
+
include Result[T, ReasonT]
|
47
|
+
|
48
|
+
attr_reader failure: T
|
49
|
+
attr_reader reason: ReasonT
|
50
|
+
attr_reader meta: dict
|
51
|
+
|
52
|
+
def self.[]: [T, ReasonT] (T, ?ReasonT?, **untyped) -> Err[T, ReasonT?]
|
53
|
+
|
54
|
+
def initialize: (T, ReasonT, **untyped) -> void
|
55
|
+
def either: [ErrR] (^(T) -> void, ^(T, ?ReasonT?, **untyped) -> ErrR) -> ErrR
|
56
|
+
def inspect: -> String
|
57
|
+
alias to_s inspect
|
58
|
+
end
|
59
|
+
|
60
|
+
module DictResult[ReasonT]
|
61
|
+
include Result[dict, ReasonT]
|
62
|
+
|
63
|
+
EMPTY: dict_result
|
64
|
+
|
65
|
+
def self.[]: (?dict) ?{ (Sequence) -> void } -> dict_result
|
66
|
+
|
67
|
+
def key? : (Symbol) -> bool
|
68
|
+
def get : (Symbol) -> untyped
|
69
|
+
def set: (Symbol | [Symbol, Symbol], untyped) -> dict_result
|
70
|
+
def sequence: () { (Sequence) -> void } -> dict_result
|
71
|
+
def to_h: -> dict
|
72
|
+
|
73
|
+
class OK < Mona::OK[dict]
|
74
|
+
include DictResult[nil]
|
75
|
+
end
|
76
|
+
|
77
|
+
class Err[ReasonT] < Mona::Err[dict, ReasonT]
|
78
|
+
include DictResult[ReasonT]
|
79
|
+
end
|
80
|
+
|
81
|
+
class Sequence
|
82
|
+
@throw: Object
|
83
|
+
@result: dict_result
|
84
|
+
|
85
|
+
def self.call: (?dict_result) { (Sequence) -> void } -> dict_result
|
86
|
+
def initialize: (?dict_result) -> void
|
87
|
+
def call: () { (Sequence) -> void } -> dict_result
|
88
|
+
def set: (Symbol | [Symbol,Symbol], untyped) -> void
|
89
|
+
def get: (Symbol) -> untyped
|
90
|
+
def key?: (Symbol) -> bool
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class Error < StandardError
|
95
|
+
end
|
96
|
+
|
97
|
+
class NoMatchError < Error
|
98
|
+
attr_reader result: result
|
99
|
+
def initialize: (result) -> void
|
100
|
+
end
|
101
|
+
|
102
|
+
class ResultMatch
|
103
|
+
@throw: Object
|
104
|
+
@result: result
|
105
|
+
|
106
|
+
def self.call: (result) { (ResultMatch) -> void } -> untyped
|
107
|
+
def initialize: (result) -> void
|
108
|
+
def call: () { (ResultMatch) -> void } -> untyped
|
109
|
+
def ok: () { (untyped) -> void } -> void
|
110
|
+
def err: (?untyped, **untyped) { (untyped, ?untyped, **untyped) -> void } -> void
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def match_reason?: (untyped, untyped) -> bool
|
115
|
+
def match_meta?: (untyped, untyped) -> bool
|
116
|
+
end
|
117
|
+
|
118
|
+
module ResultAction
|
119
|
+
class Ephemeral
|
120
|
+
include ResultAction
|
121
|
+
|
122
|
+
@perform: ^(*untyped, **untyped) -> void
|
123
|
+
|
124
|
+
def initialize: () { (*untyped, **untyped) -> void } -> void
|
125
|
+
def perform: (*untyped, **untyped) -> void
|
126
|
+
end
|
127
|
+
|
128
|
+
@sequence: DictResult::Sequence
|
129
|
+
|
130
|
+
def call: (*untyped, **untyped) -> dict_result
|
131
|
+
def perform: (*untyped, **untyped) -> void
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
def get: (Symbol) -> untyped
|
136
|
+
def set: (Symbol | [Symbol,Symbol], untyped) -> void
|
137
|
+
def key?: (Symbol) -> bool
|
138
|
+
def respond_to_missing?: (Symbol, ?bool) -> bool
|
139
|
+
def method_missing: (Symbol, *untyped) -> untyped
|
140
|
+
end
|
141
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mona-result
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ian White
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-08-
|
11
|
+
date: 2022-08-25 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Mona::Result provides a result monad, and dict result monad with do-notation
|
14
14
|
email:
|
@@ -25,16 +25,18 @@ files:
|
|
25
25
|
- README.md
|
26
26
|
- Rakefile
|
27
27
|
- Steepfile
|
28
|
+
- lib/mona.rb
|
29
|
+
- lib/mona/dict_result.rb
|
30
|
+
- lib/mona/dict_result/err.rb
|
31
|
+
- lib/mona/dict_result/ok.rb
|
32
|
+
- lib/mona/dict_result/sequence.rb
|
33
|
+
- lib/mona/err.rb
|
34
|
+
- lib/mona/no_match_error.rb
|
35
|
+
- lib/mona/ok.rb
|
28
36
|
- lib/mona/result.rb
|
29
|
-
- lib/mona/
|
30
|
-
- lib/mona/
|
31
|
-
-
|
32
|
-
- lib/mona/result/error.rb
|
33
|
-
- lib/mona/result/match.rb
|
34
|
-
- lib/mona/result/ok.rb
|
35
|
-
- lib/mona/result/sequence.rb
|
36
|
-
- lib/mona/result/version.rb
|
37
|
-
- sig/mona/result.rbs
|
37
|
+
- lib/mona/result_action.rb
|
38
|
+
- lib/mona/result_match.rb
|
39
|
+
- sig/mona.rbs
|
38
40
|
homepage: https://github.com/mona-rb/mona-result
|
39
41
|
licenses:
|
40
42
|
- MIT
|
@@ -42,7 +44,7 @@ metadata:
|
|
42
44
|
rubygems_mfa_required: 'true'
|
43
45
|
homepage_uri: https://github.com/mona-rb/mona-result
|
44
46
|
source_code_uri: https://github.com/mona-rb/mona-result
|
45
|
-
changelog_uri: https://github.com/mona-rb/mona-result/CHANGELOG.md
|
47
|
+
changelog_uri: https://github.com/mona-rb/mona-result/blob/main/CHANGELOG.md
|
46
48
|
post_install_message:
|
47
49
|
rdoc_options: []
|
48
50
|
require_paths:
|
data/lib/mona/result/action.rb
DELETED
@@ -1,84 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Mona
|
4
|
-
module Result
|
5
|
-
# mixin for objects that return Result::Dict results
|
6
|
-
#
|
7
|
-
# define #perform to do the work, use #set to add results to the Result::Dict, the first failure will abort the
|
8
|
-
# rest of the #perform method and the result will be returned. This works like monadic 'do' notation.
|
9
|
-
#
|
10
|
-
# After a result is set at a key, it can be accessed via its key name as a method.
|
11
|
-
#
|
12
|
-
# use the Action with #call
|
13
|
-
#
|
14
|
-
# example:
|
15
|
-
#
|
16
|
-
# class UpdateUser
|
17
|
-
# inlcude Mona::Result::Action
|
18
|
-
# include Auditing # for example, adds #audit(model, input) method
|
19
|
-
#
|
20
|
-
# def perform(user_id, attributes)
|
21
|
-
# set :user, UserRepo.find(user_id) # if find is Err, the block exits with failure of :user
|
22
|
-
# set :input, UserInput.valid(attributes) # likewise if valid is Err, the block exits
|
23
|
-
# set [:user, :input], UserRepo.update(user.id, input) # note that #input and #user are available methods
|
24
|
-
# # if update is Err it is set on the :input key
|
25
|
-
# audit(user, input) # this only runs if all of the above set successful results
|
26
|
-
# end
|
27
|
-
# end
|
28
|
-
#
|
29
|
-
# UpdateUser.new.call(user_id, attributes) # => Result
|
30
|
-
module Action
|
31
|
-
# You can create an Ephemeral Action as follows:
|
32
|
-
#
|
33
|
-
# Example:
|
34
|
-
# compute = Mona::Result::Action::Ephemeral.new do |x, y|
|
35
|
-
# set :numerator, x
|
36
|
-
# puts "set numerator: #{numerator}"
|
37
|
-
# set :denominator, y.zero? ? Result.failure(y, :zero) : y
|
38
|
-
# puts "set denominator: #{denominator}"
|
39
|
-
# set :answer, numerator / denominator
|
40
|
-
# puts "answer: #{answer}"
|
41
|
-
# end
|
42
|
-
#
|
43
|
-
# > compute.call(10,2)
|
44
|
-
# set numerator: 10
|
45
|
-
# set denominator: 2
|
46
|
-
# answer: 5
|
47
|
-
# => #<Result success: {:numerator=>10, :denominator=>2, :answer=>5}>
|
48
|
-
#
|
49
|
-
# > compute.call(10,0)
|
50
|
-
# set numerator: 10
|
51
|
-
# => #<Result failure: {:numerator=>10, :denominator=>0}, error: {:error=>:zero, :on=>:denominator}>
|
52
|
-
#
|
53
|
-
# Mona::Result.action is a shortcut for this
|
54
|
-
class Ephemeral
|
55
|
-
include Action
|
56
|
-
|
57
|
-
def initialize(&perform)
|
58
|
-
@perform = perform
|
59
|
-
end
|
60
|
-
|
61
|
-
def perform(*args, **kwargs) = instance_exec(*args, **kwargs, &@perform)
|
62
|
-
end
|
63
|
-
|
64
|
-
def call(*args, **kwargs)
|
65
|
-
@sequence = Sequence.new
|
66
|
-
@sequence.call { |_sequence| perform(*args, **kwargs) }
|
67
|
-
ensure
|
68
|
-
remove_instance_variable :@sequence
|
69
|
-
end
|
70
|
-
|
71
|
-
private
|
72
|
-
|
73
|
-
def set(key, value) = @sequence.set(key, value)
|
74
|
-
|
75
|
-
def respond_to_missing?(key, _include_private = false) = @sequence.key?(key)
|
76
|
-
|
77
|
-
def method_missing(key, *args)
|
78
|
-
return @sequence.fetch(key) if args.empty? && @sequence.key?(key)
|
79
|
-
|
80
|
-
raise NoMethodError, "no method `#{key}' for #{self}"
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
data/lib/mona/result/dict.rb
DELETED
@@ -1,57 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Mona
|
4
|
-
module Result
|
5
|
-
# Represents a dictionary of results, it is successful if all results are successful, and a failure if one
|
6
|
-
# is a failure. A Result::Dict can only contain one failure.
|
7
|
-
class Dict
|
8
|
-
# factory method that returns {Dict::Success} or {Dict::Failure}
|
9
|
-
def self.new(initial = {}, &block)
|
10
|
-
result = Dict::EMPTY
|
11
|
-
initial.each { |k, v| result = result.set(k, v) }
|
12
|
-
result = result.sequence(&block) if block
|
13
|
-
result
|
14
|
-
end
|
15
|
-
|
16
|
-
# Dict read interface
|
17
|
-
module ReadInterface
|
18
|
-
def [](key) = to_h[key]
|
19
|
-
|
20
|
-
def key?(key) = to_h.key?(key)
|
21
|
-
|
22
|
-
def fetch(key) = to_h.fetch(key)
|
23
|
-
end
|
24
|
-
|
25
|
-
# OK dict result
|
26
|
-
class OK < Result::OK
|
27
|
-
include ReadInterface
|
28
|
-
|
29
|
-
def set(key, to_result)
|
30
|
-
key, failure_key = key if key.is_a?(Array)
|
31
|
-
failure_key ||= key
|
32
|
-
|
33
|
-
Result[to_result].either \
|
34
|
-
->(value) { OK.new to_h.merge(key => value) },
|
35
|
-
->(failure, reason, **m) { Err.new to_h.merge(failure_key => failure), reason, **m, key: failure_key }
|
36
|
-
end
|
37
|
-
|
38
|
-
def sequence(&) = Sequence.new(self).call(&)
|
39
|
-
|
40
|
-
def to_h = @value
|
41
|
-
end
|
42
|
-
|
43
|
-
# Err dict result
|
44
|
-
class Err < Result::Err
|
45
|
-
include ReadInterface
|
46
|
-
|
47
|
-
def set(_key, _val) = raise(Error, "cannot #set on #{self}")
|
48
|
-
|
49
|
-
def sequence(&) = self
|
50
|
-
|
51
|
-
def to_h = @failure
|
52
|
-
end
|
53
|
-
|
54
|
-
EMPTY = OK.new({}).freeze
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
data/lib/mona/result/err.rb
DELETED
@@ -1,48 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Mona
|
4
|
-
module Result
|
5
|
-
# A Error or failure result, with optional reason, and metadata
|
6
|
-
class Err
|
7
|
-
def initialize(failure, reason = nil, **meta)
|
8
|
-
raise ArgumentError, "meta can't contain :reason key" if meta.key?(:reason)
|
9
|
-
|
10
|
-
@failure = failure.freeze
|
11
|
-
@reason = reason
|
12
|
-
@meta = meta
|
13
|
-
end
|
14
|
-
|
15
|
-
attr_reader :failure, :reason, :meta
|
16
|
-
|
17
|
-
def ok? = false
|
18
|
-
|
19
|
-
def err? = true
|
20
|
-
|
21
|
-
def value_or(&) = yield
|
22
|
-
|
23
|
-
def ok(&) = nil
|
24
|
-
|
25
|
-
def err(&) = yield @failure, @reason, **@meta
|
26
|
-
|
27
|
-
def either(_ok, err) = err.call(@failure, @reason, **@meta)
|
28
|
-
|
29
|
-
def and_then(&) = self
|
30
|
-
|
31
|
-
def and_tap(&) = self
|
32
|
-
|
33
|
-
def or_else(&) = Result[yield @failure, @reason, **@meta]
|
34
|
-
|
35
|
-
def deconstruct = [:err, @failure, @reason, @meta]
|
36
|
-
|
37
|
-
def deconstruct_keys(_keys = nil) = { err: @failure, reason: @reason, **@meta }
|
38
|
-
|
39
|
-
def to_result = self
|
40
|
-
|
41
|
-
def to_s
|
42
|
-
"#<Err #{@failure.inspect} #{{ reason: @reason, **@meta }.map{ "#{_1}: #{_2.inspect}" }.join(", ")}>"
|
43
|
-
end
|
44
|
-
|
45
|
-
alias inspect to_s
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
data/lib/mona/result/error.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Mona
|
4
|
-
module Result
|
5
|
-
# Base error for Result
|
6
|
-
class Error < StandardError; end
|
7
|
-
|
8
|
-
# raised when Result::Match does not match the result
|
9
|
-
class NoMatchError < Error
|
10
|
-
attr_reader :result
|
11
|
-
|
12
|
-
def initialize(result)
|
13
|
-
@result = result
|
14
|
-
super("No match found for #{result}")
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
data/lib/mona/result/match.rb
DELETED
@@ -1,53 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Mona
|
4
|
-
module Result
|
5
|
-
# Use Match.call to respond to result success or failure
|
6
|
-
#
|
7
|
-
# Result::Match.call(result) do |r|
|
8
|
-
# r.ok { |value| ... }
|
9
|
-
# r.err(error, **meta) { |failure, reason, **meta| ... }
|
10
|
-
# r.err(error) { |failure, reason, **meta| ... }
|
11
|
-
# r.err { |failure, reason, **meta| ... }
|
12
|
-
# end
|
13
|
-
class Match
|
14
|
-
def self.call(result, &) = new(result).call(&)
|
15
|
-
|
16
|
-
def initialize(result)
|
17
|
-
@throw = Object.new
|
18
|
-
@result = result
|
19
|
-
end
|
20
|
-
|
21
|
-
def call
|
22
|
-
catch @throw do
|
23
|
-
yield self
|
24
|
-
raise NoMatchError, @result
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def ok
|
29
|
-
@result.and_then do |value|
|
30
|
-
throw @throw, yield(value)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def err(match_reason = nil, **match_meta)
|
35
|
-
@result.or_else do |failure, reason, **meta|
|
36
|
-
if match_reason?(match_reason, reason) && match_meta?(match_meta, meta)
|
37
|
-
throw @throw, yield(failure, reason, **meta)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
|
44
|
-
def match_reason?(match_reason, reason)
|
45
|
-
match_reason.nil? || match_reason === reason # rubocop:disable Style/CaseEquality
|
46
|
-
end
|
47
|
-
|
48
|
-
def match_meta?(match_meta, meta)
|
49
|
-
match_meta.all? { |key, val| val.nil? || val === meta[key] } # rubocop:disable Style/CaseEquality
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
data/lib/mona/result/ok.rb
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Mona
|
4
|
-
module Result
|
5
|
-
# A Successful (OK) result
|
6
|
-
class OK
|
7
|
-
def initialize(value)
|
8
|
-
@value = value.freeze
|
9
|
-
end
|
10
|
-
|
11
|
-
attr_reader :value
|
12
|
-
|
13
|
-
def value_or(&) = @value
|
14
|
-
|
15
|
-
def ok? = true
|
16
|
-
|
17
|
-
def err? = false
|
18
|
-
|
19
|
-
def ok(&) = yield @value
|
20
|
-
|
21
|
-
def err(&) = nil
|
22
|
-
|
23
|
-
def either(ok, _err) = ok.call(@value)
|
24
|
-
|
25
|
-
def and_then(&) = Result[yield @value]
|
26
|
-
|
27
|
-
def and_tap(&) = tap { yield @value }
|
28
|
-
|
29
|
-
def or_else(&) = self
|
30
|
-
|
31
|
-
def deconstruct = [:ok, @value]
|
32
|
-
|
33
|
-
def deconstruct_keys(_keys = nil) = { ok: @value }
|
34
|
-
|
35
|
-
def to_result = self
|
36
|
-
|
37
|
-
def to_s = "#<OK #{@value.inspect}>"
|
38
|
-
|
39
|
-
alias inspect to_s
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
data/lib/mona/result/sequence.rb
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Mona
|
4
|
-
module Result
|
5
|
-
# Sequence.call { ... } allows monadic 'do' notation for Result::Dict, where #set-ing the first failure skips the
|
6
|
-
# remainder of the block and returns the Result::Dict
|
7
|
-
class Sequence
|
8
|
-
include Dict::ReadInterface
|
9
|
-
|
10
|
-
def self.call(result = Dict::EMPTY, &) = new(result).call(&)
|
11
|
-
|
12
|
-
def initialize(result = Dict::EMPTY)
|
13
|
-
@result = result
|
14
|
-
@throw = Object.new
|
15
|
-
end
|
16
|
-
|
17
|
-
def call
|
18
|
-
catch(@throw) do
|
19
|
-
yield self
|
20
|
-
@result
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def set(key, value)
|
25
|
-
@result = @result.set(key, value)
|
26
|
-
ensure
|
27
|
-
throw @throw, @result if @result.err?
|
28
|
-
end
|
29
|
-
|
30
|
-
def to_h = @result.to_h
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
data/lib/mona/result/version.rb
DELETED
data/sig/mona/result.rbs
DELETED
@@ -1,177 +0,0 @@
|
|
1
|
-
module Mona
|
2
|
-
module Result
|
3
|
-
VERSION: String
|
4
|
-
|
5
|
-
type dict = Hash[Symbol, untyped]
|
6
|
-
type keys = Array[Symbol]
|
7
|
-
type result[T] = (_OK[T] | _Err[T])
|
8
|
-
type dict_result = result[dict] & _Dict
|
9
|
-
type set_key = (Symbol | [Symbol, Symbol])
|
10
|
-
|
11
|
-
def self.[]: (untyped) -> result[untyped]
|
12
|
-
def self?.to_result: (untyped) -> result[untyped]
|
13
|
-
def self?.ok: [T] (T) -> _OK[T]
|
14
|
-
def self?.err: [T] (T, ?untyped, **untyped) -> _Err[T]
|
15
|
-
def self?.dict: (?dict) ?{ (Sequence) -> void } -> dict_result
|
16
|
-
def self?.on_result: (result[untyped]) { (Match) -> void } -> untyped
|
17
|
-
def self?.on_ok: (result[untyped]) { (untyped) -> void } -> untyped
|
18
|
-
def self?.action: () { (*untyped) -> void } -> Action::Ephemeral
|
19
|
-
|
20
|
-
interface _OK[T]
|
21
|
-
def initialize: (T) -> void
|
22
|
-
def value: -> T
|
23
|
-
def ok?: -> true
|
24
|
-
def err?: -> false
|
25
|
-
def ok: [R] () { (T) -> R } -> R
|
26
|
-
def err: () { (T, untyped, **untyped) -> void } -> nil
|
27
|
-
def either: [R] (^(T) -> R, ^(T, untyped, **untyped) -> void) -> R
|
28
|
-
def value_or: () { -> void } -> T
|
29
|
-
def and_then: () { (T) -> untyped } -> result[untyped]
|
30
|
-
def and_tap: () { (T) -> void } -> self
|
31
|
-
def or_else: () { (T, ?untyped, **untyped) -> untyped } -> self
|
32
|
-
def deconstruct: -> [:ok, T]
|
33
|
-
def deconstruct_keys: (?keys?) -> { ok: T }
|
34
|
-
end
|
35
|
-
|
36
|
-
interface _Err[T]
|
37
|
-
def initialize: (T, ?untyped, **untyped) -> void
|
38
|
-
def failure: -> T
|
39
|
-
def reason: -> untyped
|
40
|
-
def meta: -> dict
|
41
|
-
def ok?: -> false
|
42
|
-
def err?: -> true
|
43
|
-
def ok: () { (untyped) -> void } -> nil
|
44
|
-
def err: [R] () { (T, untyped, **untyped) -> R } -> R
|
45
|
-
def either: [R] (^(T) -> void, ^(T, untyped, **untyped) -> R) -> R
|
46
|
-
def value_or: [R] () { -> R } -> R
|
47
|
-
def and_then: () { (T) -> untyped } -> self
|
48
|
-
def and_tap: () { (T) -> void } -> self
|
49
|
-
def or_else: () { (T, ?untyped, **untyped) -> untyped } -> result[untyped]
|
50
|
-
def deconstruct: -> [:err, T, untyped, dict]
|
51
|
-
def deconstruct_keys: (?keys?) -> dict
|
52
|
-
end
|
53
|
-
|
54
|
-
interface _ToH
|
55
|
-
def to_h: -> dict
|
56
|
-
end
|
57
|
-
|
58
|
-
interface _DictRead
|
59
|
-
def key? : (Symbol) -> bool
|
60
|
-
def fetch : (Symbol) -> untyped
|
61
|
-
def [] : (Symbol) -> untyped
|
62
|
-
end
|
63
|
-
|
64
|
-
interface _Dict
|
65
|
-
include _DictRead
|
66
|
-
def set: (set_key, untyped) -> dict_result
|
67
|
-
def sequence: () { (Sequence) -> void } -> dict_result
|
68
|
-
def to_h: -> dict
|
69
|
-
end
|
70
|
-
|
71
|
-
interface _Perform
|
72
|
-
def perform: (*untyped, **untyped) -> void
|
73
|
-
end
|
74
|
-
|
75
|
-
class Error < StandardError
|
76
|
-
end
|
77
|
-
|
78
|
-
class NoMatchError < Error
|
79
|
-
attr_reader result: _Err[untyped]
|
80
|
-
def initialize: (_Err[untyped]) -> void
|
81
|
-
end
|
82
|
-
|
83
|
-
class OK
|
84
|
-
include _OK[untyped]
|
85
|
-
|
86
|
-
@value: untyped
|
87
|
-
end
|
88
|
-
|
89
|
-
class Err
|
90
|
-
include _Err[untyped]
|
91
|
-
|
92
|
-
@failure: untyped
|
93
|
-
@reason: untyped
|
94
|
-
@meta: dict
|
95
|
-
end
|
96
|
-
|
97
|
-
class Dict
|
98
|
-
EMPTY: dict_result
|
99
|
-
|
100
|
-
module ReadInterface : _ToH
|
101
|
-
include _DictRead
|
102
|
-
end
|
103
|
-
|
104
|
-
class OK < Result::OK
|
105
|
-
include ReadInterface
|
106
|
-
|
107
|
-
include _OK[dict]
|
108
|
-
include _Dict
|
109
|
-
|
110
|
-
@value: dict
|
111
|
-
end
|
112
|
-
|
113
|
-
class Err < Result::Err
|
114
|
-
include ReadInterface
|
115
|
-
|
116
|
-
include _Err[dict]
|
117
|
-
include _Dict
|
118
|
-
|
119
|
-
@failure: dict
|
120
|
-
@reason: untyped
|
121
|
-
@meta: dict
|
122
|
-
end
|
123
|
-
|
124
|
-
def self.new: (?dict) ?{ (Sequence) -> void } -> dict_result
|
125
|
-
end
|
126
|
-
|
127
|
-
class Match
|
128
|
-
@throw: Object
|
129
|
-
@result: result[untyped]
|
130
|
-
|
131
|
-
def self.call: (result[untyped]) { (Match) -> void } -> untyped
|
132
|
-
def initialize: (result[untyped]) -> void
|
133
|
-
def call: () { (Match) -> void } -> untyped
|
134
|
-
def ok: () { (untyped) -> void } -> void
|
135
|
-
def err: (?untyped, **untyped) { (untyped, ?untyped, **untyped) -> void } -> void
|
136
|
-
|
137
|
-
private
|
138
|
-
|
139
|
-
def match_reason?: (untyped, untyped) -> bool
|
140
|
-
def match_meta?: (untyped, untyped) -> bool
|
141
|
-
end
|
142
|
-
|
143
|
-
class Sequence
|
144
|
-
include Dict::ReadInterface
|
145
|
-
|
146
|
-
@throw: Object
|
147
|
-
@result: dict_result
|
148
|
-
|
149
|
-
def self.call: (?dict_result) { (Sequence) -> void } -> dict_result
|
150
|
-
def initialize: (?dict_result) -> void
|
151
|
-
def call: () { (Sequence) -> void } -> dict_result
|
152
|
-
def set: (set_key, untyped) -> void
|
153
|
-
def to_h: -> dict
|
154
|
-
end
|
155
|
-
|
156
|
-
module Action : _Perform
|
157
|
-
class Ephemeral
|
158
|
-
include Action
|
159
|
-
|
160
|
-
@perform: ^(*untyped, **untyped) -> void
|
161
|
-
|
162
|
-
def initialize: () { (*untyped, **untyped) -> void } -> void
|
163
|
-
def perform: (*untyped, **untyped) -> void
|
164
|
-
end
|
165
|
-
|
166
|
-
@sequence: Sequence
|
167
|
-
|
168
|
-
def call: (*untyped, **untyped) -> dict_result
|
169
|
-
|
170
|
-
private
|
171
|
-
|
172
|
-
def set: (set_key, untyped) -> void
|
173
|
-
def respond_to_missing?: (Symbol, ?bool) -> bool
|
174
|
-
def method_missing: (Symbol, *untyped) -> untyped
|
175
|
-
end
|
176
|
-
end
|
177
|
-
end
|