tapioca 0.6.1 → 0.7.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 +4 -4
- data/Gemfile +13 -2
- data/README.md +79 -25
- data/Rakefile +10 -14
- data/lib/tapioca/cli.rb +66 -80
- data/lib/tapioca/{generators/base.rb → commands/command.rb} +17 -10
- data/lib/tapioca/{generators → commands}/dsl.rb +59 -45
- data/lib/tapioca/{generators → commands}/gem.rb +93 -30
- data/lib/tapioca/{generators → commands}/init.rb +9 -13
- data/lib/tapioca/{generators → commands}/require.rb +8 -10
- data/lib/tapioca/commands/todo.rb +84 -0
- data/lib/tapioca/commands.rb +13 -0
- data/lib/tapioca/dsl/compiler.rb +185 -0
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/aasm.rb +12 -9
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/action_controller_helpers.rb +13 -20
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/action_mailer.rb +10 -8
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_job.rb +11 -9
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_model_attributes.rb +32 -24
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_model_secure_password.rb +10 -12
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_associations.rb +29 -35
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_columns.rb +26 -24
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_enum.rb +14 -12
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_fixtures.rb +10 -8
- data/lib/tapioca/dsl/compilers/active_record_relations.rb +712 -0
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_scope.rb +21 -20
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_typed_store.rb +12 -17
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_resource.rb +10 -8
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_storage.rb +11 -11
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_support_concern.rb +19 -14
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_support_current_attributes.rb +16 -21
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/config.rb +10 -8
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/frozen_record.rb +13 -11
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/identity_cache.rb +28 -25
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/mixed_in_class_attributes.rb +12 -10
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/protobuf.rb +10 -8
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/rails_generators.rb +13 -14
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/sidekiq_worker.rb +14 -13
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/smart_properties.rb +12 -13
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/state_machines.rb +12 -10
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/url_helpers.rb +16 -14
- data/lib/tapioca/dsl/compilers.rb +31 -0
- data/lib/tapioca/{compilers/dsl → dsl}/extensions/frozen_record.rb +2 -2
- data/lib/tapioca/dsl/helpers/active_record_column_type_helper.rb +114 -0
- data/lib/tapioca/dsl/helpers/active_record_constants_helper.rb +29 -0
- data/lib/tapioca/{compilers/dsl → dsl/helpers}/param_helper.rb +2 -2
- data/lib/tapioca/{compilers/dsl_compiler.rb → dsl/pipeline.rb} +41 -33
- data/lib/tapioca/gem/events.rb +120 -0
- data/lib/tapioca/gem/listeners/base.rb +48 -0
- data/lib/tapioca/gem/listeners/dynamic_mixins.rb +32 -0
- data/lib/tapioca/gem/listeners/methods.rb +183 -0
- data/lib/tapioca/gem/listeners/mixins.rb +101 -0
- data/lib/tapioca/gem/listeners/remove_empty_payload_scopes.rb +21 -0
- data/lib/tapioca/gem/listeners/sorbet_enums.rb +26 -0
- data/lib/tapioca/gem/listeners/sorbet_helpers.rb +29 -0
- data/lib/tapioca/gem/listeners/sorbet_props.rb +33 -0
- data/lib/tapioca/gem/listeners/sorbet_required_ancestors.rb +23 -0
- data/lib/tapioca/gem/listeners/sorbet_signatures.rb +79 -0
- data/lib/tapioca/gem/listeners/sorbet_type_variables.rb +51 -0
- data/lib/tapioca/gem/listeners/subconstants.rb +37 -0
- data/lib/tapioca/gem/listeners/yard_doc.rb +96 -0
- data/lib/tapioca/gem/listeners.rb +16 -0
- data/lib/tapioca/gem/pipeline.rb +365 -0
- data/lib/tapioca/gemfile.rb +44 -20
- data/lib/tapioca/helpers/cli_helper.rb +16 -8
- data/lib/tapioca/helpers/config_helper.rb +113 -0
- data/lib/tapioca/helpers/rbi_helper.rb +17 -0
- data/lib/tapioca/helpers/shims_helper.rb +87 -0
- data/lib/tapioca/helpers/sorbet_helper.rb +57 -0
- data/lib/tapioca/helpers/test/dsl_compiler.rb +118 -0
- data/lib/tapioca/helpers/test/isolation.rb +1 -1
- data/lib/tapioca/helpers/test/template.rb +13 -2
- data/lib/tapioca/internal.rb +17 -10
- data/lib/tapioca/rbi_ext/model.rb +2 -48
- data/lib/tapioca/rbi_formatter.rb +37 -0
- data/lib/tapioca/runtime/dynamic_mixin_compiler.rb +227 -0
- data/lib/tapioca/runtime/generic_type_registry.rb +166 -0
- data/lib/tapioca/runtime/loader.rb +123 -0
- data/lib/tapioca/runtime/reflection.rb +153 -0
- data/lib/tapioca/runtime/trackers/autoload.rb +72 -0
- data/lib/tapioca/runtime/trackers/constant_definition.rb +44 -0
- data/lib/tapioca/runtime/trackers/mixin.rb +80 -0
- data/lib/tapioca/runtime/trackers/required_ancestor.rb +50 -0
- data/lib/tapioca/{trackers.rb → runtime/trackers.rb} +4 -3
- data/lib/tapioca/sorbet_ext/generic_name_patch.rb +110 -54
- data/lib/tapioca/sorbet_ext/name_patch.rb +7 -1
- data/lib/tapioca/{compilers → static}/requires_compiler.rb +5 -12
- data/lib/tapioca/static/symbol_loader.rb +83 -0
- data/lib/tapioca/static/symbol_table_parser.rb +63 -0
- data/lib/tapioca/version.rb +1 -1
- data/lib/tapioca.rb +2 -7
- metadata +82 -62
- data/lib/tapioca/compilers/dsl/active_record_relations.rb +0 -711
- data/lib/tapioca/compilers/dsl/base.rb +0 -179
- data/lib/tapioca/compilers/dsl/helper/active_record_constants.rb +0 -27
- data/lib/tapioca/compilers/dynamic_mixin_compiler.rb +0 -198
- data/lib/tapioca/compilers/sorbet.rb +0 -59
- data/lib/tapioca/compilers/symbol_table/symbol_generator.rb +0 -780
- data/lib/tapioca/compilers/symbol_table/symbol_loader.rb +0 -90
- data/lib/tapioca/compilers/symbol_table_compiler.rb +0 -17
- data/lib/tapioca/compilers/todos_compiler.rb +0 -32
- data/lib/tapioca/generators/todo.rb +0 -76
- data/lib/tapioca/generators.rb +0 -9
- data/lib/tapioca/generic_type_registry.rb +0 -149
- data/lib/tapioca/helpers/active_record_column_type_helper.rb +0 -98
- data/lib/tapioca/loader.rb +0 -119
- data/lib/tapioca/reflection.rb +0 -151
- data/lib/tapioca/trackers/autoload.rb +0 -70
- data/lib/tapioca/trackers/constant_definition.rb +0 -42
- data/lib/tapioca/trackers/mixin.rb +0 -78
|
@@ -17,23 +17,37 @@ module T
|
|
|
17
17
|
constant = super
|
|
18
18
|
# `register_type` method builds and returns an instantiated clone of the generic type
|
|
19
19
|
# so, we just return that from this method as well.
|
|
20
|
-
Tapioca::GenericTypeRegistry.register_type(constant, types)
|
|
20
|
+
Tapioca::Runtime::GenericTypeRegistry.register_type(constant, types)
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
def type_member(variance = :invariant, fixed: nil, lower: T.untyped, upper: BasicObject)
|
|
24
24
|
# `T::Generic#type_member` just instantiates a `T::Type::TypeMember` instance and returns it.
|
|
25
25
|
# We use that when registering the type member and then later return it from this method.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
Tapioca::TypeVariableModule.new(
|
|
27
|
+
T.cast(self, Module),
|
|
28
|
+
Tapioca::TypeVariableModule::Type::Member,
|
|
29
|
+
variance,
|
|
30
|
+
fixed,
|
|
31
|
+
lower,
|
|
32
|
+
upper
|
|
33
|
+
).tap do |type_variable|
|
|
34
|
+
Tapioca::Runtime::GenericTypeRegistry.register_type_variable(self, type_variable)
|
|
35
|
+
end
|
|
29
36
|
end
|
|
30
37
|
|
|
31
38
|
def type_template(variance = :invariant, fixed: nil, lower: T.untyped, upper: BasicObject)
|
|
32
39
|
# `T::Generic#type_template` just instantiates a `T::Type::TypeTemplate` instance and returns it.
|
|
33
40
|
# We use that when registering the type template and then later return it from this method.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
41
|
+
Tapioca::TypeVariableModule.new(
|
|
42
|
+
T.cast(self, Module),
|
|
43
|
+
Tapioca::TypeVariableModule::Type::Template,
|
|
44
|
+
variance,
|
|
45
|
+
fixed,
|
|
46
|
+
lower,
|
|
47
|
+
upper
|
|
48
|
+
).tap do |type_variable|
|
|
49
|
+
Tapioca::Runtime::GenericTypeRegistry.register_type_variable(self, type_variable)
|
|
50
|
+
end
|
|
37
51
|
end
|
|
38
52
|
end
|
|
39
53
|
|
|
@@ -42,15 +56,14 @@ module T
|
|
|
42
56
|
|
|
43
57
|
module Types
|
|
44
58
|
class Simple
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
module GenericNamePatch
|
|
59
|
+
module GenericPatch
|
|
60
|
+
# This method intercepts calls to the `name` method for simple types, so that
|
|
61
|
+
# it can ask the name to the type if the type is generic, since, by this point,
|
|
62
|
+
# we've created a clone of that type with the `name` method returning the
|
|
63
|
+
# appropriate name for that specific concrete type.
|
|
51
64
|
def name
|
|
52
|
-
if T::Generic === @raw_type
|
|
53
|
-
# for types that are generic, use the name
|
|
65
|
+
if T::Generic === @raw_type || Tapioca::TypeVariableModule === @raw_type
|
|
66
|
+
# for types that are generic or are type variables, use the name
|
|
54
67
|
# returned by the "name" method of this instance
|
|
55
68
|
@name ||= T.unsafe(@raw_type).name.freeze
|
|
56
69
|
else
|
|
@@ -60,60 +73,82 @@ module T
|
|
|
60
73
|
end
|
|
61
74
|
end
|
|
62
75
|
|
|
63
|
-
prepend
|
|
76
|
+
prepend GenericPatch
|
|
64
77
|
end
|
|
65
78
|
end
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
module Tapioca
|
|
69
|
-
class TypeMember < T::Types::TypeMember
|
|
70
|
-
extend T::Sig
|
|
71
79
|
|
|
72
|
-
|
|
73
|
-
|
|
80
|
+
module Utils
|
|
81
|
+
module CoercePatch
|
|
82
|
+
def coerce(val)
|
|
83
|
+
if val.is_a?(Tapioca::TypeVariableModule)
|
|
84
|
+
val.coerce_to_type_variable
|
|
85
|
+
else
|
|
86
|
+
super
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
74
90
|
|
|
75
|
-
|
|
76
|
-
|
|
91
|
+
class << self
|
|
92
|
+
prepend(CoercePatch)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
77
96
|
|
|
78
|
-
|
|
79
|
-
|
|
97
|
+
module Tapioca
|
|
98
|
+
class TypeVariable < ::T::Types::TypeVariable
|
|
99
|
+
def initialize(name, variance)
|
|
100
|
+
@name = name
|
|
80
101
|
super(variance)
|
|
81
|
-
@fixed = fixed
|
|
82
|
-
@lower = lower
|
|
83
|
-
@upper = upper
|
|
84
102
|
end
|
|
85
103
|
|
|
86
|
-
|
|
87
|
-
def serialize
|
|
88
|
-
parts = []
|
|
89
|
-
parts << ":#{@variance}" unless @variance == :invariant
|
|
90
|
-
parts << "fixed: #{@fixed}" if @fixed
|
|
91
|
-
parts << "lower: #{@lower}" unless @lower == T.untyped
|
|
92
|
-
parts << "upper: #{@upper}" unless @upper == BasicObject
|
|
93
|
-
|
|
94
|
-
parameters = parts.join(", ")
|
|
95
|
-
|
|
96
|
-
serialized = +"type_member"
|
|
97
|
-
serialized << "(#{parameters})" unless parameters.empty?
|
|
98
|
-
serialized
|
|
99
|
-
end
|
|
104
|
+
attr_reader :name
|
|
100
105
|
end
|
|
101
106
|
|
|
102
|
-
|
|
107
|
+
# This is subclassing from `Module` so that instances of this type will be modules.
|
|
108
|
+
# The reason why we want that is because that means those instances will automatically
|
|
109
|
+
# get bound to the constant names they are assigned to by Ruby. As a result, we don't
|
|
110
|
+
# need to do any matching of constants to type variables to bind their names, Ruby will
|
|
111
|
+
# do that automatically for us and we get the `name` method for free from `Module`.
|
|
112
|
+
class TypeVariableModule < Module
|
|
103
113
|
extend T::Sig
|
|
104
114
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
115
|
+
class Type < T::Enum
|
|
116
|
+
enums do
|
|
117
|
+
Member = new("type_member")
|
|
118
|
+
Template = new("type_template")
|
|
119
|
+
end
|
|
120
|
+
end
|
|
110
121
|
|
|
111
|
-
sig
|
|
112
|
-
|
|
113
|
-
|
|
122
|
+
sig do
|
|
123
|
+
params(context: Module, type: Type, variance: Symbol, fixed: T.untyped, lower: T.untyped, upper: T.untyped).void
|
|
124
|
+
end
|
|
125
|
+
def initialize(context, type, variance, fixed, lower, upper) # rubocop:disable Metrics/ParameterLists
|
|
126
|
+
@context = context
|
|
127
|
+
@type = type
|
|
128
|
+
@variance = variance
|
|
114
129
|
@fixed = fixed
|
|
115
130
|
@lower = lower
|
|
116
131
|
@upper = upper
|
|
132
|
+
super()
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
sig { returns(T.nilable(String)) }
|
|
136
|
+
def name
|
|
137
|
+
constant_name = super
|
|
138
|
+
|
|
139
|
+
# This is a hack to work around modules under anonymous modules not having
|
|
140
|
+
# names in 2.6 and 2.7: https://bugs.ruby-lang.org/issues/14895
|
|
141
|
+
#
|
|
142
|
+
# This happens when a type variable is declared under `class << self`, for
|
|
143
|
+
# example.
|
|
144
|
+
#
|
|
145
|
+
# The workaround is to give the parent context a name, at which point, our
|
|
146
|
+
# module gets bound to a name under that name, as well.
|
|
147
|
+
unless constant_name
|
|
148
|
+
constant_name = with_bound_name_pre_3_0 { super }
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
constant_name&.split("::")&.last
|
|
117
152
|
end
|
|
118
153
|
|
|
119
154
|
sig { returns(String) }
|
|
@@ -126,9 +161,30 @@ module Tapioca
|
|
|
126
161
|
|
|
127
162
|
parameters = parts.join(", ")
|
|
128
163
|
|
|
129
|
-
serialized =
|
|
164
|
+
serialized = @type.serialize.dup
|
|
130
165
|
serialized << "(#{parameters})" unless parameters.empty?
|
|
131
166
|
serialized
|
|
132
167
|
end
|
|
168
|
+
|
|
169
|
+
sig { returns(Tapioca::TypeVariable) }
|
|
170
|
+
def coerce_to_type_variable
|
|
171
|
+
TypeVariable.new(name, @variance)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
private
|
|
175
|
+
|
|
176
|
+
sig do
|
|
177
|
+
type_parameters(:Result)
|
|
178
|
+
.params(block: T.proc.returns(T.type_parameter(:Result)))
|
|
179
|
+
.returns(T.type_parameter(:Result))
|
|
180
|
+
end
|
|
181
|
+
def with_bound_name_pre_3_0(&block)
|
|
182
|
+
require "securerandom"
|
|
183
|
+
temp_name = "TYPE_VARIABLE_TRACKING_#{SecureRandom.hex}"
|
|
184
|
+
self.class.const_set(temp_name, @context)
|
|
185
|
+
block.call
|
|
186
|
+
ensure
|
|
187
|
+
self.class.send(:remove_const, temp_name) if temp_name
|
|
188
|
+
end
|
|
133
189
|
end
|
|
134
190
|
end
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
# typed: true
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
+
# We need sorbet to compile the signature for `qualified_name_of` before applying
|
|
5
|
+
# the patch to avoid an infinite loop.
|
|
6
|
+
T::Utils.signature_for_method(::Tapioca::Runtime::Reflection.method(:qualified_name_of))
|
|
7
|
+
|
|
4
8
|
module T
|
|
5
9
|
module Types
|
|
6
10
|
class Simple
|
|
7
11
|
module NamePatch
|
|
8
12
|
def name
|
|
9
|
-
|
|
13
|
+
# Sorbet memoizes this method into the `@name` instance variable but
|
|
14
|
+
# doing so means that types get memoized before this patch is applied
|
|
15
|
+
::Tapioca::Runtime::Reflection.qualified_name_of(@raw_type)
|
|
10
16
|
end
|
|
11
17
|
end
|
|
12
18
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
require "spoom"
|
|
5
5
|
|
|
6
6
|
module Tapioca
|
|
7
|
-
module
|
|
7
|
+
module Static
|
|
8
8
|
class RequiresCompiler
|
|
9
9
|
extend T::Sig
|
|
10
10
|
|
|
@@ -17,10 +17,9 @@ module Tapioca
|
|
|
17
17
|
def compile
|
|
18
18
|
config = Spoom::Sorbet::Config.parse_file(@sorbet_path)
|
|
19
19
|
files = collect_files(config)
|
|
20
|
+
names_in_project = files.to_h { |file| [File.basename(file, ".rb"), true] }
|
|
20
21
|
files.flat_map do |file|
|
|
21
|
-
collect_requires(file).reject
|
|
22
|
-
name_in_project?(files, req)
|
|
23
|
-
end
|
|
22
|
+
collect_requires(file).reject { |req| names_in_project[req] }
|
|
24
23
|
end.sort.uniq.map do |name|
|
|
25
24
|
"require \"#{name}\"\n"
|
|
26
25
|
end.join
|
|
@@ -45,9 +44,10 @@ module Tapioca
|
|
|
45
44
|
|
|
46
45
|
sig { params(file_path: String).returns(T::Enumerable[String]) }
|
|
47
46
|
def collect_requires(file_path)
|
|
48
|
-
File.
|
|
47
|
+
File.binread(file_path).lines.map do |line|
|
|
49
48
|
/^\s*require\s*(\(\s*)?['"](?<name>[^'"]+)['"](\s*\))?/.match(line) { |m| m["name"] }
|
|
50
49
|
end.compact
|
|
50
|
+
.reject { |require| require.include?('#{') } # ignore interpolation
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
sig { params(config: Spoom::Sorbet::Config, file_path: Pathname).returns(T::Boolean) }
|
|
@@ -83,13 +83,6 @@ module Tapioca
|
|
|
83
83
|
def path_parts(path)
|
|
84
84
|
T.unsafe(path).descend.map { |part| part.basename.to_s }
|
|
85
85
|
end
|
|
86
|
-
|
|
87
|
-
sig { params(files: T::Enumerable[String], name: String).returns(T::Boolean) }
|
|
88
|
-
def name_in_project?(files, name)
|
|
89
|
-
files.any? do |file|
|
|
90
|
-
File.basename(file, ".rb") == name
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
86
|
end
|
|
94
87
|
end
|
|
95
88
|
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "json"
|
|
5
|
+
require "tempfile"
|
|
6
|
+
|
|
7
|
+
module Tapioca
|
|
8
|
+
module Static
|
|
9
|
+
module SymbolLoader
|
|
10
|
+
class << self
|
|
11
|
+
extend T::Sig
|
|
12
|
+
include SorbetHelper
|
|
13
|
+
include Runtime::Reflection
|
|
14
|
+
|
|
15
|
+
sig { returns(T::Set[String]) }
|
|
16
|
+
def payload_symbols
|
|
17
|
+
unless @payload_symbols
|
|
18
|
+
output = symbol_table_json_from("-e ''", table_type: "symbol-table-full-json")
|
|
19
|
+
@payload_symbols = T.let(SymbolTableParser.parse_json(output), T.nilable(T::Set[String]))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
T.must(@payload_symbols)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
sig { returns(T::Set[String]) }
|
|
26
|
+
def engine_symbols
|
|
27
|
+
unless @engine_symbols
|
|
28
|
+
@engine_symbols = T.let(load_engine_symbols, T.nilable(T::Set[String]))
|
|
29
|
+
end
|
|
30
|
+
T.must(@engine_symbols)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
sig { params(gem: Gemfile::GemSpec).returns(T::Set[String]) }
|
|
34
|
+
def gem_symbols(gem)
|
|
35
|
+
symbols_from_paths(gem.files)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
sig { params(input: String, table_type: String).returns(String) }
|
|
41
|
+
def symbol_table_json_from(input, table_type: "symbol-table-json")
|
|
42
|
+
sorbet("--no-config", "--quiet", "--print=#{table_type}", input).out
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
sig { returns(T::Set[String]) }
|
|
46
|
+
def load_engine_symbols
|
|
47
|
+
return Set.new unless Object.const_defined?("Rails::Engine")
|
|
48
|
+
|
|
49
|
+
engine = descendants_of(Object.const_get("Rails::Engine"))
|
|
50
|
+
.reject(&:abstract_railtie?)
|
|
51
|
+
.find do |klass|
|
|
52
|
+
name = name_of(klass)
|
|
53
|
+
!name.nil? && payload_symbols.include?(name)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
return Set.new unless engine
|
|
57
|
+
|
|
58
|
+
paths = engine.config.eager_load_paths.flat_map do |load_path|
|
|
59
|
+
Pathname.glob("#{load_path}/**/*.rb")
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
symbols_from_paths(paths)
|
|
63
|
+
rescue
|
|
64
|
+
Set.new
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
sig { params(paths: T::Array[Pathname]).returns(T::Set[String]) }
|
|
68
|
+
def symbols_from_paths(paths)
|
|
69
|
+
output = T.cast(Tempfile.create("sorbet") do |file|
|
|
70
|
+
file.write(Array(paths).join("\n"))
|
|
71
|
+
file.flush
|
|
72
|
+
|
|
73
|
+
symbol_table_json_from("@#{file.path.shellescape}")
|
|
74
|
+
end, T.nilable(String))
|
|
75
|
+
|
|
76
|
+
return Set.new if output.nil? || output.empty?
|
|
77
|
+
|
|
78
|
+
SymbolTableParser.parse_json(output)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "json"
|
|
5
|
+
require "tempfile"
|
|
6
|
+
|
|
7
|
+
module Tapioca
|
|
8
|
+
module Static
|
|
9
|
+
class SymbolTableParser
|
|
10
|
+
extend T::Sig
|
|
11
|
+
|
|
12
|
+
sig { params(json_string: String).returns(T::Set[String]) }
|
|
13
|
+
def self.parse_json(json_string)
|
|
14
|
+
obj = JSON.parse(json_string)
|
|
15
|
+
|
|
16
|
+
parser = SymbolTableParser.new
|
|
17
|
+
parser.parse_object(obj)
|
|
18
|
+
parser.symbols
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
sig { returns(T::Set[String]) }
|
|
22
|
+
attr_reader :symbols
|
|
23
|
+
|
|
24
|
+
sig { void }
|
|
25
|
+
def initialize
|
|
26
|
+
@symbols = T.let(Set.new, T::Set[String])
|
|
27
|
+
@parents = T.let([], T::Array[String])
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
sig { params(object: T::Hash[String, T.untyped]).void }
|
|
31
|
+
def parse_object(object)
|
|
32
|
+
children = object.fetch("children", [])
|
|
33
|
+
|
|
34
|
+
children.each do |child|
|
|
35
|
+
kind = child.fetch("kind")
|
|
36
|
+
name = child.fetch("name")
|
|
37
|
+
name = name.fetch("name") if name.is_a?(Hash)
|
|
38
|
+
|
|
39
|
+
next if kind.nil? || name.nil?
|
|
40
|
+
|
|
41
|
+
# TODO: CLASS is removed since v0.4.4730 of Sorbet
|
|
42
|
+
# but keeping here for backward compatibility. Remove
|
|
43
|
+
# once the minimum version is moved past that.
|
|
44
|
+
next unless ["CLASS", "CLASS_OR_MODULE", "STATIC_FIELD"].include?(kind)
|
|
45
|
+
next if name =~ /[<>()$]/
|
|
46
|
+
next if name =~ /^[0-9]+$/
|
|
47
|
+
next if name == "T::Helpers"
|
|
48
|
+
|
|
49
|
+
@symbols.add(fully_qualified_name(name))
|
|
50
|
+
|
|
51
|
+
@parents << name
|
|
52
|
+
parse_object(child)
|
|
53
|
+
@parents.pop
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
sig { params(name: String).returns(String) }
|
|
58
|
+
def fully_qualified_name(name)
|
|
59
|
+
[*@parents, name].join("::")
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
data/lib/tapioca/version.rb
CHANGED
data/lib/tapioca.rb
CHANGED
|
@@ -14,7 +14,7 @@ module Tapioca
|
|
|
14
14
|
def self.silence_warnings(&blk)
|
|
15
15
|
original_verbosity = $VERBOSE
|
|
16
16
|
$VERBOSE = nil
|
|
17
|
-
Gem::DefaultUserInteraction.use_ui(Gem::SilentUI.new) do
|
|
17
|
+
::Gem::DefaultUserInteraction.use_ui(::Gem::SilentUI.new) do
|
|
18
18
|
blk.call
|
|
19
19
|
end
|
|
20
20
|
ensure
|
|
@@ -28,7 +28,7 @@ module Tapioca
|
|
|
28
28
|
TAPIOCA_DIR = T.let("#{SORBET_DIR}/tapioca", String)
|
|
29
29
|
TAPIOCA_CONFIG_FILE = T.let("#{TAPIOCA_DIR}/config.yml", String)
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
BINARY_FILE = T.let("bin/tapioca", String)
|
|
32
32
|
DEFAULT_POSTREQUIRE_FILE = T.let("#{TAPIOCA_DIR}/require.rb", String)
|
|
33
33
|
DEFAULT_RBI_DIR = T.let("#{SORBET_DIR}/rbi", String)
|
|
34
34
|
DEFAULT_DSL_DIR = T.let("#{DEFAULT_RBI_DIR}/dsl", String)
|
|
@@ -43,9 +43,4 @@ module Tapioca
|
|
|
43
43
|
}.freeze, T::Hash[String, String])
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
-
require "tapioca/reflection"
|
|
47
|
-
require "tapioca/trackers"
|
|
48
|
-
require "tapioca/compilers/dsl/base"
|
|
49
|
-
require "tapioca/compilers/dynamic_mixin_compiler"
|
|
50
|
-
require "tapioca/helpers/active_record_column_type_helper"
|
|
51
46
|
require "tapioca/version"
|