mangrove 0.34.0 → 0.36.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/.rubocop.yml +5 -0
- data/README.md +33 -8
- data/lib/mangrove/result/ext.rb +18 -0
- data/lib/mangrove/result.rb +1 -0
- data/lib/mangrove/try_from_ext.rb +41 -0
- data/lib/mangrove/version.rb +1 -1
- data/lib/mangrove.rb +1 -0
- data/lib/tapioca/dsl/compilers/mangrove_result_ext.rb +40 -0
- data/lib/tapioca/dsl/compilers/mangrove_try_from_ext.rb +33 -0
- data/sorbet/rbi/dsl/.gitattributes +1 -0
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 371d0e77c422e441bc8798ca1aacf77cf11126fcea3bec2aba243c1a4970300f
|
4
|
+
data.tar.gz: 1cc24f3c80fe6c2fdca11c75193df51f7d77be047ecb40f98700b30a6eb5df4a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2aa303b678e5a045c0f1b13acd24cbbed698ea69e278b94fb7df2015924f5627cc86841c5769e30434ca613fef2adf489c6b555875dafff8733b593358321ea1
|
7
|
+
data.tar.gz: 62c17b4a366a86ec193b09196af476e58f921edebf1118ac875af70e6a61ca5eaa58e5a764f28f82372a266149be5b02a0af3276e2c8857e1d0699019366073a
|
data/.rubocop.yml
CHANGED
@@ -31,10 +31,14 @@ Metrics/ModuleLength:
|
|
31
31
|
Naming/BlockForwarding:
|
32
32
|
EnforcedStyle: explicit
|
33
33
|
|
34
|
+
Naming/MethodParameterName:
|
35
|
+
Enabled: false
|
36
|
+
|
34
37
|
Naming/VariableNumber:
|
35
38
|
EnforcedStyle: snake_case
|
36
39
|
|
37
40
|
Style/BlockDelimiters:
|
41
|
+
Enabled: false
|
38
42
|
EnforcedStyle: always_braces
|
39
43
|
AllowedMethods:
|
40
44
|
- describe
|
@@ -63,3 +67,4 @@ Style/StringLiterals:
|
|
63
67
|
Style/StringLiteralsInInterpolation:
|
64
68
|
Enabled: true
|
65
69
|
EnforcedStyle: double_quotes
|
70
|
+
|
data/README.md
CHANGED
@@ -2,22 +2,21 @@
|
|
2
2
|
|
3
3
|
Mangrove is a Ruby toolkit that brings a functional, statically-typed flavor to your Sorbet-enabled projects. Inspired by concepts from languages like Rust and Haskell, Mangrove provides a robust set of tools—primarily `Result` and ADT-like Enums—to help you write safer, more expressive Ruby code.
|
4
4
|
|
5
|
-
-
|
6
|
-
-
|
5
|
+
- [Documentation](https://kazzix14.github.io/mangrove/docs/)
|
6
|
+
- [Coverage](https://kazzix14.github.io/mangrove/coverage/index.html#_AllFiles)
|
7
7
|
|
8
8
|
---
|
9
9
|
|
10
10
|
## Highlights
|
11
11
|
|
12
12
|
- **Sorbet Integration**
|
13
|
-
Built from the ground up to work smoothly with Sorbet
|
13
|
+
Built from the ground up to work smoothly with Sorbet's type system.
|
14
14
|
|
15
15
|
- **Result Type**
|
16
|
-
Model success/failure outcomes with explicit types—no more
|
16
|
+
Model success/failure outcomes with explicit types—no more "return false or nil" for errors!
|
17
17
|
|
18
18
|
- **Enums (ADTs)**
|
19
19
|
Define your own sealed enums with typed variants. Each variant can hold distinct inner data.
|
20
|
-
|
21
20
|
- **Functional Patterns**
|
22
21
|
Chain transformations or short-circuit error handling with a clean monadic style.
|
23
22
|
|
@@ -33,17 +32,17 @@ bundle add mangrove
|
|
33
32
|
|
34
33
|
## Usage Overview
|
35
34
|
|
36
|
-
Mangrove revolves around **`Result`** and a sealed
|
35
|
+
Mangrove revolves around **`Result`** and a sealed "Enum" mechanism for ADTs.
|
37
36
|
|
38
37
|
### Result
|
39
38
|
|
40
39
|
A `Result` is either `Ok(T)` or `Err(E)`. You can compose them with monadic methods like `and_then` and `or_else`.
|
41
|
-
For
|
40
|
+
For "early returns" in a functional style, you have two main approaches:
|
42
41
|
|
43
42
|
1. **A context-based DSL (e.g., `ctx.try!`)**
|
44
43
|
2. **An instance method on `Result` itself, `unwrap_in(ctx)`**, which behaves similarly.
|
45
44
|
|
46
|
-
Here
|
45
|
+
Here's an example of chaining requests and short-circuiting on error:
|
47
46
|
|
48
47
|
```ruby
|
49
48
|
class MyClient
|
@@ -87,6 +86,32 @@ end
|
|
87
86
|
# More chaining, etc...
|
88
87
|
```
|
89
88
|
|
89
|
+
### Extension Methods
|
90
|
+
|
91
|
+
Mangrove provides convenient extension methods through `Mangrove::Result::Ext`. These methods allow you to easily wrap any value in a `Result`:
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
# Include the extension in your classes
|
95
|
+
class Object
|
96
|
+
include Mangrove::Result::Ext
|
97
|
+
end
|
98
|
+
|
99
|
+
# Now you can use in_ok and in_err on any object
|
100
|
+
"success".in_ok # => Result::Ok("success")
|
101
|
+
"error".in_err # => Result::Err("error")
|
102
|
+
|
103
|
+
# Useful in method chains
|
104
|
+
"hello"
|
105
|
+
.upcase
|
106
|
+
.in_ok
|
107
|
+
.map_ok { |s| "#{s}!" } # => Result::Ok("HELLO!")
|
108
|
+
|
109
|
+
# Error case
|
110
|
+
"error message"
|
111
|
+
.in_err
|
112
|
+
.map_err { |e| "#{e}!" } # => Result::Err("error message!")
|
113
|
+
```
|
114
|
+
|
90
115
|
### Enums (ADTs)
|
91
116
|
|
92
117
|
Define an enum with typed variants:
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Mangrove
|
5
|
+
module Result
|
6
|
+
module Ext
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
def in_ok
|
10
|
+
Mangrove::Result::Ok.new(self)
|
11
|
+
end
|
12
|
+
|
13
|
+
def in_err
|
14
|
+
Mangrove::Result::Err.new(self)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/mangrove/result.rb
CHANGED
@@ -0,0 +1,41 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Mangrove
|
5
|
+
module TryFromExt
|
6
|
+
extend T::Sig
|
7
|
+
extend T::Helpers
|
8
|
+
|
9
|
+
include Kernel
|
10
|
+
|
11
|
+
sig {
|
12
|
+
type_parameters(:I, :O, :E)
|
13
|
+
.params(
|
14
|
+
from: T::Class[T.type_parameter(:I)],
|
15
|
+
to: T::Class[T.type_parameter(:O)],
|
16
|
+
err: T::Class[T.type_parameter(:E)],
|
17
|
+
block: T.proc.params(arg: T.type_parameter(:I)).returns(Mangrove::Result[T.type_parameter(:O), T.type_parameter(:E)])
|
18
|
+
).void
|
19
|
+
}
|
20
|
+
# rubocop:disable Lint/UnusedMethodArgument
|
21
|
+
def try_convert_from(from:, to:, err:, &block)
|
22
|
+
T.bind(self, T::Class[T.type_parameter(:O)])
|
23
|
+
|
24
|
+
vars = from.instance_variable_get(:@convertable_to) || {}
|
25
|
+
vars[self] = block
|
26
|
+
from.instance_variable_set(:@convertable_to, vars)
|
27
|
+
|
28
|
+
@convertable_from ||= {}
|
29
|
+
@convertable_from[from] = [err, block]
|
30
|
+
|
31
|
+
into_t = T.cast(self, Class)
|
32
|
+
|
33
|
+
from.define_method("try_into_#{T.must_because(into_t.name) { "name is required" }.gsub(/::|([A-Z]+)([A-Z][a-z])/, '\1_\2').gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase}") do
|
34
|
+
proc = T.unsafe(self).class.ancestors.lazy.map { |klass| klass.instance_variable_get(:@convertable_to)&.[](into_t) }.find(&:itself)
|
35
|
+
|
36
|
+
proc.call(self)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
# rubocop:enable Lint/UnusedMethodArgument
|
40
|
+
end
|
41
|
+
end
|
data/lib/mangrove/version.rb
CHANGED
data/lib/mangrove.rb
CHANGED
@@ -0,0 +1,40 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "mangrove/result/ext"
|
5
|
+
require "tapioca/dsl"
|
6
|
+
require "sorbet-runtime"
|
7
|
+
|
8
|
+
module Tapioca
|
9
|
+
module Compilers
|
10
|
+
class MangroveResultExt < Tapioca::Dsl::Compiler
|
11
|
+
extend T::Sig
|
12
|
+
|
13
|
+
ConstantType = type_member { { fixed: T.class_of(Mangrove::Result::Ext) } }
|
14
|
+
|
15
|
+
sig { override.returns(T::Enumerable[Module]) }
|
16
|
+
def self.gather_constants
|
17
|
+
all_classes.select { |c| c < ::Mangrove::Result::Ext }
|
18
|
+
end
|
19
|
+
|
20
|
+
sig { override.void }
|
21
|
+
def decorate
|
22
|
+
return unless valid_constant_name?(constant.to_s)
|
23
|
+
|
24
|
+
root.create_path(constant) do |klass|
|
25
|
+
klass.create_method("in_ok", return_type: "Mangrove::Result::Ok[#{constant}]")
|
26
|
+
klass.create_method("in_err", return_type: "Mangrove::Result::Err[#{constant}]")
|
27
|
+
end
|
28
|
+
rescue NameError
|
29
|
+
# 握りつぶす
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
sig { params(string: String).returns(T::Boolean) }
|
35
|
+
def valid_constant_name?(string)
|
36
|
+
Object.const_defined?(string) && !!(string =~ /\A[A-Z][a-zA-Z0-9_]*\z/)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "mangrove/try_from_ext"
|
5
|
+
require "tapioca/dsl"
|
6
|
+
require "sorbet-runtime"
|
7
|
+
|
8
|
+
module Tapioca
|
9
|
+
module Compilers
|
10
|
+
class TryFromExt < Tapioca::Dsl::Compiler
|
11
|
+
extend T::Sig
|
12
|
+
|
13
|
+
ConstantType = type_member { { fixed: T.class_of(Mangrove::TryFromExt) } }
|
14
|
+
|
15
|
+
sig { override.returns(T::Enumerable[Module]) }
|
16
|
+
def self.gather_constants
|
17
|
+
all_classes.select { |c| c.singleton_class < ::Mangrove::TryFromExt }
|
18
|
+
end
|
19
|
+
|
20
|
+
sig { override.void }
|
21
|
+
def decorate
|
22
|
+
return if constant.instance_variable_get(:@convertable_from).nil? || constant.instance_variable_get(:@convertable_from).empty?
|
23
|
+
|
24
|
+
constant.instance_variable_get(:@convertable_from)&.each do |convertable_from, values|
|
25
|
+
err_constant, _block = values
|
26
|
+
root.create_path(convertable_from) do |klass|
|
27
|
+
klass.create_method("try_into_#{constant.to_s.gsub(/::|([A-Z]+)([A-Z][a-z])/, '\1_\2').gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase}", return_type: "Mangrove::Result[#{constant}, #{err_constant}]")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
**/*.rbi linguist-generated=true
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mangrove
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.36.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kazuma Murata
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-
|
10
|
+
date: 2025-03-28 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: sorbet-runtime
|
@@ -46,13 +46,18 @@ files:
|
|
46
46
|
- lib/mangrove/option/control_signal.rb
|
47
47
|
- lib/mangrove/result.rb
|
48
48
|
- lib/mangrove/result/control_signal.rb
|
49
|
+
- lib/mangrove/result/ext.rb
|
50
|
+
- lib/mangrove/try_from_ext.rb
|
49
51
|
- lib/mangrove/version.rb
|
50
52
|
- lib/tapioca/dsl/compilers/mangrove_enum.rb
|
53
|
+
- lib/tapioca/dsl/compilers/mangrove_result_ext.rb
|
54
|
+
- lib/tapioca/dsl/compilers/mangrove_try_from_ext.rb
|
51
55
|
- rbi/mangrove.rbi
|
52
56
|
- sig/mangrove.rbs
|
53
57
|
- sorbet/config
|
54
58
|
- sorbet/rbi/annotations/.gitattributes
|
55
59
|
- sorbet/rbi/annotations/rainbow.rbi
|
60
|
+
- sorbet/rbi/dsl/.gitattributes
|
56
61
|
- sorbet/rbi/gems/.gitattributes
|
57
62
|
- sorbet/rbi/gems/ast@2.4.2.rbi
|
58
63
|
- sorbet/rbi/gems/benchmark@0.4.0.rbi
|