Class: Mutineer::Config
- Inherits:
-
Struct
- Object
- Struct
- Mutineer::Config
- Defined in:
- lib/mutineer/config.rb
Overview
Plain run configuration, populated by the CLI (or directly by the
integration test). operators nil means "all default operators";
threshold 0.0 means the CI gate is off (spec §10).
M5 adds: jobs (parallel workers), format (human|json), output (report file), strategy (reload|redefine), require_paths (extra files to load). Config loading and the CLI > file > default precedence merge live here (KTD3/KTD4).
Boot mode adds: boot (a file to require ONCE in the parent so the app env — e.g. Rails — is booted before forking; sources are then NOT manually required) and rails (sugar: defaults boot to config/environment and strategy to redefine, and reconnects ActiveRecord per fork).
Constant Summary collapse
- CONFIG_FILE =
Config file name.
".mutineer.yml"- KNOWN_KEYS =
Keys accepted in .mutineer.yml (R7).
requiremaps to the :require_paths field. %w[operators jobs threshold only require boot rails since framework verbose ignore baseline fail_fast test_command daemon].freeze
Instance Attribute Summary collapse
-
#baseline ⇒ Object
Returns the value of attribute baseline.
-
#baseline_epsilon ⇒ Object
Returns the value of attribute baseline_epsilon.
-
#boot ⇒ Object
Returns the value of attribute boot.
-
#cache_dir ⇒ Object
Returns the value of attribute cache_dir.
-
#daemon ⇒ Object
Returns the value of attribute daemon.
-
#daemon_timeout ⇒ Object
Returns the value of attribute daemon_timeout.
-
#dry_run ⇒ Object
Returns the value of attribute dry_run.
-
#fail_fast ⇒ Object
Returns the value of attribute fail_fast.
-
#format ⇒ Object
Returns the value of attribute format.
-
#framework ⇒ Object
Returns the value of attribute framework.
-
#ignore ⇒ Object
Returns the value of attribute ignore.
-
#jobs ⇒ Object
Returns the value of attribute jobs.
-
#load_paths ⇒ Object
Returns the value of attribute load_paths.
-
#only ⇒ Object
Returns the value of attribute only.
-
#operators ⇒ Object
Returns the value of attribute operators.
-
#output ⇒ Object
Returns the value of attribute output.
-
#project_root ⇒ Object
Returns the value of attribute project_root.
-
#rails ⇒ Object
Returns the value of attribute rails.
-
#require_paths ⇒ Object
Returns the value of attribute require_paths.
-
#since ⇒ Object
Returns the value of attribute since.
-
#sources ⇒ Object
Returns the value of attribute sources.
-
#strategy ⇒ Object
Returns the value of attribute strategy.
-
#test_command ⇒ Object
Returns the value of attribute test_command.
-
#tests ⇒ Object
Returns the value of attribute tests.
-
#threshold ⇒ Object
Returns the value of attribute threshold.
-
#verbose ⇒ Object
Returns the value of attribute verbose.
Class Method Summary collapse
-
.coerce(known_key, value, file_name) ⇒ Object
Coerces a config value to its target type.
-
.detect_framework(tests) ⇒ String
Pick rspec when a MAJORITY of the given test files end with _spec.rb; otherwise minitest.
-
.field_for(known_key) ⇒ Symbol
Maps a config key to its Struct field.
-
.filter_operators(names, file_name) ⇒ Array<String>
private
Drop (with a warning) operator names the registry doesn't know (R7).
-
.find_file(start = Dir.pwd, home = File.expand_path("~")) ⇒ Object
Walk from
starttowardhome, returning the first .mutineer.yml path found or nil. -
.from_file(path) ⇒ Object
Parse a .mutineer.yml into a symbol-keyed hash of recognized keys.
-
.resolve(cli_opts, file_hash, explicit) ⇒ Object
Apply precedence (KTD3): start from the CLI-provided values, then fill in a config-file value only for keys the user did NOT type on the command line.
Instance Method Summary collapse
-
#initialize(**kwargs) ⇒ Config
constructor
A new instance of Config.
Constructor Details
#initialize(**kwargs) ⇒ Config
Returns a new instance of Config.
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/mutineer/config.rb', line 39 def initialize(**kwargs) super self.sources ||= [] self.tests ||= [] self.threshold ||= 0.0 self.dry_run ||= false self.cache_dir ||= ".mutineer" self.project_root ||= Dir.pwd self.load_paths ||= ["lib"] self.jobs ||= Etc.nprocessors self.format ||= "human" self.strategy ||= "reload" self.require_paths ||= [] self.rails = false if rails.nil? self.verbose = false if verbose.nil? self.ignore ||= [] self.baseline_epsilon ||= 0.0 self.fail_fast = false if fail_fast.nil? self.daemon = false if daemon.nil? end |
Instance Attribute Details
#baseline ⇒ Object
Returns the value of attribute baseline
23 24 25 |
# File 'lib/mutineer/config.rb', line 23 def baseline @baseline end |
#baseline_epsilon ⇒ Object
Returns the value of attribute baseline_epsilon
23 24 25 |
# File 'lib/mutineer/config.rb', line 23 def baseline_epsilon @baseline_epsilon end |
#boot ⇒ Object
Returns the value of attribute boot
23 24 25 |
# File 'lib/mutineer/config.rb', line 23 def boot @boot end |
#cache_dir ⇒ Object
Returns the value of attribute cache_dir
23 24 25 |
# File 'lib/mutineer/config.rb', line 23 def cache_dir @cache_dir end |
#daemon ⇒ Object
Returns the value of attribute daemon
23 24 25 |
# File 'lib/mutineer/config.rb', line 23 def daemon @daemon end |
#daemon_timeout ⇒ Object
Returns the value of attribute daemon_timeout
23 24 25 |
# File 'lib/mutineer/config.rb', line 23 def daemon_timeout @daemon_timeout end |
#dry_run ⇒ Object
Returns the value of attribute dry_run
23 24 25 |
# File 'lib/mutineer/config.rb', line 23 def dry_run @dry_run end |
#fail_fast ⇒ Object
Returns the value of attribute fail_fast
23 24 25 |
# File 'lib/mutineer/config.rb', line 23 def fail_fast @fail_fast end |
#format ⇒ Object
Returns the value of attribute format
23 24 25 |
# File 'lib/mutineer/config.rb', line 23 def format @format end |
#framework ⇒ Object
Returns the value of attribute framework
23 24 25 |
# File 'lib/mutineer/config.rb', line 23 def framework @framework end |
#ignore ⇒ Object
Returns the value of attribute ignore
23 24 25 |
# File 'lib/mutineer/config.rb', line 23 def ignore @ignore end |
#jobs ⇒ Object
Returns the value of attribute jobs
23 24 25 |
# File 'lib/mutineer/config.rb', line 23 def jobs @jobs end |
#load_paths ⇒ Object
Returns the value of attribute load_paths
23 24 25 |
# File 'lib/mutineer/config.rb', line 23 def load_paths @load_paths end |
#only ⇒ Object
Returns the value of attribute only
23 24 25 |
# File 'lib/mutineer/config.rb', line 23 def only @only end |
#operators ⇒ Object
Returns the value of attribute operators
23 24 25 |
# File 'lib/mutineer/config.rb', line 23 def operators @operators end |
#output ⇒ Object
Returns the value of attribute output
23 24 25 |
# File 'lib/mutineer/config.rb', line 23 def output @output end |
#project_root ⇒ Object
Returns the value of attribute project_root
23 24 25 |
# File 'lib/mutineer/config.rb', line 23 def project_root @project_root end |
#rails ⇒ Object
Returns the value of attribute rails
23 24 25 |
# File 'lib/mutineer/config.rb', line 23 def rails @rails end |
#require_paths ⇒ Object
Returns the value of attribute require_paths
23 24 25 |
# File 'lib/mutineer/config.rb', line 23 def require_paths @require_paths end |
#since ⇒ Object
Returns the value of attribute since
23 24 25 |
# File 'lib/mutineer/config.rb', line 23 def since @since end |
#sources ⇒ Object
Returns the value of attribute sources
23 24 25 |
# File 'lib/mutineer/config.rb', line 23 def sources @sources end |
#strategy ⇒ Object
Returns the value of attribute strategy
23 24 25 |
# File 'lib/mutineer/config.rb', line 23 def strategy @strategy end |
#test_command ⇒ Object
Returns the value of attribute test_command
23 24 25 |
# File 'lib/mutineer/config.rb', line 23 def test_command @test_command end |
#tests ⇒ Object
Returns the value of attribute tests
23 24 25 |
# File 'lib/mutineer/config.rb', line 23 def tests @tests end |
#threshold ⇒ Object
Returns the value of attribute threshold
23 24 25 |
# File 'lib/mutineer/config.rb', line 23 def threshold @threshold end |
#verbose ⇒ Object
Returns the value of attribute verbose
23 24 25 |
# File 'lib/mutineer/config.rb', line 23 def verbose @verbose end |
Class Method Details
.coerce(known_key, value, file_name) ⇒ Object
Coerces a config value to its target type.
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/mutineer/config.rb', line 165 def self.coerce(known_key, value, file_name) case known_key when "operators" then filter_operators(Array(value).map(&:to_s), file_name) when "jobs" then value.to_i when "threshold" f = Float(value, exception: false) if f.nil? raise ConfigError, "#{file_name}: threshold must be a number between 0 and 100 " \ "(got: #{value.inspect})" end f when "require" then Array(value).map(&:to_s) when "boot" then value.to_s when "framework" then value.to_s when "rails" then value == true || value.to_s == "true" when "daemon" then value == true || value.to_s == "true" when "verbose" then value == true || value.to_s == "true" when "fail_fast" then value == true || value.to_s == "true" when "ignore" then Array(value).map(&:to_s) when "baseline" then value.to_s when "test_command" then value.to_s else value end end |
.detect_framework(tests) ⇒ String
Pick rspec when a MAJORITY of the given test files end with _spec.rb; otherwise minitest. Empty/ambiguous -> minitest (the safe default). Detects the test framework from the file list.
145 146 147 148 149 |
# File 'lib/mutineer/config.rb', line 145 def self.detect_framework(tests) tests = Array(tests) specs = tests.count { |t| t.to_s.end_with?("_spec.rb") } specs > tests.length / 2.0 ? "rspec" : "minitest" end |
.field_for(known_key) ⇒ Symbol
Maps a config key to its Struct field.
155 156 157 |
# File 'lib/mutineer/config.rb', line 155 def self.field_for(known_key) known_key == "require" ? :require_paths : known_key.to_sym end |
.filter_operators(names, file_name) ⇒ Array<String>
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.
Drop (with a warning) operator names the registry doesn't know (R7). Referenced lazily so config.rb carries no load-order dependency on the registry; by the time a config is parsed at runtime, it is loaded. Filters out unknown operator names.
199 200 201 202 203 204 205 206 207 208 |
# File 'lib/mutineer/config.rb', line 199 def self.filter_operators(names, file_name) known = MutatorRegistry::ALL.keys names.select do |n| next true if known.include?(n) warn "mutineer: unknown operator #{n.inspect} in #{file_name} " \ "(known: #{known.join(', ')}); ignored" false end end |
.find_file(start = Dir.pwd, home = File.expand_path("~")) ⇒ Object
Walk from start toward home, returning the first .mutineer.yml path found
or nil. Checks home itself, then stops; if start is above home
(e.g. /tmp), the walk continues to the filesystem root (KTD4). Pure
discovery — reads no file content.
64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/mutineer/config.rb', line 64 def self.find_file(start = Dir.pwd, home = File.("~")) dir = File.(start) loop do candidate = File.join(dir, CONFIG_FILE) return candidate if File.file?(candidate) break if dir == home parent = File.dirname(dir) break if parent == dir # filesystem root dir = parent end nil end |
.from_file(path) ⇒ Object
Parse a .mutineer.yml into a symbol-keyed hash of recognized keys. Unknown keys / unknown operator names emit a one-line stderr warning and are ignored (R7). A YAML syntax error raises ConfigError (R7a/R8) — never a silent fallback to defaults, and never an exit from the lib layer.
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/mutineer/config.rb', line 83 def self.from_file(path) raw = YAML.safe_load(File.read(path)) || {} name = File.basename(path) unless raw.is_a?(Hash) warn "mutineer: #{name} ignored: expected a YAML mapping of keys to values" return {} end out = {} raw.each do |key, value| ks = key.to_s unless KNOWN_KEYS.include?(ks) warn "mutineer: unknown config key #{ks.inspect} in #{name} " \ "(known: #{KNOWN_KEYS.join(', ')}); ignored" next end out[field_for(ks)] = coerce(ks, value, name) end out rescue Psych::SyntaxError => e raise ConfigError, "#{File.basename(path)} parse error: #{e.}" end |
.resolve(cli_opts, file_hash, explicit) ⇒ Object
Apply precedence (KTD3): start from the CLI-provided values, then fill in a
config-file value only for keys the user did NOT type on the command line.
explicit is a Set of field symbols the CLI saw with a value; a Set (not
nil-sentinels) is used because some valid values are zero/false.
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/mutineer/config.rb', line 110 def self.resolve(cli_opts, file_hash, explicit) merged = cli_opts.dup file_hash.each { |k, v| merged[k] = v unless explicit.include?(k) } config = new(**merged) # --rails sugar: boot config/environment. Prefer redefine only for the # in-process path (daemon is whole-file reload only). In-process --rails # shares one test database — force serial unless --daemon. if config.rails config.boot ||= "config/environment" unless config.daemon || explicit.include?(:strategy) config.strategy = "redefine" end unless config.daemon if config.jobs.to_i > 1 warn "[mutineer] --rails without --daemon runs serially (shared test DB); " \ "forcing --jobs 1. Use --daemon for safe --jobs N." end config.jobs = 1 end end # Auto-detect the framework only when neither CLI nor config file set it # (explicit value, from either source, is already on config.framework and # always wins). Default minitest unless the test files clearly look RSpec. config.framework ||= detect_framework(config.tests) config end |