Module: Moult::Boundaries

Defined in:
lib/moult/boundaries.rb,
lib/moult/boundaries/packwerk.rb,
lib/moult/boundaries/severity.rb

Overview

Orchestrates the architecture-boundaries analysis: it asks the Packwerk adapter for every recorded violation, groups them into findings, and grades each group through the pure Severity model. The result is a ranked BoundariesReport of confidence-null, severity-classified boundary violations — recorded facts, never claims that the code is wrong.

This is the only layer that knows where the facts come from; Severity stays a pure function of the violation type so it can be pinned in isolation.

Defined Under Namespace

Modules: Packwerk, Severity

Constant Summary collapse

GROUP_KEY =

A finding is one group of violations sharing this identity (the same constant crossing the same package boundary in the same way); its occurrences are the referencing files.

%i[referencing_package defining_package constant violation_type].freeze

Class Method Summary collapse

Class Method Details

.build_report(root:, min_severity: nil, git_ref: nil, generated_at: nil) ⇒ BoundariesReport

Parameters:

  • root (String)

    absolute analysis root

  • min_severity (String, nil) (defaults to: nil)

    drop findings below this severity (low<medium<high)

Returns:



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/moult/boundaries.rb', line 23

def build_report(root:, min_severity: nil, git_ref: nil, generated_at: nil)
  result = Packwerk.detect(root: root)

  findings = group(result.violations).map { |key, violations| finding_for(key, violations) }
  findings.select! { |f| meets?(f.severity, min_severity) } if min_severity
  findings.sort_by! { |f| sort_key(f) }

  BoundariesReport.new(
    root: root,
    findings: findings,
    git_ref: git_ref,
    generated_at: generated_at,
    backend: result.backend,
    backend_version: result.backend_version,
    configured: result.configured
  )
end

.finding_for(key, violations) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/moult/boundaries.rb', line 45

def finding_for(key, violations)
  referencing_package, defining_package, constant, violation_type = key
  assessment = Severity.classify(violation_type: violation_type)
  occurrences = violations
    .map(&:path).uniq.sort
    .map { |path| BoundariesReport::Occurrence.new(symbol_id: nil, path: path) }
  BoundariesReport::Finding.new(
    violation_type: violation_type,
    severity: assessment.severity,
    referencing_package: referencing_package,
    defining_package: defining_package,
    constant: constant,
    reasons: assessment.reasons,
    occurrences: occurrences
  )
end

.group(violations) ⇒ Object



41
42
43
# File 'lib/moult/boundaries.rb', line 41

def group(violations)
  violations.group_by { |v| GROUP_KEY.map { |k| v[k] } }
end

.meets?(severity, floor) ⇒ Boolean

Returns:

  • (Boolean)


69
70
71
# File 'lib/moult/boundaries.rb', line 69

def meets?(severity, floor)
  Severity::SCALE.index(severity) >= Severity::SCALE.index(floor.to_s)
end

.sort_key(finding) ⇒ Object

Most-severe first, then a deterministic alphabetical tie-break so output is stable across runs.



64
65
66
67
# File 'lib/moult/boundaries.rb', line 64

def sort_key(finding)
  [-Severity::SCALE.index(finding.severity), finding.violation_type,
    finding.referencing_package, finding.defining_package, finding.constant]
end