sorbet-runtime 0.0.1.pre.prealpha → 0.4.4253
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 +5 -5
- data/lib/sorbet-runtime.rb +100 -0
- data/lib/types/_types.rb +245 -0
- data/lib/types/abstract_utils.rb +50 -0
- data/lib/types/boolean.rb +8 -0
- data/lib/types/compatibility_patches.rb +37 -0
- data/lib/types/configuration.rb +368 -0
- data/lib/types/generic.rb +23 -0
- data/lib/types/helpers.rb +31 -0
- data/lib/types/interface_wrapper.rb +158 -0
- data/lib/types/private/abstract/data.rb +36 -0
- data/lib/types/private/abstract/declare.rb +39 -0
- data/lib/types/private/abstract/hooks.rb +43 -0
- data/lib/types/private/abstract/validate.rb +128 -0
- data/lib/types/private/casts.rb +22 -0
- data/lib/types/private/class_utils.rb +102 -0
- data/lib/types/private/decl_state.rb +18 -0
- data/lib/types/private/error_handler.rb +37 -0
- data/lib/types/private/methods/_methods.rb +344 -0
- data/lib/types/private/methods/call_validation.rb +1177 -0
- data/lib/types/private/methods/decl_builder.rb +275 -0
- data/lib/types/private/methods/modes.rb +18 -0
- data/lib/types/private/methods/signature.rb +196 -0
- data/lib/types/private/methods/signature_validation.rb +232 -0
- data/lib/types/private/mixins/mixins.rb +27 -0
- data/lib/types/private/runtime_levels.rb +41 -0
- data/lib/types/private/types/not_typed.rb +23 -0
- data/lib/types/private/types/string_holder.rb +26 -0
- data/lib/types/private/types/void.rb +33 -0
- data/lib/types/profile.rb +27 -0
- data/lib/types/props/_props.rb +165 -0
- data/lib/types/props/constructor.rb +20 -0
- data/lib/types/props/custom_type.rb +84 -0
- data/lib/types/props/decorator.rb +826 -0
- data/lib/types/props/errors.rb +8 -0
- data/lib/types/props/optional.rb +73 -0
- data/lib/types/props/plugin.rb +15 -0
- data/lib/types/props/pretty_printable.rb +106 -0
- data/lib/types/props/serializable.rb +376 -0
- data/lib/types/props/type_validation.rb +98 -0
- data/lib/types/props/utils.rb +49 -0
- data/lib/types/props/weak_constructor.rb +30 -0
- data/lib/types/runtime_profiled.rb +36 -0
- data/lib/types/sig.rb +28 -0
- data/lib/types/struct.rb +8 -0
- data/lib/types/types/base.rb +141 -0
- data/lib/types/types/class_of.rb +38 -0
- data/lib/types/types/enum.rb +42 -0
- data/lib/types/types/fixed_array.rb +60 -0
- data/lib/types/types/fixed_hash.rb +59 -0
- data/lib/types/types/intersection.rb +36 -0
- data/lib/types/types/noreturn.rb +25 -0
- data/lib/types/types/proc.rb +51 -0
- data/lib/types/types/self_type.rb +31 -0
- data/lib/types/types/simple.rb +33 -0
- data/lib/types/types/type_member.rb +7 -0
- data/lib/types/types/type_parameter.rb +23 -0
- data/lib/types/types/type_template.rb +7 -0
- data/lib/types/types/type_variable.rb +31 -0
- data/lib/types/types/typed_array.rb +20 -0
- data/lib/types/types/typed_enumerable.rb +141 -0
- data/lib/types/types/typed_enumerator.rb +22 -0
- data/lib/types/types/typed_hash.rb +29 -0
- data/lib/types/types/typed_range.rb +22 -0
- data/lib/types/types/typed_set.rb +22 -0
- data/lib/types/types/union.rb +59 -0
- data/lib/types/types/untyped.rb +25 -0
- data/lib/types/utils.rb +223 -0
- metadata +122 -15
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: false
|
3
|
+
|
4
|
+
module T::Props::TypeValidation
|
5
|
+
include T::Props::Plugin
|
6
|
+
|
7
|
+
BANNED_TYPES = [Object, BasicObject, Kernel]
|
8
|
+
|
9
|
+
class UnderspecifiedType < ArgumentError; end
|
10
|
+
|
11
|
+
module DecoratorMethods
|
12
|
+
extend T::Sig
|
13
|
+
|
14
|
+
sig {returns(T::Array[Symbol])}
|
15
|
+
def valid_props
|
16
|
+
super + [:DEPRECATED_underspecified_type]
|
17
|
+
end
|
18
|
+
|
19
|
+
sig do
|
20
|
+
params(
|
21
|
+
name: T.any(Symbol, String),
|
22
|
+
_cls: Module,
|
23
|
+
rules: T::Hash[Symbol, T.untyped],
|
24
|
+
type: T.any(T::Types::Base, Module)
|
25
|
+
)
|
26
|
+
.void
|
27
|
+
end
|
28
|
+
def prop_validate_definition!(name, _cls, rules, type)
|
29
|
+
super
|
30
|
+
|
31
|
+
if !rules[:DEPRECATED_underspecified_type] && !(type.singleton_class <= T::Props::CustomType)
|
32
|
+
validate_type(type, field_name: name)
|
33
|
+
elsif rules[:DEPRECATED_underspecified_type] && find_invalid_subtype(type).nil?
|
34
|
+
raise ArgumentError.new("DEPRECATED_underspecified_type set unnecessarily for #{@class.name}.#{name} - #{type} is a valid type")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
sig do
|
39
|
+
params(
|
40
|
+
type: T::Types::Base,
|
41
|
+
field_name: T.any(Symbol, String),
|
42
|
+
)
|
43
|
+
.void
|
44
|
+
end
|
45
|
+
private def validate_type(type, field_name:)
|
46
|
+
if (invalid_subtype = find_invalid_subtype(type))
|
47
|
+
raise UnderspecifiedType.new(type_error_message(invalid_subtype, field_name, type))
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns an invalid type, if any, found in the given top-level type.
|
52
|
+
# This might be the type itself, if it is e.g. "Object", or might be
|
53
|
+
# a subtype like the type of the values of a typed hash.
|
54
|
+
#
|
55
|
+
# If the type is fully valid, returns nil.
|
56
|
+
sig {params(type: T::Types::Base).returns(T.nilable(T::Types::Base))}
|
57
|
+
private def find_invalid_subtype(type)
|
58
|
+
case type
|
59
|
+
when T::Types::TypedEnumerable
|
60
|
+
find_invalid_subtype(type.type)
|
61
|
+
when T::Types::FixedHash
|
62
|
+
type.types.values.map {|subtype| find_invalid_subtype(subtype)}.compact.first
|
63
|
+
when T::Types::Union, T::Types::FixedArray
|
64
|
+
type.types.map {|subtype| find_invalid_subtype(subtype)}.compact.first
|
65
|
+
when T::Types::Enum, T::Types::ClassOf
|
66
|
+
nil
|
67
|
+
when T::Types::Simple
|
68
|
+
# TODO Could we manage to define a whitelist, consisting of something
|
69
|
+
# like primitives, subdocs, DataInterfaces, and collections/enums/unions
|
70
|
+
# thereof?
|
71
|
+
if BANNED_TYPES.include?(type.raw_type)
|
72
|
+
type
|
73
|
+
else
|
74
|
+
nil
|
75
|
+
end
|
76
|
+
else
|
77
|
+
type
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
sig do
|
82
|
+
params(
|
83
|
+
type: T::Types::Base,
|
84
|
+
field_name: T.any(Symbol, String),
|
85
|
+
orig_type: T::Types::Base,
|
86
|
+
)
|
87
|
+
.returns(String)
|
88
|
+
end
|
89
|
+
private def type_error_message(type, field_name, orig_type)
|
90
|
+
msg_prefix = "#{@class.name}.#{field_name}: #{orig_type} is invalid in prop definition"
|
91
|
+
if type == orig_type
|
92
|
+
"#{msg_prefix}. Please choose a more specific type (T.untyped and ~equivalents like Object are banned)."
|
93
|
+
else
|
94
|
+
"#{msg_prefix}. Please choose a subtype more specific than #{type} (T.untyped and ~equivalents like Object are banned)."
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
module T::Props::Utils
|
5
|
+
# Deep copy an object. The object must consist of Ruby primitive
|
6
|
+
# types and Hashes and Arrays.
|
7
|
+
def self.deep_clone_object(what, freeze: false)
|
8
|
+
result = case what
|
9
|
+
when true
|
10
|
+
true
|
11
|
+
when false
|
12
|
+
false
|
13
|
+
when Symbol, NilClass, Numeric
|
14
|
+
what
|
15
|
+
when Array
|
16
|
+
what.map {|v| deep_clone_object(v, freeze: freeze)}
|
17
|
+
when Hash
|
18
|
+
h = what.class.new
|
19
|
+
what.each do |k, v|
|
20
|
+
k.freeze if freeze
|
21
|
+
h[k] = deep_clone_object(v, freeze: freeze)
|
22
|
+
end
|
23
|
+
h
|
24
|
+
when Regexp
|
25
|
+
what.dup
|
26
|
+
else
|
27
|
+
# Some unfortunate nastiness to get around Opus::Enum potentially not
|
28
|
+
# being defined.
|
29
|
+
if defined?(Opus) && defined?(Opus::Enum) && what.class == Opus::Enum
|
30
|
+
what
|
31
|
+
else
|
32
|
+
what.clone
|
33
|
+
end
|
34
|
+
end
|
35
|
+
freeze ? result.freeze : result
|
36
|
+
end
|
37
|
+
|
38
|
+
# The prop_rules indicate whether we should check for reading a nil value for the prop/field.
|
39
|
+
# This is mostly for the compatibility check that we allow existing documents carry some nil prop/field.
|
40
|
+
def self.need_nil_read_check?(prop_rules)
|
41
|
+
# . :on_load allows nil read, but we need to check for the read for future writes
|
42
|
+
prop_rules[:optional] == :on_load || prop_rules[:raise_on_nil_write]
|
43
|
+
end
|
44
|
+
|
45
|
+
# The prop_rules indicate whether we should check for writing a nil value for the prop/field.
|
46
|
+
def self.need_nil_write_check?(prop_rules)
|
47
|
+
need_nil_read_check?(prop_rules) || T::Utils::Props.required_prop?(prop_rules)
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: false
|
3
|
+
|
4
|
+
module T::Props::WeakConstructor
|
5
|
+
include T::Props::Optional
|
6
|
+
|
7
|
+
def initialize(hash={})
|
8
|
+
expected_keys = {}
|
9
|
+
hash.each_key {|key| expected_keys[key] = true}
|
10
|
+
|
11
|
+
decorator = self.class.decorator
|
12
|
+
|
13
|
+
decorator.props.each do |p, rules|
|
14
|
+
if hash.key?(p)
|
15
|
+
expected_keys.delete(p)
|
16
|
+
val = hash[p]
|
17
|
+
elsif decorator.has_default?(rules)
|
18
|
+
val = decorator.get_default(rules, self.class)
|
19
|
+
else
|
20
|
+
next
|
21
|
+
end
|
22
|
+
|
23
|
+
decorator.prop_set(self, p, val, rules)
|
24
|
+
end
|
25
|
+
|
26
|
+
unless expected_keys.empty?
|
27
|
+
raise ArgumentError.new("#{@class}: Unrecognized properties in #with_props: #{expected_keys.keys.join(', ')}")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# Hey there!
|
6
|
+
#
|
7
|
+
# T::Utils::Runtime is just a random class. Chances are[^1] it's not a class
|
8
|
+
# that your variables are instances of, which means that using it in a type
|
9
|
+
# signature will always create a type error.
|
10
|
+
#
|
11
|
+
# At first, it doesn't seem like a good thing to have a type that's sole
|
12
|
+
# purpose is to cause type errors. The trick is that within the ruby-types
|
13
|
+
# team, we only use T::Utils::RuntimeProfiled within 'generated' sigs.
|
14
|
+
#
|
15
|
+
# Unlike normal sigs, generated sigs never raise at runtime. They also log the
|
16
|
+
# actual, observed type on type error to a central location. We're using these
|
17
|
+
# observed types to refine and expand our type coverage in pay-server.
|
18
|
+
#
|
19
|
+
# What does this all mean for you?
|
20
|
+
#
|
21
|
+
# - If you were just curious, that's it! Leave the sig as is, and carry on.
|
22
|
+
# - If you wanted to replace this sig with a better, hand-authored one:
|
23
|
+
#
|
24
|
+
# 1. Remove 'generated' from the sig.
|
25
|
+
# 2. Update the sig to your liking
|
26
|
+
#
|
27
|
+
# Questions? :portal-to: => #ruby-types
|
28
|
+
#
|
29
|
+
# [^1]: Unless you happen to be calling T::Utils::RuntimeProfiled.new directly...
|
30
|
+
#
|
31
|
+
|
32
|
+
module T; end
|
33
|
+
module T::Utils; end
|
34
|
+
# Sorbet guesses this type instead of T.untyped when passed --suggest-runtime-profiled
|
35
|
+
|
36
|
+
class T::Utils::RuntimeProfiled; end
|
data/lib/types/sig.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
# rubocop:disable PrisonGuard/NoTopLevelDeclarations, PrisonGuard/PackageMatchesDirectory
|
4
|
+
|
5
|
+
# Used as a mixin to any class so that you can call `sig`.
|
6
|
+
# Docs at http://go/types
|
7
|
+
module T::Sig
|
8
|
+
module WithoutRuntime
|
9
|
+
# At runtime, does nothing, but statically it is treated exactly the same
|
10
|
+
# as T::Sig#sig. Only use it in cases where you can't use T::Sig#sig.
|
11
|
+
def self.sig(&blk); end # rubocop:disable PrisonGuard/BanBuiltinMethodOverride
|
12
|
+
|
13
|
+
# At runtime, does nothing, but statically it is treated exactly the same
|
14
|
+
# as T::Sig#sig. Only use it in cases where you can't use T::Sig#sig.
|
15
|
+
T::Sig::WithoutRuntime.sig {params(blk: T.proc.bind(T::Private::Methods::DeclBuilder).void).void} # rubocop:disable PrisonGuard/PrivateModule
|
16
|
+
def self.sig(&blk); end # rubocop:disable PrisonGuard/BanBuiltinMethodOverride, Lint/DuplicateMethods
|
17
|
+
end
|
18
|
+
|
19
|
+
# Declares a method with type signatures and/or
|
20
|
+
# abstract/override/... helpers. See the documentation URL on
|
21
|
+
# {T::Helpers}
|
22
|
+
T::Sig::WithoutRuntime.sig {params(blk: T.proc.bind(T::Private::Methods::DeclBuilder).void).void}
|
23
|
+
def sig(&blk) # rubocop:disable PrisonGuard/BanBuiltinMethodOverride
|
24
|
+
T::Private::Methods.declare_sig(self, &blk)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# rubocop:enable PrisonGuard/NoTopLevelDeclarations, PrisonGuard/PackageMatchesDirectory
|
data/lib/types/struct.rb
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
module T::Types
|
5
|
+
class Base
|
6
|
+
def self.method_added(method_name)
|
7
|
+
# What is now `subtype_of_single?` used to be named `subtype_of?`. Make sure people don't
|
8
|
+
# override the wrong thing.
|
9
|
+
#
|
10
|
+
# NB: Outside of T::Types, we would enforce this by using `sig` and not declaring the method
|
11
|
+
# as overridable, but doing so here would result in a dependency cycle.
|
12
|
+
if method_name == :subtype_of? && self != T::Types::Base
|
13
|
+
raise "`subtype_of?` should not be overridden. You probably want to override " \
|
14
|
+
"`subtype_of_single?` instead."
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def valid?(obj)
|
19
|
+
raise NotImplementedError
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return [T::Boolean] This method must be implemented to return whether the subclass is a subtype
|
23
|
+
# of `type`. This should only be called by `subtype_of?`, which guarantees that `type` will be
|
24
|
+
# a "single" type, by which we mean it won't be a Union or an Intersection (c.f.
|
25
|
+
# `isSubTypeSingle` in sorbet).
|
26
|
+
private def subtype_of_single?(type)
|
27
|
+
raise NotImplementedError
|
28
|
+
end
|
29
|
+
|
30
|
+
# Equality is based on name, so be sure the name reflects all relevant state when implementing.
|
31
|
+
def name
|
32
|
+
raise NotImplementedError
|
33
|
+
end
|
34
|
+
|
35
|
+
# Mirrors ruby_typer::core::Types::isSubType
|
36
|
+
# See https://git.corp.stripe.com/stripe-internal/ruby-typer/blob/9fc8ed998c04ac0b96592ae6bb3493b8a925c5c1/core/types/subtyping.cc#L912-L950
|
37
|
+
#
|
38
|
+
# This method cannot be overridden (see `method_added` above).
|
39
|
+
# Subclasses only need to implement `subtype_of_single?`).
|
40
|
+
def subtype_of?(t2)
|
41
|
+
t1 = self
|
42
|
+
|
43
|
+
# pairs to cover: 1 (_, _)
|
44
|
+
# 2 (_, And)
|
45
|
+
# 3 (_, Or)
|
46
|
+
# 4 (And, _)
|
47
|
+
# 5 (And, And)
|
48
|
+
# 6 (And, Or)
|
49
|
+
# 7 (Or, _)
|
50
|
+
# 8 (Or, And)
|
51
|
+
# 9 (Or, Or)
|
52
|
+
|
53
|
+
# Note: order of cases here matters!
|
54
|
+
if t1.is_a?(T::Types::Union) # 7, 8, 9
|
55
|
+
# this will be incorrect if/when we have Type members
|
56
|
+
return t1.types.all? {|t1_member| t1_member.subtype_of?(t2)}
|
57
|
+
end
|
58
|
+
|
59
|
+
if t2.is_a?(T::Types::Intersection) # 2, 5
|
60
|
+
# this will be incorrect if/when we have Type members
|
61
|
+
return t2.types.all? {|t2_member| t1.subtype_of?(t2_member)}
|
62
|
+
end
|
63
|
+
|
64
|
+
if t2.is_a?(T::Types::Union)
|
65
|
+
if t1.is_a?(T::Types::Intersection) # 6
|
66
|
+
# dropping either of parts eagerly make subtype test be too strict.
|
67
|
+
# we have to try both cases, when we normally try only one
|
68
|
+
return t2.types.any? {|t2_member| t1.subtype_of?(t2_member)} ||
|
69
|
+
t1.types.any? {|t1_member| t1_member.subtype_of?(t2)}
|
70
|
+
end
|
71
|
+
return t2.types.any? {|t2_member| t1.subtype_of?(t2_member)} # 3
|
72
|
+
end
|
73
|
+
|
74
|
+
if t1.is_a?(T::Types::Intersection) # 4
|
75
|
+
# this will be incorrect if/when we have Type members
|
76
|
+
return t1.types.any? {|t1_member| t1_member.subtype_of?(t2)}
|
77
|
+
end
|
78
|
+
|
79
|
+
# 1; Start with some special cases
|
80
|
+
if t1.is_a?(T::Private::Types::Void)
|
81
|
+
return t2.is_a?(T::Private::Types::Void)
|
82
|
+
end
|
83
|
+
|
84
|
+
if t1.is_a?(T::Types::Untyped) || t2.is_a?(T::Types::Untyped)
|
85
|
+
return true
|
86
|
+
end
|
87
|
+
|
88
|
+
# Rest of (1)
|
89
|
+
subtype_of_single?(t2)
|
90
|
+
end
|
91
|
+
|
92
|
+
def to_s
|
93
|
+
name
|
94
|
+
end
|
95
|
+
|
96
|
+
def describe_obj(obj)
|
97
|
+
# Would be redundant to print class and value in these common cases.
|
98
|
+
case obj
|
99
|
+
when nil, true, false
|
100
|
+
return "type #{obj.class}"
|
101
|
+
end
|
102
|
+
|
103
|
+
# In rare cases, obj.inspect may fail, or be undefined, so rescue.
|
104
|
+
begin
|
105
|
+
# Default inspect behavior of, eg; `#<Object:0x0...>` is ugly; just print the hash instead, which is more concise/readable.
|
106
|
+
if obj.method(:inspect).owner == Kernel
|
107
|
+
"type #{obj.class} with hash #{obj.hash}"
|
108
|
+
else
|
109
|
+
"type #{obj.class} with value #{T::Utils.string_truncate_middle(obj.inspect, 30, 30)}"
|
110
|
+
end
|
111
|
+
rescue StandardError, SystemStackError
|
112
|
+
"type #{obj.class} with unprintable value"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def error_message_for_obj(obj)
|
117
|
+
if valid?(obj)
|
118
|
+
nil
|
119
|
+
else
|
120
|
+
"Expected type #{self.name}, got #{describe_obj(obj)}"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def validate!(obj)
|
125
|
+
err = error_message_for_obj(obj)
|
126
|
+
raise TypeError.new(err) if err
|
127
|
+
end
|
128
|
+
|
129
|
+
### Equality methods (necessary for deduping types with `uniq`)
|
130
|
+
|
131
|
+
def hash
|
132
|
+
name.hash
|
133
|
+
end
|
134
|
+
|
135
|
+
def ==(other)
|
136
|
+
other.class == self.class && other.name == self.name
|
137
|
+
end
|
138
|
+
|
139
|
+
alias_method :eql?, :==
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
module T::Types
|
5
|
+
# Validates that an object belongs to the specified class.
|
6
|
+
class ClassOf < Base
|
7
|
+
attr_reader :type
|
8
|
+
|
9
|
+
def initialize(type)
|
10
|
+
@type = type
|
11
|
+
end
|
12
|
+
|
13
|
+
# @override Base
|
14
|
+
def name
|
15
|
+
"T.class_of(#{@type})"
|
16
|
+
end
|
17
|
+
|
18
|
+
# @override Base
|
19
|
+
def valid?(obj)
|
20
|
+
obj.is_a?(Module) && obj <= @type
|
21
|
+
end
|
22
|
+
|
23
|
+
# @override Base
|
24
|
+
def subtype_of_single?(other)
|
25
|
+
case other
|
26
|
+
when ClassOf
|
27
|
+
@type <= other.type
|
28
|
+
else
|
29
|
+
false
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# @override Base
|
34
|
+
def describe_obj(obj)
|
35
|
+
obj.inspect
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
module T::Types
|
5
|
+
# validates that the provided value is within a given set/enum
|
6
|
+
class Enum < Base
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
attr_reader :values
|
10
|
+
|
11
|
+
# TODO Ideally Hash would not be accepted but there are a lot of uses with prop enum.
|
12
|
+
sig {params(values: T.any(Array, Set, Hash, T::Range[T.untyped])).void}
|
13
|
+
def initialize(values)
|
14
|
+
@values = values
|
15
|
+
end
|
16
|
+
|
17
|
+
# @override Base
|
18
|
+
def valid?(obj)
|
19
|
+
@values.member?(obj)
|
20
|
+
end
|
21
|
+
|
22
|
+
# @override Base
|
23
|
+
private def subtype_of_single?(other)
|
24
|
+
case other
|
25
|
+
when Enum
|
26
|
+
(other.values - @values).empty?
|
27
|
+
else
|
28
|
+
false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# @override Base
|
33
|
+
def name
|
34
|
+
"T.enum([#{@values.map(&:inspect).join(', ')}])"
|
35
|
+
end
|
36
|
+
|
37
|
+
# @override Base
|
38
|
+
def describe_obj(obj)
|
39
|
+
obj.inspect
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|