sorbet-runtime 0.5.5255 → 0.5.5284

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5001cde809170dd114c21f38d060d0ea4d0c7447bd719020a28d8219c733593b
4
- data.tar.gz: 8af071522bf9cfcfd89eea4ca0567e06ad6109cfe2e827f00542a00e971eaa26
3
+ metadata.gz: 6570883296df2b8c2f0f18bd126c932daeac62effd65b35544f04c5b467a66a2
4
+ data.tar.gz: 43f95a4fdd8d83076f72d3094438995b05440d222d487a67c20b328745eecc13
5
5
  SHA512:
6
- metadata.gz: a7695bfaf0c47b193d1cccc99cb89159a7d29e2efbfb89c21c3dbbcd9c1ba4c5cb53ba12dbceb9589b2d51b6bc765854d45469931740a3c73f2c40517defa4c0
7
- data.tar.gz: 812f4170808544d2d320fb834f28fcbdeba7d9ba26041b18cdece81aaae8feb1d924d7e942a60b4d305b5747867cd75abd6149da2be9ffc30ef966c3c89bb9f8
6
+ metadata.gz: 26308242fd0d09ba1585594cf116150ed3a3112c503e4f11ee96a3301450a9cf7742729200c8a4652d28a6b166eca32dd1c6e766c6a16099b6c403d67006c45b
7
+ data.tar.gz: c5e8e54c724a32857f50642cd18400a0886e66a33cc5a84ddc68d75919ebc576e5d7bd52365919f69b5a653591dced8c5566223fa375071af8ae3cd75f02297f
@@ -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'
@@ -51,8 +51,14 @@ class T::Private::Methods::Signature
51
51
  @on_failure = on_failure
52
52
  @override_allow_incompatible = override_allow_incompatible
53
53
 
54
- param_names = parameters.map {|_, name| name}
55
54
  declared_param_names = raw_arg_types.keys
55
+ # If sig params are declared but there is a single parameter with a missing name
56
+ # **and** the method ends with a "=", assume it is a writer method generated
57
+ # by attr_writer or attr_accessor
58
+ writer_method = declared_param_names != [nil] && parameters == [[:req]] && method_name[-1] == "="
59
+ # For writer methods, map the single parameter to the method name without the "=" at the end
60
+ parameters = [[:req, method_name[0...-1].to_sym]] if writer_method
61
+ param_names = parameters.map {|_, name| name}
56
62
  missing_names = param_names - declared_param_names
57
63
  extra_names = declared_param_names - param_names
58
64
  if !missing_names.empty?
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- # typed: false
2
+ # typed: strict
3
3
 
4
4
  # NB: This is not actually a decorator. It's just named that way for consistency
5
5
  # with DocumentDecorator and ModelDecorator (which both seem to have been written
@@ -11,26 +11,27 @@ class T::Props::Decorator
11
11
  extend T::Sig
12
12
 
13
13
  Rules = T.type_alias {T::Hash[Symbol, T.untyped]}
14
- DecoratedClass = T.type_alias {T.untyped} # T.class_of(T::Props), but that produces circular reference errors in some circumstances
15
- DecoratedInstance = T.type_alias {T.untyped} # Would be T::Props, but that produces circular reference errors in some circumstances
14
+ DecoratedInstance = T.type_alias {Object} # Would be T::Props, but that produces circular reference errors in some circumstances
16
15
  PropType = T.type_alias {T.any(T::Types::Base, T::Props::CustomType)}
17
16
  PropTypeOrClass = T.type_alias {T.any(PropType, Module)}
18
17
 
19
18
  class NoRulesError < StandardError; end
20
19
 
21
- sig {params(klass: DecoratedClass).void.checked(:never)}
20
+ EMPTY_PROPS = T.let({}.freeze, T::Hash[Symbol, Rules])
21
+ private_constant :EMPTY_PROPS
22
+
23
+ sig {params(klass: T.untyped).void}
22
24
  def initialize(klass)
23
- @class = klass
24
- klass.plugins.each do |mod|
25
- Private.apply_decorator_methods(mod, self)
25
+ @class = T.let(klass, T.all(Module, T::Props::ClassMethods))
26
+ @class.plugins.each do |mod|
27
+ T::Props::Plugin::Private.apply_decorator_methods(mod, self)
26
28
  end
29
+ @props = T.let(EMPTY_PROPS, T::Hash[Symbol, Rules])
27
30
  end
28
31
 
29
32
  # checked(:never) - O(prop accesses)
30
33
  sig {returns(T::Hash[Symbol, Rules]).checked(:never)}
31
- def props
32
- @props ||= {}.freeze
33
- end
34
+ attr_reader :props
34
35
 
35
36
  # Try to avoid using this; post-definition mutation of prop rules is
36
37
  # surprising and hard to reason about.
@@ -62,7 +63,7 @@ class T::Props::Decorator
62
63
  @props = @props.merge(prop => rules.freeze).freeze
63
64
  end
64
65
 
65
- VALID_RULE_KEYS = %i{
66
+ VALID_RULE_KEYS = T.let(%i{
66
67
  enum
67
68
  foreign
68
69
  foreign_hint_only
@@ -76,16 +77,16 @@ class T::Props::Decorator
76
77
  extra
77
78
  optional
78
79
  _tnilable
79
- }.map {|k| [k, true]}.to_h.freeze
80
+ }.map {|k| [k, true]}.to_h.freeze, T::Hash[Symbol, T::Boolean])
80
81
  private_constant :VALID_RULE_KEYS
81
82
 
82
83
  sig {params(key: Symbol).returns(T::Boolean).checked(:never)}
83
84
  def valid_rule_key?(key)
84
- VALID_RULE_KEYS[key]
85
+ !!VALID_RULE_KEYS[key]
85
86
  end
86
87
 
87
88
  # checked(:never) - O(prop accesses)
88
- sig {returns(DecoratedClass).checked(:never)}
89
+ sig {returns(T.all(Module, T::Props::ClassMethods)).checked(:never)}
89
90
  def decorated_class; @class; end
90
91
 
91
92
  # Accessors
@@ -109,87 +110,15 @@ class T::Props::Decorator
109
110
  instance.instance_variable_get(rules ? rules[:accessor_key] : '@' + prop.to_s) # rubocop:disable PrisonGuard/NoLurkyInstanceVariableAccess
110
111
  end
111
112
 
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
113
  # 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
114
  #
134
115
  # checked(:never) - potentially O(prop accesses) depending on usage pattern
135
116
  sig {params(prop: Symbol, val: T.untyped).void.checked(:never)}
136
117
  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
- )
118
+ # We call `setter_proc` here without binding to an instance, so it'll run
119
+ # `instance_variable_set` if validation passes, but nothing will care.
120
+ # We only care about the validation.
121
+ prop_rules(prop).fetch(:setter_proc).call(val)
193
122
  end
194
123
 
195
124
  # For performance, don't use named params here.
@@ -198,21 +127,24 @@ class T::Props::Decorator
198
127
  # the default, which raises if the prop doesn't exist (this maintains
199
128
  # preexisting behavior).
200
129
  #
130
+ # Note this path is NOT used by generated setters on instances,
131
+ # which are defined using `setter_proc` directly.
132
+ #
201
133
  # checked(:never) - O(prop accesses)
202
134
  sig do
203
135
  params(
204
136
  instance: DecoratedInstance,
205
137
  prop: Symbol,
206
138
  val: T.untyped,
207
- rules: T.nilable(Rules)
139
+ rules: Rules
208
140
  )
209
141
  .void
210
142
  .checked(:never)
211
143
  end
212
144
  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)
145
+ instance.instance_exec(val, &rules.fetch(:setter_proc))
215
146
  end
147
+ alias_method :set, :prop_set
216
148
 
217
149
  # For performance, don't use named params here.
218
150
  # Passing in rules here is purely a performance optimization.
@@ -253,18 +185,18 @@ class T::Props::Decorator
253
185
  prop: Symbol,
254
186
  foreign_class: Module,
255
187
  rules: Rules,
256
- opts: Hash
188
+ opts: T::Hash[Symbol, T.untyped],
257
189
  )
258
190
  .returns(T.untyped)
259
191
  .checked(:never)
260
192
  end
261
193
  def foreign_prop_get(instance, prop, foreign_class, rules=props[prop.to_sym], opts={})
262
194
  return if !(value = prop_get(instance, prop, rules))
263
- foreign_class.load(value, {}, opts)
195
+ T.unsafe(foreign_class).load(value, {}, opts)
264
196
  end
265
197
 
266
198
  # TODO: we should really be checking all the methods on `cls`, not just Object
267
- BANNED_METHOD_NAMES = Object.instance_methods.to_set.freeze
199
+ BANNED_METHOD_NAMES = T.let(Object.instance_methods.to_set.freeze, T::Set[Symbol])
268
200
 
269
201
  # checked(:never) - Rules hash is expensive to check
270
202
  sig do
@@ -314,6 +246,7 @@ class T::Props::Decorator
314
246
  nil
315
247
  end
316
248
 
249
+ sig {params(name: Symbol).void}
317
250
  private def validate_prop_name(name)
318
251
  if name !~ /\A[A-Za-z_][A-Za-z0-9_-]*\z/
319
252
  raise ArgumentError.new("Invalid prop name in #{@class.name}: #{name}")
@@ -440,7 +373,7 @@ class T::Props::Decorator
440
373
  # are ultimately included does.
441
374
  #
442
375
  if defined?(Opus) && defined?(Opus::Sensitivity) && defined?(Opus::Sensitivity::PIIable)
443
- if sensitivity_and_pii[:pii] && @class.is_a?(Class) && !@class.contains_pii?
376
+ if sensitivity_and_pii[:pii] && @class.is_a?(Class) && !T.unsafe(@class).contains_pii?
444
377
  raise ArgumentError.new(
445
378
  'Cannot include a pii prop in a class that declares `contains_no_pii`'
446
379
  )
@@ -499,7 +432,10 @@ class T::Props::Decorator
499
432
  rules[:serializable_subtype] = hash_key_custom_type
500
433
  end
501
434
 
435
+ rules[:setter_proc] = T::Props::Private::SetterFactory.build_setter_proc(@class, name, rules).freeze
436
+
502
437
  add_prop_definition(name, rules)
438
+
503
439
  # NB: using `without_accessors` doesn't make much sense unless you also define some other way to
504
440
  # get at the property (e.g., Chalk::ODM::Document exposes `get` and `set`).
505
441
  define_getter_and_setter(name, rules) unless rules[:without_accessors]
@@ -518,18 +454,23 @@ class T::Props::Decorator
518
454
  private def define_getter_and_setter(name, rules)
519
455
  T::Configuration.without_ruby_warnings do
520
456
  if !rules[:immutable]
521
- @class.send(:define_method, "#{name}=") do |x|
522
- self.class.decorator.prop_set(self, name, x, rules)
457
+ if method(:prop_set).owner != T::Props::Decorator
458
+ @class.send(:define_method, "#{name}=") do |val|
459
+ T.unsafe(self.class).decorator.prop_set(self, name, val, rules)
460
+ end
461
+ else
462
+ # Fast path (~4x faster as of Ruby 2.6)
463
+ @class.send(:define_method, "#{name}=", &rules.fetch(:setter_proc))
523
464
  end
524
465
  end
525
466
 
526
467
  if method(:prop_get).owner != T::Props::Decorator || rules.key?(:ifunset)
527
468
  @class.send(:define_method, name) do
528
- self.class.decorator.prop_get(self, name, rules)
469
+ T.unsafe(self.class).decorator.prop_get(self, name, rules)
529
470
  end
530
471
  else
531
472
  # Fast path (~30x faster as of Ruby 2.6)
532
- @class.attr_reader(name)
473
+ @class.send(:attr_reader, name) # send is used because `attr_reader` is private in 2.4
533
474
  end
534
475
  end
535
476
  end
@@ -597,10 +538,10 @@ class T::Props::Decorator
597
538
  end
598
539
 
599
540
  # From T::Props::Utils.deep_clone_object, plus String
600
- TYPES_NOT_NEEDING_CLONE = [TrueClass, FalseClass, NilClass, Symbol, String, Numeric]
541
+ TYPES_NOT_NEEDING_CLONE = T.let([TrueClass, FalseClass, NilClass, Symbol, String, Numeric], T::Array[Module])
601
542
 
602
543
  # checked(:never) - Typechecks internally
603
- sig {params(type: PropType).returns(T::Boolean).checked(:never)}
544
+ sig {params(type: PropType).returns(T.nilable(T::Boolean)).checked(:never)}
604
545
  private def shallow_clone_ok(type)
605
546
  inner_type =
606
547
  if type.is_a?(T::Types::TypedArray)
@@ -642,7 +583,7 @@ class T::Props::Decorator
642
583
  end
643
584
 
644
585
  # checked(:never) - Rules hash is expensive to check
645
- sig {params(prop_name: Symbol, rules: Hash).void.checked(:never)}
586
+ sig {params(prop_name: Symbol, rules: Rules).void.checked(:never)}
646
587
  private def validate_not_missing_sensitivity(prop_name, rules)
647
588
  if rules[:sensitivity].nil?
648
589
  if rules[:redaction]
@@ -747,6 +688,7 @@ class T::Props::Decorator
747
688
  # *haven't* allowed additional options in the past and want to
748
689
  # default to keeping this interface narrow.
749
690
  @class.send(:define_method, fk_method) do |allow_direct_mutation: nil|
691
+ foreign = T.let(foreign, T.untyped)
750
692
  if foreign.is_a?(Proc)
751
693
  resolved_foreign = foreign.call
752
694
  if !resolved_foreign.respond_to?(:load)
@@ -765,7 +707,7 @@ class T::Props::Decorator
765
707
  opts = {allow_direct_mutation: allow_direct_mutation}
766
708
  end
767
709
 
768
- self.class.decorator.foreign_prop_get(self, prop_name, foreign, rules, opts)
710
+ T.unsafe(self.class).decorator.foreign_prop_get(self, prop_name, foreign, rules, opts)
769
711
  end
770
712
 
771
713
  force_fk_method = "#{fk_method}!"
@@ -777,7 +719,7 @@ class T::Props::Decorator
777
719
  storytime: {method: force_fk_method, class: self.class}
778
720
  )
779
721
  end
780
- T.must(loaded_foreign)
722
+ loaded_foreign
781
723
  end
782
724
 
783
725
  @class.send(:define_method, "#{prop_name}_record") do |allow_direct_mutation: nil|
@@ -826,16 +768,17 @@ class T::Props::Decorator
826
768
  #
827
769
  # This gets called when a module or class that extends T::Props gets included, extended,
828
770
  # prepended, or inherited.
829
- sig {params(child: DecoratedClass).void.checked(:never)}
771
+ sig {params(child: Module).void.checked(:never)}
830
772
  def model_inherited(child)
831
773
  child.extend(T::Props::ClassMethods)
832
- child.plugins.concat(decorated_class.plugins)
774
+ child = T.cast(child, T.all(Module, T::Props::ClassMethods))
833
775
 
776
+ child.plugins.concat(decorated_class.plugins)
834
777
  decorated_class.plugins.each do |mod|
835
778
  # NB: apply_class_methods must not be an instance method on the decorator itself,
836
779
  # otherwise we'd have to call child.decorator here, which would create the decorator
837
780
  # before any `decorator_class` override has a chance to take effect (see the comment below).
838
- Private.apply_class_methods(mod, child)
781
+ T::Props::Plugin::Private.apply_class_methods(mod, child)
839
782
  end
840
783
 
841
784
  props.each do |name, rules|
@@ -855,9 +798,18 @@ class T::Props::Decorator
855
798
  #
856
799
  unless rules[:without_accessors]
857
800
  if child.decorator.method(:prop_get).owner != method(:prop_get).owner &&
858
- child.instance_method(name).source_location.first == __FILE__
801
+ child.instance_method(name).source_location&.first == __FILE__
859
802
  child.send(:define_method, name) do
860
- self.class.decorator.prop_get(self, name, rules)
803
+ T.unsafe(self.class).decorator.prop_get(self, name, rules)
804
+ end
805
+ end
806
+
807
+ unless rules[:immutable]
808
+ if child.decorator.method(:prop_set).owner != method(:prop_set).owner &&
809
+ child.instance_method("#{name}=").source_location&.first == __FILE__
810
+ child.send(:define_method, "#{name}=") do |val|
811
+ T.unsafe(self.class).decorator.prop_set(self, name, val, rules)
812
+ end
861
813
  end
862
814
  end
863
815
  end
@@ -867,27 +819,7 @@ class T::Props::Decorator
867
819
  sig {params(mod: Module).void.checked(:never)}
868
820
  def plugin(mod)
869
821
  decorated_class.plugins << mod
870
- Private.apply_class_methods(mod, decorated_class)
871
- Private.apply_decorator_methods(mod, self)
872
- end
873
-
874
- module Private
875
- # These need to be non-instance methods so we can use them without prematurely creating the
876
- # child decorator in `model_inherited` (see comments there for details).
877
- def self.apply_class_methods(plugin, target)
878
- if plugin.const_defined?('ClassMethods')
879
- # FIXME: This will break preloading, selective test execution, etc if `mod::ClassMethods`
880
- # is ever defined in a separate file from `mod`.
881
- target.extend(plugin::ClassMethods) # rubocop:disable PrisonGuard/NoDynamicConstAccess
882
- end
883
- end
884
-
885
- def self.apply_decorator_methods(plugin, target)
886
- if plugin.const_defined?('DecoratorMethods')
887
- # FIXME: This will break preloading, selective test execution, etc if `mod::DecoratorMethods`
888
- # is ever defined in a separate file from `mod`.
889
- target.extend(plugin::DecoratorMethods) # rubocop:disable PrisonGuard/NoDynamicConstAccess
890
- end
891
- end
822
+ T::Props::Plugin::Private.apply_class_methods(mod, decorated_class)
823
+ T::Props::Plugin::Private.apply_decorator_methods(mod, self)
892
824
  end
893
825
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- # typed: true
2
+ # typed: false
3
3
 
4
4
  module T::Props::Plugin
5
5
  include T::Props
@@ -12,4 +12,26 @@ module T::Props::Plugin
12
12
  end
13
13
  end
14
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
15
37
  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.5255
4
+ version: 0.5.5284
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-16 00:00:00.000000000 Z
11
+ date: 2020-01-25 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