sorbet_typed-props 1.1.1 → 1.2.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: e1a574f476951cc6ed98f8980d38b05af4d3d84133749ec11c53b21d0329707b
4
- data.tar.gz: 03c9d3b394eb287a41ac07b353ddf33d104d6d6e1a46ec524e822bdeebf8eb02
3
+ metadata.gz: 3e03f38fb86ea87d47dddba75811d8f4e5e61772abd33f039fc4c8d044e2568e
4
+ data.tar.gz: 517097aab6f9f90ba81aca6a4879d4e93917a8097a357140a856a6125f54f2e9
5
5
  SHA512:
6
- metadata.gz: 81293d96ac1958922e74bcd81ff1ede7d122833d3d7b31f5b8652d22e17d5eb76533c92920cd13fd30c30afbf95122da781462a11408d72ecc9c6e93bbcb090a
7
- data.tar.gz: e4303aec243a78bc62e7a735ab86d151d4d8217a07c7fb8c811974aea3540cb48d0aea1fd47d6f1cbc108ad81e46b4f19057a3c9b80e59c52d654160e3f92b15
6
+ metadata.gz: 352ec5bdd85d44eb0c0d0162f38601fd31b43d90469feec2e1770021fb6f158af26e2412d3ccfde9945c8cef729258eab4e5821aac7be40879cbb333a6a38c98
7
+ data.tar.gz: 4651beb5802ba54e72da7b8859f44dcfd1fcf821011b60deee83b73d2d6f954caee3425f827ee862b1e07a4f6b8749ee9c4b2e58f97db26936a32ae73794d94e
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.1.1
18
+ version: 1.2.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,14 @@
1
+ ## v1.2.0 (2025-11-26)
2
+
3
+ ### Feat
4
+
5
+ - skip RBI generation for abstract classes
6
+
7
+ ### Fix
8
+
9
+ - include superclass in generated RBI if it exists
10
+ - ensure correct type signatures for optional props
11
+
1
12
  ## v1.1.1 (2025-11-25)
2
13
 
3
14
  ### Fix
@@ -5,7 +5,7 @@
5
5
 
6
6
  module SorbetTyped
7
7
  module Props
8
- VERSION = '1.1.1'
8
+ VERSION = '1.2.0'
9
9
  end
10
10
  end
11
11
 
@@ -1,6 +1,8 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
+ # spellchecker:ignore tnilable
5
+
4
6
  module Tapioca
5
7
  module Dsl
6
8
  module Compilers
@@ -18,14 +20,22 @@ module Tapioca
18
20
 
19
21
  extend T::Sig
20
22
 
23
+ const :_tnilable, T::Boolean, default: false
21
24
  const :type_object, T::Types::Simple
22
- const :_nilable, T::Boolean, default: false
23
25
  const :default, Object, default: ParamValueEnum::ParamNotPresent
24
26
  const :factory, Object, default: ParamValueEnum::ParamNotPresent
25
27
 
26
28
  sig { returns(T::Boolean) }
27
29
  def optional?
28
- _nilable || default != ParamValueEnum::ParamNotPresent || factory != ParamValueEnum::ParamNotPresent
30
+ # NOTE: explicitly not checking for _tnilable. This makes nilable
31
+ # props required, when no default or factory is set. The default
32
+ # behavior would have been to be optional with an implied default
33
+ # of nil
34
+ # (https://sorbet.org/docs/tstruct#tnilable-without-implying-default-nil).
35
+ # But because we don't know here, if a type alias has been used as
36
+ # workaround not not imply default, we simply never treat nilable
37
+ # as optional.
38
+ default != ParamValueEnum::ParamNotPresent || factory != ParamValueEnum::ParamNotPresent
29
39
  end
30
40
  end
31
41
 
@@ -51,7 +61,11 @@ module Tapioca
51
61
  sig { returns(RBI::TypedParam) }
52
62
  def create_rbi_parameter
53
63
  if optional?
54
- create_kw_opt_param(parameter_name, type:, default: 'nil')
64
+ # NOTE: unfortunately, there is no way to get the actual default
65
+ # value as code string from within this compiler, as everything
66
+ # has already been processed at this point. So we use
67
+ # `T.unsafe(nil)` as stand-in.
68
+ create_kw_opt_param(parameter_name, type:, default: 'T.unsafe(nil)')
55
69
  else
56
70
  create_kw_param(parameter_name, type:)
57
71
  end
@@ -66,13 +80,7 @@ module Tapioca
66
80
 
67
81
  sig { returns(String) }
68
82
  def type
69
- details_type = details.type_object.name
70
-
71
- if optional?
72
- "T.nilable(#{details_type})"
73
- else
74
- details_type.to_s
75
- end
83
+ details.type_object.name.to_s
76
84
  end
77
85
 
78
86
  sig { returns(String) }
@@ -11,7 +11,7 @@ module Tapioca
11
11
  extend T::Sig
12
12
  extend T::Generic
13
13
 
14
- ConstantType = type_member { { fixed: T.class_of(SorbetTyped::Props) } }
14
+ ConstantType = type_member { { upper: T::Class[SorbetTyped::Props] } }
15
15
 
16
16
  sig { override.returns(T::Enumerable[T::Module[T.anything]]) }
17
17
  def self.gather_constants
@@ -21,24 +21,34 @@ module Tapioca
21
21
 
22
22
  sig { override.void }
23
23
  def decorate
24
+ # NOTE: skip RBI generation for abstract classes, as they cannot be instantiated anyways
25
+ return if T::AbstractUtils.abstract_module?(constant)
24
26
  # NOTE: do not generate a new initializer signature, if the class sets
25
27
  # its own and doesn't use the prop generated one.
26
28
  return if constant.private_instance_methods(false).include?(:initialize)
27
-
28
- props = constant.props
29
29
  return if props.empty?
30
30
 
31
- params = params_from_props(props:)
32
- define_initializer_sig(constant:, params:)
31
+ define_initializer_sig
33
32
  end
34
33
 
35
34
  private
36
35
 
37
- sig { params(props: T.untyped).returns(T::Array[RBI::TypedParam]) }
38
- def params_from_props(props:)
39
- props.map do |name, details|
40
- create_param_from_prop(name:, details:)
41
- end
36
+ sig { returns(T::Hash[Symbol, T::Hash[Symbol, T.untyped]]) }
37
+ def props
38
+ # NOTE: we already filtered in `::gather_constants` for classes that
39
+ # include `SorbetTyped::Props`, so we know it must be this type and
40
+ # has method `props`.
41
+ @props ||= T.let(T.unsafe(constant).props, T.nilable(T::Hash[Symbol, T::Hash[Symbol, T.untyped]]))
42
+ end
43
+
44
+ sig { returns(T::Array[RBI::TypedParam]) }
45
+ def params
46
+ @params ||= T.let(
47
+ props.map do |name, details|
48
+ create_param_from_prop(name:, details:)
49
+ end,
50
+ T.nilable(T::Array[RBI::TypedParam])
51
+ )
42
52
  end
43
53
 
44
54
  sig { params(name: Symbol, details: T.untyped).returns(RBI::TypedParam) }
@@ -55,23 +65,23 @@ module Tapioca
55
65
  create_rbi_parameter
56
66
  end
57
67
 
58
- # HACK: `T.class_of(SorbetTyped::Props)` fails when passed one of the
59
- # anonymous test classes. But active typechecking isn't that relevant
60
- # here.
61
- sig { params(constant: T.class_of(SorbetTyped::Props), params: T::Array[RBI::TypedParam]).void.checked(:never) }
62
- def define_initializer_sig(constant:, params:)
63
- root.create_path(constant) do |klass|
68
+ sig { void }
69
+ def define_initializer_sig
70
+ superclass = constant.superclass
71
+ superclass_name = superclass == Object ? nil : superclass.to_s
72
+
73
+ root.create_class(constant.to_s, superclass_name:) do |klass|
64
74
  klass.create_method(
65
75
  'initialize',
66
- parameters: params_with_optional_last(params:),
76
+ parameters: params_with_optional_last,
67
77
  return_type: 'void',
68
78
  comments: [RBI::Comment.new('Generated from sorbet SorbetTyped::Props')]
69
79
  )
70
80
  end
71
81
  end
72
82
 
73
- sig { params(params: T::Array[RBI::TypedParam]).returns(T::Array[RBI::TypedParam]) }
74
- def params_with_optional_last(params:)
83
+ sig { returns(T::Array[RBI::TypedParam]) }
84
+ def params_with_optional_last
75
85
  required_params, optional_params = params.partition do |param|
76
86
  param.param.is_a?(RBI::KwParam)
77
87
  end
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.1.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Kramer