sorbet-runtime 0.5.5262 → 0.5.5265
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/sorbet-runtime.rb +1 -0
- data/lib/types/props/decorator.rb +28 -80
- data/lib/types/props/private/setter_factory.rb +92 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8ece434fac1367289d5e0cba4706f2163bffe4875e5c55aa6a2e926398777961
|
4
|
+
data.tar.gz: 7ecbe46eb8bb3531dc649711e09a7f3c0a4ea357bea901c754ea240491f0a9bc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3b3a3a27c2ec61f17702301ee54e944aee9c9aaa534072ba9769466ef1c3bbae8aac3da46d631ac9ad66273fda1cb728c24ab07d37f35e58dc12bbf63c8fa7ce
|
7
|
+
data.tar.gz: b2ba393fd64aab9b5f0a1cd2fded2d9fa0639d5d8fb5355b5d96f13682af9c9802571a1f81ba1dfd3c85ef884195fb5f3a950a9511518b44e2bb099126595f8c
|
data/lib/sorbet-runtime.rb
CHANGED
@@ -93,6 +93,7 @@ require_relative 'types/props/errors'
|
|
93
93
|
require_relative 'types/props/plugin'
|
94
94
|
require_relative 'types/props/utils'
|
95
95
|
# Props that run sigs statically so have to be after all the others :(
|
96
|
+
require_relative 'types/props/private/setter_factory'
|
96
97
|
require_relative 'types/props/optional'
|
97
98
|
require_relative 'types/props/weak_constructor'
|
98
99
|
require_relative 'types/props/constructor'
|
@@ -109,87 +109,15 @@ class T::Props::Decorator
|
|
109
109
|
instance.instance_variable_get(rules ? rules[:accessor_key] : '@' + prop.to_s) # rubocop:disable PrisonGuard/NoLurkyInstanceVariableAccess
|
110
110
|
end
|
111
111
|
|
112
|
-
# For performance, don't use named params here.
|
113
|
-
# Passing in rules here is purely a performance optimization.
|
114
|
-
#
|
115
|
-
# checked(:never) - O(prop accesses)
|
116
|
-
sig do
|
117
|
-
params(
|
118
|
-
instance: DecoratedInstance,
|
119
|
-
prop: Symbol,
|
120
|
-
value: T.untyped,
|
121
|
-
rules: T.nilable(Rules)
|
122
|
-
)
|
123
|
-
.void
|
124
|
-
.checked(:never)
|
125
|
-
end
|
126
|
-
def set(instance, prop, value, rules=props[prop.to_sym])
|
127
|
-
# For backwards compatibility, fall back to reconstructing the accessor key
|
128
|
-
# (though it would probably make more sense to raise in that case).
|
129
|
-
instance.instance_variable_set(rules ? rules[:accessor_key] : '@' + prop.to_s, value) # rubocop:disable PrisonGuard/NoLurkyInstanceVariableAccess
|
130
|
-
end
|
131
|
-
|
132
112
|
# Use this to validate that a value will validate for a given prop. Useful for knowing whether a value can be set on a model without setting it.
|
133
113
|
#
|
134
114
|
# checked(:never) - potentially O(prop accesses) depending on usage pattern
|
135
115
|
sig {params(prop: Symbol, val: T.untyped).void.checked(:never)}
|
136
116
|
def validate_prop_value(prop, val)
|
137
|
-
#
|
138
|
-
#
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
# Passing in rules here is purely a performance optimization.
|
143
|
-
#
|
144
|
-
# checked(:never) - O(prop accesses)
|
145
|
-
sig {params(prop: Symbol, val: T.untyped, rules: Rules).void.checked(:never)}
|
146
|
-
private def check_prop_type(prop, val, rules=prop_rules(prop))
|
147
|
-
type_object = rules.fetch(:type_object)
|
148
|
-
type = rules.fetch(:type)
|
149
|
-
|
150
|
-
# TODO: ideally we'd add `&& rules[:optional] != :existing` to this check
|
151
|
-
# (it makes sense to treat those props required in this context), but we'd need
|
152
|
-
# to be sure that doesn't break any existing code first.
|
153
|
-
if val.nil?
|
154
|
-
if !T::Props::Utils.need_nil_write_check?(rules) || (rules.key?(:default) && rules[:default].nil?)
|
155
|
-
return
|
156
|
-
end
|
157
|
-
|
158
|
-
if rules[:raise_on_nil_write]
|
159
|
-
raise T::Props::InvalidValueError.new("Can't set #{@class.name}.#{prop} to #{val.inspect} " \
|
160
|
-
"(instance of #{val.class}) - need a #{type}")
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
# T::Props::CustomType is not a real object based class so that we can not run real type check call.
|
165
|
-
# T::Props::CustomType.valid?() is only a helper function call.
|
166
|
-
valid =
|
167
|
-
if type.is_a?(T::Props::CustomType) && T::Props::Utils.optional_prop?(rules)
|
168
|
-
type.valid?(val)
|
169
|
-
else
|
170
|
-
type_object.valid?(val)
|
171
|
-
end
|
172
|
-
|
173
|
-
if !valid
|
174
|
-
raise T::Props::InvalidValueError.new("Can't set #{@class.name}.#{prop} to #{val.inspect} " \
|
175
|
-
"(instance of #{val.class}) - need a #{type_object}")
|
176
|
-
end
|
177
|
-
rescue T::Props::InvalidValueError => err
|
178
|
-
caller_loc = T.must(caller_locations(8, 1))[0]
|
179
|
-
|
180
|
-
pretty_message = "Parameter '#{prop}': #{err.message}\n" \
|
181
|
-
"Caller: #{caller_loc.path}:#{caller_loc.lineno}\n"
|
182
|
-
|
183
|
-
T::Configuration.call_validation_error_handler(
|
184
|
-
nil,
|
185
|
-
message: err.message,
|
186
|
-
pretty_message: pretty_message,
|
187
|
-
kind: 'Parameter',
|
188
|
-
name: prop,
|
189
|
-
type: type,
|
190
|
-
value: val,
|
191
|
-
location: caller_loc,
|
192
|
-
)
|
117
|
+
# We call `setter_proc` here without binding to an instance, so it'll run
|
118
|
+
# `instance_variable_set` if validation passes, but nothing will care.
|
119
|
+
# We only care about the validation.
|
120
|
+
prop_rules(prop).fetch(:setter_proc).call(val)
|
193
121
|
end
|
194
122
|
|
195
123
|
# For performance, don't use named params here.
|
@@ -198,6 +126,9 @@ class T::Props::Decorator
|
|
198
126
|
# the default, which raises if the prop doesn't exist (this maintains
|
199
127
|
# preexisting behavior).
|
200
128
|
#
|
129
|
+
# Note this path is NOT used by generated setters on instances,
|
130
|
+
# which are defined using `setter_proc` directly.
|
131
|
+
#
|
201
132
|
# checked(:never) - O(prop accesses)
|
202
133
|
sig do
|
203
134
|
params(
|
@@ -210,9 +141,9 @@ class T::Props::Decorator
|
|
210
141
|
.checked(:never)
|
211
142
|
end
|
212
143
|
def prop_set(instance, prop, val, rules=prop_rules(prop))
|
213
|
-
|
214
|
-
set(instance, prop, val, rules)
|
144
|
+
instance.instance_exec(val, &rules[:setter_proc])
|
215
145
|
end
|
146
|
+
alias_method :set, :prop_set
|
216
147
|
|
217
148
|
# For performance, don't use named params here.
|
218
149
|
# Passing in rules here is purely a performance optimization.
|
@@ -499,7 +430,10 @@ class T::Props::Decorator
|
|
499
430
|
rules[:serializable_subtype] = hash_key_custom_type
|
500
431
|
end
|
501
432
|
|
433
|
+
rules[:setter_proc] = T::Props::Private::SetterFactory.build_setter_proc(@class, name, rules).freeze
|
434
|
+
|
502
435
|
add_prop_definition(name, rules)
|
436
|
+
|
503
437
|
# NB: using `without_accessors` doesn't make much sense unless you also define some other way to
|
504
438
|
# get at the property (e.g., Chalk::ODM::Document exposes `get` and `set`).
|
505
439
|
define_getter_and_setter(name, rules) unless rules[:without_accessors]
|
@@ -518,8 +452,13 @@ class T::Props::Decorator
|
|
518
452
|
private def define_getter_and_setter(name, rules)
|
519
453
|
T::Configuration.without_ruby_warnings do
|
520
454
|
if !rules[:immutable]
|
521
|
-
|
522
|
-
|
455
|
+
if method(:prop_set).owner != T::Props::Decorator
|
456
|
+
@class.send(:define_method, "#{name}=") do |val|
|
457
|
+
self.class.decorator.prop_set(self, name, val, rules)
|
458
|
+
end
|
459
|
+
else
|
460
|
+
# Fast path (~4x faster as of Ruby 2.6)
|
461
|
+
@class.send(:define_method, "#{name}=", &rules[:setter_proc])
|
523
462
|
end
|
524
463
|
end
|
525
464
|
|
@@ -860,6 +799,15 @@ class T::Props::Decorator
|
|
860
799
|
self.class.decorator.prop_get(self, name, rules)
|
861
800
|
end
|
862
801
|
end
|
802
|
+
|
803
|
+
unless rules[:immutable]
|
804
|
+
if child.decorator.method(:prop_set).owner != method(:prop_set).owner &&
|
805
|
+
child.instance_method("#{name}=").source_location.first == __FILE__
|
806
|
+
child.send(:define_method, "#{name}=") do |val|
|
807
|
+
self.class.decorator.prop_set(self, name, val, rules)
|
808
|
+
end
|
809
|
+
end
|
810
|
+
end
|
863
811
|
end
|
864
812
|
end
|
865
813
|
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: strict
|
3
|
+
|
4
|
+
module T::Props
|
5
|
+
module Private
|
6
|
+
module SetterFactory
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
SetterProc = T.type_alias {T.proc.params(val: T.untyped).void}
|
10
|
+
|
11
|
+
sig do
|
12
|
+
params(
|
13
|
+
klass: T.all(Module, T::Props::ClassMethods),
|
14
|
+
prop: Symbol,
|
15
|
+
rules: T::Hash[Symbol, T.untyped]
|
16
|
+
)
|
17
|
+
.returns(SetterProc)
|
18
|
+
.checked(:never)
|
19
|
+
end
|
20
|
+
def self.build_setter_proc(klass, prop, rules)
|
21
|
+
# Our nil check works differently than a simple T.nilable for various
|
22
|
+
# reasons (including the `raise_on_nil_write` setting, the existence
|
23
|
+
# of defaults & factories, and the fact that we allow `T.nilable(Foo)`
|
24
|
+
# where Foo < T::Props::CustomType as a prop type even though calling
|
25
|
+
# `valid?` on it won't work as expected), so unwrap any T.nilable and
|
26
|
+
# do a check manually. (Note this hack does not fix custom types as
|
27
|
+
# collection elements.)
|
28
|
+
non_nil_type = if rules[:type_is_custom_type]
|
29
|
+
rules.fetch(:type)
|
30
|
+
else
|
31
|
+
T::Utils::Nilable.get_underlying_type_object(rules.fetch(:type_object))
|
32
|
+
end
|
33
|
+
accessor_key = rules.fetch(:accessor_key)
|
34
|
+
raise_error = ->(val) {raise_pretty_error(klass, prop, non_nil_type, val)}
|
35
|
+
|
36
|
+
# It seems like a bug that this affects the behavior of setters, but
|
37
|
+
# some existing code relies on this behavior
|
38
|
+
has_explicit_nil_default = rules.key?(:default) && rules.fetch(:default).nil?
|
39
|
+
|
40
|
+
if !T::Props::Utils.need_nil_write_check?(rules) || has_explicit_nil_default
|
41
|
+
proc do |val|
|
42
|
+
if val.nil?
|
43
|
+
instance_variable_set(accessor_key, nil)
|
44
|
+
elsif non_nil_type.valid?(val)
|
45
|
+
instance_variable_set(accessor_key, val)
|
46
|
+
else
|
47
|
+
raise_error.call(val)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
else
|
51
|
+
proc do |val|
|
52
|
+
if non_nil_type.valid?(val)
|
53
|
+
instance_variable_set(accessor_key, val)
|
54
|
+
else
|
55
|
+
raise_error.call(val)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
sig do
|
62
|
+
params(
|
63
|
+
klass: T.all(Module, T::Props::ClassMethods),
|
64
|
+
prop: Symbol,
|
65
|
+
type: T.any(T::Types::Base, Module),
|
66
|
+
val: T.untyped,
|
67
|
+
)
|
68
|
+
.void
|
69
|
+
end
|
70
|
+
private_class_method def self.raise_pretty_error(klass, prop, type, val)
|
71
|
+
base_message = "Can't set #{klass.name}.#{prop} to #{val.inspect} (instance of #{val.class}) - need a #{type}"
|
72
|
+
|
73
|
+
pretty_message = "Parameter '#{prop}': #{base_message}\n"
|
74
|
+
caller_loc = caller_locations&.find {|l| !l.to_s.include?('sorbet-runtime/lib/types/props')}
|
75
|
+
if caller_loc
|
76
|
+
pretty_message += "Caller: #{caller_loc.path}:#{caller_loc.lineno}\n"
|
77
|
+
end
|
78
|
+
|
79
|
+
T::Configuration.call_validation_error_handler(
|
80
|
+
nil,
|
81
|
+
message: base_message,
|
82
|
+
pretty_message: pretty_message,
|
83
|
+
kind: 'Parameter',
|
84
|
+
name: prop,
|
85
|
+
type: type,
|
86
|
+
value: val,
|
87
|
+
location: caller_loc,
|
88
|
+
)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sorbet-runtime
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.5265
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stripe
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-01-
|
11
|
+
date: 2020-01-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -112,6 +112,7 @@ files:
|
|
112
112
|
- lib/types/props/optional.rb
|
113
113
|
- lib/types/props/plugin.rb
|
114
114
|
- lib/types/props/pretty_printable.rb
|
115
|
+
- lib/types/props/private/setter_factory.rb
|
115
116
|
- lib/types/props/serializable.rb
|
116
117
|
- lib/types/props/type_validation.rb
|
117
118
|
- lib/types/props/utils.rb
|