tapioca 0.4.25 → 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +14 -14
- data/README.md +2 -2
- data/Rakefile +5 -7
- data/exe/tapioca +2 -2
- data/lib/tapioca/cli.rb +256 -2
- data/lib/tapioca/compilers/dsl/aasm.rb +122 -0
- data/lib/tapioca/compilers/dsl/action_controller_helpers.rb +52 -12
- data/lib/tapioca/compilers/dsl/action_mailer.rb +6 -9
- data/lib/tapioca/compilers/dsl/active_job.rb +8 -12
- data/lib/tapioca/compilers/dsl/active_model_attributes.rb +131 -0
- data/lib/tapioca/compilers/dsl/active_record_associations.rb +33 -54
- data/lib/tapioca/compilers/dsl/active_record_columns.rb +10 -105
- data/lib/tapioca/compilers/dsl/active_record_enum.rb +8 -10
- data/lib/tapioca/compilers/dsl/active_record_scope.rb +7 -10
- data/lib/tapioca/compilers/dsl/active_record_typed_store.rb +5 -8
- data/lib/tapioca/compilers/dsl/active_resource.rb +9 -37
- data/lib/tapioca/compilers/dsl/active_storage.rb +98 -0
- data/lib/tapioca/compilers/dsl/active_support_concern.rb +108 -0
- data/lib/tapioca/compilers/dsl/active_support_current_attributes.rb +13 -8
- data/lib/tapioca/compilers/dsl/base.rb +96 -82
- data/lib/tapioca/compilers/dsl/config.rb +111 -0
- data/lib/tapioca/compilers/dsl/frozen_record.rb +5 -7
- data/lib/tapioca/compilers/dsl/identity_cache.rb +66 -29
- data/lib/tapioca/compilers/dsl/protobuf.rb +19 -69
- data/lib/tapioca/compilers/dsl/sidekiq_worker.rb +25 -12
- data/lib/tapioca/compilers/dsl/smart_properties.rb +21 -33
- data/lib/tapioca/compilers/dsl/state_machines.rb +56 -78
- data/lib/tapioca/compilers/dsl/url_helpers.rb +7 -10
- data/lib/tapioca/compilers/dsl_compiler.rb +22 -38
- data/lib/tapioca/compilers/requires_compiler.rb +2 -2
- data/lib/tapioca/compilers/sorbet.rb +26 -5
- data/lib/tapioca/compilers/symbol_table/symbol_generator.rb +139 -154
- data/lib/tapioca/compilers/symbol_table/symbol_loader.rb +4 -4
- data/lib/tapioca/compilers/symbol_table_compiler.rb +1 -1
- data/lib/tapioca/compilers/todos_compiler.rb +1 -1
- data/lib/tapioca/config.rb +2 -0
- data/lib/tapioca/config_builder.rb +4 -2
- data/lib/tapioca/constant_locator.rb +6 -8
- data/lib/tapioca/gemfile.rb +10 -12
- data/lib/tapioca/generator.rb +129 -45
- data/lib/tapioca/generic_type_registry.rb +25 -98
- data/lib/tapioca/helpers/active_record_column_type_helper.rb +98 -0
- data/lib/tapioca/internal.rb +1 -10
- data/lib/tapioca/loader.rb +14 -48
- data/lib/tapioca/rbi_ext/model.rb +122 -0
- data/lib/tapioca/reflection.rb +131 -0
- data/lib/tapioca/sorbet_ext/fixed_hash_patch.rb +1 -1
- data/lib/tapioca/sorbet_ext/generic_name_patch.rb +72 -4
- data/lib/tapioca/sorbet_ext/name_patch.rb +1 -1
- data/lib/tapioca/version.rb +1 -1
- data/lib/tapioca.rb +3 -0
- metadata +34 -22
- data/lib/tapioca/cli/main.rb +0 -146
- data/lib/tapioca/core_ext/class.rb +0 -28
- data/lib/tapioca/core_ext/string.rb +0 -18
- data/lib/tapioca/rbi/model.rb +0 -405
- data/lib/tapioca/rbi/printer.rb +0 -410
- data/lib/tapioca/rbi/rewriters/group_nodes.rb +0 -106
- data/lib/tapioca/rbi/rewriters/nest_non_public_methods.rb +0 -65
- data/lib/tapioca/rbi/rewriters/nest_singleton_methods.rb +0 -42
- data/lib/tapioca/rbi/rewriters/sort_nodes.rb +0 -86
- data/lib/tapioca/rbi/visitor.rb +0 -21
@@ -1,8 +1,8 @@
|
|
1
1
|
# typed: true
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require
|
5
|
-
require
|
4
|
+
require "json"
|
5
|
+
require "tempfile"
|
6
6
|
|
7
7
|
module Tapioca
|
8
8
|
module Compilers
|
@@ -25,7 +25,7 @@ module Tapioca
|
|
25
25
|
|
26
26
|
sig { params(paths: T::Array[String]).returns(T::Set[String]) }
|
27
27
|
def load_symbols(paths)
|
28
|
-
output = T.cast(Tempfile.create(
|
28
|
+
output = T.cast(Tempfile.create("sorbet") do |file|
|
29
29
|
file.write(Array(paths).join("\n"))
|
30
30
|
file.flush
|
31
31
|
|
@@ -69,7 +69,7 @@ module Tapioca
|
|
69
69
|
# TODO: CLASS is removed since v0.4.4730 of Sorbet
|
70
70
|
# but keeping here for backward compatibility. Remove
|
71
71
|
# once the minimum version is moved past that.
|
72
|
-
next unless
|
72
|
+
next unless ["CLASS", "CLASS_OR_MODULE", "STATIC_FIELD"].include?(kind)
|
73
73
|
next if name =~ /[<>()$]/
|
74
74
|
next if name =~ /^[0-9]+$/
|
75
75
|
next if name == "T::Helpers"
|
@@ -13,7 +13,7 @@ module Tapioca
|
|
13
13
|
def compile
|
14
14
|
list_todos.each_line.map do |line|
|
15
15
|
next if line.include?("<") || line.include?("class_of")
|
16
|
-
"module #{line.strip.gsub(
|
16
|
+
"module #{line.strip.gsub("T.untyped::", "")}; end"
|
17
17
|
end.compact.join("\n")
|
18
18
|
end
|
19
19
|
|
data/lib/tapioca/config.rb
CHANGED
@@ -9,9 +9,11 @@ module Tapioca
|
|
9
9
|
const(:prerequire, T.nilable(String))
|
10
10
|
const(:postrequire, String)
|
11
11
|
const(:exclude, T::Array[String])
|
12
|
+
const(:exclude_generators, T::Array[String])
|
12
13
|
const(:typed_overrides, T::Hash[String, String])
|
13
14
|
const(:todos_path, String)
|
14
15
|
const(:generators, T::Array[String])
|
16
|
+
const(:file_header, T::Boolean, default: true)
|
15
17
|
|
16
18
|
sig { returns(Pathname) }
|
17
19
|
def outpath
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require
|
4
|
+
require "yaml"
|
5
5
|
|
6
6
|
module Tapioca
|
7
7
|
class ConfigBuilder
|
@@ -33,7 +33,7 @@ module Tapioca
|
|
33
33
|
sig { params(command: Symbol).returns(T::Hash[String, T.untyped]) }
|
34
34
|
def default_options(command)
|
35
35
|
default_outdir = case command
|
36
|
-
when :sync, :generate
|
36
|
+
when :sync, :generate, :gem
|
37
37
|
Config::DEFAULT_GEMDIR
|
38
38
|
when :dsl
|
39
39
|
Config::DEFAULT_DSLDIR
|
@@ -62,9 +62,11 @@ module Tapioca
|
|
62
62
|
"postrequire" => Config::DEFAULT_POSTREQUIRE,
|
63
63
|
"outdir" => nil,
|
64
64
|
"exclude" => [],
|
65
|
+
"exclude_generators" => [],
|
65
66
|
"typed_overrides" => Config::DEFAULT_OVERRIDES,
|
66
67
|
"todos_path" => Config::DEFAULT_TODOSPATH,
|
67
68
|
"generators" => [],
|
69
|
+
"file_header" => true,
|
68
70
|
}.freeze, T::Hash[String, T.untyped])
|
69
71
|
end
|
70
72
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# typed: true
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require
|
4
|
+
require "set"
|
5
5
|
|
6
6
|
module Tapioca
|
7
7
|
# Registers a TracePoint immediately upon load to track points at which
|
@@ -9,15 +9,14 @@ module Tapioca
|
|
9
9
|
# correspondence between classes/modules and files, as this information isn't
|
10
10
|
# available in the ruby runtime without extra accounting.
|
11
11
|
module ConstantLocator
|
12
|
-
|
12
|
+
extend Reflection
|
13
13
|
|
14
|
-
|
15
|
-
private_constant :NAME
|
14
|
+
@class_files = {}
|
16
15
|
|
17
16
|
# Immediately activated upon load. Observes class/module definition.
|
18
17
|
TracePoint.trace(:class) do |tp|
|
19
18
|
unless tp.self.singleton_class?
|
20
|
-
key =
|
19
|
+
key = name_of(tp.self)
|
21
20
|
@class_files[key] ||= Set.new
|
22
21
|
@class_files[key] << tp.path
|
23
22
|
end
|
@@ -26,11 +25,10 @@ module Tapioca
|
|
26
25
|
# Returns the files in which this class or module was opened. Doesn't know
|
27
26
|
# about situations where the class was opened prior to +require+ing,
|
28
27
|
# or where metaprogramming was used via +eval+, etc.
|
29
|
-
def files_for(klass)
|
30
|
-
name = String === klass ? klass :
|
28
|
+
def self.files_for(klass)
|
29
|
+
name = String === klass ? klass : name_of(klass)
|
31
30
|
files = @class_files[name]
|
32
31
|
files || Set.new
|
33
32
|
end
|
34
|
-
module_function :files_for
|
35
33
|
end
|
36
34
|
end
|
data/lib/tapioca/gemfile.rb
CHANGED
@@ -20,7 +20,7 @@ module Tapioca
|
|
20
20
|
sig { returns(Bundler::Definition) }
|
21
21
|
attr_reader(:definition)
|
22
22
|
|
23
|
-
sig { returns(T::Array[
|
23
|
+
sig { returns(T::Array[GemSpec]) }
|
24
24
|
attr_reader(:dependencies)
|
25
25
|
|
26
26
|
sig { returns(T::Array[String]) }
|
@@ -32,18 +32,18 @@ module Tapioca
|
|
32
32
|
@lockfile = T.let(File.new(Bundler.default_lockfile), File)
|
33
33
|
@definition = T.let(Bundler::Dsl.evaluate(gemfile, lockfile, {}), Bundler::Definition)
|
34
34
|
dependencies, missing_specs = load_dependencies
|
35
|
-
@dependencies = T.let(dependencies, T::Array[
|
35
|
+
@dependencies = T.let(dependencies, T::Array[GemSpec])
|
36
36
|
@missing_specs = T.let(missing_specs, T::Array[String])
|
37
37
|
end
|
38
38
|
|
39
|
-
sig { params(gem_name: String).returns(T.nilable(
|
39
|
+
sig { params(gem_name: String).returns(T.nilable(GemSpec)) }
|
40
40
|
def gem(gem_name)
|
41
41
|
dependencies.detect { |dep| dep.name == gem_name }
|
42
42
|
end
|
43
43
|
|
44
44
|
sig { void }
|
45
|
-
def
|
46
|
-
T.unsafe(runtime).
|
45
|
+
def require_bundle
|
46
|
+
T.unsafe(runtime).require(*groups)
|
47
47
|
end
|
48
48
|
|
49
49
|
private
|
@@ -51,18 +51,18 @@ module Tapioca
|
|
51
51
|
sig { returns(File) }
|
52
52
|
attr_reader(:gemfile, :lockfile)
|
53
53
|
|
54
|
-
sig { returns([T::Array[
|
54
|
+
sig { returns([T::Array[GemSpec], T::Array[String]]) }
|
55
55
|
def load_dependencies
|
56
56
|
materialized_dependencies, missing_specs = materialize_deps
|
57
57
|
dependencies = materialized_dependencies
|
58
|
-
.map { |spec|
|
58
|
+
.map { |spec| GemSpec.new(spec) }
|
59
59
|
.reject { |gem| gem.ignore?(dir) }
|
60
60
|
.uniq(&:rbi_file_name)
|
61
61
|
.sort_by(&:rbi_file_name)
|
62
62
|
[dependencies, missing_specs]
|
63
63
|
end
|
64
64
|
|
65
|
-
sig { returns([T::
|
65
|
+
sig { returns([T::Enumerable[Spec], T::Array[String]]) }
|
66
66
|
def materialize_deps
|
67
67
|
deps = definition.locked_gems.dependencies.values
|
68
68
|
missing_specs = T::Array[String].new
|
@@ -92,12 +92,10 @@ module Tapioca
|
|
92
92
|
File.expand_path(gemfile.path + "/..")
|
93
93
|
end
|
94
94
|
|
95
|
-
class
|
95
|
+
class GemSpec
|
96
96
|
extend(T::Sig)
|
97
97
|
|
98
|
-
IGNORED_GEMS = T.let(
|
99
|
-
sorbet sorbet-static sorbet-runtime
|
100
|
-
}.freeze, T::Array[String])
|
98
|
+
IGNORED_GEMS = T.let(["sorbet", "sorbet-static", "sorbet-runtime"].freeze, T::Array[String])
|
101
99
|
|
102
100
|
sig { returns(String) }
|
103
101
|
attr_reader :full_gem_path, :version
|
data/lib/tapioca/generator.rb
CHANGED
@@ -1,9 +1,8 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require "tapioca/core_ext/string"
|
4
|
+
require "pathname"
|
5
|
+
require "thor"
|
7
6
|
|
8
7
|
module Tapioca
|
9
8
|
class Generator < ::Thor::Shell::Color
|
@@ -63,11 +62,8 @@ module Tapioca
|
|
63
62
|
File.delete(requires_path) if File.exist?(requires_path)
|
64
63
|
|
65
64
|
content = String.new
|
66
|
-
content <<
|
67
|
-
|
68
|
-
reason: "explicit gem requires",
|
69
|
-
strictness: "false"
|
70
|
-
)
|
65
|
+
content << "# typed: true\n"
|
66
|
+
content << "# frozen_string_literal: true\n\n"
|
71
67
|
content << rb_string
|
72
68
|
|
73
69
|
outdir = File.dirname(requires_path)
|
@@ -77,7 +73,7 @@ module Tapioca
|
|
77
73
|
say("Done", :green)
|
78
74
|
|
79
75
|
say("All requires from this application have been written to #{name}.", [:green, :bold])
|
80
|
-
cmd = set_color("#{Config::DEFAULT_COMMAND}
|
76
|
+
cmd = set_color("#{Config::DEFAULT_COMMAND} gem", :yellow, :bold)
|
81
77
|
say("Please review changes and commit them, then run `#{cmd}`.", [:green, :bold])
|
82
78
|
end
|
83
79
|
|
@@ -121,11 +117,13 @@ module Tapioca
|
|
121
117
|
params(
|
122
118
|
requested_constants: T::Array[String],
|
123
119
|
should_verify: T::Boolean,
|
124
|
-
quiet: T::Boolean
|
120
|
+
quiet: T::Boolean,
|
121
|
+
verbose: T::Boolean
|
125
122
|
).void
|
126
123
|
end
|
127
|
-
def build_dsl(requested_constants, should_verify: false, quiet: false)
|
124
|
+
def build_dsl(requested_constants, should_verify: false, quiet: false, verbose: false)
|
128
125
|
load_application(eager_load: requested_constants.empty?)
|
126
|
+
abort_if_pending_migrations!
|
129
127
|
load_dsl_generators
|
130
128
|
|
131
129
|
if should_verify
|
@@ -140,20 +138,26 @@ module Tapioca
|
|
140
138
|
|
141
139
|
compiler = Compilers::DslCompiler.new(
|
142
140
|
requested_constants: constantize(requested_constants),
|
143
|
-
requested_generators: config.generators,
|
141
|
+
requested_generators: constantize_generators(config.generators),
|
142
|
+
excluded_generators: constantize_generators(config.exclude_generators),
|
144
143
|
error_handler: ->(error) {
|
145
144
|
say_error(error, :bold, :red)
|
146
145
|
}
|
147
146
|
)
|
148
147
|
|
149
148
|
compiler.run do |constant, contents|
|
150
|
-
constant_name =
|
149
|
+
constant_name = T.must(Reflection.name_of(constant))
|
150
|
+
|
151
|
+
if verbose && !quiet
|
152
|
+
say("Processing: ", [:yellow])
|
153
|
+
say(constant_name)
|
154
|
+
end
|
151
155
|
|
152
156
|
filename = compile_dsl_rbi(
|
153
157
|
constant_name,
|
154
158
|
contents,
|
155
159
|
outpath: outpath,
|
156
|
-
quiet: should_verify || quiet
|
160
|
+
quiet: should_verify || quiet && !verbose
|
157
161
|
)
|
158
162
|
|
159
163
|
if filename
|
@@ -174,8 +178,15 @@ module Tapioca
|
|
174
178
|
end
|
175
179
|
end
|
176
180
|
|
177
|
-
sig { void }
|
178
|
-
def sync_rbis_with_gemfile
|
181
|
+
sig { params(should_verify: T::Boolean).void }
|
182
|
+
def sync_rbis_with_gemfile(should_verify: false)
|
183
|
+
if should_verify
|
184
|
+
say("Checking for out-of-date RBIs...")
|
185
|
+
say("")
|
186
|
+
perform_sync_verification
|
187
|
+
return
|
188
|
+
end
|
189
|
+
|
179
190
|
anything_done = [
|
180
191
|
perform_removals,
|
181
192
|
perform_additions,
|
@@ -195,7 +206,7 @@ module Tapioca
|
|
195
206
|
|
196
207
|
EMPTY_RBI_COMMENT = <<~CONTENT
|
197
208
|
# THIS IS AN EMPTY RBI FILE.
|
198
|
-
# see https://github.com/Shopify/tapioca/
|
209
|
+
# see https://github.com/Shopify/tapioca/wiki/Manual-Gem-Requires
|
199
210
|
CONTENT
|
200
211
|
|
201
212
|
sig { returns(Gemfile) }
|
@@ -205,7 +216,7 @@ module Tapioca
|
|
205
216
|
|
206
217
|
sig { returns(Loader) }
|
207
218
|
def loader
|
208
|
-
@loader ||= Loader.new
|
219
|
+
@loader ||= Loader.new
|
209
220
|
end
|
210
221
|
|
211
222
|
sig { returns(Compilers::SymbolTableCompiler) }
|
@@ -217,7 +228,7 @@ module Tapioca
|
|
217
228
|
def require_gem_file
|
218
229
|
say("Requiring all gems to prepare for compiling... ")
|
219
230
|
begin
|
220
|
-
loader.load_bundle(config.prerequire, config.postrequire)
|
231
|
+
loader.load_bundle(bundle, config.prerequire, config.postrequire)
|
221
232
|
rescue LoadError => e
|
222
233
|
explain_failed_require(config.postrequire, e)
|
223
234
|
exit(1)
|
@@ -225,7 +236,7 @@ module Tapioca
|
|
225
236
|
say(" Done", :green)
|
226
237
|
unless bundle.missing_specs.empty?
|
227
238
|
say(" completed with missing specs: ")
|
228
|
-
say(bundle.missing_specs.join(
|
239
|
+
say(bundle.missing_specs.join(", "), :yellow)
|
229
240
|
end
|
230
241
|
puts
|
231
242
|
end
|
@@ -260,7 +271,7 @@ module Tapioca
|
|
260
271
|
def load_application(eager_load:)
|
261
272
|
say("Loading Rails application... ")
|
262
273
|
|
263
|
-
loader.
|
274
|
+
loader.load_rails_application(
|
264
275
|
environment_load: true,
|
265
276
|
eager_load: eager_load
|
266
277
|
)
|
@@ -285,11 +296,9 @@ module Tapioca
|
|
285
296
|
sig { params(constant_names: T::Array[String]).returns(T::Array[Module]) }
|
286
297
|
def constantize(constant_names)
|
287
298
|
constant_map = constant_names.map do |name|
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
[name, nil]
|
292
|
-
end
|
299
|
+
[name, Object.const_get(name)]
|
300
|
+
rescue NameError
|
301
|
+
[name, nil]
|
293
302
|
end.to_h
|
294
303
|
|
295
304
|
unprocessable_constants = constant_map.select { |_, v| v.nil? }
|
@@ -305,6 +314,32 @@ module Tapioca
|
|
305
314
|
constant_map.values
|
306
315
|
end
|
307
316
|
|
317
|
+
sig { params(generator_names: T::Array[String]).returns(T::Array[T.class_of(Compilers::Dsl::Base)]) }
|
318
|
+
def constantize_generators(generator_names)
|
319
|
+
generator_map = generator_names.map do |name|
|
320
|
+
# Try to find built-in tapioca generator first, then globally defined generator. The
|
321
|
+
# explicit `break` ensures the class is returned, not the `potential_name`.
|
322
|
+
generator_klass = ["Tapioca::Compilers::Dsl::#{name}", name].find do |potential_name|
|
323
|
+
break Object.const_get(potential_name)
|
324
|
+
rescue NameError
|
325
|
+
# Skip if we can't find generator by the potential name
|
326
|
+
end
|
327
|
+
|
328
|
+
[name, generator_klass]
|
329
|
+
end.to_h
|
330
|
+
|
331
|
+
unprocessable_generators = generator_map.select { |_, v| v.nil? }
|
332
|
+
unless unprocessable_generators.empty?
|
333
|
+
unprocessable_generators.each do |name, _|
|
334
|
+
say("Error: Cannot find generator '#{name}'", :red)
|
335
|
+
end
|
336
|
+
|
337
|
+
exit(1)
|
338
|
+
end
|
339
|
+
|
340
|
+
generator_map.values
|
341
|
+
end
|
342
|
+
|
308
343
|
sig { params(requested_constants: T::Array[String], path: Pathname).returns(T::Set[Pathname]) }
|
309
344
|
def existing_rbi_filenames(requested_constants, path: config.outpath)
|
310
345
|
filenames = if requested_constants.empty?
|
@@ -321,7 +356,7 @@ module Tapioca
|
|
321
356
|
sig { returns(T::Hash[String, String]) }
|
322
357
|
def existing_rbis
|
323
358
|
@existing_rbis ||= Pathname.glob((config.outpath / "*@*.rbi").to_s)
|
324
|
-
.map { |f| T.cast(f.basename(".*").to_s.split(
|
359
|
+
.map { |f| T.cast(f.basename(".*").to_s.split("@", 2), [String, String]) }
|
325
360
|
.to_h
|
326
361
|
end
|
327
362
|
|
@@ -335,7 +370,7 @@ module Tapioca
|
|
335
370
|
|
336
371
|
sig { params(constant_name: String).returns(Pathname) }
|
337
372
|
def dsl_rbi_filename(constant_name)
|
338
|
-
config.outpath / "#{constant_name
|
373
|
+
config.outpath / "#{underscore(constant_name)}.rbi"
|
339
374
|
end
|
340
375
|
|
341
376
|
sig { params(gem_name: String, version: String).returns(Pathname) }
|
@@ -456,7 +491,7 @@ module Tapioca
|
|
456
491
|
|
457
492
|
sig do
|
458
493
|
params(gem_names: T::Array[String])
|
459
|
-
.returns(T::Array[Gemfile::
|
494
|
+
.returns(T::Array[Gemfile::GemSpec])
|
460
495
|
end
|
461
496
|
def gems_to_generate(gem_names)
|
462
497
|
return bundle.dependencies if gem_names.empty?
|
@@ -483,10 +518,16 @@ module Tapioca
|
|
483
518
|
# typed: #{strictness}
|
484
519
|
SIGIL
|
485
520
|
|
486
|
-
|
521
|
+
if config.file_header
|
522
|
+
[statement, sigil].compact.join("\n").strip.concat("\n\n")
|
523
|
+
elsif sigil
|
524
|
+
sigil.strip.concat("\n\n")
|
525
|
+
else
|
526
|
+
""
|
527
|
+
end
|
487
528
|
end
|
488
529
|
|
489
|
-
sig { params(gem: Gemfile::
|
530
|
+
sig { params(gem: Gemfile::GemSpec).void }
|
490
531
|
def compile_gem_rbi(gem)
|
491
532
|
compiler = Compilers::SymbolTableCompiler.new
|
492
533
|
gem_name = set_color(gem.name, :yellow, :bold)
|
@@ -496,7 +537,7 @@ module Tapioca
|
|
496
537
|
rbi_body_content = compiler.compile(gem)
|
497
538
|
content = String.new
|
498
539
|
content << rbi_header(
|
499
|
-
"#{Config::DEFAULT_COMMAND}
|
540
|
+
"#{Config::DEFAULT_COMMAND} gem #{gem.name}",
|
500
541
|
reason: "types exported from the `#{gem.name}` gem",
|
501
542
|
strictness: strictness
|
502
543
|
)
|
@@ -525,7 +566,7 @@ module Tapioca
|
|
525
566
|
def compile_dsl_rbi(constant_name, contents, outpath: config.outpath, quiet: false)
|
526
567
|
return if contents.nil?
|
527
568
|
|
528
|
-
rbi_name = constant_name
|
569
|
+
rbi_name = underscore(constant_name) + ".rbi"
|
529
570
|
filename = outpath / rbi_name
|
530
571
|
|
531
572
|
out = String.new
|
@@ -598,11 +639,47 @@ module Tapioca
|
|
598
639
|
def perform_dsl_verification(dir)
|
599
640
|
diff = verify_dsl_rbi(tmp_dir: dir)
|
600
641
|
|
642
|
+
report_diff_and_exit_if_out_of_date(diff, "dsl")
|
643
|
+
ensure
|
644
|
+
FileUtils.remove_entry(dir)
|
645
|
+
end
|
646
|
+
|
647
|
+
sig { params(files: T::Set[Pathname]).void }
|
648
|
+
def purge_stale_dsl_rbi_files(files)
|
649
|
+
if files.any?
|
650
|
+
say("Removing stale RBI files...")
|
651
|
+
|
652
|
+
files.sort.each do |filename|
|
653
|
+
remove(filename)
|
654
|
+
end
|
655
|
+
say("")
|
656
|
+
end
|
657
|
+
end
|
658
|
+
|
659
|
+
sig { void }
|
660
|
+
def perform_sync_verification
|
661
|
+
diff = {}
|
662
|
+
|
663
|
+
removed_rbis.each do |gem_name|
|
664
|
+
filename = existing_rbi(gem_name)
|
665
|
+
diff[filename] = :removed
|
666
|
+
end
|
667
|
+
|
668
|
+
added_rbis.each do |gem_name|
|
669
|
+
filename = expected_rbi(gem_name)
|
670
|
+
diff[filename] = gem_rbi_exists?(gem_name) ? :changed : :added
|
671
|
+
end
|
672
|
+
|
673
|
+
report_diff_and_exit_if_out_of_date(diff, "gem")
|
674
|
+
end
|
675
|
+
|
676
|
+
sig { params(diff: T::Hash[String, Symbol], command: String).void }
|
677
|
+
def report_diff_and_exit_if_out_of_date(diff, command)
|
601
678
|
if diff.empty?
|
602
679
|
say("Nothing to do, all RBIs are up-to-date.")
|
603
680
|
else
|
604
681
|
say("RBI files are out-of-date. In your development environment, please run:", :green)
|
605
|
-
say(" `#{Config::DEFAULT_COMMAND}
|
682
|
+
say(" `#{Config::DEFAULT_COMMAND} #{command}`", [:green, :bold])
|
606
683
|
say("Once it is complete, be sure to commit and push any changes", :green)
|
607
684
|
|
608
685
|
say("")
|
@@ -614,20 +691,27 @@ module Tapioca
|
|
614
691
|
|
615
692
|
exit(1)
|
616
693
|
end
|
617
|
-
ensure
|
618
|
-
FileUtils.remove_entry(dir)
|
619
694
|
end
|
620
695
|
|
621
|
-
sig {
|
622
|
-
def
|
623
|
-
|
624
|
-
|
696
|
+
sig { void }
|
697
|
+
def abort_if_pending_migrations!
|
698
|
+
return unless File.exist?("config/application.rb")
|
699
|
+
return unless defined?(::Rake)
|
625
700
|
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
701
|
+
Rails.application.load_tasks
|
702
|
+
Rake::Task["db:abort_if_pending_migrations"].invoke if Rake::Task.task_defined?("db:abort_if_pending_migrations")
|
703
|
+
end
|
704
|
+
|
705
|
+
sig { params(class_name: String).returns(String) }
|
706
|
+
def underscore(class_name)
|
707
|
+
return class_name unless /[A-Z-]|::/.match?(class_name)
|
708
|
+
|
709
|
+
word = class_name.to_s.gsub("::", "/")
|
710
|
+
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
|
711
|
+
word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
|
712
|
+
word.tr!("-", "_")
|
713
|
+
word.downcase!
|
714
|
+
word
|
631
715
|
end
|
632
716
|
end
|
633
717
|
end
|