sorbet_typed-props 0.1.1 → 0.2.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/.cz.yaml +20 -0
- data/.pre-commit-config.yaml +8 -0
- data/.shellcheckrc.tmp +14 -0
- data/CHANGELOG.md +54 -0
- data/README.md +24 -1
- data/lib/sorbet_typed/props/version.rb +6 -2
- data/lib/sorbet_typed/props.rb +11 -9
- data/lib/tapioca/dsl/compilers/sorbet_typed_props_constructor/prop_to_param_converter.rb +1 -1
- data/lib/tapioca/dsl/compilers/sorbet_typed_props_constructor.rb +11 -2
- metadata +9 -11
- data/lib/sorbet_typed/railway.rb.tmp +0 -9
- data/lib/sorbet_typed/railway.tmp/fail_fast.rb.tmp +0 -16
- data/lib/sorbet_typed/railway.tmp/railway.rb.tmp +0 -259
- data/lib/sorbet_typed/railway.tmp/version.rb.tmp +0 -8
- data/lib/sorbet_typed/railway.tmp/version_spec.rb.tmp +0 -10
- data/lib/sorbet_typed/railway_spec.rb.tmp +0 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e2b4406fdae5d3c123f3af9dbb6e3c8330fbb13dfa25aa047b010565971f7e84
|
|
4
|
+
data.tar.gz: e76a37ee0d36b57307c95a09f76befb494bbec6e3319eece03c80d22f9e6d3f2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f21a3d18b847cf6cbb6cca9ea453a1887c1dee87a9cd656408a1572bb6851a583bf078fe04f3c323e38d785ee0e8d6a0d572c5bf980735b4df33870166890551
|
|
7
|
+
data.tar.gz: 8351ee9a1af05ad8298b2a8b5ac7d1f46c98ea98d1df1de8a67d89d6f4afaa1019f3b6534ab3e772610146274fc0a5f4b475fcd3b43aa20482f6eec071fa2ba6
|
data/.cz.yaml
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
commitizen:
|
|
3
|
+
allowed_prefixes:
|
|
4
|
+
- fixup!
|
|
5
|
+
- squash!
|
|
6
|
+
- Merge
|
|
7
|
+
- Revert
|
|
8
|
+
- '[WIP]'
|
|
9
|
+
annotated_tag: true
|
|
10
|
+
name: cz_conventional_commits
|
|
11
|
+
pre_bump_hooks:
|
|
12
|
+
- mise x -- bundle install
|
|
13
|
+
- mise run update
|
|
14
|
+
- mise run format
|
|
15
|
+
tag_format: v$version
|
|
16
|
+
update_changelog_on_bump: true
|
|
17
|
+
version: 0.2.0
|
|
18
|
+
version_files:
|
|
19
|
+
- lib/sorbet_typed/props/version.rb
|
|
20
|
+
version_scheme: semver2
|
data/.shellcheckrc.tmp
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# WIP: use this configuration
|
|
2
|
+
|
|
3
|
+
disable=SC1091
|
|
4
|
+
|
|
5
|
+
# spellchecker:ignore nullary
|
|
6
|
+
enable=add-default-case
|
|
7
|
+
enable=avoid-nullary-conditions
|
|
8
|
+
enable=check-extra-masked-returns
|
|
9
|
+
enable=check-set-e-suppressed
|
|
10
|
+
enable=check-unassigned-uppercase
|
|
11
|
+
enable=deprecate-which
|
|
12
|
+
enable=quote-safe-variables
|
|
13
|
+
enable=require-double-brackets
|
|
14
|
+
enable=require-variable-braces
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
## v0.2.0 (2025-11-05)
|
|
2
|
+
|
|
3
|
+
### Feat
|
|
4
|
+
|
|
5
|
+
- **renovate**: update CI matrix on lockFileMaintenance
|
|
6
|
+
- **ci**: trigger renovate pipeline on default branch
|
|
7
|
+
- **mise**: change default task output to "replacing"
|
|
8
|
+
- **mise**: execute typecheck when running lint task
|
|
9
|
+
- **renovate**: remove manual usage dependency declaration, as renovate seems to know it now
|
|
10
|
+
- **renovate**: do postUpgradeTasks for lockfileMaintenance via mise and move update:appraisal task to file
|
|
11
|
+
|
|
12
|
+
### Fix
|
|
13
|
+
|
|
14
|
+
- **tapioca**: put optional params at end of signature, independent from their definition order
|
|
15
|
+
- **ci**: adjust job rules
|
|
16
|
+
- **sorbet**: robust tapioca dsl update/verification + removal of unused DSL RBIs
|
|
17
|
+
- **sorbet**: exclude local gem from RBI generation
|
|
18
|
+
- **renovate**: group ci ruby version matrix upgrades
|
|
19
|
+
- **ci**: run ruby version matrix validation only for merge request events
|
|
20
|
+
- **ci**: generate ruby version matrix only for versions with valid docker images
|
|
21
|
+
- **appraisals**: make appraisal gem versions always deterministic by tracking lock files and update them via renovate
|
|
22
|
+
lockFileMaintenance
|
|
23
|
+
- **coverage**: explicitly track all relevant ruby files, even if they don't get required
|
|
24
|
+
- **ci**: make pipeline jobs interruptible by default to support canceling redundant pipelines
|
|
25
|
+
- **ci**: check coverage when testing
|
|
26
|
+
|
|
27
|
+
## v0.1.1 (2025-10-17)
|
|
28
|
+
|
|
29
|
+
### Feat
|
|
30
|
+
|
|
31
|
+
- **gem**: configure for publishing
|
|
32
|
+
- **renovate, gemspec**: pin upper ruby version and manage with renovate
|
|
33
|
+
- **ci**: don't version pin ruby images in CI matrix
|
|
34
|
+
- **license**: add GPL v3 license
|
|
35
|
+
- **ci**: test against multiple ruby versions
|
|
36
|
+
- **renovate**: automatically update appraisals when updating gemspecs
|
|
37
|
+
- **mise**: add formatting task
|
|
38
|
+
- **ci**: add docker tag to use private runner
|
|
39
|
+
- **props**: setup gemspec
|
|
40
|
+
- **renovate**: group specific update types for bun and ruby
|
|
41
|
+
- **props**: re-add, refactor and test tapioca compiler
|
|
42
|
+
- **railway**: add first implementation mvp
|
|
43
|
+
|
|
44
|
+
### Fix
|
|
45
|
+
|
|
46
|
+
- **renovate**: use ruby as mise tool in ruby postUpgradeTasks
|
|
47
|
+
- **renovate**: better ruby postUpgradeTask to ensure, updates actually run or at least fail instead of being suppressed
|
|
48
|
+
- **renovate**: correctly run file update tasks after rubygems updates
|
|
49
|
+
- **renovate**: correctly update mise versions in renovate.json
|
|
50
|
+
- **renovate**: ensure correct postUpgradeTask execution
|
|
51
|
+
- **dependencies**: move from appraisal to appraisal2
|
|
52
|
+
- **mise**: do not install appraisal in test task, as it reformats the gemspecs
|
|
53
|
+
- **renovate**: use correct docker tag to group bun updates
|
|
54
|
+
- **renovate**: disable digest pinning for custom regex managers
|
data/README.md
CHANGED
|
@@ -9,9 +9,16 @@ class.
|
|
|
9
9
|
This should make it easier to create classes with a set of attributes they should be initialized with. Inspiration was
|
|
10
10
|
the
|
|
11
11
|
[integration of literal properties into phlex](https://www.phlex.fun/miscellaneous/literal-properties.html#literal-properties),
|
|
12
|
-
which doesn't really work with sorbet, if you want to have everything fully typed (and not use two runtime
|
|
12
|
+
which doesn't really work with sorbet, if you want to have everything fully typed (and not use two runtime type-systems
|
|
13
13
|
in parallel).
|
|
14
14
|
|
|
15
|
+
- [Installation](#installation)
|
|
16
|
+
- [Usage](#usage)
|
|
17
|
+
- [Phlex Example](#phlex-example)
|
|
18
|
+
- [Method Visibility](#method-visibility)
|
|
19
|
+
- [Development](#development)
|
|
20
|
+
- [Contributing](#contributing)
|
|
21
|
+
|
|
15
22
|
## Installation
|
|
16
23
|
|
|
17
24
|
Install the gem and add to the application's Gemfile by executing:
|
|
@@ -43,6 +50,22 @@ my_object.my_prop # => "foo"
|
|
|
43
50
|
my_object.my_prop = 'bar'
|
|
44
51
|
```
|
|
45
52
|
|
|
53
|
+
### Phlex Example
|
|
54
|
+
|
|
55
|
+
```ruby
|
|
56
|
+
class Components::HelloWorld < Phlex::HTML
|
|
57
|
+
extend T::Sig
|
|
58
|
+
include SorbetTyped::Props
|
|
59
|
+
|
|
60
|
+
prop :name, String
|
|
61
|
+
|
|
62
|
+
sig { void }
|
|
63
|
+
def view_template
|
|
64
|
+
h1 { "Hello, #{name}!" }
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
```
|
|
68
|
+
|
|
46
69
|
### Method Visibility
|
|
47
70
|
|
|
48
71
|
If you want attributes in your initializer but not be part of your public class interface, you can use ruby's visibility
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
# typed: strict
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
# :nocov: -- The file seems to get required before simplecov is started
|
|
5
|
+
|
|
6
|
+
module SorbetTyped
|
|
5
7
|
module Props
|
|
6
|
-
VERSION = '0.
|
|
8
|
+
VERSION = '0.2.0'
|
|
7
9
|
end
|
|
8
10
|
end
|
|
11
|
+
|
|
12
|
+
# :nocov:
|
data/lib/sorbet_typed/props.rb
CHANGED
|
@@ -4,15 +4,17 @@
|
|
|
4
4
|
require 'sorbet-runtime'
|
|
5
5
|
require_relative 'props/version'
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
module SorbetTyped
|
|
8
|
+
# an abstraction module to be included in any class to make the sorbet props
|
|
9
|
+
# interface usable. It's just a wrapper around T::Props and
|
|
10
|
+
# T::Props::Constructor. It's also the ancestor the custom tapioca compiler
|
|
11
|
+
# looks for when generating initializer type annotation rbis.
|
|
12
|
+
module Props
|
|
13
|
+
extend T::Helpers
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
include T::Props
|
|
16
|
+
include T::Props::Constructor
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
mixes_in_class_methods(T::Props::ClassMethods)
|
|
19
|
+
end
|
|
18
20
|
end
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
require_relative 'sorbet_typed_props_constructor/prop_to_param_converter'
|
|
5
5
|
|
|
6
|
-
module Tapioca
|
|
6
|
+
module Tapioca
|
|
7
7
|
module Dsl
|
|
8
8
|
module Compilers
|
|
9
9
|
# tapioca compiler generating rbi containing the initializer interface for a class using sorbet SorbetTyped::Props
|
|
@@ -63,12 +63,21 @@ module Tapioca # rubocop:disable Style/ClassAndModuleChildren
|
|
|
63
63
|
root.create_path(constant) do |klass|
|
|
64
64
|
klass.create_method(
|
|
65
65
|
'initialize',
|
|
66
|
-
parameters: params,
|
|
66
|
+
parameters: params_with_optional_last(params:),
|
|
67
67
|
return_type: 'void',
|
|
68
68
|
comments: [RBI::Comment.new('Generated from sorbet SorbetTyped::Props')]
|
|
69
69
|
)
|
|
70
70
|
end
|
|
71
71
|
end
|
|
72
|
+
|
|
73
|
+
sig { params(params: T::Array[RBI::TypedParam]).returns(T::Array[RBI::TypedParam]) }
|
|
74
|
+
def params_with_optional_last(params:)
|
|
75
|
+
required_params, optional_params = params.partition do |param|
|
|
76
|
+
param.param.is_a?(RBI::KwParam)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
required_params + optional_params
|
|
80
|
+
end
|
|
72
81
|
end
|
|
73
82
|
end
|
|
74
83
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sorbet_typed-props
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Richard Kramer
|
|
@@ -18,7 +18,7 @@ dependencies:
|
|
|
18
18
|
version: '0.5'
|
|
19
19
|
- - "<="
|
|
20
20
|
- !ruby/object:Gem::Version
|
|
21
|
-
version: 0.6.
|
|
21
|
+
version: 0.6.12650
|
|
22
22
|
type: :runtime
|
|
23
23
|
prerelease: false
|
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -28,7 +28,7 @@ dependencies:
|
|
|
28
28
|
version: '0.5'
|
|
29
29
|
- - "<="
|
|
30
30
|
- !ruby/object:Gem::Version
|
|
31
|
-
version: 0.6.
|
|
31
|
+
version: 0.6.12650
|
|
32
32
|
- !ruby/object:Gem::Dependency
|
|
33
33
|
name: tapioca
|
|
34
34
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -38,7 +38,7 @@ dependencies:
|
|
|
38
38
|
version: 0.16.11
|
|
39
39
|
- - "<="
|
|
40
40
|
- !ruby/object:Gem::Version
|
|
41
|
-
version: 0.17.
|
|
41
|
+
version: 0.17.9
|
|
42
42
|
type: :runtime
|
|
43
43
|
prerelease: false
|
|
44
44
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -48,7 +48,7 @@ dependencies:
|
|
|
48
48
|
version: 0.16.11
|
|
49
49
|
- - "<="
|
|
50
50
|
- !ruby/object:Gem::Version
|
|
51
|
-
version: 0.17.
|
|
51
|
+
version: 0.17.9
|
|
52
52
|
description: A small wrapper around sorbets T::Props and T::Props::Constructor. Also
|
|
53
53
|
provides an accompanying tapioca compiler to allow using the prop-syntax in any
|
|
54
54
|
class and getting the correct signature for an initializer.
|
|
@@ -58,16 +58,14 @@ executables: []
|
|
|
58
58
|
extensions: []
|
|
59
59
|
extra_rdoc_files: []
|
|
60
60
|
files:
|
|
61
|
+
- ".cz.yaml"
|
|
62
|
+
- ".pre-commit-config.yaml"
|
|
63
|
+
- ".shellcheckrc.tmp"
|
|
64
|
+
- CHANGELOG.md
|
|
61
65
|
- COPYING
|
|
62
66
|
- README.md
|
|
63
67
|
- lib/sorbet_typed/props.rb
|
|
64
68
|
- lib/sorbet_typed/props/version.rb
|
|
65
|
-
- lib/sorbet_typed/railway.rb.tmp
|
|
66
|
-
- lib/sorbet_typed/railway.tmp/fail_fast.rb.tmp
|
|
67
|
-
- lib/sorbet_typed/railway.tmp/railway.rb.tmp
|
|
68
|
-
- lib/sorbet_typed/railway.tmp/version.rb.tmp
|
|
69
|
-
- lib/sorbet_typed/railway.tmp/version_spec.rb.tmp
|
|
70
|
-
- lib/sorbet_typed/railway_spec.rb.tmp
|
|
71
69
|
- lib/tapioca/dsl/compilers/sorbet_typed_props_constructor.rb
|
|
72
70
|
- lib/tapioca/dsl/compilers/sorbet_typed_props_constructor/prop_to_param_converter.rb
|
|
73
71
|
homepage: https://gitlab.com/sorbet_typed/props
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
# typed: strict
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
require 'sorbet-runtime'
|
|
5
|
-
require_relative 'railway/version'
|
|
6
|
-
|
|
7
|
-
# This class provides a monad-style railway pattern, inspired by dry-monads do-syntax
|
|
8
|
-
class SorbetTyped::Railway # rubocop:todo Lint/EmptyClass
|
|
9
|
-
end
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# typed: strict
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
class Typed::Monads::FailFast
|
|
5
|
-
extend T::Generic
|
|
6
|
-
|
|
7
|
-
Elem = type_member(:out)
|
|
8
|
-
|
|
9
|
-
sig { returns(Elem) }
|
|
10
|
-
attr_reader :value
|
|
11
|
-
|
|
12
|
-
sig { params(value: Elem).void }
|
|
13
|
-
def initialize(value)
|
|
14
|
-
@value = value
|
|
15
|
-
end
|
|
16
|
-
end
|
|
@@ -1,259 +0,0 @@
|
|
|
1
|
-
# typed: strict
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
# WIP: do we need https://sorbet.org/docs/exhaustiveness
|
|
5
|
-
|
|
6
|
-
# WIP: move subclasses into own files
|
|
7
|
-
|
|
8
|
-
# implementation of a sorbet-typed and -compatible monad-style short-circuit
|
|
9
|
-
# pattern where you can run multiple steps, use their value OR let them return
|
|
10
|
-
# early.
|
|
11
|
-
#
|
|
12
|
-
# Developed by @kramer and pending to be made open source under
|
|
13
|
-
# https://gitlab.com/richardkramer/sorbet_typed. But for now we want to already
|
|
14
|
-
# use the code, so everything already working is copied here.
|
|
15
|
-
#
|
|
16
|
-
# How to use:
|
|
17
|
-
#
|
|
18
|
-
|
|
19
|
-
# ```ruby
|
|
20
|
-
# sig { returns(SuccessType, SorbetTyped::ShortCircuit::Shorted[ShortedType]))}
|
|
21
|
-
# def do_something
|
|
22
|
-
# return SorbetTyped::ShortCircuit.short(info: shorted_value) if something_wrong?
|
|
23
|
-
|
|
24
|
-
# return SuccessType.new
|
|
25
|
-
# end
|
|
26
|
-
#
|
|
27
|
-
# # this will return either the success type or a shorted result with the
|
|
28
|
-
# # shorted type as `info`.
|
|
29
|
-
# result = SorbetTyped::ShortCircuit[SuccessType, ShortedType].new.run do |circuit_breaker|
|
|
30
|
-
# foo = circuit_breaker.(do_something) # => will break early if do_something returns a circuit breaking result
|
|
31
|
-
# bar = circuit_breaker.(do_something_else(foo)) # => will break early if do_something_else(foo) returns a circuit breaking result
|
|
32
|
-
|
|
33
|
-
# do_another_thing(bar)
|
|
34
|
-
# end
|
|
35
|
-
# ```
|
|
36
|
-
#
|
|
37
|
-
# SuccessType and ShortedType might be anything you want. Just specify the types
|
|
38
|
-
# when initializing the SorbetTyped::ShortCircuit instance.
|
|
39
|
-
module SorbetTyped
|
|
40
|
-
class ShortCircuit
|
|
41
|
-
extend T::Generic
|
|
42
|
-
|
|
43
|
-
# The error thrown and handled within the run block to implement failing fast
|
|
44
|
-
# from any nesting depth
|
|
45
|
-
class CircuitShortedError < StandardError
|
|
46
|
-
extend T::Generic
|
|
47
|
-
|
|
48
|
-
FailureData = type_member(:out)
|
|
49
|
-
|
|
50
|
-
sig { returns(FailureData) }
|
|
51
|
-
attr_reader :failure
|
|
52
|
-
|
|
53
|
-
sig { params(failure: FailureData).void }
|
|
54
|
-
def initialize(failure:)
|
|
55
|
-
@failure = failure
|
|
56
|
-
super()
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
# Signal to fail fast. Info can be anything, e.g. an error object.
|
|
61
|
-
class Shorted
|
|
62
|
-
extend T::Generic
|
|
63
|
-
|
|
64
|
-
Elem = type_member(:out)
|
|
65
|
-
|
|
66
|
-
sig { returns(Elem) }
|
|
67
|
-
attr_reader :info
|
|
68
|
-
|
|
69
|
-
sig { params(info: Elem).void }
|
|
70
|
-
def initialize(info)
|
|
71
|
-
@info = info
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
# Callable implementing the logic to fail fast if the input is a Shorted
|
|
76
|
-
# signal. To be used like Dry::Monads `yield foo` syntax.
|
|
77
|
-
#
|
|
78
|
-
# Injected as parameter into a short circuit block, so you can use it directly.
|
|
79
|
-
class CircuitBreaker
|
|
80
|
-
extend T::Generic
|
|
81
|
-
|
|
82
|
-
Result = type_member
|
|
83
|
-
|
|
84
|
-
sig do
|
|
85
|
-
type_parameters(:U, :E).
|
|
86
|
-
params(result: T.all(T.type_parameter(:U), T.any(T.type_parameter(:E), Shorted[Result]))).
|
|
87
|
-
returns(T.type_parameter(:E))
|
|
88
|
-
end
|
|
89
|
-
def call(result)
|
|
90
|
-
case result
|
|
91
|
-
when Shorted
|
|
92
|
-
raise CircuitShortedError[T.type_parameter(:U)].new(failure: result)
|
|
93
|
-
else
|
|
94
|
-
result
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
SuccessResult = type_member
|
|
100
|
-
ShortedResult = type_member
|
|
101
|
-
|
|
102
|
-
sig do
|
|
103
|
-
type_parameters(:U).
|
|
104
|
-
params(info: T.type_parameter(:U)).
|
|
105
|
-
returns(Shorted[T.type_parameter(:U)])
|
|
106
|
-
end
|
|
107
|
-
def self.short(info:)
|
|
108
|
-
Shorted[T.type_parameter(:U)].new(info)
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
private
|
|
112
|
-
|
|
113
|
-
# Store if the last run failed fast
|
|
114
|
-
sig { returns(T::Boolean) }
|
|
115
|
-
attr_accessor :short_circuited
|
|
116
|
-
|
|
117
|
-
public
|
|
118
|
-
|
|
119
|
-
sig { void }
|
|
120
|
-
def initialize
|
|
121
|
-
@short_circuited = T.let(false, T::Boolean)
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
sig do
|
|
125
|
-
params(
|
|
126
|
-
_block:
|
|
127
|
-
T.proc.
|
|
128
|
-
params(fail_fast: CircuitBreaker[ShortedResult]).
|
|
129
|
-
returns(T.any(SuccessResult, Shorted[ShortedResult]))
|
|
130
|
-
).
|
|
131
|
-
returns(T.any(SuccessResult, Shorted[ShortedResult]))
|
|
132
|
-
end
|
|
133
|
-
def run(&_block)
|
|
134
|
-
self.short_circuited = false
|
|
135
|
-
yield(CircuitBreaker[ShortedResult].new)
|
|
136
|
-
rescue CircuitShortedError => exception
|
|
137
|
-
self.short_circuited = true
|
|
138
|
-
|
|
139
|
-
# NOTE: sorbet is only able to detect
|
|
140
|
-
# `SorbetTyped::ShortCircuit::CircuitShortedError[T.anything]` in this rescue. But
|
|
141
|
-
# because it must come from `CircuitBreaker[ShortedResult]`, it will also
|
|
142
|
-
# always be `CircuitShortedError[SorbetTyped::ShortCircuit::Shorted[ShortedResult]]`. So we
|
|
143
|
-
# tell that to sorbet.
|
|
144
|
-
#
|
|
145
|
-
# NOTE: if one would manually throw this exception WITH A DIFFERENT failure
|
|
146
|
-
# data type, this would not raise, because generic types get erased at
|
|
147
|
-
# runtime. So just NEVER raise the CircuitShortedError error manually.
|
|
148
|
-
T.cast(exception, CircuitShortedError[SorbetTyped::ShortCircuit::Shorted[ShortedResult]]).failure
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
sig do
|
|
152
|
-
params(
|
|
153
|
-
block:
|
|
154
|
-
T.proc.
|
|
155
|
-
params(fail_fast: CircuitBreaker[ShortedResult]).
|
|
156
|
-
returns(T.any(SuccessResult, SorbetTyped::ShortCircuit::Shorted[ShortedResult]))
|
|
157
|
-
).
|
|
158
|
-
returns(T.any(SuccessResult, ShortedResult))
|
|
159
|
-
end
|
|
160
|
-
def run_without_signal(&block)
|
|
161
|
-
result = run(&block)
|
|
162
|
-
|
|
163
|
-
case result
|
|
164
|
-
when SorbetTyped::ShortCircuit::Shorted
|
|
165
|
-
# NOTE: sorbet is only able to detect
|
|
166
|
-
# ```
|
|
167
|
-
# T.all(
|
|
168
|
-
# Typed::Monads::Shorted[T.anything],
|
|
169
|
-
# T.any(
|
|
170
|
-
# Typed::Monads::Shorted[Typed::Monads::ShortCircuit::ShortedResult],
|
|
171
|
-
# Typed::Monads::ShortCircuit::SuccessResult
|
|
172
|
-
# )
|
|
173
|
-
# )
|
|
174
|
-
# ```
|
|
175
|
-
# , which leaves no other option
|
|
176
|
-
# than `Typed::Monads::Shorted[ShortedResult]`. So we tell that to the
|
|
177
|
-
# typechecker.
|
|
178
|
-
T.cast(result, SorbetTyped::ShortCircuit::Shorted[ShortedResult]).info
|
|
179
|
-
else
|
|
180
|
-
result
|
|
181
|
-
end
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
sig { returns(T::Boolean) }
|
|
185
|
-
def last_run_short_circuited?
|
|
186
|
-
short_circuited
|
|
187
|
-
end
|
|
188
|
-
end
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
#####################################################################################################################
|
|
192
|
-
#####################################################################################################################
|
|
193
|
-
#####################################################################################################################
|
|
194
|
-
|
|
195
|
-
# An example class using above short circuit pattern. It's fully checked against
|
|
196
|
-
# sorbet. You might comment it in and run `srb tc` to see the `T.reveal_type`
|
|
197
|
-
# results in action.
|
|
198
|
-
#
|
|
199
|
-
# WIP: move to specs
|
|
200
|
-
class SorbetTyped::ShortCircuitExample
|
|
201
|
-
extend T::Sig
|
|
202
|
-
|
|
203
|
-
sig { void }
|
|
204
|
-
def call
|
|
205
|
-
# Success Value might be Integer or String, Failure might be Symbol or Float
|
|
206
|
-
short_circuit = SorbetTyped::ShortCircuit[T.any(Integer, String), T.any(Symbol, Float)].new
|
|
207
|
-
|
|
208
|
-
result = short_circuit.run do |circuit_breaker|
|
|
209
|
-
T.reveal_type(circuit_breaker.(3)) # => Integer; sorbet knows the Input-Type would be the output type if it's not a Shorted signal
|
|
210
|
-
|
|
211
|
-
foo_var = circuit_breaker.(foo)
|
|
212
|
-
|
|
213
|
-
'bar'
|
|
214
|
-
end
|
|
215
|
-
|
|
216
|
-
short_circuit.last_run_short_circuited?
|
|
217
|
-
# => depends on the `[true, false].sample` below
|
|
218
|
-
#
|
|
219
|
-
# the `foo` call contains a shorted circuit, propagating the Shorted signal outwards and failing this circuit
|
|
220
|
-
|
|
221
|
-
T.reveal_type(result) # => T.any(Integer, String, SorbetTyped::Railway::Shorted[T.any(Symbol, Float)])
|
|
222
|
-
|
|
223
|
-
# the result might be any of the success type or the Shorted signal object
|
|
224
|
-
# with its shorted types. Use case...when logic to operate on the different
|
|
225
|
-
# return types.
|
|
226
|
-
|
|
227
|
-
if result.is_a? SorbetTyped::ShortCircuit::Shorted
|
|
228
|
-
T.reveal_type result # => SorbetTyped::Railway::Shorted[T.any(Symbol, Float)]
|
|
229
|
-
T.reveal_type result.info # => T.any(Symbol, Float); the for the railway defined shorted result types
|
|
230
|
-
puts result.info
|
|
231
|
-
|
|
232
|
-
# handle error
|
|
233
|
-
return
|
|
234
|
-
end
|
|
235
|
-
|
|
236
|
-
T.reveal_type(result)
|
|
237
|
-
# => T.any(Integer, String); sorbet statically knows, this can never be a shorted result, as we handled it before and return early
|
|
238
|
-
end
|
|
239
|
-
|
|
240
|
-
sig do
|
|
241
|
-
returns(T.any(Integer, SorbetTyped::ShortCircuit::Shorted[Symbol]))
|
|
242
|
-
end
|
|
243
|
-
def foo
|
|
244
|
-
SorbetTyped::ShortCircuit[Integer, Symbol].new.run do |circuit_breaker|
|
|
245
|
-
# this sometimes fast fails the short circuit and propagates the signal outwards
|
|
246
|
-
circuit_breaker.call validate # WIP: edge case where wrong typing does not raise errors in typechecker
|
|
247
|
-
|
|
248
|
-
42
|
|
249
|
-
end
|
|
250
|
-
end
|
|
251
|
-
|
|
252
|
-
sig { returns(T.any(NilClass, SorbetTyped::ShortCircuit::Shorted[Symbol])) }
|
|
253
|
-
def validate
|
|
254
|
-
return SorbetTyped::ShortCircuit.short(info: :foo) if [true, false].sample
|
|
255
|
-
return SorbetTyped::ShortCircuit.short(info: :bar) if [true, false].sample
|
|
256
|
-
|
|
257
|
-
SorbetTyped::ShortCircuit.short(info: :baz) if [true, false].sample
|
|
258
|
-
end
|
|
259
|
-
end
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
# typed: strict
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
RSpec.describe SorbetTyped::Railway do # rubocop:disable RSpec/SpecFilePathFormat
|
|
5
|
-
let(:version) { SorbetTyped::Railway::VERSION }
|
|
6
|
-
|
|
7
|
-
it 'has a version number' do
|
|
8
|
-
expect(version).not_to be_nil
|
|
9
|
-
end
|
|
10
|
-
end
|