tapioca 0.5.3 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +114 -23
- data/lib/tapioca/cli.rb +188 -65
- data/lib/tapioca/compilers/dsl/active_record_associations.rb +94 -8
- data/lib/tapioca/compilers/dsl/active_record_columns.rb +5 -4
- data/lib/tapioca/compilers/dsl/active_record_enum.rb +1 -1
- data/lib/tapioca/compilers/dsl/active_record_relations.rb +703 -0
- data/lib/tapioca/compilers/dsl/active_record_scope.rb +43 -13
- data/lib/tapioca/compilers/dsl/active_record_typed_store.rb +39 -33
- data/lib/tapioca/compilers/dsl/base.rb +26 -42
- data/lib/tapioca/compilers/dsl/extensions/frozen_record.rb +29 -0
- data/lib/tapioca/compilers/dsl/frozen_record.rb +37 -0
- data/lib/tapioca/compilers/dsl/helper/active_record_constants.rb +27 -0
- data/lib/tapioca/compilers/dsl/identity_cache.rb +0 -1
- data/lib/tapioca/compilers/dsl/param_helper.rb +52 -0
- data/lib/tapioca/compilers/dsl/rails_generators.rb +120 -0
- data/lib/tapioca/compilers/dsl_compiler.rb +34 -6
- data/lib/tapioca/compilers/sorbet.rb +2 -0
- data/lib/tapioca/compilers/symbol_table/symbol_generator.rb +51 -48
- data/lib/tapioca/executor.rb +79 -0
- data/lib/tapioca/gemfile.rb +28 -4
- data/lib/tapioca/generators/base.rb +11 -18
- data/lib/tapioca/generators/dsl.rb +33 -38
- data/lib/tapioca/generators/gem.rb +64 -34
- data/lib/tapioca/generators/init.rb +41 -16
- data/lib/tapioca/generators/todo.rb +6 -6
- data/lib/tapioca/helpers/cli_helper.rb +26 -0
- data/lib/tapioca/helpers/config_helper.rb +84 -0
- data/lib/tapioca/helpers/test/content.rb +51 -0
- data/lib/tapioca/helpers/test/isolation.rb +125 -0
- data/lib/tapioca/helpers/test/template.rb +34 -0
- data/lib/tapioca/internal.rb +3 -2
- data/lib/tapioca/rbi_ext/model.rb +13 -10
- data/lib/tapioca/reflection.rb +13 -0
- data/lib/tapioca/trackers/autoload.rb +70 -0
- data/lib/tapioca/trackers/constant_definition.rb +42 -0
- data/lib/tapioca/trackers/mixin.rb +78 -0
- data/lib/tapioca/trackers.rb +14 -0
- data/lib/tapioca/version.rb +1 -1
- data/lib/tapioca.rb +28 -2
- metadata +37 -13
- data/lib/tapioca/config.rb +0 -45
- data/lib/tapioca/config_builder.rb +0 -73
- data/lib/tapioca/constant_locator.rb +0 -34
@@ -85,26 +85,13 @@ module Tapioca
|
|
85
85
|
|
86
86
|
sig { params(tree: RBI::Tree, symbol: String).void }
|
87
87
|
def generate_from_symbol(tree, symbol)
|
88
|
-
constant =
|
88
|
+
constant = constantize(symbol)
|
89
89
|
|
90
90
|
return unless constant
|
91
91
|
|
92
92
|
compile(tree, symbol, constant)
|
93
93
|
end
|
94
94
|
|
95
|
-
sig do
|
96
|
-
params(
|
97
|
-
symbol: String,
|
98
|
-
inherit: T::Boolean,
|
99
|
-
namespace: Module
|
100
|
-
).returns(BasicObject).checked(:never)
|
101
|
-
end
|
102
|
-
def resolve_constant(symbol, inherit: false, namespace: Object)
|
103
|
-
namespace.const_get(symbol, inherit)
|
104
|
-
rescue NameError, LoadError, RuntimeError, ArgumentError, TypeError
|
105
|
-
nil
|
106
|
-
end
|
107
|
-
|
108
95
|
sig { params(tree: RBI::Tree, name: T.nilable(String), constant: BasicObject).void.checked(:never) }
|
109
96
|
def compile(tree, name, constant)
|
110
97
|
return unless constant
|
@@ -168,7 +155,8 @@ module Tapioca
|
|
168
155
|
comments = documentation_comments(name)
|
169
156
|
|
170
157
|
if klass_name == "T::Private::Types::TypeAlias"
|
171
|
-
|
158
|
+
type_alias = sanitize_signature_types(T.unsafe(value).aliased_type.to_s)
|
159
|
+
constant = RBI::Const.new(name, "T.type_alias { #{type_alias} }", comments: comments)
|
172
160
|
tree << constant
|
173
161
|
return
|
174
162
|
end
|
@@ -229,7 +217,8 @@ module Tapioca
|
|
229
217
|
|
230
218
|
sig { params(tree: RBI::Tree, constant: Module).void }
|
231
219
|
def compile_module_helpers(tree, constant)
|
232
|
-
abstract_type = T::Private::Abstract::Data.get(constant, :abstract_type)
|
220
|
+
abstract_type = T::Private::Abstract::Data.get(constant, :abstract_type) ||
|
221
|
+
T::Private::Abstract::Data.get(singleton_class_of(constant), :abstract_type)
|
233
222
|
|
234
223
|
tree << RBI::Helper.new(abstract_type.to_s) if abstract_type
|
235
224
|
tree << RBI::Helper.new("final") if T::Private::Final.final_module?(constant)
|
@@ -267,7 +256,7 @@ module Tapioca
|
|
267
256
|
def compile_subconstants(tree, name, constant)
|
268
257
|
constants_of(constant).sort.uniq.map do |constant_name|
|
269
258
|
symbol = (name == "Object" ? "" : name) + "::#{constant_name}"
|
270
|
-
subconstant =
|
259
|
+
subconstant = constantize(symbol)
|
271
260
|
|
272
261
|
# Don't compile modules of Object because Object::Foo == Foo
|
273
262
|
# Don't compile modules of BasicObject because BasicObject::BasicObject == BasicObject
|
@@ -298,7 +287,7 @@ module Tapioca
|
|
298
287
|
# variable via the value of the type variable constant.
|
299
288
|
subconstant_to_name_lookup = constants_of(constant)
|
300
289
|
.each_with_object({}.compare_by_identity) do |constant_name, table|
|
301
|
-
table[
|
290
|
+
table[constantize(constant_name.to_s, namespace: constant)] = constant_name.to_s
|
302
291
|
end
|
303
292
|
|
304
293
|
# Map each type variable to its string representation.
|
@@ -354,7 +343,7 @@ module Tapioca
|
|
354
343
|
superclass_name = name_of(superclass)
|
355
344
|
next unless superclass_name
|
356
345
|
|
357
|
-
resolved_superclass =
|
346
|
+
resolved_superclass = constantize(superclass_name)
|
358
347
|
next unless Module === resolved_superclass
|
359
348
|
next if name_of(resolved_superclass) == constant_name
|
360
349
|
|
@@ -380,43 +369,44 @@ module Tapioca
|
|
380
369
|
interesting_ancestors = interesting_ancestors_of(constant)
|
381
370
|
interesting_singleton_class_ancestors = interesting_ancestors_of(singleton_class)
|
382
371
|
|
383
|
-
|
384
|
-
|
385
|
-
|
372
|
+
prepends = interesting_ancestors.take_while { |c| !are_equal?(constant, c) }
|
373
|
+
includes = interesting_ancestors.drop(prepends.size + 1)
|
374
|
+
extends = interesting_singleton_class_ancestors.reject do |mod|
|
386
375
|
Module != class_of(mod) || are_equal?(mod, singleton_class)
|
387
376
|
end
|
388
377
|
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
add_to_symbol_queue(name_of(mod))
|
394
|
-
|
395
|
-
# TODO: Sorbet currently does not handle prepend
|
396
|
-
# properly for method resolution, so we generate an
|
397
|
-
# include statement instead
|
398
|
-
qname = qualified_name_of(mod)
|
399
|
-
tree << RBI::Include.new(T.must(qname))
|
400
|
-
end
|
378
|
+
add_mixins(tree, prepends.reverse, Trackers::Mixin::Type::Prepend)
|
379
|
+
add_mixins(tree, includes.reverse, Trackers::Mixin::Type::Include)
|
380
|
+
add_mixins(tree, extends.reverse, Trackers::Mixin::Type::Extend)
|
381
|
+
end
|
401
382
|
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
383
|
+
sig do
|
384
|
+
params(
|
385
|
+
tree: RBI::Tree,
|
386
|
+
mods: T::Array[Module],
|
387
|
+
mixin_type: Trackers::Mixin::Type
|
388
|
+
).void
|
389
|
+
end
|
390
|
+
def add_mixins(tree, mods, mixin_type)
|
391
|
+
mods
|
392
|
+
.select do |mod|
|
393
|
+
name = name_of(mod)
|
407
394
|
|
408
|
-
|
409
|
-
tree << RBI::Include.new(T.must(qname))
|
395
|
+
name && !name.start_with?("T::")
|
410
396
|
end
|
411
|
-
|
412
|
-
extend
|
413
|
-
.reverse
|
414
|
-
.select { |mod| (name = name_of(mod)) && !name.start_with?("T::") }
|
415
397
|
.map do |mod|
|
416
398
|
add_to_symbol_queue(name_of(mod))
|
417
399
|
|
418
400
|
qname = qualified_name_of(mod)
|
419
|
-
|
401
|
+
case mixin_type
|
402
|
+
# TODO: Sorbet currently does not handle prepend
|
403
|
+
# properly for method resolution, so we generate an
|
404
|
+
# include statement instead
|
405
|
+
when Trackers::Mixin::Type::Include, Trackers::Mixin::Type::Prepend
|
406
|
+
tree << RBI::Include.new(T.must(qname))
|
407
|
+
when Trackers::Mixin::Type::Extend
|
408
|
+
tree << RBI::Extend.new(T.must(qname))
|
409
|
+
end
|
420
410
|
end
|
421
411
|
end
|
422
412
|
|
@@ -645,7 +635,7 @@ module Tapioca
|
|
645
635
|
sig { params(constant: Module, strict: T::Boolean).returns(T::Boolean) }
|
646
636
|
def defined_in_gem?(constant, strict: true)
|
647
637
|
files = Set.new(get_file_candidates(constant))
|
648
|
-
.merge(Tapioca::
|
638
|
+
.merge(Tapioca::Trackers::ConstantDefinition.files_for(constant))
|
649
639
|
|
650
640
|
return !strict if files.empty?
|
651
641
|
|
@@ -654,6 +644,19 @@ module Tapioca
|
|
654
644
|
end
|
655
645
|
end
|
656
646
|
|
647
|
+
sig do
|
648
|
+
params(
|
649
|
+
mod: Module,
|
650
|
+
mixin_type: Trackers::Mixin::Type,
|
651
|
+
mixin_locations: T::Hash[Trackers::Mixin::Type, T::Hash[Module, T::Array[String]]]
|
652
|
+
).returns(T::Boolean)
|
653
|
+
end
|
654
|
+
def mixed_in_by_gem?(mod, mixin_type, mixin_locations)
|
655
|
+
locations = mixin_locations.dig(mixin_type, mod)
|
656
|
+
return true unless locations
|
657
|
+
locations.any? { |location| gem.contains_path?(location) }
|
658
|
+
end
|
659
|
+
|
657
660
|
sig { params(constant: Module).returns(T::Array[String]) }
|
658
661
|
def get_file_candidates(constant)
|
659
662
|
wrapped_module = Pry::WrappedModule.new(constant)
|
@@ -725,7 +728,7 @@ module Tapioca
|
|
725
728
|
return name if name
|
726
729
|
name = super(constant)
|
727
730
|
return if name.nil?
|
728
|
-
return unless are_equal?(constant,
|
731
|
+
return unless are_equal?(constant, constantize(name, inherit: true))
|
729
732
|
name = "Struct" if name =~ /^(::)?Struct::[^:]+$/
|
730
733
|
name
|
731
734
|
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "etc"
|
5
|
+
|
6
|
+
module Tapioca
|
7
|
+
class Executor
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
MINIMUM_ITEMS_PER_WORKER = T.let(2, Integer)
|
11
|
+
|
12
|
+
sig { params(queue: T::Array[T.untyped], number_of_workers: T.nilable(Integer)).void }
|
13
|
+
def initialize(queue, number_of_workers: nil)
|
14
|
+
@queue = queue
|
15
|
+
|
16
|
+
# Forking workers is expensive and not worth it for a low number of gems. Here we assign the number of workers to
|
17
|
+
# be the minimum between the number of available processors (max) or the number of workers to make sure that each
|
18
|
+
# one has at least 4 items to process
|
19
|
+
@number_of_workers = T.let(
|
20
|
+
number_of_workers || [Etc.nprocessors, (queue.length.to_f / MINIMUM_ITEMS_PER_WORKER).ceil].min,
|
21
|
+
Integer
|
22
|
+
)
|
23
|
+
|
24
|
+
# The number of items that will be processed per worker, so that we can split the queue into groups and assign
|
25
|
+
# them to each one of the workers
|
26
|
+
@items_per_worker = T.let((queue.length.to_f / @number_of_workers).ceil, Integer)
|
27
|
+
end
|
28
|
+
|
29
|
+
sig do
|
30
|
+
type_parameters(:T).params(
|
31
|
+
block: T.proc.params(item: T.untyped).returns(T.type_parameter(:T))
|
32
|
+
).returns(T::Array[T.type_parameter(:T)])
|
33
|
+
end
|
34
|
+
def run_in_parallel(&block)
|
35
|
+
# If we only have one worker selected, it's not worth forking, just run sequentially
|
36
|
+
return @queue.map { |item| block.call(item) } if @number_of_workers == 1
|
37
|
+
|
38
|
+
read_pipes = []
|
39
|
+
write_pipes = []
|
40
|
+
|
41
|
+
# If we have more than one worker, fork the pool by shifting the expected number of items per worker from the
|
42
|
+
# queue
|
43
|
+
workers = (0...@number_of_workers).map do
|
44
|
+
items = @queue.shift(@items_per_worker)
|
45
|
+
|
46
|
+
# Each worker has their own pair of pipes, so that we can read the result from each worker separately
|
47
|
+
read, write = IO.pipe
|
48
|
+
read_pipes << read
|
49
|
+
write_pipes << write
|
50
|
+
|
51
|
+
fork do
|
52
|
+
read.close
|
53
|
+
result = items.map { |item| block.call(item) }
|
54
|
+
|
55
|
+
# Pack the result as a Base64 string of the Marshal dump of the array of values returned by the block that we
|
56
|
+
# ran in parallel
|
57
|
+
packed = [Marshal.dump(result)].pack("m")
|
58
|
+
write.puts(packed)
|
59
|
+
write.close
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Close all the write pipes, then read and close from all the read pipes
|
64
|
+
write_pipes.each(&:close)
|
65
|
+
result = read_pipes.map do |pipe|
|
66
|
+
content = pipe.read
|
67
|
+
pipe.close
|
68
|
+
content
|
69
|
+
end
|
70
|
+
|
71
|
+
# Wait until all the workers finish. Notice that waiting for the PIDs can only happen after we read and close the
|
72
|
+
# pipe or else we may end up in a condition where writing to the pipe hangs indefinitely
|
73
|
+
workers.each { |pid| Process.waitpid(pid) }
|
74
|
+
|
75
|
+
# Decode the value back into the Ruby objects by doing the inverse of what each worker does
|
76
|
+
result.flat_map { |item| T.unsafe(Marshal.load(item.unpack1("m"))) }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/tapioca/gemfile.rb
CHANGED
@@ -105,6 +105,7 @@ module Tapioca
|
|
105
105
|
real_gem_path = to_realpath(@spec.full_gem_path)
|
106
106
|
@full_gem_path = T.let(real_gem_path, String)
|
107
107
|
@version = T.let(version_string, String)
|
108
|
+
@exported_rbi_files = T.let(nil, T.nilable(T::Array[String]))
|
108
109
|
end
|
109
110
|
|
110
111
|
sig { params(gemfile_dir: String).returns(T::Boolean) }
|
@@ -114,13 +115,14 @@ module Tapioca
|
|
114
115
|
|
115
116
|
sig { returns(T::Array[Pathname]) }
|
116
117
|
def files
|
117
|
-
|
118
|
-
|
119
|
-
|
118
|
+
if default_gem?
|
119
|
+
# `Bundler::RemoteSpecification` delegates missing methods to
|
120
|
+
# `Gem::Specification`, so `files` actually always exists on spec.
|
121
|
+
T.unsafe(@spec).files.map do |file|
|
120
122
|
ruby_lib_dir.join(file)
|
121
123
|
end
|
122
124
|
else
|
123
|
-
spec.full_require_paths.flat_map do |path|
|
125
|
+
@spec.full_require_paths.flat_map do |path|
|
124
126
|
Pathname.glob((Pathname.new(path) / "**/*.rb").to_s)
|
125
127
|
end
|
126
128
|
end
|
@@ -150,6 +152,28 @@ module Tapioca
|
|
150
152
|
files.each { |path| YARD.parse(path.to_s, [], Logger::Severity::FATAL) }
|
151
153
|
end
|
152
154
|
|
155
|
+
sig { returns(T::Array[String]) }
|
156
|
+
def exported_rbi_files
|
157
|
+
@exported_rbi_files ||= Dir.glob("#{full_gem_path}/rbi/**/*.rbi")
|
158
|
+
end
|
159
|
+
|
160
|
+
sig { returns(T::Boolean) }
|
161
|
+
def export_rbi_files?
|
162
|
+
exported_rbi_files.any?
|
163
|
+
end
|
164
|
+
|
165
|
+
sig { returns(RBI::MergeTree) }
|
166
|
+
def exported_rbi_tree
|
167
|
+
rewriter = RBI::Rewriters::Merge.new(keep: RBI::Rewriters::Merge::Keep::NONE)
|
168
|
+
|
169
|
+
exported_rbi_files.each do |file|
|
170
|
+
rbi = RBI::Parser.parse_file(file)
|
171
|
+
rewriter.merge(rbi)
|
172
|
+
end
|
173
|
+
|
174
|
+
rewriter.tree
|
175
|
+
end
|
176
|
+
|
153
177
|
private
|
154
178
|
|
155
179
|
sig { returns(T::Boolean) }
|
@@ -1,9 +1,6 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
# TODO: Remove me when logging logic has been abstracted.
|
5
|
-
require "thor"
|
6
|
-
|
7
4
|
module Tapioca
|
8
5
|
module Generators
|
9
6
|
class Base
|
@@ -14,7 +11,7 @@ module Tapioca
|
|
14
11
|
include Thor::Actions
|
15
12
|
end
|
16
13
|
|
17
|
-
|
14
|
+
include CliHelper
|
18
15
|
include Thor::Base
|
19
16
|
|
20
17
|
abstract!
|
@@ -30,20 +27,6 @@ module Tapioca
|
|
30
27
|
|
31
28
|
private
|
32
29
|
|
33
|
-
# TODO: Remove me when logging logic has been abstracted
|
34
|
-
sig { params(message: String, color: T.any(Symbol, T::Array[Symbol])).void }
|
35
|
-
def say_error(message = "", *color)
|
36
|
-
force_new_line = (message.to_s !~ /( |\t)\Z/)
|
37
|
-
# NOTE: This is a hack. We're no longer subclassing from Thor::Shell::Color
|
38
|
-
# so we no longer have access to the prepare_message call.
|
39
|
-
# We should update this to remove this.
|
40
|
-
buffer = shell.send(:prepare_message, *T.unsafe([message, *T.unsafe(color)]))
|
41
|
-
buffer << "\n" if force_new_line && !message.to_s.end_with?("\n")
|
42
|
-
|
43
|
-
$stderr.print(buffer)
|
44
|
-
$stderr.flush
|
45
|
-
end
|
46
|
-
|
47
30
|
sig do
|
48
31
|
params(
|
49
32
|
path: T.any(String, Pathname),
|
@@ -56,6 +39,16 @@ module Tapioca
|
|
56
39
|
def create_file(path, content, force: true, skip: false, verbose: true)
|
57
40
|
@file_writer.create_file(path, force: force, skip: skip, verbose: verbose) { content }
|
58
41
|
end
|
42
|
+
|
43
|
+
sig do
|
44
|
+
params(
|
45
|
+
path: T.any(String, Pathname),
|
46
|
+
verbose: T::Boolean
|
47
|
+
).void
|
48
|
+
end
|
49
|
+
def remove_file(path, verbose: true)
|
50
|
+
@file_writer.remove_file(path, verbose: verbose)
|
51
|
+
end
|
59
52
|
end
|
60
53
|
end
|
61
54
|
end
|
@@ -8,8 +8,8 @@ module Tapioca
|
|
8
8
|
params(
|
9
9
|
requested_constants: T::Array[String],
|
10
10
|
outpath: Pathname,
|
11
|
-
|
12
|
-
|
11
|
+
only: T::Array[String],
|
12
|
+
exclude: T::Array[String],
|
13
13
|
file_header: T::Boolean,
|
14
14
|
compiler_path: String,
|
15
15
|
tapioca_path: String,
|
@@ -17,14 +17,15 @@ module Tapioca
|
|
17
17
|
file_writer: Thor::Actions,
|
18
18
|
should_verify: T::Boolean,
|
19
19
|
quiet: T::Boolean,
|
20
|
-
verbose: T::Boolean
|
20
|
+
verbose: T::Boolean,
|
21
|
+
number_of_workers: T.nilable(Integer),
|
21
22
|
).void
|
22
23
|
end
|
23
24
|
def initialize(
|
24
25
|
requested_constants:,
|
25
26
|
outpath:,
|
26
|
-
|
27
|
-
|
27
|
+
only:,
|
28
|
+
exclude:,
|
28
29
|
file_header:,
|
29
30
|
compiler_path:,
|
30
31
|
tapioca_path:,
|
@@ -32,18 +33,20 @@ module Tapioca
|
|
32
33
|
file_writer: FileWriter.new,
|
33
34
|
should_verify: false,
|
34
35
|
quiet: false,
|
35
|
-
verbose: false
|
36
|
+
verbose: false,
|
37
|
+
number_of_workers: nil
|
36
38
|
)
|
37
39
|
@requested_constants = requested_constants
|
38
40
|
@outpath = outpath
|
39
|
-
@
|
40
|
-
@
|
41
|
+
@only = only
|
42
|
+
@exclude = exclude
|
41
43
|
@file_header = file_header
|
42
44
|
@compiler_path = compiler_path
|
43
45
|
@tapioca_path = tapioca_path
|
44
46
|
@should_verify = should_verify
|
45
47
|
@quiet = quiet
|
46
48
|
@verbose = verbose
|
49
|
+
@number_of_workers = number_of_workers
|
47
50
|
|
48
51
|
super(default_command: default_command, file_writer: file_writer)
|
49
52
|
|
@@ -52,6 +55,7 @@ module Tapioca
|
|
52
55
|
|
53
56
|
sig { override.void }
|
54
57
|
def generate
|
58
|
+
load_dsl_extensions
|
55
59
|
load_application(eager_load: @requested_constants.empty?)
|
56
60
|
abort_if_pending_migrations!
|
57
61
|
load_dsl_generators
|
@@ -68,31 +72,31 @@ module Tapioca
|
|
68
72
|
|
69
73
|
compiler = Compilers::DslCompiler.new(
|
70
74
|
requested_constants: constantize(@requested_constants),
|
71
|
-
requested_generators: constantize_generators(@
|
72
|
-
excluded_generators: constantize_generators(@
|
75
|
+
requested_generators: constantize_generators(@only),
|
76
|
+
excluded_generators: constantize_generators(@exclude),
|
73
77
|
error_handler: ->(error) {
|
74
78
|
say_error(error, :bold, :red)
|
75
|
-
}
|
79
|
+
},
|
80
|
+
number_of_workers: @number_of_workers
|
76
81
|
)
|
77
82
|
|
78
|
-
compiler.run do |constant, contents|
|
83
|
+
processed_files = compiler.run do |constant, contents|
|
79
84
|
constant_name = T.must(Reflection.name_of(constant))
|
80
85
|
|
81
86
|
if @verbose && !@quiet
|
82
87
|
say_status(:processing, constant_name, :yellow)
|
83
88
|
end
|
84
89
|
|
85
|
-
|
90
|
+
compile_dsl_rbi(
|
86
91
|
constant_name,
|
87
92
|
contents,
|
88
93
|
outpath: outpath,
|
89
94
|
quiet: @should_verify || @quiet && !@verbose
|
90
95
|
)
|
91
|
-
|
92
|
-
if filename
|
93
|
-
rbi_files_to_purge.delete(filename)
|
94
|
-
end
|
95
96
|
end
|
97
|
+
|
98
|
+
processed_files.each { |filename| rbi_files_to_purge.delete(T.must(filename)) }
|
99
|
+
|
96
100
|
say("")
|
97
101
|
|
98
102
|
if @should_verify
|
@@ -171,7 +175,8 @@ module Tapioca
|
|
171
175
|
unless unprocessable_constants.empty?
|
172
176
|
unprocessable_constants.each do |name, _|
|
173
177
|
say("Error: Cannot find constant '#{name}'", :red)
|
174
|
-
|
178
|
+
filename = dsl_rbi_filename(name)
|
179
|
+
remove_file(filename) if File.file?(filename)
|
175
180
|
end
|
176
181
|
|
177
182
|
exit(1)
|
@@ -182,17 +187,9 @@ module Tapioca
|
|
182
187
|
|
183
188
|
sig { params(generator_names: T::Array[String]).returns(T::Array[T.class_of(Compilers::Dsl::Base)]) }
|
184
189
|
def constantize_generators(generator_names)
|
185
|
-
generator_map = generator_names.
|
186
|
-
|
187
|
-
|
188
|
-
generator_klass = ["Tapioca::Compilers::Dsl::#{name}", name].find do |potential_name|
|
189
|
-
break Object.const_get(potential_name)
|
190
|
-
rescue NameError
|
191
|
-
# Skip if we can't find generator by the potential name
|
192
|
-
end
|
193
|
-
|
194
|
-
[name, generator_klass]
|
195
|
-
end.to_h
|
190
|
+
generator_map = generator_names.to_h do |name|
|
191
|
+
[name, Compilers::Dsl::Base.resolve(name)]
|
192
|
+
end
|
196
193
|
|
197
194
|
unprocessable_generators = generator_map.select { |_, v| v.nil? }
|
198
195
|
unless unprocessable_generators.empty?
|
@@ -203,7 +200,7 @@ module Tapioca
|
|
203
200
|
exit(1)
|
204
201
|
end
|
205
202
|
|
206
|
-
generator_map.values
|
203
|
+
T.cast(generator_map.values, T::Array[T.class_of(Compilers::Dsl::Base)])
|
207
204
|
end
|
208
205
|
|
209
206
|
sig do
|
@@ -245,7 +242,7 @@ module Tapioca
|
|
245
242
|
say("Removing stale RBI files...")
|
246
243
|
|
247
244
|
files.sort.each do |filename|
|
248
|
-
|
245
|
+
remove_file(filename)
|
249
246
|
end
|
250
247
|
say("")
|
251
248
|
end
|
@@ -256,13 +253,6 @@ module Tapioca
|
|
256
253
|
@outpath / "#{underscore(constant_name)}.rbi"
|
257
254
|
end
|
258
255
|
|
259
|
-
sig { params(filename: Pathname).void }
|
260
|
-
def remove(filename)
|
261
|
-
return unless filename.exist?
|
262
|
-
say("-- Removing: #{filename}")
|
263
|
-
filename.unlink
|
264
|
-
end
|
265
|
-
|
266
256
|
sig { params(tmp_dir: Pathname).returns(T::Hash[String, Symbol]) }
|
267
257
|
def verify_dsl_rbi(tmp_dir:)
|
268
258
|
diff = {}
|
@@ -357,6 +347,11 @@ module Tapioca
|
|
357
347
|
def generate_command_for(constant)
|
358
348
|
"#{@default_command} dsl #{constant}"
|
359
349
|
end
|
350
|
+
|
351
|
+
sig { void }
|
352
|
+
def load_dsl_extensions
|
353
|
+
Dir["#{__dir__}/../compilers/dsl/extensions/*.rb"].sort.each { |f| require(f) }
|
354
|
+
end
|
360
355
|
end
|
361
356
|
end
|
362
357
|
end
|