tapioca 0.4.19 → 0.4.20
Sign up to get free protection for your applications and to get access to all the features.
- 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
|