tapioca 0.5.3 → 0.6.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/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
|