mangrove 0.30.1 → 0.34.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +107 -43
  3. data/lib/mangrove/result.rb +78 -0
  4. data/lib/mangrove/version.rb +1 -1
  5. data/lib/tapioca/dsl/compilers/mangrove_enum.rb +7 -1
  6. data/sorbet/config +1 -0
  7. data/sorbet/rbi/gems/benchmark@0.4.0.rbi +618 -0
  8. data/sorbet/rbi/gems/date@3.4.1.rbi +75 -0
  9. data/sorbet/rbi/gems/{docile@1.4.0.rbi → docile@1.4.1.rbi} +2 -1
  10. data/sorbet/rbi/gems/{erubi@1.12.0.rbi → erubi@1.13.1.rbi} +26 -17
  11. data/sorbet/rbi/gems/{json@2.7.2.rbi → json@2.9.1.rbi} +516 -134
  12. data/sorbet/rbi/gems/logger@1.6.5.rbi +940 -0
  13. data/sorbet/rbi/gems/{parallel@1.24.0.rbi → parallel@1.26.3.rbi} +31 -21
  14. data/sorbet/rbi/gems/{parser@3.3.2.0.rbi → parser@3.3.7.0.rbi} +23 -1736
  15. data/sorbet/rbi/gems/{prism@0.29.0.rbi → prism@1.3.0.rbi} +13817 -10401
  16. data/sorbet/rbi/gems/{psych@5.1.2.rbi → psych@5.2.3.rbi} +289 -236
  17. data/sorbet/rbi/gems/{racc@1.8.0.rbi → racc@1.8.1.rbi} +0 -4
  18. data/sorbet/rbi/gems/rbi@0.2.3.rbi +4542 -0
  19. data/sorbet/rbi/gems/rbs@3.8.1.rbi +6882 -0
  20. data/sorbet/rbi/gems/{rdoc@6.7.0.rbi → rdoc@6.11.0.rbi} +1115 -1058
  21. data/sorbet/rbi/gems/{regexp_parser@2.9.2.rbi → regexp_parser@2.10.0.rbi} +193 -170
  22. data/sorbet/rbi/gems/{rspec-core@3.13.0.rbi → rspec-core@3.13.2.rbi} +146 -280
  23. data/sorbet/rbi/gems/{rspec-expectations@3.13.0.rbi → rspec-expectations@3.13.3.rbi} +323 -294
  24. data/sorbet/rbi/gems/{rspec-mocks@3.13.1.rbi → rspec-mocks@3.13.2.rbi} +46 -46
  25. data/sorbet/rbi/gems/{rspec-support@3.13.1.rbi → rspec-support@3.13.2.rbi} +22 -22
  26. data/sorbet/rbi/gems/ruboclean@0.7.1.rbi +473 -0
  27. data/sorbet/rbi/gems/{rubocop-ast@1.31.3.rbi → rubocop-ast@1.37.0.rbi} +1293 -745
  28. data/sorbet/rbi/gems/{rubocop-rspec@2.30.0.rbi → rubocop-rspec@3.4.0.rbi} +341 -1073
  29. data/sorbet/rbi/gems/{rubocop@1.64.1.rbi → rubocop@1.70.0.rbi} +5693 -3796
  30. data/sorbet/rbi/gems/{simplecov-html@0.12.3.rbi → simplecov-html@0.13.1.rbi} +77 -68
  31. data/sorbet/rbi/gems/{spoom@1.3.2.rbi → spoom@1.5.1.rbi} +2306 -1701
  32. data/sorbet/rbi/gems/{stringio@3.1.0.rbi → stringio@3.1.2.rbi} +1 -0
  33. data/sorbet/rbi/gems/{tapioca@0.14.3.rbi → tapioca@0.16.8.rbi} +411 -332
  34. data/sorbet/rbi/gems/{thor@1.3.1.rbi → thor@1.3.2.rbi} +57 -31
  35. data/sorbet/rbi/gems/unicode-display_width@3.1.4.rbi +132 -0
  36. data/sorbet/rbi/gems/unicode-emoji@4.0.4.rbi +251 -0
  37. data/sorbet/rbi/gems/{webrick@1.8.1.rbi → webrick@1.9.1.rbi} +92 -72
  38. data/sorbet/rbi/gems/{yard-sorbet@0.8.1.rbi → yard-sorbet@0.9.0.rbi} +36 -29
  39. data/sorbet/rbi/gems/{yard@0.9.36.rbi → yard@0.9.37.rbi} +393 -235
  40. metadata +39 -42
  41. data/sorbet/rbi/gems/rbi@0.1.13.rbi +0 -3078
  42. data/sorbet/rbi/gems/rexml@3.2.8.rbi +0 -4794
  43. data/sorbet/rbi/gems/ruboclean@0.6.0.rbi +0 -315
  44. data/sorbet/rbi/gems/rubocop-capybara@2.20.0.rbi +0 -1208
  45. data/sorbet/rbi/gems/rubocop-factory_bot@2.25.1.rbi +0 -928
  46. data/sorbet/rbi/gems/rubocop-rspec_rails@2.28.3.rbi +0 -911
  47. data/sorbet/rbi/gems/strscan@3.1.0.rbi +0 -9
  48. data/sorbet/rbi/gems/unicode-display_width@2.5.0.rbi +0 -65
  49. /data/sorbet/rbi/gems/{io-console@0.7.2.rbi → io-console@0.8.0.rbi} +0 -0
  50. /data/sorbet/rbi/gems/{reline@0.5.8.rbi → reline@0.6.0.rbi} +0 -0
  51. /data/sorbet/rbi/gems/{ruby-lsp@0.17.2.rbi → ruby-lsp@0.23.6.rbi} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 71226a1d7d1fb7eba306e5838de3bb3f0558c5ecaaf765c4f598fbeac1177e5c
4
- data.tar.gz: abce827cd50183555927cf71dbd87d04d2e42273ac71c2eeee6e7d14307ba95c
3
+ metadata.gz: 1dfe08aeea0151d419caef995589fe18b98674aeb78817f651d58eef0b45e552
4
+ data.tar.gz: 070b2971e1f62072b6e26289fe7e04f0f5527c414db63664cb76ef82c19b9ffd
5
5
  SHA512:
6
- metadata.gz: 973e7abe2127c628b8fd3172f62d82103c571910269b6a0256bb6f405ae29eefbd4779bcef569369a5ab79b5a8fc1709c6ef5190f9b2af26be04eb9a8a377d39
7
- data.tar.gz: 8daf78cf239f11cd7bf83efad79dfe163f42953dc7d9be08e1bf0dc6a5976db6630f5c5b037062bc2acabe69c568737819adeaea158b42b0d91188006399910b
6
+ metadata.gz: 75bc64b315237e3e0c44ce8245a13e4562daf2ac2444cd3aff98151869f3c3f796259b9f62ab3d3b5fc2a58749033e3839e44d86785c20f0ec1a6b3a51e23474
7
+ data.tar.gz: 60204fffda1d393f5d1b6b0c893c29757d66c14c149685b0e138d2b1e86926da61784315f789c45dcec549dd564cc87f0c7166f9c5f259ce9de05081d41ddac2
data/README.md CHANGED
@@ -1,79 +1,124 @@
1
1
  # Mangrove
2
2
 
3
- Mangrove is a Ruby Gem designed to be the definitive toolkit for leveraging Sorbet's type system in Ruby applications. It's designed to offer a robust, statically-typed experience, focusing on solid types, a functional programming style, and an interface-driven approach.
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
- - [Documentation](https://kazzix14.github.io/mangrove/docs/)
6
- - [Coverage](https://kazzix14.github.io/mangrove/coverage/index.html#_AllFiles)
5
+ - **[Documentation](https://kazzix14.github.io/mangrove/docs/)**
6
+ - **[Coverage](https://kazzix14.github.io/mangrove/coverage/index.html#_AllFiles)**
7
7
 
8
- ## Features
8
+ ---
9
9
 
10
- - Option Type
11
- - Result Type
12
- - Enums with inner types (ADTs)
10
+ ## Highlights
11
+
12
+ - **Sorbet Integration**
13
+ Built from the ground up to work smoothly with Sorbet’s type system.
14
+
15
+ - **Result Type**
16
+ Model success/failure outcomes with explicit types—no more “return false or nil” for errors!
17
+
18
+ - **Enums (ADTs)**
19
+ Define your own sealed enums with typed variants. Each variant can hold distinct inner data.
20
+
21
+ - **Functional Patterns**
22
+ Chain transformations or short-circuit error handling with a clean monadic style.
23
+
24
+ ---
13
25
 
14
26
  ## Installation
15
27
 
16
- ```
28
+ ```bash
17
29
  bundle add mangrove
18
30
  ```
19
31
 
20
- ## Usage
32
+ ---
33
+
34
+ ## Usage Overview
35
+
36
+ Mangrove revolves around **`Result`** and a sealed “Enum” mechanism for ADTs.
37
+
38
+ ### Result
39
+
40
+ A `Result` is either `Ok(T)` or `Err(E)`. You can compose them with monadic methods like `and_then` and `or_else`.
41
+ For “early returns” in a functional style, you have two main approaches:
21
42
 
22
- [Documentation is available here](https://kazzix14.github.io/mangrove/docs).
23
- For more concrete examples, see [`spec/**/**_spec.rb`](https://github.com/kazzix14/mangrove/tree/main/spec).
43
+ 1. **A context-based DSL (e.g., `ctx.try!`)**
44
+ 2. **An instance method on `Result` itself, `unwrap_in(ctx)`**, which behaves similarly.
45
+
46
+ Here’s an example of chaining requests and short-circuiting on error:
24
47
 
25
48
  ```ruby
26
- Mangrove::Result[OkType, ErrType]
27
- Mangrove::Result::Ok[OkType, ErrType]
28
- Mangrove::Result::Err[OkType, ErrType]
29
- Mangrove::Option[InnerType]
30
- Mangrove::Option::Some[InnerType]
31
- Mangrove::Option::None[InnerType]
32
-
33
- my_ok = Result::Ok.new("my value")
34
- my_err = Result::Err.new("my err")
35
- my_some = Option::Some.new(1234)
36
- my_none = Option::None.new
37
-
38
- ##############################
39
-
40
- response = MyClient
41
- .new
42
- .and_then { |client| client.get_response() }
43
- .and_then { |response| response.body }
44
-
45
- case response
46
- when Mangrove::Result::Ok
47
- puts response.ok_inner
48
- when Mangrove::Result::Err
49
- puts response.err_inner
49
+ class MyClient
50
+ extend T::Sig
51
+
52
+ sig { returns(Mangrove::Result[String, StandardError]) }
53
+ def connect
54
+ # ...
55
+ Mangrove::Result::Ok.new("Connected")
56
+ end
57
+
58
+ sig { params(data: String).returns(Mangrove::Result[String, StandardError]) }
59
+ def request(data)
60
+ # ...
61
+ Mangrove::Result::Ok.new("Response: #{data}")
62
+ end
63
+ end
64
+
65
+ # Let's say we have a special DSL context for collecting short-circuits:
66
+ # (Hypothetical usage)
67
+
68
+ Result.collecting(String, StandardError) do |ctx|
69
+ final_result = MyClient.new
70
+ .connect
71
+ .and_then do |connection|
72
+ MyClient.new.request("Payload from #{connection}")
73
+ end
74
+
75
+ # Option 1: Call from the context
76
+ response_data = ctx.try!(final_result)
77
+ # => If 'final_result' is Err, short-circuits now;
78
+ # otherwise returns the Ok(T) value.
79
+
80
+ puts response_data # If no errors, prints "Response: Connected"
81
+
82
+ # Option 2: Call via 'unwrap_in(ctx)'
83
+ # This does the same short-circuit if 'Err', using the context:
84
+ response_data_alt = final_result.unwrap_in(ctx)
50
85
  end
51
86
 
52
- ##############################
87
+ # More chaining, etc...
88
+ ```
89
+
90
+ ### Enums (ADTs)
91
+
92
+ Define an enum with typed variants:
53
93
 
94
+ ```ruby
54
95
  class MyEnum
55
96
  extend Mangrove::Enum
56
97
 
57
98
  variants do
58
- variant VariantWithInteger, Integer
59
- variant VariantWithString, String
60
- variant VariantWithException, Exception
61
- variant VariantWithTuple, [Integer, String]
62
- variant VariantWithShape, { name: String, age: Integer }
99
+ variant IntVariant, Integer
100
+ variant StrVariant, String
101
+ variant ShapeVariant, { name: String, age: Integer }
63
102
  end
64
103
  end
104
+
105
+ int_v = MyEnum::IntVariant.new(123)
106
+ puts int_v.inner # => 123
65
107
  ```
66
108
 
109
+ For more details on monadic methods, short-circuit contexts, and advanced usage, please visit the [official documentation](https://kazzix14.github.io/mangrove/docs/) or see real-world usages in [`spec/`](https://github.com/kazzix14/mangrove/tree/main/spec).
110
+
111
+ ---
112
+
67
113
  ## Commands for Development
68
114
 
69
- ```
115
+ ```bash
70
116
  git config core.hooksPath hooks
71
117
  bundle install
72
118
  bundle exec tapioca init
73
119
  bundle exec tapioca gems -w `nproc`
74
120
  bundle exec tapioca dsl -w `nproc`
75
121
  bundle exec tapioca check-shims
76
- bundle exec tapioca init
77
122
  bundle exec rspec -f d
78
123
  bundle exec rubocop -DESP
79
124
  bundle exec srb typecheck
@@ -85,3 +130,22 @@ bundle exec yard server --reload --plugin yard-sorbet
85
130
  rake build
86
131
  rake release
87
132
  ```
133
+
134
+ Run these commands to maintain code quality, generate documentation, and verify type safety under Sorbet.
135
+
136
+ ---
137
+
138
+ ## Contributing
139
+
140
+ We welcome contributions! To get started:
141
+
142
+ 1. Fork & clone the repo
143
+ 2. Install dependencies: `bundle install`
144
+ 3. Make your changes and add tests
145
+ 4. Submit a PR
146
+
147
+ ---
148
+
149
+ ## License
150
+
151
+ Mangrove is available under the [MIT License](https://opensource.org/licenses/MIT). See the LICENSE file for details.
@@ -42,6 +42,9 @@ module Mangrove
42
42
  sig { abstract.returns(OkType) }
43
43
  def unwrap_or_raise_inner!; end
44
44
 
45
+ sig { abstract.params(ctx: Result::CollectingContext[OkType, ErrType]).returns(OkType) }
46
+ def unwrap_in(ctx); end
47
+
45
48
  sig { abstract.params(message: String).returns(OkType) }
46
49
  def expect!(message); end
47
50
 
@@ -66,6 +69,12 @@ module Mangrove
66
69
  sig { abstract.type_parameters(:NewErrType).params(_t_new_err: T::Class[T.type_parameter(:NewErrType)], block: T.proc.params(this: ErrType).returns(T.type_parameter(:NewErrType))).returns(Result[OkType, T.type_parameter(:NewErrType)]) }
67
70
  def map_err_wt(_t_new_err, &block); end
68
71
 
72
+ sig { abstract.params(block: T.proc.params(this: OkType).void).returns(Result[OkType, ErrType]) }
73
+ def tap_ok(&block); end
74
+
75
+ sig { abstract.params(block: T.proc.params(this: ErrType).void).returns(Result[OkType, ErrType]) }
76
+ def tap_err(&block); end
77
+
69
78
  sig { abstract.type_parameters(:NewOkType, :NewErrType).params(other: Result[T.type_parameter(:NewOkType), T.type_parameter(:NewErrType)]).returns(T.any(Result[T.type_parameter(:NewOkType), T.type_parameter(:NewErrType)], Result[T.type_parameter(:NewOkType), ErrType])) }
70
79
  def and(other); end
71
80
 
@@ -151,6 +160,43 @@ module Mangrove
151
160
  def err_wt(_t_ok, inner)
152
161
  Result::Err[T.type_parameter(:ErrType)].new(inner)
153
162
  end
163
+
164
+ sig {
165
+ type_parameters(:O, :E)
166
+ .params(
167
+ _t_ok: T::Class[T.type_parameter(:O)],
168
+ _t_err: T::Class[T.type_parameter(:E)],
169
+ block: T.proc.params(
170
+ do_block: CollectingContext[T.type_parameter(:O), T.type_parameter(:E)]
171
+ ).returns(Mangrove::Result[T.type_parameter(:O), T.type_parameter(:E)])
172
+ )
173
+ .returns(Mangrove::Result[T.type_parameter(:O), T.type_parameter(:E)])
174
+ }
175
+ def collecting(_t_ok, _t_err, &block)
176
+ catch(:__mangrove_result_collecting_context_return) {
177
+ block.call(CollectingContext[T.type_parameter(:O), T.type_parameter(:E)].new)
178
+ }
179
+ end
180
+ end
181
+
182
+ class CollectingContext
183
+ extend T::Sig
184
+ extend T::Generic
185
+
186
+ O = type_member
187
+ E = type_member
188
+
189
+ sig { params(result: Mangrove::Result[O, E]).returns(O) }
190
+ def try!(result)
191
+ case result
192
+ when Mangrove::Result::Ok
193
+ result.ok_inner
194
+ when Mangrove::Result::Err
195
+ throw :__mangrove_result_collecting_context_return, result
196
+ else
197
+ T.absurd(result)
198
+ end
199
+ end
154
200
  end
155
201
 
156
202
  class Ok
@@ -206,6 +252,11 @@ module Mangrove
206
252
  @inner
207
253
  end
208
254
 
255
+ sig { override.params(_ctx: Result::CollectingContext[OkType, ErrType]).returns(OkType) }
256
+ def unwrap_in(_ctx)
257
+ @inner
258
+ end
259
+
209
260
  sig { override.params(_message: String).returns(OkType) }
210
261
  def expect!(_message)
211
262
  @inner
@@ -256,6 +307,17 @@ module Mangrove
256
307
  self
257
308
  end
258
309
 
310
+ sig { override.params(block: T.proc.params(this: OkType).void).returns(Result[OkType, ErrType]) }
311
+ def tap_ok(&block)
312
+ block.call(@inner)
313
+ self
314
+ end
315
+
316
+ sig { override.params(_block: T.proc.params(this: ErrType).void).returns(Result[OkType, ErrType]) }
317
+ def tap_err(&_block)
318
+ self
319
+ end
320
+
259
321
  sig { override.type_parameters(:NewOkType, :NewErrType).params(other: Result[T.type_parameter(:NewOkType), T.type_parameter(:NewErrType)]).returns(Result[T.type_parameter(:NewOkType), T.type_parameter(:NewErrType)]) }
260
322
  def and(other)
261
323
  other
@@ -381,6 +443,11 @@ module Mangrove
381
443
  raise T.unsafe(@inner)
382
444
  end
383
445
 
446
+ sig { override.params(_ctx: Result::CollectingContext[OkType, ErrType]).returns(T.noreturn) }
447
+ def unwrap_in(_ctx)
448
+ throw :__mangrove_result_collecting_context_return, self
449
+ end
450
+
384
451
  sig { override.params(message: String).returns(OkType) }
385
452
  def expect!(message)
386
453
  raise Result::ControlSignal, message
@@ -429,6 +496,17 @@ module Mangrove
429
496
  Result::Err[T.type_parameter(:NewErrType)].new(block.call(@inner))
430
497
  end
431
498
 
499
+ sig { override.params(_block: T.proc.params(this: OkType).void).returns(Result[OkType, ErrType]) }
500
+ def tap_ok(&_block)
501
+ self
502
+ end
503
+
504
+ sig { override.params(block: T.proc.params(this: ErrType).void).returns(Result[OkType, ErrType]) }
505
+ def tap_err(&block)
506
+ block.call(@inner)
507
+ self
508
+ end
509
+
432
510
  sig { override.type_parameters(:NewOkType, :NewErrType).params(_other: Result[T.type_parameter(:NewOkType), T.type_parameter(:NewErrType)]).returns(Result[T.type_parameter(:NewOkType), ErrType]) }
433
511
  def and(_other)
434
512
  self
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Mangrove
5
- VERSION = "0.30.1"
5
+ VERSION = "0.34.0"
6
6
  end
@@ -48,7 +48,13 @@ module Tapioca
48
48
  inner_type
49
49
  }
50
50
 
51
- constant_type.create_method("inner", return_type: "T.any(#{inner_types.join(", ")})")
51
+ return_type = if inner_types.size == 1
52
+ T.must(inner_types.first)
53
+ else
54
+ "T.any(#{inner_types.join(", ")})"
55
+ end
56
+
57
+ constant_type.create_method("inner", return_type:)
52
58
  constant_type.create_method("as_super", return_type: constant.name.to_s)
53
59
  constant_type.sort_nodes!
54
60
  }
data/sorbet/config CHANGED
@@ -3,3 +3,4 @@
3
3
  --ignore=tmp/
4
4
  --ignore=vendor/
5
5
  --enable-experimental-requires-ancestor
6
+ --disable-watchman