tapioca 0.4.19 → 0.4.20
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/lib/tapioca/cli/main.rb +5 -1
- data/lib/tapioca/generator.rb +86 -30
- data/lib/tapioca/generic_type_registry.rb +27 -1
- data/lib/tapioca/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 567eff35f7ee24b52dafcd45ceb40ccaf042b6e0661c13cc5dcb87228c21c792
|
4
|
+
data.tar.gz: 0c5143d8407d5583ccd80edbb047fb0b7500b2de8bba1717723a92f97994c045
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d82321ab3b2516a4410825a6dfe4b253d7cccf49abc8c62db0b2a8cdbe13f064aa43df759108685ba06c36073c7a59cfda4ff361cdad1bfc736d7e557ae3ada6
|
7
|
+
data.tar.gz: 0b9bd1ff91ca97cbbb1922e9915a8d046421ff40f3cd2774b4518c979f44635507b48fcce93a904770b1a65e7214a9897ce5444ce26ee6d532523a595e24ec96
|
data/lib/tapioca/cli/main.rb
CHANGED
@@ -66,9 +66,13 @@ module Tapioca
|
|
66
66
|
type: :boolean,
|
67
67
|
default: false,
|
68
68
|
desc: "Verifies RBIs are up-to-date"
|
69
|
+
option :quiet,
|
70
|
+
aliases: ["-q"],
|
71
|
+
type: :boolean,
|
72
|
+
desc: "Supresses file creation output"
|
69
73
|
def dsl(*constants)
|
70
74
|
Tapioca.silence_warnings do
|
71
|
-
generator.build_dsl(constants, should_verify: options[:verify])
|
75
|
+
generator.build_dsl(constants, should_verify: options[:verify], quiet: options[:quiet])
|
72
76
|
end
|
73
77
|
end
|
74
78
|
|
data/lib/tapioca/generator.rb
CHANGED
@@ -120,9 +120,10 @@ module Tapioca
|
|
120
120
|
params(
|
121
121
|
requested_constants: T::Array[String],
|
122
122
|
should_verify: T::Boolean,
|
123
|
+
quiet: T::Boolean
|
123
124
|
).void
|
124
125
|
end
|
125
|
-
def build_dsl(requested_constants, should_verify: false)
|
126
|
+
def build_dsl(requested_constants, should_verify: false, quiet: false)
|
126
127
|
load_application(eager_load: requested_constants.empty?)
|
127
128
|
load_dsl_generators
|
128
129
|
|
@@ -133,7 +134,7 @@ module Tapioca
|
|
133
134
|
end
|
134
135
|
say("")
|
135
136
|
|
136
|
-
outpath = should_verify ? Dir.mktmpdir : config.outpath
|
137
|
+
outpath = should_verify ? Pathname.new(Dir.mktmpdir) : config.outpath
|
137
138
|
rbi_files_to_purge = existing_rbi_filenames(requested_constants)
|
138
139
|
|
139
140
|
compiler = Compilers::DslCompiler.new(
|
@@ -144,14 +145,27 @@ module Tapioca
|
|
144
145
|
}
|
145
146
|
)
|
146
147
|
|
148
|
+
constant_lookup = {}
|
149
|
+
|
147
150
|
compiler.run do |constant, contents|
|
148
|
-
|
149
|
-
|
151
|
+
constant_name = Module.instance_method(:name).bind(constant).call
|
152
|
+
|
153
|
+
filename = compile_dsl_rbi(
|
154
|
+
constant_name,
|
155
|
+
contents,
|
156
|
+
outpath: outpath,
|
157
|
+
quiet: should_verify || quiet
|
158
|
+
)
|
159
|
+
|
160
|
+
if filename
|
161
|
+
rbi_files_to_purge.delete(filename)
|
162
|
+
constant_lookup[filename.relative_path_from(outpath)] = constant_name
|
163
|
+
end
|
150
164
|
end
|
151
165
|
say("")
|
152
166
|
|
153
167
|
if should_verify
|
154
|
-
perform_dsl_verification(outpath)
|
168
|
+
perform_dsl_verification(outpath, constant_lookup)
|
155
169
|
else
|
156
170
|
purge_stale_dsl_rbi_files(rbi_files_to_purge)
|
157
171
|
|
@@ -506,60 +520,102 @@ module Tapioca
|
|
506
520
|
end
|
507
521
|
end
|
508
522
|
|
509
|
-
sig
|
510
|
-
|
523
|
+
sig do
|
524
|
+
params(constant_name: String, contents: String, outpath: Pathname, quiet: T::Boolean)
|
525
|
+
.returns(T.nilable(Pathname))
|
526
|
+
end
|
527
|
+
def compile_dsl_rbi(constant_name, contents, outpath: config.outpath, quiet: false)
|
511
528
|
return if contents.nil?
|
512
529
|
|
513
|
-
constant_name = Module.instance_method(:name).bind(constant).call
|
514
530
|
rbi_name = constant_name.underscore + ".rbi"
|
515
531
|
filename = outpath / rbi_name
|
516
532
|
|
517
533
|
out = String.new
|
518
534
|
out << rbi_header(
|
519
535
|
"#{Config::DEFAULT_COMMAND} dsl #{constant_name}",
|
520
|
-
reason: "dynamic methods in `#{
|
536
|
+
reason: "dynamic methods in `#{constant_name}`"
|
521
537
|
)
|
522
538
|
out << contents
|
523
539
|
|
524
540
|
FileUtils.mkdir_p(File.dirname(filename))
|
525
541
|
File.write(filename, out)
|
526
|
-
|
527
|
-
|
542
|
+
|
543
|
+
unless quiet
|
544
|
+
say("Wrote: ", [:green])
|
545
|
+
say(filename)
|
546
|
+
end
|
528
547
|
|
529
548
|
filename
|
530
549
|
end
|
531
550
|
|
532
|
-
sig { params(tmp_dir: Pathname).returns(T
|
551
|
+
sig { params(tmp_dir: Pathname).returns(T::Hash[String, Symbol]) }
|
533
552
|
def verify_dsl_rbi(tmp_dir:)
|
534
|
-
|
535
|
-
new_rbis = existing_rbi_filenames([], path: tmp_dir).grep_v(/gem|shim/).sort
|
553
|
+
diff = {}
|
536
554
|
|
537
|
-
|
555
|
+
existing_rbis = rbi_files_in(config.outpath)
|
556
|
+
new_rbis = rbi_files_in(tmp_dir)
|
538
557
|
|
539
|
-
|
558
|
+
added_files = (new_rbis - existing_rbis)
|
540
559
|
|
541
|
-
|
542
|
-
|
560
|
+
added_files.each do |file|
|
561
|
+
diff[file] = :added
|
543
562
|
end
|
544
563
|
|
545
|
-
|
546
|
-
filenames = desynced_files.map { |f| f.to_s.sub!(tmp_dir.to_s, "sorbet/rbi/dsl") }.join("\n - ")
|
564
|
+
removed_files = (existing_rbis - new_rbis)
|
547
565
|
|
548
|
-
|
566
|
+
removed_files.each do |file|
|
567
|
+
diff[file] = :removed
|
549
568
|
end
|
550
569
|
|
551
|
-
|
570
|
+
common_files = (existing_rbis & new_rbis)
|
571
|
+
|
572
|
+
changed_files = common_files.map do |filename|
|
573
|
+
filename unless FileUtils.identical?(config.outpath / filename, tmp_dir / filename)
|
574
|
+
end.compact
|
575
|
+
|
576
|
+
changed_files.each do |file|
|
577
|
+
diff[file] = :changed
|
578
|
+
end
|
579
|
+
|
580
|
+
diff
|
552
581
|
end
|
553
582
|
|
554
|
-
sig { params(
|
555
|
-
def
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
583
|
+
sig { params(cause: Symbol, files: T::Array[String]).returns(String) }
|
584
|
+
def build_error_for_files(cause, files)
|
585
|
+
filenames = files.map do |file|
|
586
|
+
config.outpath / file
|
587
|
+
end.join("\n - ")
|
588
|
+
|
589
|
+
" File(s) #{cause}:\n - #{filenames}"
|
590
|
+
end
|
591
|
+
|
592
|
+
sig { params(path: Pathname).returns(T::Array[Pathname]) }
|
593
|
+
def rbi_files_in(path)
|
594
|
+
Pathname.glob(path / "**/*.rbi").map do |file|
|
595
|
+
file.relative_path_from(path)
|
596
|
+
end.sort
|
597
|
+
end
|
598
|
+
|
599
|
+
sig { params(dir: Pathname, constant_lookup: T::Hash[String, String]).void }
|
600
|
+
def perform_dsl_verification(dir, constant_lookup)
|
601
|
+
diff = verify_dsl_rbi(tmp_dir: dir)
|
602
|
+
|
603
|
+
if diff.empty?
|
562
604
|
say("Nothing to do, all RBIs are up-to-date.")
|
605
|
+
else
|
606
|
+
constants = T.unsafe(constant_lookup).values_at(*diff.keys).join(" ")
|
607
|
+
|
608
|
+
say("RBI files are out-of-date, please run:")
|
609
|
+
say(" `#{Config::DEFAULT_COMMAND} dsl #{constants}`")
|
610
|
+
|
611
|
+
say("")
|
612
|
+
|
613
|
+
say("Reason:", [:red])
|
614
|
+
diff.group_by(&:last).sort.each do |cause, diff_for_cause|
|
615
|
+
say(build_error_for_files(cause, diff_for_cause.map(&:first)))
|
616
|
+
end
|
617
|
+
|
618
|
+
exit(1)
|
563
619
|
end
|
564
620
|
ensure
|
565
621
|
FileUtils.remove_entry(dir)
|
@@ -100,7 +100,7 @@ module Tapioca
|
|
100
100
|
# the generic class `Foo[Bar]` is still a `Foo`. That is:
|
101
101
|
# `Foo[Bar].new.is_a?(Foo)` should be true, which isn't the case
|
102
102
|
# if we just clone the class. But subclassing works just fine.
|
103
|
-
|
103
|
+
create_sealed_safe_subclass(constant)
|
104
104
|
else
|
105
105
|
# This can only be a module and it is fine to just clone modules
|
106
106
|
# since they can't have instances and will not have `is_a?` relationships.
|
@@ -150,6 +150,32 @@ module Tapioca
|
|
150
150
|
)
|
151
151
|
end
|
152
152
|
|
153
|
+
sig { params(constant: Class).returns(Class) }
|
154
|
+
def create_sealed_safe_subclass(constant)
|
155
|
+
# If the constant is not sealed let's just bail early.
|
156
|
+
# We just return a subclass of the constant.
|
157
|
+
return Class.new(constant) unless T::Private::Sealed.sealed_module?(constant)
|
158
|
+
|
159
|
+
# Since sealed classes can normally not be subclassed, we need to trick
|
160
|
+
# sealed classes into thinking that the generic type we are
|
161
|
+
# creating by subclassing is actually safe for sealed types.
|
162
|
+
#
|
163
|
+
# Get the filename the sealed class was declared in
|
164
|
+
decl_file = constant.instance_variable_get(:@sorbet_sealed_module_decl_file)
|
165
|
+
begin
|
166
|
+
# Clear the current declaration filename on the class
|
167
|
+
constant.remove_instance_variable(:@sorbet_sealed_module_decl_file)
|
168
|
+
# Make this file be the declaration filename so that Sorbet runtime
|
169
|
+
# does not shout at us for an invalid subclassing.
|
170
|
+
T.cast(constant, T::Helpers).sealed!
|
171
|
+
# return a subclass
|
172
|
+
Class.new(constant)
|
173
|
+
ensure
|
174
|
+
# Reinstate the original declaration filename
|
175
|
+
constant.instance_variable_set(:@sorbet_sealed_module_decl_file, decl_file)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
153
179
|
sig { params(constant: Module).returns(T::Hash[Integer, String]) }
|
154
180
|
def lookup_or_initialize_type_variables(constant)
|
155
181
|
@type_variables[object_id_of(constant)] ||= {}
|
data/lib/tapioca/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tapioca
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.20
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ufuk Kayserilioglu
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: exe
|
13
13
|
cert_chain: []
|
14
|
-
date: 2021-
|
14
|
+
date: 2021-04-01 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: bundler
|