sorbet_typed-props 1.2.25 → 1.3.0

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: e3abd84c594ddc4874392dc63b3a174f880e6d9483b0710dbb22818902469efa
4
- data.tar.gz: bf57501a1305b3d117f5b36e18fa0d3318d3cf816178242ae9cfba6ec7e9a1f0
3
+ metadata.gz: cf6255a0a1adc5c5a5aa2a793fca6040f6db8b54f8f02323de37d4ab04a0350f
4
+ data.tar.gz: 1cd56d60ce8a5d695673ee3c1f8070774e14465e4ed3c2cfeeaef3802bb43ace
5
5
  SHA512:
6
- metadata.gz: ae2a0065e8f05c0b6e82a24783d5b388f4216f16f77f253f77e15cd629f2c884d2d8999166a561d2bd3fecfb1928dce563c91af2d5384cd05746fdb385d610bb
7
- data.tar.gz: 041d7a0f7d955b82084a1f86886ed6b05c6d3507d8df6c27bc62795c9704e643ec4eb1aafd286d2662eaffa5f291e8c76eb37131c53bff56a69ccc68e3fe403b
6
+ metadata.gz: 56e7784fcac731992752234db265d02dfbbe005e06e65c9e411d2e8b5c603fe8a0f1bf33f0a50972b07b3b47c6d01ecfcba99bf78ae9da058be26ed159e9a235
7
+ data.tar.gz: e162abc3d651c77ea7a604cf13192302bba22e7fbea584a1919f49886e570eda8bf1c95fb43cce8ce828d2f7d886539a6b4db7558832f4b2a7b6ab1a42c83059
data/.cz.yaml CHANGED
@@ -15,7 +15,7 @@ commitizen:
15
15
  - mise run format
16
16
  tag_format: v$version
17
17
  update_changelog_on_bump: true
18
- version: 1.2.25
18
+ version: 1.3.0
19
19
  version_files:
20
20
  - lib/sorbet_typed/props/version.rb
21
21
  version_scheme: semver2
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## v1.3.0 (2026-01-27)
2
+
3
+ ### Feat
4
+
5
+ - add a way to define props that should not be part of the initializer
6
+
7
+ ## v1.2.26 (2026-01-24)
8
+
9
+ ### Fix
10
+
11
+ - **deps**: update sorbet to v0.6.12897
12
+
1
13
  ## v1.2.25 (2026-01-17)
2
14
 
3
15
  ### Fix
@@ -5,7 +5,7 @@
5
5
 
6
6
  module SorbetTyped
7
7
  module Props
8
- VERSION = '1.2.25'
8
+ VERSION = '1.3.0'
9
9
  end
10
10
  end
11
11
 
@@ -0,0 +1,70 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module SorbetTyped
5
+ module Props
6
+ class WithoutInitParamAndNoDefaultPlugin
7
+ # this overrides how T::Props::Constructor builds props without defaults.
8
+ # It allows defining props excluded from the initializer without setting a
9
+ # default.
10
+ module DecoratorMethods
11
+ extend T::Sig
12
+ include Kernel
13
+
14
+ sig do
15
+ params(
16
+ instance: T::Props::Decorator::DecoratedInstance,
17
+ prop: Symbol,
18
+ rules: T::Props::Decorator::Rules
19
+ ).
20
+ returns(T.untyped)
21
+ end
22
+ def prop_get(instance, prop, rules = T.unsafe(self).prop_rules(prop))
23
+ value = super
24
+
25
+ if rules.dig(:extra, :initializer) == false && !rules[:type_object].valid?(value)
26
+ raise TypeError, "Property `#{prop}` of `#{T.unsafe(instance).class.name}` was read before being set"
27
+ end
28
+
29
+ value
30
+ end
31
+
32
+ # DEBT: is there a better way to do this without duplicating so much code?
33
+ #
34
+ # source: https://github.com/sorbet/sorbet/blob/e554cf64fa404eb5ee5fdc87186968a190613759/gems/sorbet-runtime/lib/types/props/constructor.rb#L19-L39
35
+ sig { params(instance: T::Props::Constructor, hash: T::Hash[Symbol, T.untyped]).returns(Integer).checked(:never) }
36
+ # :reek:DuplicateMethodCall :reek:TooManyStatements
37
+ # :reek:UncommunicativeVariableName -- We ignore these smells, as they
38
+ # are already present in the original code and I don't want to modify
39
+ # it.
40
+ def construct_props_without_defaults(instance, hash) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength -- we ignore these warnings as they stem from the original implementation which we keep as much as possible
41
+ # Use `each_pair` rather than `count` because, as of Ruby 2.6, the latter delegates to Enumerator
42
+ # and therefore allocates for each entry.
43
+ result = 0
44
+
45
+ # :nocov: -- NOTE: There is no test covering the nil-scenario. But this
46
+ # code is taken straight from sorbet, so there shouldn't really be a
47
+ # need to retest it. I also couldn't even construct a scenario where
48
+ # props_without_defaults would be nil.
49
+ T.unsafe(self).props_without_defaults&.each_pair do |p, setter_proc|
50
+ # :nocov:
51
+
52
+ # NOTE: this is the only change to this method taken from T::Props::Constructor::DecoratorMethods
53
+ next if T.unsafe(self).props.dig(p, :extra, :initializer) == false
54
+
55
+ val = hash[p]
56
+ instance.instance_exec(val, &setter_proc)
57
+ result += 1 if val || hash.key?(p)
58
+ rescue TypeError, T::Props::InvalidValueError
59
+ unless hash.key?(p)
60
+ raise ArgumentError, "Missing required prop `#{p}` for class `#{T.unsafe(instance).class.name}`"
61
+ end
62
+
63
+ raise
64
+ end
65
+ result
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -3,6 +3,7 @@
3
3
 
4
4
  require 'sorbet-runtime'
5
5
  require_relative 'props/version'
6
+ require_relative 'props/without_init_param_and_no_default_plugin'
6
7
 
7
8
  module SorbetTyped
8
9
  # an abstraction module to be included in any class to make the sorbet props
@@ -20,10 +21,29 @@ module SorbetTyped
20
21
  include T::Props
21
22
  include T::Props::Constructor
22
23
 
23
- mixes_in_class_methods(T::Props::ClassMethods)
24
+ include Kernel
25
+
26
+ plugin(WithoutInitParamAndNoDefaultPlugin)
27
+
28
+ # Methods injected into the including class singleton
29
+ module ClassMethods
30
+ extend T::Sig
31
+ include T::Props::ClassMethods
32
+
33
+ sig { returns(T::Hash[Symbol, T::Props::Decorator::Rules]) }
34
+ def props_in_initializer
35
+ props.reject do |_, prop_parameters|
36
+ prop_parameters.dig(:extra, :initializer) == false
37
+ end
38
+ end
39
+ end
40
+
41
+ mixes_in_class_methods(ClassMethods)
42
+
43
+ sig { params(hash: T::Hash[Symbol, T.untyped]).void }
44
+ def initialize(hash = {})
45
+ reject_unknown_parameters(hash)
24
46
 
25
- sig { params(args: T.anything, kwargs: T.anything).void }
26
- def initialize(*args, **kwargs)
27
47
  super
28
48
  post_props_initialize
29
49
  end
@@ -32,5 +52,14 @@ module SorbetTyped
32
52
 
33
53
  sig { overridable.void }
34
54
  def post_props_initialize; end
55
+
56
+ sig { params(parameters: T::Hash[Symbol, T.anything]).void }
57
+ # :reek:DuplicateMethodCall { allow_calls: ["self.class"] }
58
+ def reject_unknown_parameters(parameters)
59
+ unknown_parameters = parameters.keys - self.class.props_in_initializer.keys
60
+ return unless unknown_parameters.any?
61
+
62
+ raise ArgumentError, "#{self.class}: Unrecognized properties: #{unknown_parameters.join(', ')}"
63
+ end
35
64
  end
36
65
  end
@@ -11,7 +11,7 @@ module Tapioca
11
11
  extend T::Sig
12
12
  extend T::Generic
13
13
 
14
- ConstantType = type_member { { upper: T::Class[SorbetTyped::Props] } }
14
+ ConstantType = type_member { { upper: T.all(T::Class[SorbetTyped::Props], T::Props::ClassMethods) } }
15
15
 
16
16
  sig { override.returns(T::Enumerable[T::Module[T.anything]]) }
17
17
  def self.gather_constants
@@ -35,16 +35,29 @@ module Tapioca
35
35
 
36
36
  sig { returns(T::Hash[Symbol, T::Hash[Symbol, T.untyped]]) }
37
37
  def props
38
+ @props ||= T.let(
39
+ constant.props,
40
+ T.nilable(T::Hash[Symbol, T::Hash[Symbol, T.untyped]])
41
+ )
42
+ end
43
+
44
+ sig { returns(T::Hash[Symbol, T::Hash[Symbol, T.untyped]]) }
45
+ def props_in_initializer
38
46
  # NOTE: we already filtered in `::gather_constants` for classes that
39
47
  # include `SorbetTyped::Props`, so we know it must be this type and
40
48
  # has method `props`.
41
- @props ||= T.let(T.unsafe(constant).props, T.nilable(T::Hash[Symbol, T::Hash[Symbol, T.untyped]]))
49
+ @props_in_initializer ||= T.let(
50
+ props.reject do |_name, rules|
51
+ rules.dig(:extra, :initializer) == false
52
+ end,
53
+ T.nilable(T::Hash[Symbol, T::Hash[Symbol, T.untyped]])
54
+ )
42
55
  end
43
56
 
44
57
  sig { returns(T::Array[RBI::TypedParam]) }
45
58
  def params
46
59
  @params ||= T.let(
47
- props.map do |name, details|
60
+ props_in_initializer.map do |name, details|
48
61
  create_param_from_prop(name:, details:)
49
62
  end,
50
63
  T.nilable(T::Array[RBI::TypedParam])
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sorbet_typed-props
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.25
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Kramer
@@ -18,7 +18,7 @@ dependencies:
18
18
  version: 0.6.0
19
19
  - - "<="
20
20
  - !ruby/object:Gem::Version
21
- version: 0.6.12894
21
+ version: 0.6.12897
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -28,7 +28,7 @@ dependencies:
28
28
  version: 0.6.0
29
29
  - - "<="
30
30
  - !ruby/object:Gem::Version
31
- version: 0.6.12894
31
+ version: 0.6.12897
32
32
  - !ruby/object:Gem::Dependency
33
33
  name: tapioca
34
34
  requirement: !ruby/object:Gem::Requirement
@@ -68,6 +68,7 @@ files:
68
68
  - README.md
69
69
  - lib/sorbet_typed/props.rb
70
70
  - lib/sorbet_typed/props/version.rb
71
+ - lib/sorbet_typed/props/without_init_param_and_no_default_plugin.rb
71
72
  - lib/tapioca/dsl/compilers/sorbet_typed_props_constructor.rb
72
73
  - lib/tapioca/dsl/compilers/sorbet_typed_props_constructor/prop_to_param_converter.rb
73
74
  homepage: https://gitlab.com/sorbet_typed/props