Module: RSpecTracer::CLI Private
- Defined in:
- lib/rspec_tracer/cli.rb,
lib/rspec_tracer/cli/doctor.rb,
lib/rspec_tracer/cli/explain.rb,
lib/rspec_tracer/cli/cache_info.rb,
lib/rspec_tracer/cli/cache_clear.rb,
lib/rspec_tracer/cli/report_open.rb,
lib/rspec_tracer/cli/blast_radius.rb,
lib/rspec_tracer/cli/snapshot_helpers.rb
Overview
This module is part of a private API. You should avoid using this module if possible, as it may be removed or be changed in the future.
Internal CLI -- see RSpecTracer for the user-facing surface.
Defined Under Namespace
Modules: BlastRadius, CacheClear, CacheInfo, Doctor, Explain, ReportOpen, SnapshotHelpers
Constant Summary collapse
- SUB_COMMANDS =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
Internal constant.
{ 'doctor' => 'Doctor', 'cache:info' => 'CacheInfo', 'cache:clear' => 'CacheClear', 'report:open' => 'ReportOpen', 'explain' => 'Explain', 'blast-radius' => 'BlastRadius' }.freeze
Class Method Summary collapse
-
.dispatch(args, stdout:, stderr:) ⇒ Object
private
Internal helper for the tracer pipeline.
-
.load_sub_command(klass_name) ⇒ Object
private
Internal helper for the tracer pipeline.
-
.load_tracer(stderr) ⇒ Boolean
private
Boot the tracer library for sub-command dispatch.
-
.print_top_level_help(stdout) ⇒ Object
private
Internal helper for the tracer pipeline.
-
.print_version(stdout) ⇒ Object
private
Internal helper for the tracer pipeline.
-
.run(argv, stdout: $stdout, stderr: $stderr) ⇒ Integer
private
CLI entry.
-
.unknown_sub_command(sub, stderr) ⇒ Object
private
Internal helper for the tracer pipeline.
-
.with_default_logger_out(stderr) ⇒ Object
private
Point Logger.default_out at the CLI's stderr for the duration of library boot + sub-command dispatch, restoring the previous value afterwards.
-
.with_logger_on(stderr) ⇒ Object
private
Bind the tracer's internal logger to the CLI's stderr for the duration of sub-command dispatch, restoring the previous logger afterwards.
Class Method Details
.dispatch(args, stdout:, stderr:) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Internal helper for the tracer pipeline.
155 156 157 158 159 160 161 162 |
# File 'lib/rspec_tracer/cli.rb', line 155 def self.dispatch(args, stdout:, stderr:) sub = args.shift klass_name = SUB_COMMANDS[sub] return unknown_sub_command(sub, stderr) if klass_name.nil? load_sub_command(klass_name) RSpecTracer::CLI.const_get(klass_name).run(args, stdout: stdout, stderr: stderr) end |
.load_sub_command(klass_name) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Internal helper for the tracer pipeline.
208 209 210 211 212 213 214 215 216 217 |
# File 'lib/rspec_tracer/cli.rb', line 208 def self.load_sub_command(klass_name) filename = klass_name.gsub(/([A-Z])/) { |m| "_#{m.downcase}" }.sub(/^_/, '') require_relative "cli/#{filename}" rescue LoadError => e # LoadError isn't a StandardError, so the outer `rescue StandardError` # in `.run` wouldn't catch it. Re-raise as StandardError so the dispatch # path stays uniform: any sub-command resolution failure prints # `rspec-tracer: <class>: <message>` and exits 1. raise StandardError, "could not load sub-command #{klass_name.inspect}: #{e.}" end |
.load_tracer(stderr) ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Boot the tracer library for sub-command dispatch. Loading
rspec_tracer pulls in docile (used by
Configuration#configure), the modules sub-commands rely on,
AND the project's .rspec-tracer / ~/.rspec-tracer configs
(arbitrary user Ruby, loaded by load_config). It does NOT
call RSpecTracer.start - the engine stays inert until a user
invokes it explicitly.
A raising config must not crash the binary with a backtrace:
rescue ScriptError as well as StandardError because a syntax
error in .rspec-tracer raises SyntaxError, which is not a
StandardError and would sail past run's rescue.
89 90 91 92 93 94 95 |
# File 'lib/rspec_tracer/cli.rb', line 89 def self.load_tracer(stderr) require 'rspec_tracer' true rescue ScriptError, StandardError => e stderr.puts "rspec-tracer: could not load configuration (.rspec-tracer): #{e.class}: #{e.}" false end |
.print_top_level_help(stdout) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Internal helper for the tracer pipeline.
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
# File 'lib/rspec_tracer/cli.rb', line 174 def self.print_top_level_help(stdout) stdout.puts <<~HELP Usage: rspec-tracer <sub-command> [options] Sub-commands: doctor Diagnose rspec-tracer config + environment. cache:info Show cache size, last run timestamp, and example counts. cache:clear Remove cache, coverage, and report directories. report:open Open the HTML report in the default browser. explain <id> Show why an example is scheduled to run or skip. blast-radius <f> Show which examples a file change would re-run. Options: -h, --help Print this help message. -v, --version Print rspec-tracer version. Run `rspec-tracer <sub-command> --help` for sub-command options. HELP 0 end |
.print_version(stdout) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Internal helper for the tracer pipeline.
197 198 199 200 201 202 203 204 |
# File 'lib/rspec_tracer/cli.rb', line 197 def self.print_version(stdout) # Only the version constant - not the full library, whose boot # would `load` the project config and could raise before the # version ever printed. require 'rspec_tracer/version' stdout.puts "rspec-tracer #{RSpecTracer::VERSION}" 0 end |
.run(argv, stdout: $stdout, stderr: $stderr) ⇒ Integer
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
CLI entry. Called by bin/rspec-tracer with ARGV. Wraps every
sub-command in a top-level rescue so the binary always exits
with a meaningful integer status (0 / 1) instead of a backtrace.
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/rspec_tracer/cli.rb', line 50 def self.run(argv, stdout: $stdout, stderr: $stderr) args = argv.dup return print_top_level_help(stdout) if args.empty? || %w[-h --help help].include?(args.first) return print_version(stdout) if %w[-v --version].include?(args.first) with_default_logger_out(stderr) do if load_tracer(stderr) with_logger_on(stderr) { dispatch(args, stdout: stdout, stderr: stderr) } else 1 end end rescue Errno::EPIPE # A downstream pipe consumer (`rspec-tracer ... | head`) closed # early -- routine in shell pipelines, not a failure. Exit 0 # without printing: writing to stderr could raise EPIPE again # when both streams share the closed pipe. 0 rescue StandardError => e stderr.puts "rspec-tracer: #{e.class}: #{e.}" 1 end |
.unknown_sub_command(sub, stderr) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Internal helper for the tracer pipeline.
166 167 168 169 170 |
# File 'lib/rspec_tracer/cli.rb', line 166 def self.unknown_sub_command(sub, stderr) stderr.puts "rspec-tracer: unknown sub-command #{sub.inspect}" stderr.puts " available: #{SUB_COMMANDS.keys.join(', ')}" 1 end |
.with_default_logger_out(stderr) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Point Logger.default_out at the CLI's stderr for
the duration of library boot + sub-command dispatch, restoring
the previous value afterwards. This must wrap load_tracer:
booting the library loads the project + global .rspec-tracer
configs, and those can write through the logger BEFORE
with_logger_on ever runs -- the 1.x-compat deprecation shims
(reports_s3_path, use_local_aws) fire a one-time
logger.warn at first use, and in a fresh CLI process
"one-time" means every invocation. The logger's default
destination is stdout -- the stream machine consumers parse --
so that warning would land ahead of e.g. the blast-radius --json document and break | jq. With the default rebound,
every logger constructed during the window binds to stderr,
including loggers recreated mid-load when a config sets
log_level (which resets the memoized instance) after a
deprecated DSL call. Normal rspec runs (whose users expect
stdout logs) never enter this path and stay untouched.
115 116 117 118 119 120 121 |
# File 'lib/rspec_tracer/cli.rb', line 115 def self.with_default_logger_out(stderr) previous = RSpecTracer::Logger.default_out RSpecTracer::Logger.default_out = stderr yield ensure RSpecTracer::Logger.default_out = previous end |
.with_logger_on(stderr) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Bind the tracer's internal logger to the CLI's stderr for the
duration of sub-command dispatch, restoring the previous logger
afterwards. Backend diagnostics run through RSpecTracer.logger
(e.g. Storage::Backend.build's sqlite-unavailable
fallback warning, JsonBackend's schema-mismatch info line).
with_default_logger_out already redirects loggers constructed
during the CLI window; this layer additionally rebinds a logger
that was memoized into RSpecTracer.@logger BEFORE run was
called (possible for in-process callers -- the spec suite, or a
user embedding the CLI -- where the library booted earlier with
the stdout default). Sub-commands like blast-radius --json
promise exactly one JSON document on stdout, so a diagnostic
line printed ahead of it breaks | jq; together the two layers
keep ALL diagnostics on stderr alongside the CLI's own messages.
RSpecTracer.logger memoizes into @logger
(Configuration#logger); the ivar is seeded directly because
Configuration deliberately exposes no logger setter -- adding
one would leak it into the user-facing configure DSL surface.
143 144 145 146 147 148 149 150 151 |
# File 'lib/rspec_tracer/cli.rb', line 143 def self.with_logger_on(stderr) previous = RSpecTracer.instance_variable_get(:@logger) RSpecTracer.instance_variable_set( :@logger, RSpecTracer::Logger.new(RSpecTracer.log_level, out: stderr) ) yield ensure RSpecTracer.instance_variable_set(:@logger, previous) end |