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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 04ffc124de39ff9df564bcefc3535063ae7416d3a468f565b199b0f2359838fd
4
- data.tar.gz: 74284ac5b08f7be2e1cc85b5f7415051643e7d5d9b77cd9c07934b8d85c5495e
3
+ metadata.gz: 8ece434fac1367289d5e0cba4706f2163bffe4875e5c55aa6a2e926398777961
4
+ data.tar.gz: 7ecbe46eb8bb3531dc649711e09a7f3c0a4ea357bea901c754ea240491f0a9bc
5
5
  SHA512:
6
- metadata.gz: 0ac146a8172fdd2374fb35e0ea4de6fe2b04bdcce79c79f1133eeabf395f9a8c703e1fe881389f3b497e787258d7c1fad324629c40f6e1daefd18fec56a2dc7d
7
- data.tar.gz: 296a6b1b1099111c83e8e859119eb76a36ca1499b2d0e89abb06ab07ea73575b606c5d0b54e7231330d1a27fb6fa0f78f09dcbd7bcd5dcfeb6b8f3e09df8703a
6
+ metadata.gz: 3b3a3a27c2ec61f17702301ee54e944aee9c9aaa534072ba9769466ef1c3bbae8aac3da46d631ac9ad66273fda1cb728c24ab07d37f35e58dc12bbf63c8fa7ce
7
+ data.tar.gz: b2ba393fd64aab9b5f0a1cd2fded2d9fa0639d5d8fb5355b5d96f13682af9c9802571a1f81ba1dfd3c85ef884195fb5f3a950a9511518b44e2bb099126595f8c
@@ -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
- # This implements a 'public api' on document so that we don't allow callers to pass in rules
138
- # Rules seem like an implementation detail so it seems good to now allow people to specify them manually.
139
- check_prop_type(prop, val)
140
- end
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
- check_prop_type(prop, val, T.must(rules))
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
- @class.send(:define_method, "#{name}=") do |x|
522
- self.class.decorator.prop_set(self, name, x, rules)
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.5262
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-17 00:00:00.000000000 Z
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