tapioca 0.6.4 → 0.7.2
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 +8 -2
- data/README.md +27 -15
- data/Rakefile +10 -14
- data/lib/tapioca/cli.rb +65 -80
- data/lib/tapioca/{generators/base.rb → commands/command.rb} +16 -9
- 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 +86 -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 +13 -11
- 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 +28 -34
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_columns.rb +18 -16
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_enum.rb +14 -12
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_fixtures.rb +12 -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 +11 -16
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_resource.rb +10 -8
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_storage.rb +14 -10
- 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 +11 -9
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/frozen_record.rb +13 -11
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/identity_cache.rb +23 -22
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/mixed_in_class_attributes.rb +12 -10
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/protobuf.rb +22 -10
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/rails_generators.rb +12 -13
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/sidekiq_worker.rb +14 -13
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/smart_properties.rb +11 -9
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/state_machines.rb +12 -10
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/url_helpers.rb +20 -15
- 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 +6 -3
- data/lib/tapioca/dsl/pipeline.rb +169 -0
- 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/helpers/cli_helper.rb +7 -0
- data/lib/tapioca/helpers/config_helper.rb +5 -8
- data/lib/tapioca/helpers/shims_helper.rb +87 -0
- data/lib/tapioca/helpers/signatures_helper.rb +17 -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/helpers/type_variable_helper.rb +43 -0
- data/lib/tapioca/internal.rb +18 -10
- data/lib/tapioca/rbi_ext/model.rb +14 -50
- 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 +168 -0
- data/lib/tapioca/runtime/loader.rb +123 -0
- data/lib/tapioca/runtime/reflection.rb +157 -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 +69 -34
- data/lib/tapioca/sorbet_ext/name_patch.rb +7 -1
- data/lib/tapioca/{compilers → static}/requires_compiler.rb +2 -2
- 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 +83 -62
- data/lib/tapioca/compilers/dsl/active_record_relations.rb +0 -720
- data/lib/tapioca/compilers/dsl/base.rb +0 -195
- data/lib/tapioca/compilers/dsl/helper/active_record_constants.rb +0 -27
- data/lib/tapioca/compilers/dsl_compiler.rb +0 -134
- data/lib/tapioca/compilers/dynamic_mixin_compiler.rb +0 -223
- 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 -164
- data/lib/tapioca/helpers/active_record_column_type_helper.rb +0 -108
- 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
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
# typed: strict
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
require "tapioca/rbi_ext/model"
|
|
5
|
-
require "tapioca/compilers/dsl/param_helper"
|
|
6
|
-
require "tapioca/compilers/dsl_compiler"
|
|
7
|
-
|
|
8
|
-
module Tapioca
|
|
9
|
-
module Compilers
|
|
10
|
-
module Dsl
|
|
11
|
-
DSL_COMPILERS_DIR = T.let(File.expand_path("..", __FILE__).to_s, String)
|
|
12
|
-
|
|
13
|
-
class Base
|
|
14
|
-
extend T::Sig
|
|
15
|
-
extend T::Helpers
|
|
16
|
-
|
|
17
|
-
include Reflection
|
|
18
|
-
|
|
19
|
-
abstract!
|
|
20
|
-
|
|
21
|
-
sig { returns(T::Set[Module]) }
|
|
22
|
-
attr_reader :processable_constants
|
|
23
|
-
|
|
24
|
-
sig { returns(T::Array[String]) }
|
|
25
|
-
attr_reader :errors
|
|
26
|
-
|
|
27
|
-
sig { params(name: String).returns(T.nilable(T.class_of(Tapioca::Compilers::Dsl::Base))) }
|
|
28
|
-
def self.resolve(name)
|
|
29
|
-
# Try to find built-in tapioca generator first, then globally defined generator.
|
|
30
|
-
potentials = ["Tapioca::Compilers::Dsl::#{name}", name].map do |potential_name|
|
|
31
|
-
Object.const_get(potential_name)
|
|
32
|
-
rescue NameError
|
|
33
|
-
# Skip if we can't find generator by the potential name
|
|
34
|
-
nil
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
potentials.compact.first
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
sig { params(compiler: Tapioca::Compilers::DslCompiler).void }
|
|
41
|
-
def initialize(compiler)
|
|
42
|
-
@compiler = compiler
|
|
43
|
-
@processable_constants = T.let(Set.new(gather_constants), T::Set[Module])
|
|
44
|
-
@processable_constants.compare_by_identity
|
|
45
|
-
@errors = T.let([], T::Array[String])
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
sig { params(constant: Module).returns(T::Boolean) }
|
|
49
|
-
def handles?(constant)
|
|
50
|
-
processable_constants.include?(constant)
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
sig { params(generator_name: String).returns(T::Boolean) }
|
|
54
|
-
def generator_enabled?(generator_name)
|
|
55
|
-
@compiler.generator_enabled?(generator_name)
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
sig do
|
|
59
|
-
abstract
|
|
60
|
-
.type_parameters(:T)
|
|
61
|
-
.params(
|
|
62
|
-
tree: RBI::Tree,
|
|
63
|
-
constant: T.type_parameter(:T)
|
|
64
|
-
)
|
|
65
|
-
.void
|
|
66
|
-
end
|
|
67
|
-
def decorate(tree, constant); end
|
|
68
|
-
|
|
69
|
-
sig { abstract.returns(T::Enumerable[Module]) }
|
|
70
|
-
def gather_constants; end
|
|
71
|
-
|
|
72
|
-
# NOTE: This should eventually accept an `Error` object or `Exception` rather than simply a `String`.
|
|
73
|
-
sig { params(error: String).void }
|
|
74
|
-
def add_error(error)
|
|
75
|
-
@errors << error
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
private
|
|
79
|
-
|
|
80
|
-
sig { returns(T::Enumerable[Class]) }
|
|
81
|
-
def all_classes
|
|
82
|
-
@all_classes = T.let(@all_classes, T.nilable(T::Enumerable[Class]))
|
|
83
|
-
@all_classes ||= T.cast(ObjectSpace.each_object(Class), T::Enumerable[Class]).each
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
sig { returns(T::Enumerable[Module]) }
|
|
87
|
-
def all_modules
|
|
88
|
-
@all_modules = T.let(@all_modules, T.nilable(T::Enumerable[Module]))
|
|
89
|
-
@all_modules ||= T.cast(ObjectSpace.each_object(Module), T::Enumerable[Module]).each
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
# Get the types of each parameter from a method signature
|
|
93
|
-
sig do
|
|
94
|
-
params(
|
|
95
|
-
method_def: T.any(Method, UnboundMethod),
|
|
96
|
-
signature: T.untyped # as `T::Private::Methods::Signature` is private
|
|
97
|
-
).returns(T::Array[String])
|
|
98
|
-
end
|
|
99
|
-
def parameters_types_from_signature(method_def, signature)
|
|
100
|
-
params = T.let([], T::Array[String])
|
|
101
|
-
|
|
102
|
-
return method_def.parameters.map { "T.untyped" } unless signature
|
|
103
|
-
|
|
104
|
-
# parameters types
|
|
105
|
-
signature.arg_types.each { |arg_type| params << arg_type[1].to_s }
|
|
106
|
-
|
|
107
|
-
# keyword parameters types
|
|
108
|
-
signature.kwarg_types.each { |_, kwarg_type| params << kwarg_type.to_s }
|
|
109
|
-
|
|
110
|
-
# rest parameter type
|
|
111
|
-
params << signature.rest_type.to_s if signature.has_rest
|
|
112
|
-
|
|
113
|
-
# special case `.void` in a proc
|
|
114
|
-
unless signature.block_name.nil?
|
|
115
|
-
params << signature.block_type.to_s.gsub("returns(<VOID>)", "void")
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
params
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
sig { params(scope: RBI::Scope, method_def: T.any(Method, UnboundMethod), class_method: T::Boolean).void }
|
|
122
|
-
def create_method_from_def(scope, method_def, class_method: false)
|
|
123
|
-
scope.create_method(
|
|
124
|
-
method_def.name.to_s,
|
|
125
|
-
parameters: compile_method_parameters_to_rbi(method_def),
|
|
126
|
-
return_type: compile_method_return_type_to_rbi(method_def),
|
|
127
|
-
class_method: class_method
|
|
128
|
-
)
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
include ParamHelper
|
|
132
|
-
|
|
133
|
-
sig { params(method_def: T.any(Method, UnboundMethod)).returns(T::Array[RBI::TypedParam]) }
|
|
134
|
-
def compile_method_parameters_to_rbi(method_def)
|
|
135
|
-
signature = T::Private::Methods.signature_for_method(method_def)
|
|
136
|
-
method_def = signature.nil? ? method_def : signature.method
|
|
137
|
-
method_types = parameters_types_from_signature(method_def, signature)
|
|
138
|
-
|
|
139
|
-
parameters = T.let(method_def.parameters, T::Array[[Symbol, T.nilable(Symbol)]])
|
|
140
|
-
|
|
141
|
-
parameters.each_with_index.map do |(type, name), index|
|
|
142
|
-
fallback_arg_name = "_arg#{index}"
|
|
143
|
-
|
|
144
|
-
name = name ? name.to_s : fallback_arg_name
|
|
145
|
-
name = fallback_arg_name unless valid_parameter_name?(name)
|
|
146
|
-
method_type = T.must(method_types[index])
|
|
147
|
-
|
|
148
|
-
case type
|
|
149
|
-
when :req
|
|
150
|
-
create_param(name, type: method_type)
|
|
151
|
-
when :opt
|
|
152
|
-
create_opt_param(name, type: method_type, default: "T.unsafe(nil)")
|
|
153
|
-
when :rest
|
|
154
|
-
create_rest_param(name, type: method_type)
|
|
155
|
-
when :keyreq
|
|
156
|
-
create_kw_param(name, type: method_type)
|
|
157
|
-
when :key
|
|
158
|
-
create_kw_opt_param(name, type: method_type, default: "T.unsafe(nil)")
|
|
159
|
-
when :keyrest
|
|
160
|
-
create_kw_rest_param(name, type: method_type)
|
|
161
|
-
when :block
|
|
162
|
-
create_block_param(name, type: method_type)
|
|
163
|
-
else
|
|
164
|
-
raise "Unknown type `#{type}`."
|
|
165
|
-
end
|
|
166
|
-
end
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
sig { params(method_def: T.any(Method, UnboundMethod)).returns(String) }
|
|
170
|
-
def compile_method_return_type_to_rbi(method_def)
|
|
171
|
-
signature = T::Private::Methods.signature_for_method(method_def)
|
|
172
|
-
return_type = signature.nil? ? "T.untyped" : name_of_type(signature.return_type)
|
|
173
|
-
return_type = "void" if return_type == "<VOID>"
|
|
174
|
-
# Map <NOT-TYPED> to `T.untyped`
|
|
175
|
-
return_type = "T.untyped" if return_type == "<NOT-TYPED>"
|
|
176
|
-
return_type
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
sig { params(type: String).returns(String) }
|
|
180
|
-
def as_nilable_type(type)
|
|
181
|
-
if type.start_with?("T.nilable(", "::T.nilable(") || type == "T.untyped" || type == "::T.untyped"
|
|
182
|
-
type
|
|
183
|
-
else
|
|
184
|
-
"T.nilable(#{type})"
|
|
185
|
-
end
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
sig { params(name: String).returns(T::Boolean) }
|
|
189
|
-
def valid_parameter_name?(name)
|
|
190
|
-
name.match?(/^[[[:alnum:]]_]+$/)
|
|
191
|
-
end
|
|
192
|
-
end
|
|
193
|
-
end
|
|
194
|
-
end
|
|
195
|
-
end
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# typed: strict
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
module Tapioca
|
|
5
|
-
module Compilers
|
|
6
|
-
module Dsl
|
|
7
|
-
module Helper
|
|
8
|
-
module ActiveRecordConstants
|
|
9
|
-
extend T::Sig
|
|
10
|
-
|
|
11
|
-
AttributeMethodsModuleName = T.let("GeneratedAttributeMethods", String)
|
|
12
|
-
AssociationMethodsModuleName = T.let("GeneratedAssociationMethods", String)
|
|
13
|
-
|
|
14
|
-
RelationMethodsModuleName = T.let("GeneratedRelationMethods", String)
|
|
15
|
-
AssociationRelationMethodsModuleName = T.let("GeneratedAssociationRelationMethods", String)
|
|
16
|
-
CommonRelationMethodsModuleName = T.let("CommonRelationMethods", String)
|
|
17
|
-
|
|
18
|
-
RelationClassName = T.let("PrivateRelation", String)
|
|
19
|
-
RelationWhereChainClassName = T.let("PrivateRelationWhereChain", String)
|
|
20
|
-
AssociationRelationClassName = T.let("PrivateAssociationRelation", String)
|
|
21
|
-
AssociationRelationWhereChainClassName = T.let("PrivateAssociationRelationWhereChain", String)
|
|
22
|
-
AssociationsCollectionProxyClassName = T.let("PrivateCollectionProxy", String)
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
# typed: strict
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
require "tapioca/compilers/dsl/base"
|
|
5
|
-
|
|
6
|
-
module Tapioca
|
|
7
|
-
module Compilers
|
|
8
|
-
class DslCompiler
|
|
9
|
-
extend T::Sig
|
|
10
|
-
|
|
11
|
-
sig { returns(T::Enumerable[Dsl::Base]) }
|
|
12
|
-
attr_reader :generators
|
|
13
|
-
|
|
14
|
-
sig { returns(T::Array[Module]) }
|
|
15
|
-
attr_reader :requested_constants
|
|
16
|
-
|
|
17
|
-
sig { returns(T.proc.params(error: String).void) }
|
|
18
|
-
attr_reader :error_handler
|
|
19
|
-
|
|
20
|
-
sig do
|
|
21
|
-
params(
|
|
22
|
-
requested_constants: T::Array[Module],
|
|
23
|
-
requested_generators: T::Array[T.class_of(Dsl::Base)],
|
|
24
|
-
excluded_generators: T::Array[T.class_of(Dsl::Base)],
|
|
25
|
-
error_handler: T.proc.params(error: String).void,
|
|
26
|
-
number_of_workers: T.nilable(Integer),
|
|
27
|
-
).void
|
|
28
|
-
end
|
|
29
|
-
def initialize(
|
|
30
|
-
requested_constants:,
|
|
31
|
-
requested_generators: [],
|
|
32
|
-
excluded_generators: [],
|
|
33
|
-
error_handler: $stderr.method(:puts).to_proc,
|
|
34
|
-
number_of_workers: nil
|
|
35
|
-
)
|
|
36
|
-
@generators = T.let(
|
|
37
|
-
gather_generators(requested_generators, excluded_generators),
|
|
38
|
-
T::Enumerable[Dsl::Base]
|
|
39
|
-
)
|
|
40
|
-
@requested_constants = requested_constants
|
|
41
|
-
@error_handler = error_handler
|
|
42
|
-
@number_of_workers = number_of_workers
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
sig do
|
|
46
|
-
type_parameters(:T).params(
|
|
47
|
-
blk: T.proc.params(constant: Module, rbi: RBI::File).returns(T.type_parameter(:T))
|
|
48
|
-
).returns(T::Array[T.type_parameter(:T)])
|
|
49
|
-
end
|
|
50
|
-
def run(&blk)
|
|
51
|
-
constants_to_process = gather_constants(requested_constants)
|
|
52
|
-
.select { |c| Reflection.name_of(c) && Module === c } # Filter anonymous or value constants
|
|
53
|
-
.sort_by! { |c| T.must(Reflection.name_of(c)) }
|
|
54
|
-
|
|
55
|
-
if constants_to_process.empty?
|
|
56
|
-
report_error(<<~ERROR)
|
|
57
|
-
No classes/modules can be matched for RBI generation.
|
|
58
|
-
Please check that the requested classes/modules include processable DSL methods.
|
|
59
|
-
ERROR
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
result = Executor.new(
|
|
63
|
-
constants_to_process,
|
|
64
|
-
number_of_workers: @number_of_workers
|
|
65
|
-
).run_in_parallel do |constant|
|
|
66
|
-
rbi = rbi_for_constant(constant)
|
|
67
|
-
next if rbi.nil?
|
|
68
|
-
|
|
69
|
-
blk.call(constant, rbi)
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
generators.flat_map(&:errors).each do |msg|
|
|
73
|
-
report_error(msg)
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
result.compact
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
sig { params(generator_name: String).returns(T::Boolean) }
|
|
80
|
-
def generator_enabled?(generator_name)
|
|
81
|
-
generator = Dsl::Base.resolve(generator_name)
|
|
82
|
-
|
|
83
|
-
return false unless generator
|
|
84
|
-
|
|
85
|
-
@generators.any?(generator)
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
private
|
|
89
|
-
|
|
90
|
-
sig do
|
|
91
|
-
params(
|
|
92
|
-
requested_generators: T::Array[T.class_of(Dsl::Base)],
|
|
93
|
-
excluded_generators: T::Array[T.class_of(Dsl::Base)]
|
|
94
|
-
).returns(T::Enumerable[Dsl::Base])
|
|
95
|
-
end
|
|
96
|
-
def gather_generators(requested_generators, excluded_generators)
|
|
97
|
-
generator_klasses = ::Tapioca::Reflection.descendants_of(Dsl::Base).select do |klass|
|
|
98
|
-
(requested_generators.empty? || requested_generators.include?(klass)) &&
|
|
99
|
-
!excluded_generators.include?(klass)
|
|
100
|
-
end.sort_by { |klass| T.must(klass.name) }
|
|
101
|
-
|
|
102
|
-
generator_klasses.map { |generator_klass| generator_klass.new(self) }
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
sig { params(requested_constants: T::Array[Module]).returns(T::Set[Module]) }
|
|
106
|
-
def gather_constants(requested_constants)
|
|
107
|
-
constants = generators.map(&:processable_constants).reduce(Set.new, :union)
|
|
108
|
-
constants &= requested_constants unless requested_constants.empty?
|
|
109
|
-
constants
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
sig { params(constant: Module).returns(T.nilable(RBI::File)) }
|
|
113
|
-
def rbi_for_constant(constant)
|
|
114
|
-
file = RBI::File.new(strictness: "true")
|
|
115
|
-
|
|
116
|
-
generators.each do |generator|
|
|
117
|
-
next unless generator.handles?(constant)
|
|
118
|
-
generator.decorate(file.root, constant)
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
return if file.root.empty?
|
|
122
|
-
|
|
123
|
-
file
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
sig { params(error: String).returns(T.noreturn) }
|
|
127
|
-
def report_error(error)
|
|
128
|
-
handler = error_handler
|
|
129
|
-
handler.call(error)
|
|
130
|
-
exit(1)
|
|
131
|
-
end
|
|
132
|
-
end
|
|
133
|
-
end
|
|
134
|
-
end
|
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
# typed: strict
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
class DynamicMixinCompiler
|
|
5
|
-
extend T::Sig
|
|
6
|
-
include Tapioca::Reflection
|
|
7
|
-
|
|
8
|
-
sig { returns(T::Array[Module]) }
|
|
9
|
-
attr_reader :dynamic_extends, :dynamic_includes
|
|
10
|
-
|
|
11
|
-
sig { returns(T::Array[Symbol]) }
|
|
12
|
-
attr_reader :class_attribute_readers, :class_attribute_writers, :class_attribute_predicates
|
|
13
|
-
|
|
14
|
-
sig { returns(T::Array[Symbol]) }
|
|
15
|
-
attr_reader :instance_attribute_readers, :instance_attribute_writers, :instance_attribute_predicates
|
|
16
|
-
|
|
17
|
-
sig { params(constant: Module).void }
|
|
18
|
-
def initialize(constant)
|
|
19
|
-
@constant = constant
|
|
20
|
-
mixins_from_modules = {}.compare_by_identity
|
|
21
|
-
class_attribute_readers = T.let([], T::Array[Symbol])
|
|
22
|
-
class_attribute_writers = T.let([], T::Array[Symbol])
|
|
23
|
-
class_attribute_predicates = T.let([], T::Array[Symbol])
|
|
24
|
-
|
|
25
|
-
instance_attribute_readers = T.let([], T::Array[Symbol])
|
|
26
|
-
instance_attribute_writers = T.let([], T::Array[Symbol])
|
|
27
|
-
instance_attribute_predicates = T.let([], T::Array[Symbol])
|
|
28
|
-
|
|
29
|
-
Class.new do
|
|
30
|
-
# Override the `self.include` method
|
|
31
|
-
define_singleton_method(:include) do |mod|
|
|
32
|
-
# Take a snapshot of the list of singleton class ancestors
|
|
33
|
-
# before the actual include
|
|
34
|
-
before = singleton_class.ancestors
|
|
35
|
-
# Call the actual `include` method with the supplied module
|
|
36
|
-
super(mod).tap do
|
|
37
|
-
# Take a snapshot of the list of singleton class ancestors
|
|
38
|
-
# after the actual include
|
|
39
|
-
after = singleton_class.ancestors
|
|
40
|
-
# The difference is the modules that are added to the list
|
|
41
|
-
# of ancestors of the singleton class. Those are all the
|
|
42
|
-
# modules that were `extend`ed due to the `include` call.
|
|
43
|
-
#
|
|
44
|
-
# We record those modules on our lookup table keyed by
|
|
45
|
-
# the included module with the values being all the modules
|
|
46
|
-
# that that module pulls into the singleton class.
|
|
47
|
-
#
|
|
48
|
-
# We need to reverse the order, since the extend order should
|
|
49
|
-
# be the inverse of the ancestor order. That is, earlier
|
|
50
|
-
# extended modules would be later in the ancestor chain.
|
|
51
|
-
mixins_from_modules[mod] = (after - before).reverse!
|
|
52
|
-
end
|
|
53
|
-
rescue Exception # rubocop:disable Lint/RescueException
|
|
54
|
-
# this is a best effort, bail if we can't perform this
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
define_singleton_method(:class_attribute) do |*attrs, **kwargs|
|
|
58
|
-
class_attribute_readers.concat(attrs)
|
|
59
|
-
class_attribute_writers.concat(attrs)
|
|
60
|
-
|
|
61
|
-
instance_predicate = kwargs.fetch(:instance_predicate, true)
|
|
62
|
-
instance_accessor = kwargs.fetch(:instance_accessor, true)
|
|
63
|
-
instance_reader = kwargs.fetch(:instance_reader, instance_accessor)
|
|
64
|
-
instance_writer = kwargs.fetch(:instance_writer, instance_accessor)
|
|
65
|
-
|
|
66
|
-
if instance_reader
|
|
67
|
-
instance_attribute_readers.concat(attrs)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
if instance_writer
|
|
71
|
-
instance_attribute_writers.concat(attrs)
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
if instance_predicate
|
|
75
|
-
class_attribute_predicates.concat(attrs)
|
|
76
|
-
|
|
77
|
-
if instance_reader
|
|
78
|
-
instance_attribute_predicates.concat(attrs)
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
super(*attrs, **kwargs) if defined?(super)
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
# rubocop:disable Style/MissingRespondToMissing
|
|
86
|
-
T::Sig::WithoutRuntime.sig { params(symbol: Symbol, args: T.untyped).returns(T.untyped) }
|
|
87
|
-
def method_missing(symbol, *args)
|
|
88
|
-
# We need this here so that we can handle any random instance
|
|
89
|
-
# method calls on the fake including class that may be done by
|
|
90
|
-
# the included module during the `self.included` hook.
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
class << self
|
|
94
|
-
extend T::Sig
|
|
95
|
-
|
|
96
|
-
T::Sig::WithoutRuntime.sig { params(symbol: Symbol, args: T.untyped).returns(T.untyped) }
|
|
97
|
-
def method_missing(symbol, *args)
|
|
98
|
-
# Similarly, we need this here so that we can handle any
|
|
99
|
-
# random class method calls on the fake including class
|
|
100
|
-
# that may be done by the included module during the
|
|
101
|
-
# `self.included` hook.
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
# rubocop:enable Style/MissingRespondToMissing
|
|
105
|
-
end.include(constant)
|
|
106
|
-
|
|
107
|
-
# The value that corresponds to the original included constant
|
|
108
|
-
# is the list of all dynamically extended modules because of that
|
|
109
|
-
# constant. We grab that value by deleting the key for the original
|
|
110
|
-
# constant.
|
|
111
|
-
@dynamic_extends = T.let(mixins_from_modules.delete(constant) || [], T::Array[Module])
|
|
112
|
-
|
|
113
|
-
# Since we deleted the original constant from the list of keys, all
|
|
114
|
-
# the keys that remain are the ones that are dynamically included modules
|
|
115
|
-
# during the include of the original constant.
|
|
116
|
-
@dynamic_includes = T.let(mixins_from_modules.keys, T::Array[Module])
|
|
117
|
-
|
|
118
|
-
@class_attribute_readers = T.let(class_attribute_readers, T::Array[Symbol])
|
|
119
|
-
@class_attribute_writers = T.let(class_attribute_writers, T::Array[Symbol])
|
|
120
|
-
@class_attribute_predicates = T.let(class_attribute_predicates, T::Array[Symbol])
|
|
121
|
-
|
|
122
|
-
@instance_attribute_readers = T.let(instance_attribute_readers, T::Array[Symbol])
|
|
123
|
-
@instance_attribute_writers = T.let(instance_attribute_writers, T::Array[Symbol])
|
|
124
|
-
@instance_attribute_predicates = T.let(instance_attribute_predicates, T::Array[Symbol])
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
sig { returns(T::Boolean) }
|
|
128
|
-
def empty_attributes?
|
|
129
|
-
@class_attribute_readers.empty? && @class_attribute_writers.empty?
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
sig { params(tree: RBI::Tree).void }
|
|
133
|
-
def compile_class_attributes(tree)
|
|
134
|
-
return if empty_attributes?
|
|
135
|
-
|
|
136
|
-
# Create a synthetic module to hold the generated class methods
|
|
137
|
-
tree << RBI::Module.new("GeneratedClassMethods") do |mod|
|
|
138
|
-
class_attribute_readers.each do |attribute|
|
|
139
|
-
mod << RBI::Method.new(attribute.to_s)
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
class_attribute_writers.each do |attribute|
|
|
143
|
-
mod << RBI::Method.new("#{attribute}=") do |method|
|
|
144
|
-
method << RBI::Param.new("value")
|
|
145
|
-
end
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
class_attribute_predicates.each do |attribute|
|
|
149
|
-
mod << RBI::Method.new("#{attribute}?")
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
# Create a synthetic module to hold the generated instance methods
|
|
154
|
-
tree << RBI::Module.new("GeneratedInstanceMethods") do |mod|
|
|
155
|
-
instance_attribute_readers.each do |attribute|
|
|
156
|
-
mod << RBI::Method.new(attribute.to_s)
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
instance_attribute_writers.each do |attribute|
|
|
160
|
-
mod << RBI::Method.new("#{attribute}=") do |method|
|
|
161
|
-
method << RBI::Param.new("value")
|
|
162
|
-
end
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
instance_attribute_predicates.each do |attribute|
|
|
166
|
-
mod << RBI::Method.new("#{attribute}?")
|
|
167
|
-
end
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
# Add a mixes_in_class_methods and include for the generated modules
|
|
171
|
-
tree << RBI::MixesInClassMethods.new("GeneratedClassMethods")
|
|
172
|
-
tree << RBI::Include.new("GeneratedInstanceMethods")
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
sig { params(tree: RBI::Tree).returns([T::Array[Module], T::Array[Module]]) }
|
|
176
|
-
def compile_mixes_in_class_methods(tree)
|
|
177
|
-
includes = dynamic_includes.map do |mod|
|
|
178
|
-
qname = qualified_name_of(mod)
|
|
179
|
-
|
|
180
|
-
next if qname.nil? || qname.empty?
|
|
181
|
-
next if filtered_mixin?(qname)
|
|
182
|
-
|
|
183
|
-
tree << RBI::Include.new(qname)
|
|
184
|
-
|
|
185
|
-
mod
|
|
186
|
-
end.compact
|
|
187
|
-
|
|
188
|
-
# If we can generate multiple mixes_in_class_methods, then we want to use all dynamic extends that are not the
|
|
189
|
-
# constant itself
|
|
190
|
-
mixed_in_class_methods = dynamic_extends.select do |mod|
|
|
191
|
-
mod != @constant && !module_included_by_another_dynamic_extend?(mod, dynamic_extends)
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
return [[], []] if mixed_in_class_methods.empty?
|
|
195
|
-
|
|
196
|
-
mixed_in_class_methods.each do |mod|
|
|
197
|
-
qualified_name = qualified_name_of(mod)
|
|
198
|
-
|
|
199
|
-
next if qualified_name.nil? || qualified_name.empty?
|
|
200
|
-
next if filtered_mixin?(qualified_name)
|
|
201
|
-
|
|
202
|
-
tree << RBI::MixesInClassMethods.new(qualified_name)
|
|
203
|
-
end
|
|
204
|
-
|
|
205
|
-
[mixed_in_class_methods, includes]
|
|
206
|
-
rescue
|
|
207
|
-
[[], []] # silence errors
|
|
208
|
-
end
|
|
209
|
-
|
|
210
|
-
sig { params(mod: Module, dynamic_extends: T::Array[Module]).returns(T::Boolean) }
|
|
211
|
-
def module_included_by_another_dynamic_extend?(mod, dynamic_extends)
|
|
212
|
-
dynamic_extends.any? do |dynamic_extend|
|
|
213
|
-
mod != dynamic_extend && ancestors_of(dynamic_extend).include?(mod)
|
|
214
|
-
end
|
|
215
|
-
end
|
|
216
|
-
|
|
217
|
-
sig { params(qualified_mixin_name: String).returns(T::Boolean) }
|
|
218
|
-
def filtered_mixin?(qualified_mixin_name)
|
|
219
|
-
# filter T:: namespace mixins that aren't T::Props
|
|
220
|
-
# T::Props and subconstants have semantic value
|
|
221
|
-
qualified_mixin_name.start_with?("::T::") && !qualified_mixin_name.start_with?("::T::Props")
|
|
222
|
-
end
|
|
223
|
-
end
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
# typed: strict
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
require "pathname"
|
|
5
|
-
require "shellwords"
|
|
6
|
-
|
|
7
|
-
module Tapioca
|
|
8
|
-
module Compilers
|
|
9
|
-
module Sorbet
|
|
10
|
-
SORBET_GEM_SPEC = T.let(
|
|
11
|
-
Gem::Specification.find_by_name("sorbet-static"),
|
|
12
|
-
Gem::Specification
|
|
13
|
-
)
|
|
14
|
-
SORBET = T.let(
|
|
15
|
-
Pathname.new(SORBET_GEM_SPEC.full_gem_path) / "libexec" / "sorbet",
|
|
16
|
-
Pathname
|
|
17
|
-
)
|
|
18
|
-
EXE_PATH_ENV_VAR = "TAPIOCA_SORBET_EXE"
|
|
19
|
-
|
|
20
|
-
FEATURE_REQUIREMENTS = T.let({
|
|
21
|
-
# First tag that includes https://github.com/sorbet/sorbet/pull/4706
|
|
22
|
-
to_ary_nil_support: Gem::Requirement.new(">= 0.5.9220"),
|
|
23
|
-
}.freeze, T::Hash[Symbol, Gem::Requirement])
|
|
24
|
-
|
|
25
|
-
class << self
|
|
26
|
-
extend(T::Sig)
|
|
27
|
-
|
|
28
|
-
sig { params(args: String).returns(String) }
|
|
29
|
-
def run(*args)
|
|
30
|
-
IO.popen(
|
|
31
|
-
[
|
|
32
|
-
sorbet_path,
|
|
33
|
-
"--quiet",
|
|
34
|
-
*args,
|
|
35
|
-
].join(" "),
|
|
36
|
-
err: "/dev/null"
|
|
37
|
-
).read
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
sig { returns(String) }
|
|
41
|
-
def sorbet_path
|
|
42
|
-
sorbet_path = ENV.fetch(EXE_PATH_ENV_VAR, SORBET)
|
|
43
|
-
sorbet_path = SORBET if sorbet_path.empty?
|
|
44
|
-
sorbet_path.to_s.shellescape
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
sig { params(feature: Symbol, version: T.nilable(Gem::Version)).returns(T::Boolean) }
|
|
48
|
-
def supports?(feature, version: nil)
|
|
49
|
-
version = SORBET_GEM_SPEC.version unless version
|
|
50
|
-
requirement = FEATURE_REQUIREMENTS[feature]
|
|
51
|
-
|
|
52
|
-
raise "Invalid Sorbet feature #{feature}" unless requirement
|
|
53
|
-
|
|
54
|
-
requirement.satisfied_by?(version)
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
end
|