sorbet-runtime 0.5.12225 → 0.5.12349

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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/types/props/decorator.rb +91 -8
  3. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c3aed74436b6f44fed009be1812a819efa86d7efb4f57a47aa3d195ab57f2208
4
- data.tar.gz: 57820a54f94052a51b8d44c4d25fca0ff49cf08dacc2cff8d38afc45806fe59a
3
+ metadata.gz: ed0be05b2dc1a36d22edcee3cf009bf56a33694e889ba41793966f6128853738
4
+ data.tar.gz: 906069610c18e58a01945eb7c605fe1dcc6089d8a6a20955557c8dd6cf491528
5
5
  SHA512:
6
- metadata.gz: e851aa9cc3d1d650c5c8c40ddb8b9e02d7185e165b2a4c719b52da858ee2628969b8188344cbd47061c5d7ad5f61624f045220e4151d1f77ba1e56e1463db841
7
- data.tar.gz: e7ffdbd9acd7b2dd6ad927d31c442769b57885989a39c8b33bc13fad179150810a43ff32d80d1074a8c8f2f112ab49a7d7bd7b03d1475ff1685936f522b6519e
6
+ metadata.gz: e8d6655a0e3502befabc74bb1e4ee6d55a8d8b5d7a1eccc6593ba3070cd2d45e14958239f5dba645451fed4f1bb73300e2868f90da983a8223f14f84bfad382f
7
+ data.tar.gz: 1f815a6904f8ee420f72a400cdd895df9d5b456cbaaf0f919318e84fd0b50e3ce37618fbf0b9b3aa6e069757e7fc3e8db04c17e538fb164e1abfbb4be503f3ab
@@ -14,12 +14,28 @@ class T::Props::Decorator
14
14
  DecoratedInstance = T.type_alias { Object } # Would be T::Props, but that produces circular reference errors in some circumstances
15
15
  PropType = T.type_alias { T::Types::Base }
16
16
  PropTypeOrClass = T.type_alias { T.any(PropType, Module) }
17
+ OverrideRules = T.type_alias { T::Hash[Symbol, {allow_incompatible: T::Boolean}] }
17
18
 
18
19
  class NoRulesError < StandardError; end
19
20
 
20
21
  EMPTY_PROPS = T.let({}.freeze, T::Hash[Symbol, Rules], checked: false)
21
22
  private_constant :EMPTY_PROPS
22
23
 
24
+ OVERRIDE_TRUE = T.let({
25
+ reader: {allow_incompatible: false}.freeze,
26
+ writer: {allow_incompatible: false}.freeze,
27
+ }.freeze, OverrideRules)
28
+
29
+ OVERRIDE_READER = T.let({
30
+ reader: {allow_incompatible: false}.freeze,
31
+ }.freeze, OverrideRules)
32
+
33
+ OVERRIDE_WRITER = T.let({
34
+ writer: {allow_incompatible: false}.freeze,
35
+ }.freeze, OverrideRules)
36
+
37
+ OVERRIDE_EMPTY = T.let({}.freeze, OverrideRules)
38
+
23
39
  sig { params(klass: T.untyped).void.checked(:never) }
24
40
  def initialize(klass)
25
41
  @class = T.let(klass, T.all(Module, T::Props::ClassMethods))
@@ -45,17 +61,15 @@ class T::Props::Decorator
45
61
  end
46
62
 
47
63
  # checked(:never) - Rules hash is expensive to check
48
- sig { params(prop: Symbol, rules: Rules).void.checked(:never) }
49
- def add_prop_definition(prop, rules)
64
+ sig { params(name: Symbol, rules: Rules).void.checked(:never) }
65
+ def add_prop_definition(name, rules)
50
66
  override = rules.delete(:override)
51
67
 
52
- if props.include?(prop) && !override
53
- raise ArgumentError.new("Attempted to redefine prop #{prop.inspect} on class #{@class} that's already defined without specifying :override => true: #{prop_rules(prop)}")
54
- elsif !props.include?(prop) && override
55
- raise ArgumentError.new("Attempted to override a prop #{prop.inspect} on class #{@class} that doesn't already exist")
68
+ if props.include?(name) && !override
69
+ raise ArgumentError.new("Attempted to redefine prop #{name.inspect} on class #{@class} that's already defined without specifying :override => true: #{prop_rules(name)}")
56
70
  end
57
71
 
58
- @props = @props.merge(prop => rules.freeze).freeze
72
+ @props = @props.merge(name => rules.freeze).freeze
59
73
  end
60
74
 
61
75
  # Heads up!
@@ -302,6 +316,27 @@ class T::Props::Decorator
302
316
  T::Utils::Nilable.is_union_with_nilclass(cls) || ((cls == T.untyped || cls == NilClass) && rules.key?(:default) && rules[:default].nil?)
303
317
  end
304
318
 
319
+ sig(:final) { params(name: Symbol).returns(T::Boolean).checked(:never) }
320
+ private def method_defined_on_ancestor?(name)
321
+ @class.method_defined?(name) && !@class.method_defined?(name, false)
322
+ end
323
+
324
+ sig(:final) { params(name: Symbol, rules: Rules).void.checked(:never) }
325
+ private def validate_overrides(name, rules)
326
+ override = elaborate_override(name, rules[:override])
327
+
328
+ return if rules[:without_accessors]
329
+
330
+ if override[:reader] && !method_defined_on_ancestor?(name) && !props.include?(name)
331
+ raise ArgumentError.new("You marked the getter for prop #{name.inspect} as `override`, but the method `#{name}` doesn't exist to be overridden.")
332
+ end
333
+
334
+ # Properly, we should also check whether `props[name]` is immutable, but the old code didn't either.
335
+ if !rules[:immutable] && override[:writer] && !method_defined_on_ancestor?("#{name}=".to_sym) && !props.include?(name)
336
+ raise ArgumentError.new("You marked the setter for prop #{name.inspect} as `override`, but the method `#{name}=` doesn't exist to be overridden.")
337
+ end
338
+ end
339
+
305
340
  # checked(:never) - Rules hash is expensive to check
306
341
  sig do
307
342
  params(
@@ -381,6 +416,7 @@ class T::Props::Decorator
381
416
  rules[:setter_proc] = setter_proc
382
417
  rules[:value_validate_proc] = value_validate_proc
383
418
 
419
+ validate_overrides(name, rules)
384
420
  add_prop_definition(name, rules)
385
421
 
386
422
  # NB: using `without_accessors` doesn't make much sense unless you also define some other way to
@@ -405,6 +441,7 @@ class T::Props::Decorator
405
441
  # Fast path (~4x faster as of Ruby 2.6)
406
442
  @class.send(:define_method, "#{name}=", &rules.fetch(:setter_proc))
407
443
  end
444
+
408
445
  end
409
446
 
410
447
  if method(:prop_get).owner != T::Props::Decorator || rules.key?(:ifunset)
@@ -627,7 +664,7 @@ class T::Props::Decorator
627
664
 
628
665
  props.each do |name, rules|
629
666
  copied_rules = rules.dup
630
- # NB: Calling `child.decorator` here is a timb bomb that's going to give someone a really bad
667
+ # NB: Calling `child.decorator` here is a time bomb that's going to give someone a really bad
631
668
  # time. Any class that defines props and also overrides the `decorator_class` method is going
632
669
  # to reach this line before its override take effect, turning it into a no-op.
633
670
  child.decorator.add_prop_definition(name, copied_rules)
@@ -656,6 +693,52 @@ class T::Props::Decorator
656
693
  end
657
694
  end
658
695
 
696
+ sig(:final) do
697
+ params(key: Symbol, d: T.untyped, out: T::Hash[Symbol, {allow_incompatible: T::Boolean}])
698
+ .void
699
+ .checked(:never)
700
+ end
701
+ private def elaborate_override_entry(key, d, out)
702
+ # It's written this way so that `{reader: false}` will omit the entry for `reader` in the
703
+ # result entirely
704
+ case d[key]
705
+ when TrueClass
706
+ out[key] = {allow_incompatible: false}
707
+ when Hash
708
+ out[key] = {allow_incompatible: !!d[key][:allow_incompatible]}
709
+ end
710
+ end
711
+
712
+ sig(:final) do
713
+ params(name: Symbol, d: T.untyped)
714
+ .returns(T::Hash[Symbol, {allow_incompatible: T::Boolean}])
715
+ .checked(:never)
716
+ end
717
+ private def elaborate_override(name, d)
718
+ return OVERRIDE_TRUE if d == true
719
+ return OVERRIDE_READER if d == :reader
720
+ return OVERRIDE_WRITER if d == :writer
721
+ return OVERRIDE_EMPTY if d == false || d.nil?
722
+ unless d.is_a?(Hash)
723
+ raise ArgumentError.new("`override` only accepts `true`, `:reader`, `:writer`, or a Hash in prop #{@class.name}.#{name} (got #{d.class})")
724
+ end
725
+
726
+ # cwong: should we check for bad keys? `sig { override(not_real: true) }` on a normal function
727
+ # errors statically but not at runtime.
728
+
729
+ # XX cwong: this means {reader: false, allow_incompatible: true} will become {allow_incompatible: true},
730
+ # is that fine?
731
+ unless (allow_incompatible = d[:allow_incompatible]).nil?
732
+ return {reader: {allow_incompatible: !!allow_incompatible},
733
+ writer: {allow_incompatible: !!allow_incompatible}}.to_h
734
+ end
735
+
736
+ result = {}
737
+ elaborate_override_entry(:reader, d, result)
738
+ elaborate_override_entry(:writer, d, result)
739
+ result
740
+ end
741
+
659
742
  sig { params(child: T.all(Module, T::Props::ClassMethods), prop: Symbol).returns(T::Boolean).checked(:never) }
660
743
  private def clobber_getter?(child, prop)
661
744
  !!(child.decorator.method(:prop_get).owner != method(:prop_get).owner &&
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.12225
4
+ version: 0.5.12349
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stripe
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-07-16 00:00:00.000000000 Z
11
+ date: 2025-07-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest