lib/tapioca/generator.rb in tapioca-0.4.17 vs lib/tapioca/generator.rb in tapioca-0.4.18
- old
+ new
@@ -61,11 +61,11 @@
# it with the new one found in the client code and remove the old ones.
File.delete(requires_path) if File.exist?(requires_path)
content = String.new
content << rbi_header(
- config.generate_command,
+ "#{Config::DEFAULT_COMMAND} require",
reason: "explicit gem requires",
strictness: "false"
)
content << rb_string
@@ -74,12 +74,12 @@
File.write(requires_path, content)
say("Done", :green)
say("All requires from this application have been written to #{name}.", [:green, :bold])
- cmd = set_color("tapioca sync", :yellow, :bold)
- say("Please review changes and commit them, then run #{cmd}.", [:green, :bold])
+ cmd = set_color("#{Config::DEFAULT_COMMAND} sync", :yellow, :bold)
+ say("Please review changes and commit them, then run `#{cmd}`.", [:green, :bold])
end
sig { void }
def build_todos
todos_path = config.todos_path
@@ -97,11 +97,11 @@
return
end
content = String.new
content << rbi_header(
- config.generate_command,
+ "#{Config::DEFAULT_COMMAND} todo",
reason: "unresolved constants",
strictness: "false"
)
content << rbi_string
content << "\n"
@@ -114,47 +114,54 @@
say("All unresolved constants have been written to #{name}.", [:green, :bold])
say("Please review changes and commit them.", [:green, :bold])
end
- sig { params(requested_constants: T::Array[String]).void }
- def build_dsl(requested_constants)
+ sig do
+ params(
+ requested_constants: T::Array[String],
+ should_verify: T::Boolean,
+ ).void
+ end
+ def build_dsl(requested_constants, should_verify: false)
load_application(eager_load: requested_constants.empty?)
load_dsl_generators
- rbi_files_to_purge = existing_rbi_filenames(requested_constants)
-
- say("Compiling DSL RBI files...")
+ if should_verify
+ say("Checking for out-of-date RBIs...")
+ else
+ say("Compiling DSL RBI files...")
+ end
say("")
+ outpath = should_verify ? Dir.mktmpdir : config.outpath
+ rbi_files_to_purge = existing_rbi_filenames(requested_constants)
+
compiler = Compilers::DslCompiler.new(
requested_constants: constantize(requested_constants),
requested_generators: config.generators,
error_handler: ->(error) {
say_error(error, :bold, :red)
}
)
compiler.run do |constant, contents|
- filename = compile_dsl_rbi(constant, contents)
+ filename = compile_dsl_rbi(constant, contents, outpath: Pathname.new(outpath))
rbi_files_to_purge.delete(filename) if filename
end
+ say("")
- unless rbi_files_to_purge.empty?
- say("")
- say("Removing stale RBI files...")
+ if should_verify
+ perform_dsl_verification(outpath)
+ else
+ purge_stale_dsl_rbi_files(rbi_files_to_purge)
- rbi_files_to_purge.sort.each do |filename|
- remove(filename)
- end
- end
+ say("Done", :green)
- say("")
- say("Done", :green)
-
- say("All operations performed in working directory.", [:green, :bold])
- say("Please review changes and commit them.", [:green, :bold])
+ say("All operations performed in working directory.", [:green, :bold])
+ say("Please review changes and commit them.", [:green, :bold])
+ end
end
sig { void }
def sync_rbis_with_gemfile
anything_done = [
@@ -216,11 +223,11 @@
say_error("\n\nLoadError: #{error}", :bold, :red)
say_error("\nTapioca could not load all the gems required by your application.", :yellow)
say_error("If you populated ", :yellow)
say_error("#{file} ", :bold, :blue)
say_error("with ", :yellow)
- say_error("tapioca require", :bold, :blue)
+ say_error("`#{Config::DEFAULT_COMMAND} require`", :bold, :blue)
say_error("you should probably review it and remove the faulty line.", :yellow)
end
sig do
params(
@@ -284,14 +291,14 @@
end
constant_map.values
end
- sig { params(requested_constants: T::Array[String]).returns(T::Set[Pathname]) }
- def existing_rbi_filenames(requested_constants)
+ sig { params(requested_constants: T::Array[String], path: Pathname).returns(T::Set[Pathname]) }
+ def existing_rbi_filenames(requested_constants, path: config.outpath)
filenames = if requested_constants.empty?
- Pathname.glob(config.outpath / "**/*.rbi")
+ Pathname.glob(path / "**/*.rbi")
else
requested_constants.map do |constant_name|
dsl_rbi_filename(constant_name)
end
end
@@ -475,11 +482,11 @@
strictness = config.typed_overrides[gem.name] || "true"
rbi_body_content = compiler.compile(gem)
content = String.new
content << rbi_header(
- config.generate_command,
+ "#{Config::DEFAULT_COMMAND} sync",
reason: "types exported from the `#{gem.name}` gem",
strictness: strictness
)
FileUtils.mkdir_p(config.outdir)
@@ -492,35 +499,82 @@
content << rbi_body_content
say("Done", :green)
end
File.write(filename.to_s, content)
- Pathname.glob((config.outpath / "#{gem.name}@*.rbi").to_s) do |file|
+ T.unsafe(Pathname).glob((config.outpath / "#{gem.name}@*.rbi").to_s) do |file|
remove(file) unless file.basename.to_s == gem.rbi_file_name
end
end
- sig { params(constant: Module, contents: String).returns(T.nilable(Pathname)) }
- def compile_dsl_rbi(constant, contents)
+ sig { params(constant: Module, contents: String, outpath: Pathname).returns(T.nilable(Pathname)) }
+ def compile_dsl_rbi(constant, contents, outpath: config.outpath)
return if contents.nil?
- command = format(config.generate_command, constant.name)
constant_name = Module.instance_method(:name).bind(constant).call
rbi_name = constant_name.underscore + ".rbi"
- filename = config.outpath / rbi_name
+ filename = outpath / rbi_name
out = String.new
out << rbi_header(
- command,
+ "#{Config::DEFAULT_COMMAND} dsl #{constant_name}",
reason: "dynamic methods in `#{constant.name}`"
)
out << contents
FileUtils.mkdir_p(File.dirname(filename))
File.write(filename, out)
say("Wrote: ", [:green])
say(filename)
filename
+ end
+
+ sig { params(tmp_dir: Pathname).returns(T.nilable(String)) }
+ def verify_dsl_rbi(tmp_dir:)
+ existing_rbis = existing_rbi_filenames([]).sort
+ new_rbis = existing_rbi_filenames([], path: tmp_dir).grep_v(/gem|shim/).sort
+
+ return "New file(s) introduced." if existing_rbis.length != new_rbis.length
+
+ desynced_files = []
+
+ (0..existing_rbis.length - 1).each do |i|
+ desynced_files << new_rbis[i] unless FileUtils.identical?(existing_rbis[i], new_rbis[i])
+ end
+
+ unless desynced_files.empty?
+ filenames = desynced_files.map { |f| f.to_s.sub!(tmp_dir.to_s, "sorbet/rbi/dsl") }.join("\n - ")
+
+ return "File(s) updated:\n - #{filenames}"
+ end
+
+ nil
+ end
+
+ sig { params(dir: String).void }
+ def perform_dsl_verification(dir)
+ if (error = verify_dsl_rbi(tmp_dir: Pathname.new(dir)))
+ say("RBI files are out-of-date, please run `#{Config::DEFAULT_COMMAND} dsl` to update.")
+ say("Reason: ", [:red])
+ say(error)
+ exit(1)
+ else
+ say("Nothing to do, all RBIs are up-to-date.")
+ end
+ ensure
+ FileUtils.remove_entry(dir)
+ end
+
+ sig { params(files: T::Set[Pathname]).void }
+ def purge_stale_dsl_rbi_files(files)
+ if files.any?
+ say("Removing stale RBI files...")
+
+ files.sort.each do |filename|
+ remove(filename)
+ end
+ say("")
+ end
end
end
end