rspec-sorbet-types 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a22cc57159bb8fcf6011e4f526f04da73e7cb0c6a0f8ba2be14b2fda79f1a516
4
+ data.tar.gz: 7d4cd90ee0792e2b7bd2c8aa64348b8ec2006f57133774d5d6606518a93b3604
5
+ SHA512:
6
+ metadata.gz: 4f8dea02beb1c65684aa54552cbfa96b8d7335d1da3f36c866b6fba677f88e9debe451f836c633e4053700768de559449266599bb42e9974b641f8034744245f
7
+ data.tar.gz: 80b33d85703d7b9ebdcc0e8f37c8f3ce7718af361413a2491731895fa6c44825634a5a2e17318ed65a3950c77b156d75c3361cc925b82034d3732799a0e962db
@@ -0,0 +1,22 @@
1
+ # typed: true
2
+
3
+ module RSpec
4
+ module Sorbet
5
+ module Types
6
+ module Sig
7
+ include Kernel
8
+
9
+ def self.extended(sub)
10
+ super
11
+ sub.extend(T::Sig)
12
+ end
13
+
14
+ T::Sig::WithoutRuntime.sig { params(decl: T.proc.bind(T::Private::Methods::DeclBuilder).void).void }
15
+ def rsig(&decl)
16
+ # It would be better if we could simply use the name "sig", but Sorbet falsely reports that as an overload
17
+ send(:sig, &decl)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,2 @@
1
+ require "sorbet-runtime"
2
+ require_relative "types/sig"
@@ -0,0 +1,177 @@
1
+ # frozen_string_literal: true
2
+ # typed: strict
3
+
4
+ require "rspec/core"
5
+
6
+ module Tapioca
7
+ module Dsl
8
+ module Compilers
9
+ class RSpec < Compiler
10
+ extend T::Sig
11
+
12
+ ConstantType = type_member { {fixed: T.class_of(::RSpec::Core::ExampleGroup)} }
13
+
14
+ class << self
15
+ extend T::Sig
16
+
17
+ sig { override.returns(T::Enumerable[Module]) }
18
+ def gather_constants
19
+ all_classes.select { |c| c < ::RSpec::Core::ExampleGroup }
20
+ end
21
+
22
+ private
23
+
24
+ sig { void }
25
+ def require_spec_files!
26
+ Dir.glob(spec_glob).each do |file|
27
+ require(file)
28
+ end
29
+ end
30
+
31
+ sig { returns(String) }
32
+ def spec_glob
33
+ ENV["SORBET_RSPEC_GLOB"] || File.join(".", "spec", "**", "*.rb")
34
+ end
35
+ end
36
+
37
+ # Set environment variable, allowing other code to detect that we're compiling
38
+ ENV["SORBET_RSPEC_TYPES_COMPILING"] = "1"
39
+
40
+ # Load all spec files during compiler definition
41
+ require_spec_files!
42
+
43
+ sig { override.void }
44
+ def decorate
45
+ klass = root.create_class(T.must(constant.name), superclass_name: T.must(constant.superclass).name)
46
+ create_includes(klass)
47
+ create_example_group_submodules(klass)
48
+ create_singleton_methods(klass)
49
+ end
50
+
51
+ private
52
+
53
+ sig { params(klass: RBI::Scope).void }
54
+ def create_includes(klass)
55
+ directly_included_modules_for(constant).each do |mod|
56
+ klass.create_include("::#{mod}")
57
+ end
58
+ end
59
+
60
+ # A method can have a signature even if the method is defined dynamically with define_method
61
+ # The next call to def or define_method will be the one associated with the signature
62
+ # However, an exception will be raised if we have two signature declarations in a row
63
+ # without a method definition.
64
+ sig { params(method_name: Symbol).returns(String) }
65
+ def return_type_for_let_declaration(method_name)
66
+ T::Utils.signature_for_instance_method(constant, method_name)&.return_type&.to_s || "T.untyped"
67
+ end
68
+
69
+ sig { params(klass: RBI::Scope).void }
70
+ def create_example_group_submodules(klass)
71
+ modules = directly_included_modules_for(constant).select { |mod| mod.name&.start_with?("RSpec::ExampleGroups::") }
72
+ modules.each do |mod|
73
+ scope = root.create_module(T.must(mod.name))
74
+ direct_public_instance_methods_for(mod).each do |method_name|
75
+ method_def = mod.instance_method(method_name)
76
+ return_type = return_type_for_let_declaration(method_name)
77
+
78
+ scope.create_method(
79
+ method_def.name.to_s,
80
+ parameters: compile_method_parameters_to_rbi(method_def),
81
+ return_type:,
82
+ class_method: false
83
+ )
84
+ end
85
+ end
86
+ end
87
+
88
+ sig { params(klass: RBI::Scope).void }
89
+ def create_singleton_methods(klass)
90
+ scope = klass.create_class("<< self")
91
+ scope.create_method(
92
+ "let",
93
+ parameters: [
94
+ create_rest_param("name", type: "T.untyped"),
95
+ create_block_param("block", type: "T.proc.bind(#{constant.name}).void")
96
+ ]
97
+ )
98
+ scope.create_method(
99
+ "let!",
100
+ parameters: [
101
+ create_rest_param("name", type: "T.untyped"),
102
+ create_block_param("block", type: "T.proc.bind(#{constant.name}).void")
103
+ ]
104
+ )
105
+
106
+ scope.create_method(
107
+ "before",
108
+ parameters: [
109
+ create_rest_param("args", type: "T.untyped"),
110
+ create_block_param("block", type: "T.proc.bind(#{constant.name}).void")
111
+ ]
112
+ )
113
+
114
+ scope.create_method(
115
+ "after",
116
+ parameters: [
117
+ create_rest_param("args", type: "T.untyped"),
118
+ create_block_param("block", type: "T.proc.bind(#{constant.name}).void")
119
+ ]
120
+ )
121
+
122
+ scope.create_method(
123
+ "it",
124
+ parameters: [
125
+ create_rest_param("all_args", type: "T.untyped"),
126
+ create_block_param("block", type: "T.proc.bind(#{constant.name}).void")
127
+ ]
128
+ )
129
+
130
+ scope.create_method(
131
+ "specify",
132
+ parameters: [
133
+ create_rest_param("all_args", type: "T.untyped"),
134
+ create_block_param("block", type: "T.proc.bind(#{constant.name}).void")
135
+ ]
136
+ )
137
+
138
+ scope.create_method(
139
+ "subject",
140
+ parameters: [
141
+ create_block_param("block", type: "T.proc.bind(#{constant.name}).void")
142
+ ]
143
+ )
144
+
145
+ singleton_class = constant.singleton_class
146
+ direct_public_instance_methods_for(singleton_class).each do |method_name|
147
+ create_method_from_def(scope, singleton_class.instance_method(method_name))
148
+ end
149
+ end
150
+
151
+ sig { params(constant: Module).returns(T::Enumerable[Module]) }
152
+ def directly_included_modules_for(constant)
153
+ result = constant.included_modules
154
+ result -= constant.included_modules.map do |included_mod|
155
+ included_mod.ancestors - [included_mod]
156
+ end.flatten
157
+ if constant.is_a?(Class) && constant.superclass
158
+ result -= T.must(constant.superclass).included_modules
159
+ end
160
+ result
161
+ end
162
+
163
+ sig { params(constant: Module).returns(T::Enumerable[Symbol]) }
164
+ def direct_public_instance_methods_for(constant)
165
+ result = constant.public_instance_methods
166
+ constant.included_modules.each do |included_mod|
167
+ result -= included_mod.public_instance_methods
168
+ end
169
+ if constant.is_a?(Class) && constant.superclass
170
+ result -= T.must(constant.superclass).public_instance_methods
171
+ end
172
+ result
173
+ end
174
+ end
175
+ end
176
+ end
177
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+ # typed: strict
3
+
4
+ module RSpec
5
+ class << self
6
+ sig do
7
+ params(
8
+ args: T.untyped,
9
+ example_group_block: T.proc.bind(T.class_of(RSpec::Core::ExampleGroup)).void,
10
+ ).void
11
+ end
12
+
13
+ def describe(*args, &example_group_block); end
14
+ end
15
+ end
16
+
17
+ class RSpec::Core::ExampleGroup
18
+ include ::RSpec::Matchers
19
+ include ::RSpec::Mocks::ExampleMethods
20
+
21
+ class << self
22
+ sig do
23
+ params(
24
+ all_args: T.untyped,
25
+ block: T.proc.bind(RSpec::Core::ExampleGroup).void,
26
+ ).void
27
+ end
28
+
29
+ def it(*all_args, &block); end
30
+
31
+ sig do
32
+ params(
33
+ all_args: T.untyped,
34
+ block: T.proc.bind(RSpec::Core::ExampleGroup).void,
35
+ ).void
36
+ end
37
+
38
+ def specify(*all_args, &block); end
39
+
40
+ sig do
41
+ params(
42
+ args: T.untyped,
43
+ block: T.proc.bind(RSpec::Core::ExampleGroup).void,
44
+ ).void
45
+ end
46
+
47
+ def before(*args, &block); end
48
+
49
+ sig do
50
+ params(
51
+ args: T.untyped,
52
+ block: T.proc.bind(RSpec::Core::ExampleGroup).void,
53
+ ).void
54
+ end
55
+
56
+ def after(*args, &block); end
57
+
58
+ sig do
59
+ params(
60
+ args: T.untyped,
61
+ block: T.proc.bind(RSpec::Core::ExampleGroup).void,
62
+ ).void
63
+ end
64
+
65
+ def around(*args, &block); end
66
+ end
67
+ end
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rspec-sorbet-types
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Hongli Lai
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2025-01-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sorbet-runtime
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: tapioca
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0.16'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0.16'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ description: A gem that provides integration between Sorbet type checking and RSpec
56
+ testing framework
57
+ email:
58
+ - hongli@hongli.nl
59
+ executables: []
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - lib/rspec/sorbet/types.rb
64
+ - lib/rspec/sorbet/types/sig.rb
65
+ - lib/tapioca/dsl/compilers/rspec.rb
66
+ - rbi/rspec-core.rbi
67
+ homepage: https://github.com/FooBarWidget/sorbet-rspec
68
+ licenses:
69
+ - MIT
70
+ metadata:
71
+ homepage_uri: https://github.com/FooBarWidget/sorbet-rspec
72
+ source_code_uri: https://github.com/FooBarWidget/sorbet-rspec
73
+ post_install_message:
74
+ rdoc_options: []
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: 2.7.0
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ requirements: []
88
+ rubygems_version: 3.5.3
89
+ signing_key:
90
+ specification_version: 4
91
+ summary: Sorbet integration for RSpec
92
+ test_files: []