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,268 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
module T::Props
|
5
|
+
# Helper to validate generated code, to mitigate security concerns around
|
6
|
+
# `class_eval`. Not called by default; the expectation is this will be used
|
7
|
+
# in a test iterating over all T::Props::Serializable subclasses.
|
8
|
+
#
|
9
|
+
# We validate the exact expected structure of the generated methods as far
|
10
|
+
# as we can, and then where cloning produces an arbitrarily nested structure,
|
11
|
+
# we just validate a lack of side effects.
|
12
|
+
module GeneratedCodeValidation
|
13
|
+
extend Private::Parse
|
14
|
+
|
15
|
+
class ValidationError < RuntimeError; end
|
16
|
+
|
17
|
+
def self.validate_deserialize(source)
|
18
|
+
parsed = parse(source)
|
19
|
+
|
20
|
+
# def %<name>(hash)
|
21
|
+
# ...
|
22
|
+
# end
|
23
|
+
assert_equal(:def, parsed.type)
|
24
|
+
name, args, body = parsed.children
|
25
|
+
assert_equal(:__t_props_generated_deserialize, name)
|
26
|
+
assert_equal(s(:args, s(:arg, :hash)), args)
|
27
|
+
|
28
|
+
assert_equal(:begin, body.type)
|
29
|
+
init, *prop_clauses, ret = body.children
|
30
|
+
|
31
|
+
# found = %<prop_count>
|
32
|
+
# ...
|
33
|
+
# found
|
34
|
+
assert_equal(:lvasgn, init.type)
|
35
|
+
init_name, init_val = init.children
|
36
|
+
assert_equal(:found, init_name)
|
37
|
+
assert_equal(:int, init_val.type)
|
38
|
+
assert_equal(s(:lvar, :found), ret)
|
39
|
+
|
40
|
+
prop_clauses.each_with_index do |clause, i|
|
41
|
+
if i % 2 == 0
|
42
|
+
validate_deserialize_hash_read(clause)
|
43
|
+
else
|
44
|
+
validate_deserialize_ivar_set(clause)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.validate_serialize(source)
|
50
|
+
parsed = parse(source)
|
51
|
+
|
52
|
+
# def %<name>(strict)
|
53
|
+
# ...
|
54
|
+
# end
|
55
|
+
assert_equal(:def, parsed.type)
|
56
|
+
name, args, body = parsed.children
|
57
|
+
assert_equal(:__t_props_generated_serialize, name)
|
58
|
+
assert_equal(s(:args, s(:arg, :strict)), args)
|
59
|
+
|
60
|
+
assert_equal(:begin, body.type)
|
61
|
+
init, *prop_clauses, ret = body.children
|
62
|
+
|
63
|
+
# h = {}
|
64
|
+
# ...
|
65
|
+
# h
|
66
|
+
assert_equal(s(:lvasgn, :h, s(:hash)), init)
|
67
|
+
assert_equal(s(:lvar, :h), ret)
|
68
|
+
|
69
|
+
prop_clauses.each do |clause|
|
70
|
+
validate_serialize_clause(clause)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
private_class_method def self.validate_serialize_clause(clause)
|
75
|
+
assert_equal(:if, clause.type)
|
76
|
+
condition, if_body, else_body = clause.children
|
77
|
+
|
78
|
+
# if @%<accessor_key>.nil?
|
79
|
+
assert_equal(:send, condition.type)
|
80
|
+
receiver, method = condition.children
|
81
|
+
assert_equal(:ivar, receiver.type)
|
82
|
+
assert_equal(:nil?, method)
|
83
|
+
|
84
|
+
unless if_body.nil?
|
85
|
+
# required_prop_missing_from_serialize(%<prop>) if strict
|
86
|
+
assert_equal(:if, if_body.type)
|
87
|
+
if_strict_condition, if_strict_body, if_strict_else = if_body.children
|
88
|
+
assert_equal(s(:lvar, :strict), if_strict_condition)
|
89
|
+
assert_equal(:send, if_strict_body.type)
|
90
|
+
on_strict_receiver, on_strict_method, on_strict_arg = if_strict_body.children
|
91
|
+
assert_equal(nil, on_strict_receiver)
|
92
|
+
assert_equal(:required_prop_missing_from_serialize, on_strict_method)
|
93
|
+
assert_equal(:sym, on_strict_arg.type)
|
94
|
+
assert_equal(nil, if_strict_else)
|
95
|
+
end
|
96
|
+
|
97
|
+
# h[%<serialized_form>] = ...
|
98
|
+
assert_equal(:send, else_body.type)
|
99
|
+
receiver, method, h_key, h_val = else_body.children
|
100
|
+
assert_equal(s(:lvar, :h), receiver)
|
101
|
+
assert_equal(:[]=, method)
|
102
|
+
assert_equal(:str, h_key.type)
|
103
|
+
|
104
|
+
validate_lack_of_side_effects(h_val, whitelisted_methods_for_serialize)
|
105
|
+
end
|
106
|
+
|
107
|
+
private_class_method def self.validate_deserialize_hash_read(clause)
|
108
|
+
# val = hash[%<serialized_form>s]
|
109
|
+
|
110
|
+
assert_equal(:lvasgn, clause.type)
|
111
|
+
name, val = clause.children
|
112
|
+
assert_equal(:val, name)
|
113
|
+
assert_equal(:send, val.type)
|
114
|
+
receiver, method, arg = val.children
|
115
|
+
assert_equal(s(:lvar, :hash), receiver)
|
116
|
+
assert_equal(:[], method)
|
117
|
+
assert_equal(:str, arg.type)
|
118
|
+
end
|
119
|
+
|
120
|
+
private_class_method def self.validate_deserialize_ivar_set(clause)
|
121
|
+
# %<accessor_key>s = if val.nil?
|
122
|
+
# found -= 1 unless hash.key?(%<serialized_form>s)
|
123
|
+
# %<nil_handler>s
|
124
|
+
# else
|
125
|
+
# %<serialized_val>s
|
126
|
+
# end
|
127
|
+
|
128
|
+
assert_equal(:ivasgn, clause.type)
|
129
|
+
ivar_name, deser_val = clause.children
|
130
|
+
unless ivar_name.is_a?(Symbol)
|
131
|
+
raise ValidationError.new("Unexpected ivar: #{ivar_name}")
|
132
|
+
end
|
133
|
+
|
134
|
+
assert_equal(:if, deser_val.type)
|
135
|
+
condition, if_body, else_body = deser_val.children
|
136
|
+
assert_equal(s(:send, s(:lvar, :val), :nil?), condition)
|
137
|
+
|
138
|
+
assert_equal(:begin, if_body.type)
|
139
|
+
update_found, handle_nil = if_body.children
|
140
|
+
assert_equal(:if, update_found.type)
|
141
|
+
found_condition, found_if_body, found_else_body = update_found.children
|
142
|
+
assert_equal(:send, found_condition.type)
|
143
|
+
receiver, method, arg = found_condition.children
|
144
|
+
assert_equal(s(:lvar, :hash), receiver)
|
145
|
+
assert_equal(:key?, method)
|
146
|
+
assert_equal(:str, arg.type)
|
147
|
+
assert_equal(nil, found_if_body)
|
148
|
+
assert_equal(s(:op_asgn, s(:lvasgn, :found), :-, s(:int, 1)), found_else_body)
|
149
|
+
|
150
|
+
validate_deserialize_handle_nil(handle_nil)
|
151
|
+
|
152
|
+
if else_body.type == :kwbegin
|
153
|
+
rescue_expression, = else_body.children
|
154
|
+
assert_equal(:rescue, rescue_expression.type)
|
155
|
+
|
156
|
+
try, rescue_body = rescue_expression.children
|
157
|
+
validate_lack_of_side_effects(try, whitelisted_methods_for_deserialize)
|
158
|
+
|
159
|
+
assert_equal(:resbody, rescue_body.type)
|
160
|
+
exceptions, assignment, handler = rescue_body.children
|
161
|
+
assert_equal(:array, exceptions.type)
|
162
|
+
exceptions.children.each {|c| assert_equal(:const, c.type)}
|
163
|
+
assert_equal(:lvasgn, assignment.type)
|
164
|
+
assert_equal([:e], assignment.children)
|
165
|
+
validate_lack_of_side_effects(handler, whitelisted_methods_for_deserialize)
|
166
|
+
else
|
167
|
+
validate_lack_of_side_effects(else_body, whitelisted_methods_for_deserialize)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
private_class_method def self.validate_deserialize_handle_nil(node)
|
172
|
+
case node.type
|
173
|
+
when :hash, :array, :str, :sym, :int, :float, :true, :false, :nil, :const
|
174
|
+
# Primitives and constants are safe
|
175
|
+
when :send
|
176
|
+
receiver, method, arg = node.children
|
177
|
+
if receiver.nil?
|
178
|
+
# required_prop_missing_from_deserialize(%<prop>)
|
179
|
+
assert_equal(:required_prop_missing_from_deserialize, method)
|
180
|
+
assert_equal(:sym, arg.type)
|
181
|
+
elsif receiver == self_class_decorator
|
182
|
+
# self.class.decorator.raise_nil_deserialize_error(%<serialized_form>)
|
183
|
+
assert_equal(:raise_nil_deserialize_error, method)
|
184
|
+
assert_equal(:str, arg.type)
|
185
|
+
elsif method == :default
|
186
|
+
# self.class.decorator.props_with_defaults.fetch(%<prop>).default
|
187
|
+
assert_equal(:send, receiver.type)
|
188
|
+
inner_receiver, inner_method, inner_arg = receiver.children
|
189
|
+
assert_equal(
|
190
|
+
s(:send, self_class_decorator, :props_with_defaults),
|
191
|
+
inner_receiver,
|
192
|
+
)
|
193
|
+
assert_equal(:fetch, inner_method)
|
194
|
+
assert_equal(:sym, inner_arg.type)
|
195
|
+
else
|
196
|
+
raise ValidationError.new("Unexpected receiver in nil handler: #{node.inspect}")
|
197
|
+
end
|
198
|
+
else
|
199
|
+
raise ValidationError.new("Unexpected nil handler: #{node.inspect}")
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
private_class_method def self.self_class_decorator
|
204
|
+
@self_class_decorator ||= s(:send, s(:send, s(:self), :class), :decorator).freeze
|
205
|
+
end
|
206
|
+
|
207
|
+
private_class_method def self.validate_lack_of_side_effects(node, whitelisted_methods_by_receiver_type)
|
208
|
+
case node.type
|
209
|
+
when :const
|
210
|
+
# This is ok, because we'll have validated what method has been called
|
211
|
+
# if applicable
|
212
|
+
when :hash, :array, :str, :sym, :int, :float, :true, :false, :nil, :self
|
213
|
+
# Primitives & self are ok
|
214
|
+
when :lvar, :arg, :ivar
|
215
|
+
# Reading local & instance variables & arguments is ok
|
216
|
+
unless node.children.all? {|c| c.is_a?(Symbol)}
|
217
|
+
raise ValidationError.new("Unexpected child for #{node.type}: #{node.inspect}")
|
218
|
+
end
|
219
|
+
when :args, :mlhs, :block, :begin, :if
|
220
|
+
# Blocks etc are read-only if their contents are read-only
|
221
|
+
node.children.each {|c| validate_lack_of_side_effects(c, whitelisted_methods_by_receiver_type) if c}
|
222
|
+
when :send
|
223
|
+
# Sends are riskier so check a whitelist
|
224
|
+
receiver, method, *args = node.children
|
225
|
+
if receiver
|
226
|
+
if receiver.type == :send
|
227
|
+
key = receiver
|
228
|
+
else
|
229
|
+
key = receiver.type
|
230
|
+
validate_lack_of_side_effects(receiver, whitelisted_methods_by_receiver_type)
|
231
|
+
end
|
232
|
+
|
233
|
+
if !whitelisted_methods_by_receiver_type[key]&.include?(method)
|
234
|
+
raise ValidationError.new("Unexpected method #{method} called on #{receiver.inspect}")
|
235
|
+
end
|
236
|
+
end
|
237
|
+
args.each do |arg|
|
238
|
+
validate_lack_of_side_effects(arg, whitelisted_methods_by_receiver_type)
|
239
|
+
end
|
240
|
+
else
|
241
|
+
raise ValidationError.new("Unexpected node type #{node.type}: #{node.inspect}")
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
private_class_method def self.assert_equal(expected, actual)
|
246
|
+
if expected != actual
|
247
|
+
raise ValidationError.new("Expected #{expected}, got #{actual}")
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
# Method calls generated by SerdeTransform
|
252
|
+
private_class_method def self.whitelisted_methods_for_serialize
|
253
|
+
@whitelisted_methods_for_serialize ||= {
|
254
|
+
:lvar => %i{dup map transform_values transform_keys each_with_object nil? []= serialize},
|
255
|
+
:ivar => %i{dup map transform_values transform_keys each_with_object serialize},
|
256
|
+
:const => %i{checked_serialize deep_clone_object},
|
257
|
+
}
|
258
|
+
end
|
259
|
+
|
260
|
+
# Method calls generated by SerdeTransform
|
261
|
+
private_class_method def self.whitelisted_methods_for_deserialize
|
262
|
+
@whitelisted_methods_for_deserialize ||= {
|
263
|
+
:lvar => %i{dup map transform_values transform_keys each_with_object nil? []= to_f},
|
264
|
+
:const => %i{deserialize from_hash deep_clone_object soft_assert_handler},
|
265
|
+
}
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: false
|
3
|
+
|
4
|
+
module T::Props
|
5
|
+
|
6
|
+
# Helper for generating methods that replace themselves with a specialized
|
7
|
+
# version on first use. The main use case is when we want to generate a
|
8
|
+
# method using the full set of props on a class; we can't do that during
|
9
|
+
# prop definition because we have no way of knowing whether we are defining
|
10
|
+
# the last prop.
|
11
|
+
#
|
12
|
+
# See go/M8yrvzX2 (Stripe-internal) for discussion of security considerations.
|
13
|
+
# In outline, while `class_eval` is a bit scary, we believe that as long as
|
14
|
+
# all inputs are defined in version control (and this is enforced by calling
|
15
|
+
# `disable_lazy_evaluation!` appropriately), risk isn't significantly higher
|
16
|
+
# than with build-time codegen.
|
17
|
+
module HasLazilySpecializedMethods
|
18
|
+
extend T::Sig
|
19
|
+
|
20
|
+
class SourceEvaluationDisabled < RuntimeError
|
21
|
+
def initialize
|
22
|
+
super("Evaluation of lazily-defined methods is disabled")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Disable any future evaluation of lazily-defined methods.
|
27
|
+
#
|
28
|
+
# This is intended to be called after startup but before interacting with
|
29
|
+
# the outside world, to limit attack surface for our `class_eval` use.
|
30
|
+
#
|
31
|
+
# Note it does _not_ prevent explicit calls to `eagerly_define_lazy_methods!`
|
32
|
+
# from working.
|
33
|
+
sig {void}
|
34
|
+
def self.disable_lazy_evaluation!
|
35
|
+
@lazy_evaluation_disabled ||= true
|
36
|
+
end
|
37
|
+
|
38
|
+
sig {returns(T::Boolean)}
|
39
|
+
def self.lazy_evaluation_enabled?
|
40
|
+
!@lazy_evaluation_disabled
|
41
|
+
end
|
42
|
+
|
43
|
+
module DecoratorMethods
|
44
|
+
extend T::Sig
|
45
|
+
|
46
|
+
sig {returns(T::Hash[Symbol, T.proc.returns(String)]).checked(:never)}
|
47
|
+
private def lazily_defined_methods
|
48
|
+
@lazily_defined_methods ||= {}
|
49
|
+
end
|
50
|
+
|
51
|
+
sig {params(name: Symbol).void}
|
52
|
+
private def eval_lazily_defined_method!(name)
|
53
|
+
if !HasLazilySpecializedMethods.lazy_evaluation_enabled?
|
54
|
+
raise SourceEvaluationDisabled.new
|
55
|
+
end
|
56
|
+
|
57
|
+
source = lazily_defined_methods.fetch(name).call
|
58
|
+
|
59
|
+
cls = decorated_class
|
60
|
+
cls.class_eval(source.to_s)
|
61
|
+
cls.send(:private, name)
|
62
|
+
end
|
63
|
+
|
64
|
+
sig {params(name: Symbol, blk: T.proc.returns(String)).void}
|
65
|
+
private def enqueue_lazy_method_definition!(name, &blk)
|
66
|
+
lazily_defined_methods[name] = blk
|
67
|
+
|
68
|
+
cls = decorated_class
|
69
|
+
cls.send(:define_method, name) do |*args|
|
70
|
+
self.class.decorator.send(:eval_lazily_defined_method!, name)
|
71
|
+
send(name, *args)
|
72
|
+
end
|
73
|
+
if cls.respond_to?(:ruby2_keywords, true)
|
74
|
+
cls.send(:ruby2_keywords, name)
|
75
|
+
end
|
76
|
+
cls.send(:private, name)
|
77
|
+
end
|
78
|
+
|
79
|
+
sig {void}
|
80
|
+
def eagerly_define_lazy_methods!
|
81
|
+
return if lazily_defined_methods.empty?
|
82
|
+
|
83
|
+
source = lazily_defined_methods.values.map(&:call).map(&:to_s).join("\n\n")
|
84
|
+
|
85
|
+
cls = decorated_class
|
86
|
+
cls.class_eval(source)
|
87
|
+
lazily_defined_methods.keys.each {|name| cls.send(:private, name)}
|
88
|
+
lazily_defined_methods.clear
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: false
|
3
|
+
|
4
|
+
module T::Props::Optional
|
5
|
+
include T::Props::Plugin
|
6
|
+
end
|
7
|
+
|
8
|
+
|
9
|
+
##############################################
|
10
|
+
|
11
|
+
|
12
|
+
# NB: This must stay in the same file where T::Props::Optional is defined due to
|
13
|
+
# T::Props::Decorator#apply_plugin; see https://git.corp.stripe.com/stripe-internal/pay-server/blob/fc7f15593b49875f2d0499ffecfd19798bac05b3/chalk/odm/lib/chalk-odm/document_decorator.rb#L716-L717
|
14
|
+
module T::Props::Optional::DecoratorMethods
|
15
|
+
extend T::Sig
|
16
|
+
|
17
|
+
VALID_RULE_KEYS = {
|
18
|
+
default: true,
|
19
|
+
factory: true,
|
20
|
+
}.freeze
|
21
|
+
private_constant :VALID_RULE_KEYS
|
22
|
+
|
23
|
+
DEFAULT_SETTER_RULE_KEY = :_t_props_private_apply_default
|
24
|
+
private_constant :DEFAULT_SETTER_RULE_KEY
|
25
|
+
|
26
|
+
def valid_rule_key?(key)
|
27
|
+
super || VALID_RULE_KEYS[key]
|
28
|
+
end
|
29
|
+
|
30
|
+
def prop_optional?(prop); prop_rules(prop)[:fully_optional]; end
|
31
|
+
|
32
|
+
def compute_derived_rules(rules)
|
33
|
+
rules[:fully_optional] = !T::Props::Utils.need_nil_write_check?(rules)
|
34
|
+
rules[:need_nil_read_check] = T::Props::Utils.need_nil_read_check?(rules)
|
35
|
+
end
|
36
|
+
|
37
|
+
# checked(:never) - O(runtime object construction)
|
38
|
+
sig {returns(T::Hash[Symbol, T::Props::Private::ApplyDefault]).checked(:never)}
|
39
|
+
attr_reader :props_with_defaults
|
40
|
+
|
41
|
+
# checked(:never) - O(runtime object construction)
|
42
|
+
sig {returns(T::Hash[Symbol, T::Props::Private::SetterFactory::SetterProc]).checked(:never)}
|
43
|
+
attr_reader :props_without_defaults
|
44
|
+
|
45
|
+
def add_prop_definition(prop, rules)
|
46
|
+
compute_derived_rules(rules)
|
47
|
+
|
48
|
+
default_setter = T::Props::Private::ApplyDefault.for(decorated_class, rules)
|
49
|
+
if default_setter
|
50
|
+
@props_with_defaults ||= {}
|
51
|
+
@props_with_defaults[prop] = default_setter
|
52
|
+
@props_without_defaults&.delete(prop) # Handle potential override
|
53
|
+
|
54
|
+
rules[DEFAULT_SETTER_RULE_KEY] = default_setter
|
55
|
+
else
|
56
|
+
@props_without_defaults ||= {}
|
57
|
+
@props_without_defaults[prop] = rules.fetch(:setter_proc)
|
58
|
+
@props_with_defaults&.delete(prop) # Handle potential override
|
59
|
+
end
|
60
|
+
|
61
|
+
super
|
62
|
+
end
|
63
|
+
|
64
|
+
def prop_validate_definition!(name, cls, rules, type)
|
65
|
+
result = super
|
66
|
+
|
67
|
+
if rules.key?(:default) && rules.key?(:factory)
|
68
|
+
raise ArgumentError.new("Setting both :default and :factory is invalid. See: go/chalk-docs")
|
69
|
+
end
|
70
|
+
|
71
|
+
result
|
72
|
+
end
|
73
|
+
|
74
|
+
def has_default?(rules)
|
75
|
+
rules.include?(DEFAULT_SETTER_RULE_KEY)
|
76
|
+
end
|
77
|
+
|
78
|
+
def get_default(rules, instance_class)
|
79
|
+
rules[DEFAULT_SETTER_RULE_KEY]&.default
|
80
|
+
end
|
81
|
+
end
|