sorbet-runtime 0.5.5841
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 +7 -0
- data/lib/sorbet-runtime.rb +116 -0
- data/lib/types/_types.rb +285 -0
- data/lib/types/abstract_utils.rb +50 -0
- data/lib/types/boolean.rb +8 -0
- data/lib/types/compatibility_patches.rb +95 -0
- data/lib/types/configuration.rb +428 -0
- data/lib/types/enum.rb +349 -0
- data/lib/types/generic.rb +23 -0
- data/lib/types/helpers.rb +39 -0
- data/lib/types/interface_wrapper.rb +158 -0
- data/lib/types/non_forcing_constants.rb +51 -0
- data/lib/types/private/abstract/data.rb +36 -0
- data/lib/types/private/abstract/declare.rb +48 -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 +111 -0
- data/lib/types/private/decl_state.rb +30 -0
- data/lib/types/private/final.rb +51 -0
- data/lib/types/private/methods/_methods.rb +460 -0
- data/lib/types/private/methods/call_validation.rb +1149 -0
- data/lib/types/private/methods/decl_builder.rb +228 -0
- data/lib/types/private/methods/modes.rb +16 -0
- data/lib/types/private/methods/signature.rb +196 -0
- data/lib/types/private/methods/signature_validation.rb +229 -0
- data/lib/types/private/mixins/mixins.rb +27 -0
- data/lib/types/private/retry.rb +10 -0
- data/lib/types/private/runtime_levels.rb +56 -0
- data/lib/types/private/sealed.rb +65 -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/type_alias.rb +26 -0
- data/lib/types/private/types/void.rb +34 -0
- data/lib/types/profile.rb +31 -0
- data/lib/types/props/_props.rb +161 -0
- data/lib/types/props/constructor.rb +40 -0
- data/lib/types/props/custom_type.rb +108 -0
- data/lib/types/props/decorator.rb +672 -0
- data/lib/types/props/errors.rb +8 -0
- data/lib/types/props/generated_code_validation.rb +268 -0
- data/lib/types/props/has_lazily_specialized_methods.rb +92 -0
- data/lib/types/props/optional.rb +81 -0
- data/lib/types/props/plugin.rb +37 -0
- data/lib/types/props/pretty_printable.rb +107 -0
- data/lib/types/props/private/apply_default.rb +170 -0
- data/lib/types/props/private/deserializer_generator.rb +165 -0
- data/lib/types/props/private/parser.rb +32 -0
- data/lib/types/props/private/serde_transform.rb +192 -0
- data/lib/types/props/private/serializer_generator.rb +77 -0
- data/lib/types/props/private/setter_factory.rb +134 -0
- data/lib/types/props/serializable.rb +330 -0
- data/lib/types/props/type_validation.rb +111 -0
- data/lib/types/props/utils.rb +59 -0
- data/lib/types/props/weak_constructor.rb +67 -0
- data/lib/types/runtime_profiled.rb +24 -0
- data/lib/types/sig.rb +30 -0
- data/lib/types/struct.rb +18 -0
- data/lib/types/types/attached_class.rb +37 -0
- data/lib/types/types/base.rb +151 -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 +37 -0
- data/lib/types/types/noreturn.rb +29 -0
- data/lib/types/types/proc.rb +51 -0
- data/lib/types/types/self_type.rb +35 -0
- data/lib/types/types/simple.rb +33 -0
- data/lib/types/types/t_enum.rb +38 -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 +34 -0
- data/lib/types/types/typed_enumerable.rb +161 -0
- data/lib/types/types/typed_enumerator.rb +36 -0
- data/lib/types/types/typed_hash.rb +43 -0
- data/lib/types/types/typed_range.rb +26 -0
- data/lib/types/types/typed_set.rb +36 -0
- data/lib/types/types/union.rb +56 -0
- data/lib/types/types/untyped.rb +29 -0
- data/lib/types/utils.rb +217 -0
- metadata +223 -0
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: false
|
3
|
+
|
4
|
+
module T::Props::Plugin
|
5
|
+
include T::Props
|
6
|
+
extend T::Helpers
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def included(child)
|
10
|
+
super
|
11
|
+
child.plugin(self)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
mixes_in_class_methods(ClassMethods)
|
15
|
+
|
16
|
+
module Private
|
17
|
+
# These need to be non-instance methods so we can use them without prematurely creating the
|
18
|
+
# child decorator in `model_inherited` (see comments there for details).
|
19
|
+
#
|
20
|
+
# The dynamic constant access below forces this file to be `typed: false`
|
21
|
+
def self.apply_class_methods(plugin, target)
|
22
|
+
if plugin.const_defined?('ClassMethods')
|
23
|
+
# FIXME: This will break preloading, selective test execution, etc if `mod::ClassMethods`
|
24
|
+
# is ever defined in a separate file from `mod`.
|
25
|
+
target.extend(plugin::ClassMethods) # rubocop:disable PrisonGuard/NoDynamicConstAccess
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.apply_decorator_methods(plugin, target)
|
30
|
+
if plugin.const_defined?('DecoratorMethods')
|
31
|
+
# FIXME: This will break preloading, selective test execution, etc if `mod::DecoratorMethods`
|
32
|
+
# is ever defined in a separate file from `mod`.
|
33
|
+
target.extend(plugin::DecoratorMethods) # rubocop:disable PrisonGuard/NoDynamicConstAccess
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
module T::Props::PrettyPrintable
|
5
|
+
include T::Props::Plugin
|
6
|
+
|
7
|
+
# Return a string representation of this object and all of its props
|
8
|
+
def inspect
|
9
|
+
T.unsafe(T.cast(self, Object).class).decorator.inspect_instance(self)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Override the PP gem with something that's similar, but gives us a hook
|
13
|
+
# to do redaction
|
14
|
+
def pretty_inspect
|
15
|
+
T.unsafe(T.cast(self, Object).class).decorator.inspect_instance(self, multiline: true)
|
16
|
+
end
|
17
|
+
|
18
|
+
module DecoratorMethods
|
19
|
+
extend T::Sig
|
20
|
+
|
21
|
+
sig {params(key: Symbol).returns(T::Boolean).checked(:never)}
|
22
|
+
def valid_rule_key?(key)
|
23
|
+
super || key == :inspect
|
24
|
+
end
|
25
|
+
|
26
|
+
sig do
|
27
|
+
params(instance: T::Props::PrettyPrintable, multiline: T::Boolean, indent: String)
|
28
|
+
.returns(String)
|
29
|
+
end
|
30
|
+
def inspect_instance(instance, multiline: false, indent: ' ')
|
31
|
+
components =
|
32
|
+
inspect_instance_components(
|
33
|
+
instance,
|
34
|
+
multiline: multiline,
|
35
|
+
indent: indent
|
36
|
+
)
|
37
|
+
.reject(&:empty?)
|
38
|
+
|
39
|
+
# Not using #<> here as that makes pry highlight these objects
|
40
|
+
# as if they were all comments, whereas this makes them look
|
41
|
+
# like the structured thing they are.
|
42
|
+
if multiline
|
43
|
+
"#{components[0]}:\n" + T.must(components[1..-1]).join("\n")
|
44
|
+
else
|
45
|
+
"<#{components.join(' ')}>"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
sig do
|
50
|
+
params(instance: T::Props::PrettyPrintable, multiline: T::Boolean, indent: String)
|
51
|
+
.returns(T::Array[String])
|
52
|
+
end
|
53
|
+
private def inspect_instance_components(instance, multiline:, indent:)
|
54
|
+
pretty_props = T.unsafe(self).all_props.map do |prop|
|
55
|
+
[prop, inspect_prop_value(instance, prop, multiline: multiline, indent: indent)]
|
56
|
+
end
|
57
|
+
|
58
|
+
joined_props = join_props_with_pretty_values(
|
59
|
+
pretty_props,
|
60
|
+
multiline: multiline,
|
61
|
+
indent: indent
|
62
|
+
)
|
63
|
+
|
64
|
+
[
|
65
|
+
T.unsafe(self).decorated_class.to_s,
|
66
|
+
joined_props,
|
67
|
+
]
|
68
|
+
end
|
69
|
+
|
70
|
+
sig do
|
71
|
+
params(instance: T::Props::PrettyPrintable, prop: Symbol, multiline: T::Boolean, indent: String)
|
72
|
+
.returns(String)
|
73
|
+
.checked(:never)
|
74
|
+
end
|
75
|
+
private def inspect_prop_value(instance, prop, multiline:, indent:)
|
76
|
+
val = T.unsafe(self).get(instance, prop)
|
77
|
+
rules = T.unsafe(self).prop_rules(prop)
|
78
|
+
if (custom_inspect = rules[:inspect])
|
79
|
+
if T::Utils.arity(custom_inspect) == 1
|
80
|
+
custom_inspect.call(val)
|
81
|
+
else
|
82
|
+
custom_inspect.call(val, {multiline: multiline, indent: indent})
|
83
|
+
end
|
84
|
+
elsif rules[:sensitivity] && !rules[:sensitivity].empty? && !val.nil?
|
85
|
+
"<REDACTED #{rules[:sensitivity].join(', ')}>"
|
86
|
+
else
|
87
|
+
val.inspect
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
sig do
|
92
|
+
params(pretty_kvs: T::Array[[Symbol, String]], multiline: T::Boolean, indent: String)
|
93
|
+
.returns(String)
|
94
|
+
end
|
95
|
+
private def join_props_with_pretty_values(pretty_kvs, multiline:, indent: ' ')
|
96
|
+
pairs = pretty_kvs
|
97
|
+
.sort_by {|k, _v| k.to_s}
|
98
|
+
.map {|k, v| "#{k}=#{v}"}
|
99
|
+
|
100
|
+
if multiline
|
101
|
+
indent + pairs.join("\n#{indent}")
|
102
|
+
else
|
103
|
+
pairs.join(', ')
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: strict
|
3
|
+
|
4
|
+
module T::Props
|
5
|
+
module Private
|
6
|
+
class ApplyDefault
|
7
|
+
extend T::Sig
|
8
|
+
extend T::Helpers
|
9
|
+
abstract!
|
10
|
+
|
11
|
+
# checked(:never) - O(object construction x prop count)
|
12
|
+
sig {returns(SetterFactory::SetterProc).checked(:never)}
|
13
|
+
attr_reader :setter_proc
|
14
|
+
|
15
|
+
# checked(:never) - We do this with `T.let` instead
|
16
|
+
sig {params(accessor_key: Symbol, setter_proc: SetterFactory::SetterProc).void.checked(:never)}
|
17
|
+
def initialize(accessor_key, setter_proc)
|
18
|
+
@accessor_key = T.let(accessor_key, Symbol)
|
19
|
+
@setter_proc = T.let(setter_proc, SetterFactory::SetterProc)
|
20
|
+
end
|
21
|
+
|
22
|
+
# checked(:never) - O(object construction x prop count)
|
23
|
+
sig {abstract.returns(T.untyped).checked(:never)}
|
24
|
+
def default; end
|
25
|
+
|
26
|
+
# checked(:never) - O(object construction x prop count)
|
27
|
+
sig {abstract.params(instance: T.all(T::Props::Optional, Object)).void.checked(:never)}
|
28
|
+
def set_default(instance); end
|
29
|
+
|
30
|
+
NO_CLONE_TYPES = T.let([TrueClass, FalseClass, NilClass, Symbol, Numeric, T::Enum].freeze, T::Array[Module])
|
31
|
+
|
32
|
+
# checked(:never) - Rules hash is expensive to check
|
33
|
+
sig {params(cls: Module, rules: T::Hash[Symbol, T.untyped]).returns(T.nilable(ApplyDefault)).checked(:never)}
|
34
|
+
def self.for(cls, rules)
|
35
|
+
accessor_key = rules.fetch(:accessor_key)
|
36
|
+
setter = rules.fetch(:setter_proc)
|
37
|
+
|
38
|
+
if rules.key?(:factory)
|
39
|
+
ApplyDefaultFactory.new(cls, rules.fetch(:factory), accessor_key, setter)
|
40
|
+
elsif rules.key?(:default)
|
41
|
+
default = rules.fetch(:default)
|
42
|
+
case default
|
43
|
+
when *NO_CLONE_TYPES
|
44
|
+
return ApplyPrimitiveDefault.new(default, accessor_key, setter)
|
45
|
+
when String
|
46
|
+
if default.frozen?
|
47
|
+
return ApplyPrimitiveDefault.new(default, accessor_key, setter)
|
48
|
+
end
|
49
|
+
when Array
|
50
|
+
if default.empty? && default.class == Array
|
51
|
+
return ApplyEmptyArrayDefault.new(accessor_key, setter)
|
52
|
+
end
|
53
|
+
when Hash
|
54
|
+
if default.empty? && default.default.nil? && T.unsafe(default).default_proc.nil? && default.class == Hash
|
55
|
+
return ApplyEmptyHashDefault.new(accessor_key, setter)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
ApplyComplexDefault.new(default, accessor_key, setter)
|
60
|
+
else
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class ApplyFixedDefault < ApplyDefault
|
67
|
+
abstract!
|
68
|
+
|
69
|
+
# checked(:never) - We do this with `T.let` instead
|
70
|
+
sig {params(default: BasicObject, accessor_key: Symbol, setter_proc: SetterFactory::SetterProc).void.checked(:never)}
|
71
|
+
def initialize(default, accessor_key, setter_proc)
|
72
|
+
# FIXME: Ideally we'd check here that the default is actually a valid
|
73
|
+
# value for this field, but existing code relies on the fact that we don't.
|
74
|
+
#
|
75
|
+
# :(
|
76
|
+
#
|
77
|
+
# setter_proc.call(default)
|
78
|
+
@default = T.let(default, BasicObject)
|
79
|
+
super(accessor_key, setter_proc)
|
80
|
+
end
|
81
|
+
|
82
|
+
# checked(:never) - O(object construction x prop count)
|
83
|
+
sig {override.params(instance: T.all(T::Props::Optional, Object)).void.checked(:never)}
|
84
|
+
def set_default(instance)
|
85
|
+
instance.instance_variable_set(@accessor_key, default)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
class ApplyPrimitiveDefault < ApplyFixedDefault
|
90
|
+
# checked(:never) - O(object construction x prop count)
|
91
|
+
sig {override.returns(T.untyped).checked(:never)}
|
92
|
+
attr_reader :default
|
93
|
+
end
|
94
|
+
|
95
|
+
class ApplyComplexDefault < ApplyFixedDefault
|
96
|
+
# checked(:never) - O(object construction x prop count)
|
97
|
+
sig {override.returns(T.untyped).checked(:never)}
|
98
|
+
def default
|
99
|
+
T::Props::Utils.deep_clone_object(@default)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Special case since it's so common, and a literal `[]` is meaningfully
|
104
|
+
# faster than falling back to ApplyComplexDefault or even calling
|
105
|
+
# `some_empty_array.dup`
|
106
|
+
class ApplyEmptyArrayDefault < ApplyDefault
|
107
|
+
# checked(:never) - O(object construction x prop count)
|
108
|
+
sig {override.params(instance: T.all(T::Props::Optional, Object)).void.checked(:never)}
|
109
|
+
def set_default(instance)
|
110
|
+
instance.instance_variable_set(@accessor_key, [])
|
111
|
+
end
|
112
|
+
|
113
|
+
# checked(:never) - O(object construction x prop count)
|
114
|
+
sig {override.returns(T::Array[T.untyped]).checked(:never)}
|
115
|
+
def default
|
116
|
+
[]
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Special case since it's so common, and a literal `{}` is meaningfully
|
121
|
+
# faster than falling back to ApplyComplexDefault or even calling
|
122
|
+
# `some_empty_hash.dup`
|
123
|
+
class ApplyEmptyHashDefault < ApplyDefault
|
124
|
+
# checked(:never) - O(object construction x prop count)
|
125
|
+
sig {override.params(instance: T.all(T::Props::Optional, Object)).void.checked(:never)}
|
126
|
+
def set_default(instance)
|
127
|
+
instance.instance_variable_set(@accessor_key, {})
|
128
|
+
end
|
129
|
+
|
130
|
+
# checked(:never) - O(object construction x prop count)
|
131
|
+
sig {override.returns(T::Hash[T.untyped, T.untyped]).checked(:never)}
|
132
|
+
def default
|
133
|
+
{}
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
class ApplyDefaultFactory < ApplyDefault
|
138
|
+
# checked(:never) - We do this with `T.let` instead
|
139
|
+
sig do
|
140
|
+
params(
|
141
|
+
cls: Module,
|
142
|
+
factory: T.any(Proc, Method),
|
143
|
+
accessor_key: Symbol,
|
144
|
+
setter_proc: SetterFactory::SetterProc,
|
145
|
+
)
|
146
|
+
.void
|
147
|
+
.checked(:never)
|
148
|
+
end
|
149
|
+
def initialize(cls, factory, accessor_key, setter_proc)
|
150
|
+
@class = T.let(cls, Module)
|
151
|
+
@factory = T.let(factory, T.any(Proc, Method))
|
152
|
+
super(accessor_key, setter_proc)
|
153
|
+
end
|
154
|
+
|
155
|
+
# checked(:never) - O(object construction x prop count)
|
156
|
+
sig {override.params(instance: T.all(T::Props::Optional, Object)).void.checked(:never)}
|
157
|
+
def set_default(instance)
|
158
|
+
# Use the actual setter to validate the factory returns a legitimate
|
159
|
+
# value every time
|
160
|
+
instance.instance_exec(default, &@setter_proc)
|
161
|
+
end
|
162
|
+
|
163
|
+
# checked(:never) - O(object construction x prop count)
|
164
|
+
sig {override.returns(T.untyped).checked(:never)}
|
165
|
+
def default
|
166
|
+
@class.class_exec(&@factory)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: strict
|
3
|
+
|
4
|
+
module T::Props
|
5
|
+
module Private
|
6
|
+
|
7
|
+
# Generates a specialized `deserialize` implementation for a subclass of
|
8
|
+
# T::Props::Serializable.
|
9
|
+
#
|
10
|
+
# The basic idea is that we analyze the props and for each prop, generate
|
11
|
+
# the simplest possible logic as a block of Ruby source, so that we don't
|
12
|
+
# pay the cost of supporting types like T:::Hash[CustomType, SubstructType]
|
13
|
+
# when deserializing a simple Integer. Then we join those together,
|
14
|
+
# with a little shared logic to be able to detect when we get input keys
|
15
|
+
# that don't match any prop.
|
16
|
+
module DeserializerGenerator
|
17
|
+
extend T::Sig
|
18
|
+
|
19
|
+
# Generate a method that takes a T::Hash[String, T.untyped] representing
|
20
|
+
# serialized props, sets instance variables for each prop found in the
|
21
|
+
# input, and returns the count of we props set (which we can use to check
|
22
|
+
# for unexpected input keys with minimal effect on the fast path).
|
23
|
+
sig do
|
24
|
+
params(
|
25
|
+
props: T::Hash[Symbol, T::Hash[Symbol, T.untyped]],
|
26
|
+
defaults: T::Hash[Symbol, T::Props::Private::ApplyDefault],
|
27
|
+
)
|
28
|
+
.returns(String)
|
29
|
+
.checked(:never)
|
30
|
+
end
|
31
|
+
def self.generate(props, defaults)
|
32
|
+
stored_props = props.reject {|_, rules| rules[:dont_store]}
|
33
|
+
parts = stored_props.map do |prop, rules|
|
34
|
+
# All of these strings should already be validated (directly or
|
35
|
+
# indirectly) in `validate_prop_name`, so we don't bother with a nice
|
36
|
+
# error message, but we double check here to prevent a refactoring
|
37
|
+
# from introducing a security vulnerability.
|
38
|
+
raise unless T::Props::Decorator::SAFE_NAME.match?(prop.to_s)
|
39
|
+
|
40
|
+
hash_key = rules.fetch(:serialized_form)
|
41
|
+
raise unless T::Props::Decorator::SAFE_NAME.match?(hash_key)
|
42
|
+
|
43
|
+
ivar_name = rules.fetch(:accessor_key).to_s
|
44
|
+
raise unless ivar_name.start_with?('@') && T::Props::Decorator::SAFE_NAME.match?(ivar_name[1..-1])
|
45
|
+
|
46
|
+
transformation = SerdeTransform.generate(
|
47
|
+
T::Utils::Nilable.get_underlying_type_object(rules.fetch(:type_object)),
|
48
|
+
SerdeTransform::Mode::DESERIALIZE,
|
49
|
+
'val'
|
50
|
+
)
|
51
|
+
if transformation
|
52
|
+
# Rescuing exactly NoMethodError is intended as a temporary hack
|
53
|
+
# to preserve the semantics from before codegen. More generally
|
54
|
+
# we are inconsistent about typechecking on deser and need to decide
|
55
|
+
# our strategy here.
|
56
|
+
transformed_val = <<~RUBY
|
57
|
+
begin
|
58
|
+
#{transformation}
|
59
|
+
rescue NoMethodError => e
|
60
|
+
T::Configuration.soft_assert_handler(
|
61
|
+
'Deserialization error (probably unexpected stored type)',
|
62
|
+
storytime: {
|
63
|
+
klass: self.class,
|
64
|
+
prop: #{prop.inspect},
|
65
|
+
value: val,
|
66
|
+
error: e.message,
|
67
|
+
notify: 'djudd'
|
68
|
+
}
|
69
|
+
)
|
70
|
+
val
|
71
|
+
end
|
72
|
+
RUBY
|
73
|
+
else
|
74
|
+
transformed_val = 'val'
|
75
|
+
end
|
76
|
+
|
77
|
+
nil_handler = generate_nil_handler(
|
78
|
+
prop: prop,
|
79
|
+
serialized_form: hash_key,
|
80
|
+
default: defaults[prop],
|
81
|
+
nilable_type: T::Props::Utils.optional_prop?(rules),
|
82
|
+
raise_on_nil_write: !!rules[:raise_on_nil_write],
|
83
|
+
)
|
84
|
+
|
85
|
+
<<~RUBY
|
86
|
+
val = hash[#{hash_key.inspect}]
|
87
|
+
#{ivar_name} = if val.nil?
|
88
|
+
found -= 1 unless hash.key?(#{hash_key.inspect})
|
89
|
+
#{nil_handler}
|
90
|
+
else
|
91
|
+
#{transformed_val}
|
92
|
+
end
|
93
|
+
RUBY
|
94
|
+
end
|
95
|
+
|
96
|
+
<<~RUBY
|
97
|
+
def __t_props_generated_deserialize(hash)
|
98
|
+
found = #{stored_props.size}
|
99
|
+
#{parts.join("\n\n")}
|
100
|
+
found
|
101
|
+
end
|
102
|
+
RUBY
|
103
|
+
end
|
104
|
+
|
105
|
+
# This is very similar to what we do in ApplyDefault, but has a few
|
106
|
+
# key differences that mean we don't just re-use the code:
|
107
|
+
#
|
108
|
+
# 1. Where the logic in construction is that we generate a default
|
109
|
+
# if & only if the prop key isn't present in the input, here we'll
|
110
|
+
# generate a default even to override an explicit nil, but only
|
111
|
+
# if the prop is actually required.
|
112
|
+
# 2. Since we're generating raw Ruby source, we can remove a layer
|
113
|
+
# of indirection for marginally better performance; this seems worth
|
114
|
+
# it for the common cases of literals and empty arrays/hashes.
|
115
|
+
# 3. We need to care about the distinction between `raise_on_nil_write`
|
116
|
+
# and actually non-nilable, where new-instance construction doesn't.
|
117
|
+
#
|
118
|
+
# So we fall back to ApplyDefault only when one of the cases just
|
119
|
+
# mentioned doesn't apply.
|
120
|
+
sig do
|
121
|
+
params(
|
122
|
+
prop: Symbol,
|
123
|
+
serialized_form: String,
|
124
|
+
default: T.nilable(ApplyDefault),
|
125
|
+
nilable_type: T::Boolean,
|
126
|
+
raise_on_nil_write: T::Boolean,
|
127
|
+
)
|
128
|
+
.returns(String)
|
129
|
+
.checked(:never)
|
130
|
+
end
|
131
|
+
private_class_method def self.generate_nil_handler(
|
132
|
+
prop:,
|
133
|
+
serialized_form:,
|
134
|
+
default:,
|
135
|
+
nilable_type:,
|
136
|
+
raise_on_nil_write:
|
137
|
+
)
|
138
|
+
if !nilable_type
|
139
|
+
case default
|
140
|
+
when NilClass
|
141
|
+
"self.class.decorator.raise_nil_deserialize_error(#{serialized_form.inspect})"
|
142
|
+
when ApplyPrimitiveDefault
|
143
|
+
literal = default.default
|
144
|
+
case literal
|
145
|
+
when String, Integer, Symbol, Float, TrueClass, FalseClass, NilClass
|
146
|
+
literal.inspect
|
147
|
+
else
|
148
|
+
"self.class.decorator.props_with_defaults.fetch(#{prop.inspect}).default"
|
149
|
+
end
|
150
|
+
when ApplyEmptyArrayDefault
|
151
|
+
'[]'
|
152
|
+
when ApplyEmptyHashDefault
|
153
|
+
'{}'
|
154
|
+
else
|
155
|
+
"self.class.decorator.props_with_defaults.fetch(#{prop.inspect}).default"
|
156
|
+
end
|
157
|
+
elsif raise_on_nil_write
|
158
|
+
"required_prop_missing_from_deserialize(#{prop.inspect})"
|
159
|
+
else
|
160
|
+
'nil'
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|