diff --git a/README.md b/README.md index 99529ea13..8d9d7fa53 100644 --- a/README.md +++ b/README.md @@ -500,6 +500,7 @@ Options: # Default: . [--halt-upon-load-error], [--no-halt-upon-load-error], [--skip-halt-upon-load-error] # Halt upon a load error while loading the Rails application # Default: true + [--skip-constant=constant [constant ...]] # Skip the given application constant(s) when generating RBIs -c, [--config=] # Path to the Tapioca configuration file # Default: sorbet/tapioca/config.yml -V, [--verbose], [--no-verbose], [--skip-verbose] # Verbose output for debugging purposes @@ -933,6 +934,7 @@ dsl: list_compilers: false app_root: "." halt_upon_load_error: true + skip_constant: [] gem: outdir: sorbet/rbi/gems file_header: true diff --git a/lib/tapioca/cli.rb b/lib/tapioca/cli.rb index d08ea70bf..e9690604b 100644 --- a/lib/tapioca/cli.rb +++ b/lib/tapioca/cli.rb @@ -135,6 +135,11 @@ def todo type: :boolean, desc: "Halt upon a load error while loading the Rails application", default: true + option :skip_constant, + type: :array, + banner: "constant [constant ...]", + desc: "Skip the given application constant(s) when generating RBIs", + default: [] def dsl(*constant_or_paths) set_environment(options) @@ -149,6 +154,7 @@ def dsl(*constant_or_paths) exclude: options[:exclude], file_header: options[:file_header], tapioca_path: TAPIOCA_DIR, + skip_constant: options[:skip_constant], quiet: options[:quiet], verbose: options[:verbose], number_of_workers: options[:workers], diff --git a/lib/tapioca/commands/abstract_dsl.rb b/lib/tapioca/commands/abstract_dsl.rb index e5ad59c86..0abe77ff6 100644 --- a/lib/tapioca/commands/abstract_dsl.rb +++ b/lib/tapioca/commands/abstract_dsl.rb @@ -18,6 +18,7 @@ class AbstractDsl < CommandWithoutTracker exclude: T::Array[String], file_header: T::Boolean, tapioca_path: String, + skip_constant: T::Array[String], quiet: T::Boolean, verbose: T::Boolean, number_of_workers: T.nilable(Integer), @@ -36,6 +37,7 @@ def initialize( exclude:, file_header:, tapioca_path:, + skip_constant:, quiet: false, verbose: false, number_of_workers: nil, @@ -60,6 +62,7 @@ def initialize( @rbi_formatter = rbi_formatter @app_root = app_root @halt_upon_load_error = halt_upon_load_error + @skip_constant = skip_constant super() end @@ -124,6 +127,7 @@ def create_pipeline error_handler: ->(error) { say_error(error, :bold, :red) }, + skipped_constants: constantize(@skip_constant), number_of_workers: @number_of_workers, ) end diff --git a/lib/tapioca/dsl/pipeline.rb b/lib/tapioca/dsl/pipeline.rb index 9dec39933..0bcaf52db 100644 --- a/lib/tapioca/dsl/pipeline.rb +++ b/lib/tapioca/dsl/pipeline.rb @@ -15,6 +15,9 @@ class Pipeline sig { returns(T::Array[Pathname]) } attr_reader :requested_paths + sig { returns(T::Array[Module]) } + attr_reader :skipped_constants + sig { returns(T.proc.params(error: String).void) } attr_reader :error_handler @@ -28,6 +31,7 @@ class Pipeline requested_compilers: T::Array[T.class_of(Compiler)], excluded_compilers: T::Array[T.class_of(Compiler)], error_handler: T.proc.params(error: String).void, + skipped_constants: T::Array[Module], number_of_workers: T.nilable(Integer), ).void end @@ -37,6 +41,7 @@ def initialize( requested_compilers: [], excluded_compilers: [], error_handler: $stderr.method(:puts).to_proc, + skipped_constants: [], number_of_workers: nil ) @active_compilers = T.let( @@ -46,6 +51,7 @@ def initialize( @requested_constants = requested_constants @requested_paths = requested_paths @error_handler = error_handler + @skipped_constants = skipped_constants @number_of_workers = number_of_workers @errors = T.let([], T::Array[String]) end @@ -56,7 +62,7 @@ def initialize( ).returns(T::Array[T.type_parameter(:T)]) end def run(&blk) - constants_to_process = gather_constants(requested_constants, requested_paths) + constants_to_process = gather_constants(requested_constants, requested_paths, skipped_constants) .select { |c| Module === c } # Filter value constants out .sort_by! { |c| T.must(Runtime::Reflection.name_of(c)) } @@ -128,13 +134,20 @@ def gather_active_compilers(requested_compilers, excluded_compilers) active_compilers end - sig { params(requested_constants: T::Array[Module], requested_paths: T::Array[Pathname]).returns(T::Set[Module]) } - def gather_constants(requested_constants, requested_paths) + sig do + params( + requested_constants: T::Array[Module], + requested_paths: T::Array[Pathname], + skipped_constants: T::Array[Module], + ).returns(T::Set[Module]) + end + def gather_constants(requested_constants, requested_paths, skipped_constants) constants = Set.new.compare_by_identity active_compilers.each do |compiler| constants.merge(compiler.processable_constants) end constants = filter_anonymous_and_reloaded_constants(constants) + constants -= skipped_constants constants &= requested_constants unless requested_constants.empty? && requested_paths.empty? constants diff --git a/spec/tapioca/cli/dsl_spec.rb b/spec/tapioca/cli/dsl_spec.rb index be6304114..ea6cc145b 100644 --- a/spec/tapioca/cli/dsl_spec.rb +++ b/spec/tapioca/cli/dsl_spec.rb @@ -1371,6 +1371,67 @@ def body=(body); end RBI end + it "must respect `skip_constant` option" do + @project.write!("lib/post.rb", <<~RB) + class Post; end + RB + + @project.write!("lib/job.rb", <<~RB) + class Job; end + RB + + @project.write!("lib/compilers/foo/compiler.rb", <<~RB) + require "job" + require "post" + + module Foo + class Compiler < Tapioca::Dsl::Compiler + extend T::Sig + + ConstantType = type_member { { fixed: Module } } + + sig { override.void } + def decorate + root.create_path(constant) do |job| + job.create_module("FooModule") + end + end + + sig { override.returns(T::Enumerable[Module]) } + def self.gather_constants + [Post, Job] + end + end + end + RB + + result = @project.tapioca("dsl --skip-constant Job") + + assert_stdout_equals(<<~OUT, result) + Loading DSL extension classes... Done + Loading Rails application... Done + Loading DSL compiler classes... Done + Compiling DSL RBI files... + + create sorbet/rbi/dsl/post.rbi + + Done + + Checking generated RBI files... Done + No errors found + + All operations performed in working directory. + Please review changes and commit them. + OUT + + assert_empty_stderr(result) + + refute_project_file_exist("sorbet/rbi/dsl/job.rbi") + assert_project_file_exist("sorbet/rbi/dsl/post.rbi") + + assert_success_status(result) + end + describe "pending migrations" do before do @project.write!("db/migrate/202001010000_create_articles.rb", <<~RB)