Module: Snoot::RenderReport

Defined in:
lib/snoot/render_report.rb

Overview

Given a finding_rendered Run, produces a Report. Smell findings get two sections (doc, instances); ComplexityHit and DuplicationCluster get three (header, finding_context, doc). Per-section content is built by the helpers below.

Defined Under Namespace

Classes: Report

Class Method Summary collapse

Class Method Details

.complexity_hit_sections(hit) ⇒ Object



42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/snoot/render_report.rb', line 42

def complexity_hit_sections(hit)
  loc = hit.location.description
  score = hit.score.to_s("F")
  name = hit.method_name
  {
    header: "High complexity in #{name} at #{loc} (score: #{score})",
    finding_context: "#{loc}\n\nMethod: #{name}\nScore: #{score}",
    doc: "High complexity hits indicate a method or class doing too much. " \
         "Consider extracting helpers, simplifying conditionals, or " \
         "splitting the responsibility across smaller units."
  }
end

.duplication_cluster_sections(cluster) ⇒ Object



55
56
57
58
59
60
61
62
63
64
65
# File 'lib/snoot/render_report.rb', line 55

def duplication_cluster_sections(cluster)
  locations = cluster.locations
  rendered_locations = locations.map(&:description)
  {
    header: "Structural duplication: #{locations.size} locations (signature: #{cluster.signature})",
    finding_context: "Locations:\n#{rendered_locations.join("\n")}",
    doc: "Structural duplication suggests an extracted abstraction is missing. " \
         "Consider whether the duplicated shape belongs to a single helper, " \
         "module, or value type."
  }
end

.invoke(run, smells:, orchestration:) ⇒ Object



16
17
18
19
20
21
22
23
24
# File 'lib/snoot/render_report.rb', line 16

def invoke(run, smells:, orchestration:)
  finding = run.selected_finding
  sections = if finding.is_a?(Smell)
               smell_sections(smells, finding, orchestration)
             else
               non_smell_sections(finding)
             end
  Report.new(run: run, finding: finding, sections: sections)
end

.non_smell_sections(finding) ⇒ Object



35
36
37
38
39
40
# File 'lib/snoot/render_report.rb', line 35

def non_smell_sections(finding)
  case finding
  when ComplexityHit then complexity_hit_sections(finding)
  when DuplicationCluster then duplication_cluster_sections(finding)
  end
end

.render_instance_group(path, smells) ⇒ Object



77
78
79
80
# File 'lib/snoot/render_report.rb', line 77

def render_instance_group(path, smells)
  lines = smells.map { |smell| "  Line #{smell.location.line_start}: #{smell.message}" }
  "#{path}\n#{lines.join("\n")}"
end

.render_instances(smells) ⇒ Object



67
68
69
70
# File 'lib/snoot/render_report.rb', line 67

def render_instances(smells)
  groups = smell_groups_by_path(smells)
  "## Instances\n\n#{groups.map { |path, group| render_instance_group(path, group) }.join("\n\n")}"
end

.smell_groups_by_path(smells) ⇒ Object



72
73
74
75
# File 'lib/snoot/render_report.rb', line 72

def smell_groups_by_path(smells)
  smells.group_by { |smell| smell.location.path.raw }
        .sort_by { |path, group| [-group.size, path] }
end

.smell_sections(smells, smell, orchestration) ⇒ Object



26
27
28
29
30
31
32
33
# File 'lib/snoot/render_report.rb', line 26

def smell_sections(smells, smell, orchestration)
  smell_type = smell.smell_type
  matching = smells.select { |instance| instance.smell_type == smell_type }
  {
    doc: orchestration.vendored_doc(smell_type),
    instances: render_instances(matching)
  }
end