Module: SimpleCov::CLI::Dotfile
Overview
Loads a project's .simplecov config file purely to read configuration
from it, with SimpleCov.start and the at_exit hook installer neutered so
the load doesn't trigger coverage tracking. Used by the CLI to default
paths to whatever the project's dotfile declares, without making every
read-only subcommand pay for actually starting Coverage.
Constant Summary collapse
- NEUTERED =
A constant rather than a block written at the call site, where an empty block and one answering nil are the same thing to every caller and so to every test.
proc {}
Instance Method Summary collapse
-
#baseline_file ⇒ Object
Read from
.simplecovthe waycoverage_diris, so a project that moved its baseline doesn't need--baselineon every ratchet. - #coverage_dir ⇒ Object
-
#find ⇒ Object
The nearest
.simplecovat or above the working directory, or nil, which is what the walk itself answers once it breaks at the filesystem root. -
#load_simplecov ⇒ Object
The deferred require exists for a standalone CLI process that has not loaded simplecov yet.
-
#load_with_start_neutered(path) ⇒ Object
Load
pathwithSimpleCov.startand the at_exit installer turned into no-ops, so a project whose dotfile callsSimpleCov.startdoesn't trigger Coverage just because we asked forcoverage_dir. -
#production_coverage ⇒ Object
Nil when there is no dotfile, the dotfile names no store, or it cannot be read: unlike the other reads there is no sensible fallback path, and the command's own missing-store error is the right answer.
- #read_baseline_file_from(dotfile) ⇒ Object
-
#read_from(dotfile) ⇒ Object
Snapshot and restore
SimpleCov.coverage_dirso we don't quietly clobber it in a host process that's already configured. - #read_production_coverage_from(dotfile) ⇒ Object
- #silence_verbose ⇒ Object
- #with_simplecov_loaded ⇒ Object
Instance Method Details
#baseline_file ⇒ Object
Read from .simplecov the way coverage_dir is, so a project that moved
its baseline doesn't need --baseline on every ratchet.
30 31 32 33 34 35 36 37 38 |
# File 'lib/simplecov/cli/dotfile.rb', line 30 def baseline_file dotfile = find return Baseline::DEFAULT_FILENAME unless dotfile with_simplecov_loaded { read_baseline_file_from(dotfile) } rescue ScriptError, StandardError => e warn "simplecov: failed to read baseline_file from #{dotfile}: #{e.class}: #{e}" Baseline::DEFAULT_FILENAME end |
#coverage_dir ⇒ Object
15 16 17 18 19 20 21 22 23 24 25 26 |
# File 'lib/simplecov/cli/dotfile.rb', line 15 def coverage_dir dotfile = find return "coverage" unless dotfile with_simplecov_loaded { read_from(dotfile) } rescue ScriptError, StandardError => e # ScriptError covers the SyntaxError a malformed dotfile raises from # `load` (it is not a StandardError) along with LoadError; StandardError # covers the rest. warn "simplecov: failed to read coverage_dir from #{dotfile}: #{e.class}: #{e}" "coverage" end |
#find ⇒ Object
The nearest .simplecov at or above the working directory, or nil, which
is what the walk itself answers once it breaks at the filesystem root.
88 89 90 91 92 93 94 95 96 97 |
# File 'lib/simplecov/cli/dotfile.rb', line 88 def find dir = Pathname.new(Dir.pwd) loop do candidate = dir.join(".simplecov") return candidate.to_s if candidate.exist? break if dir.root? dir = dir.parent end end |
#load_simplecov ⇒ Object
The deferred require exists for a standalone CLI process that has not loaded simplecov yet. It must happen inside the env guard above, so a first load here skips the default profile chain.
119 120 121 |
# File 'lib/simplecov/cli/dotfile.rb', line 119 def load_simplecov require "simplecov" end |
#load_with_start_neutered(path) ⇒ Object
Load path with SimpleCov.start and the at_exit installer turned into
no-ops, so a project whose dotfile calls SimpleCov.start doesn't
trigger Coverage just because we asked for coverage_dir. Config
inside any SimpleCov.start { ... } block still runs.
132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/simplecov/cli/dotfile.rb', line 132 def load_with_start_neutered(path) klass = SimpleCov.singleton_class names = %i[start_tracking install_at_exit_hook] stash = names.to_h { |name| [name, klass.instance_method(name)] } #: Hash[Symbol, UnboundMethod] # define_method over an existing method emits a "method redefined" # warning under $VERBOSE; the override and restore are intentional. silence_verbose { names.each { |name| klass.define_method(name, &(_ = NEUTERED)) } } load path ensure # @type var stash: Hash[Symbol, UnboundMethod] # @type var klass: Class silence_verbose { stash.each { |name, method| klass.define_method(name, method) } } end |
#production_coverage ⇒ Object
Nil when there is no dotfile, the dotfile names no store, or it cannot be read: unlike the other reads there is no sensible fallback path, and the command's own missing-store error is the right answer.
43 44 45 46 47 48 49 50 51 |
# File 'lib/simplecov/cli/dotfile.rb', line 43 def production_coverage dotfile = find return nil unless dotfile with_simplecov_loaded { read_production_coverage_from(dotfile) } rescue ScriptError, StandardError => e # `warn` answers nil, which is this reader's whole fallback. warn "simplecov: failed to read production_coverage from #{dotfile}: #{e.class}: #{e}" end |
#read_baseline_file_from(dotfile) ⇒ Object
62 63 64 65 66 67 68 69 |
# File 'lib/simplecov/cli/dotfile.rb', line 62 def read_baseline_file_from(dotfile) snapshot = SimpleCov.instance_variable_get(:@baseline_file) #: String? load_with_start_neutered(dotfile) SimpleCov.baseline_file ensure # @type var snapshot: String? SimpleCov.instance_variable_set(:@baseline_file, snapshot) end |
#read_from(dotfile) ⇒ Object
Snapshot and restore SimpleCov.coverage_dir so we don't quietly clobber
it in a host process that's already configured. The snapshot is
intentionally narrow: a dotfile can still mutate other configuration
via SimpleCov.configure or SimpleCov.start { ... } blocks.
75 76 77 78 79 80 81 82 83 84 |
# File 'lib/simplecov/cli/dotfile.rb', line 75 def read_from(dotfile) snapshot = SimpleCov.instance_variable_get(:@coverage_dir) #: String? load_with_start_neutered(dotfile) SimpleCov.coverage_dir ensure # Restored even when the load raises, so a bad dotfile doesn't leave a # host process's configured dir clobbered. # @type var snapshot: String? SimpleCov.instance_variable_set(:@coverage_dir, snapshot) end |
#read_production_coverage_from(dotfile) ⇒ Object
53 54 55 56 57 58 59 60 |
# File 'lib/simplecov/cli/dotfile.rb', line 53 def read_production_coverage_from(dotfile) snapshot = SimpleCov.instance_variable_get(:@production_coverage) #: String? load_with_start_neutered(dotfile) SimpleCov.production_coverage ensure # @type var snapshot: String? SimpleCov.instance_variable_set(:@production_coverage, snapshot) end |
#silence_verbose ⇒ Object
146 147 148 149 150 151 152 153 |
# File 'lib/simplecov/cli/dotfile.rb', line 146 def silence_verbose previous = $VERBOSE #: bool? $VERBOSE = nil yield ensure # @type var previous: bool? $VERBOSE = previous end |
#with_simplecov_loaded ⇒ Object
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/simplecov/cli/dotfile.rb', line 99 def with_simplecov_loaded previous_no_defaults = ENV.fetch("SIMPLECOV_NO_DEFAULTS", nil) #: String? previous_cli = ENV.fetch("SIMPLECOV_CLI", nil) #: String? ENV["SIMPLECOV_NO_DEFAULTS"] = "1" # SIMPLECOV_CLI lets a project's `.simplecov` opt some config into # CLI-only behavior: simplecov itself sets `coverage_dir` to the dogfood # path here but skips that for descendants. ENV["SIMPLECOV_CLI"] = "1" load_simplecov yield ensure # @type var previous_no_defaults: String? # @type var previous_cli: String? ENV["SIMPLECOV_NO_DEFAULTS"] = previous_no_defaults ENV["SIMPLECOV_CLI"] = previous_cli end |