Module: Snoot::AnalyserOrchestration::Default

Defined in:
lib/snoot/analyser_orchestration/default.rb

Overview

Default is the production adapter for AnalyserOrchestration. It invokes the real Reek/Flog/Flay APIs in-process (no shellouts) and resolves vendored_doc against the reek docs vendored at data/reek_docs/<PascalCase-Hyphen>.md (synced via ‘rake docs:sync`, pinned to the bundled reek version). Reek invocation honours a project-local `.reek.yml` (or any ancestor’s, falling back to ‘~/.reek.yml`) via `AppConfiguration.from_default_path`, matching reek’s own CLI discovery. Flog scoring uses Flog’s default options (every scored method emits a ComplexityHit; selection happens in AnalyseRun). Flay duplication uses Flay’s default mass threshold (16). Stateless: implemented as a module of module functions, used as the orchestration value directly (no ‘.new`).

Default’s public surface is exactly the five contracted methods (vendored_doc, significant_smells, significant_complexities, significant_duplications, analyse). The per-analyser drivers (reek_analyse, flog_analyse, flay_analyse) and the per-pathname helper (reek_smells_for) are private; their behaviour is observed through analyse. Pure third-party-output translation is delegated to the sibling module ResultMapping.

Per-analyser directory expansion mirrors each tool’s own CLI default rather than imposing a snoot-wide glob, so a directory Path resolves exactly as that tool would resolve it on the command line. Reek defers to ‘Reek::Source::SourceLocator` (which also honours `.reek.yml exclude_paths`); Flog uses `*/.rb,rake` to match `Flog::CLI`; Flay uses `*/.rb` (Flay’s CLI additionally appends extensions advertised by installed Flay plugins, which snoot does not load). The orchestration contract is path-abstract (snoot.allium:150), so this is implementation policy each adapter owns.

Constant Summary collapse

DOCS_ROOT =
File.expand_path("../../../data/reek_docs", __dir__).freeze
DOC_FILENAME_PATTERN =
/([a-z])([A-Z])/
SMELL_TYPE_INSTANCE_FLOOR =
2
COMPLEXITY_SCORE_FLOOR =
BigDecimal("25")
ANALYSER_PROBES =
[
  %i[reek reek_analyse],
  %i[flog flog_analyse],
  %i[flay flay_analyse]
].freeze

Class Method Summary collapse

Class Method Details

.analyse(paths) ⇒ Object

analyse runs the three analysers in canonical order (Reek -> Flog -> Flay), capturing each result as it succeeds. On the first failure it returns an AnalyserFailure tagged with that analyser and does not invoke the remaining ones. On full success it returns a Sources bundling the three result sets.



128
129
130
131
132
133
134
135
# File 'lib/snoot/analyser_orchestration/default.rb', line 128

def analyse(paths)
  outputs = collect_outputs(paths)
  return outputs if outputs.is_a?(AnalyserFailure)

  Sources.new(
    smells: outputs[:reek], complexities: outputs[:flog], duplications: outputs[:flay]
  )
end

.significant_complexities(complexities) ⇒ Object



117
118
119
# File 'lib/snoot/analyser_orchestration/default.rb', line 117

def significant_complexities(complexities)
  complexities.select { |hit| hit.score >= COMPLEXITY_SCORE_FLOOR }.to_set
end

.significant_duplications(duplications) ⇒ Object



121
# File 'lib/snoot/analyser_orchestration/default.rb', line 121

def significant_duplications(duplications) = duplications

.significant_smells(smells) ⇒ Object



112
113
114
115
# File 'lib/snoot/analyser_orchestration/default.rb', line 112

def significant_smells(smells)
  counts = smells.group_by(&:smell_type).transform_values(&:size)
  smells.select { |smell| counts[smell.smell_type] >= SMELL_TYPE_INSTANCE_FLOOR }.to_set
end

.vendored_doc(smell_type) ⇒ Object



104
105
106
107
108
109
110
# File 'lib/snoot/analyser_orchestration/default.rb', line 104

def vendored_doc(smell_type)
  name = smell_type.name
  @vendored_doc_cache.fetch(name) do
    path = File.join(DOCS_ROOT, "#{name.gsub(DOC_FILENAME_PATTERN, '\1-\2')}.md")
    @vendored_doc_cache[name] = File.exist?(path) ? File.read(path) : nil
  end
end