Module: ArchSpec::Formatters::Explanation
- Defined in:
- lib/archspec/formatters/explanation.rb
Overview
Renders archspec explain: why a file or constant belongs to its components, and the facts ArchSpec found for it, in the same visual language as the check output. Raises ArchSpec::Error when the subject matches no file and no constant.
Class Method Summary collapse
- .explain_constant(output, style, graph, subject) ⇒ Object
- .explain_file(output, style, graph, path) ⇒ Object
-
.in_gutters(labels) ⇒ Object
Yields each label right-justified to the widest one, with the frame gutter bar appended, so columns line up like the check output.
- .line_range(suppression) ⇒ Object
- .print(output = $stdout, graph:, subject:) ⇒ Object
- .print_component_reasons(output, style, assignments) ⇒ Object
- .print_facts(output, style, facts) ⇒ Object
- .print_parse_errors(output, style, file) ⇒ Object
- .print_suppressions(output, style, file) ⇒ Object
Class Method Details
.explain_constant(output, style, graph, subject) ⇒ Object
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/archspec/formatters/explanation.rb', line 34 def explain_constant(output, style, graph, subject) constants = graph.constants_named(subject) raise Error, "no file or constant found for #{subject.inspect}" if constants.empty? constants.each_with_index do |constant, index| output.puts unless index.zero? output.puts style.bold(constant.name) output.puts output.puts " #{style.note('kind:')} #{constant.kind}" output.puts " #{style.note('file:')} #{constant.location.relative_path(graph.root)}:#{constant.location.line}" print_component_reasons( output, style, graph.component_assignment_reasons_for_constant(constant.name, path: constant.path) ) output.puts " #{style.note('superclass:')} #{constant.superclass || '(none)'}" output.puts " #{style.note('instance methods:')} #{constant.instance_methods.to_a.sort.join(', ')}" output.puts " #{style.note('class methods:')} #{constant.class_methods.to_a.sort.join(', ')}" end end |
.explain_file(output, style, graph, path) ⇒ Object
23 24 25 26 27 28 29 30 31 32 |
# File 'lib/archspec/formatters/explanation.rb', line 23 def explain_file(output, style, graph, path) file = graph.files.fetch(path) output.puts style.bold(file.relative_path) output.puts output.puts " #{style.note('defined constants:')} #{graph.constants_for_path(path).map(&:name).join(', ')}" print_parse_errors(output, style, file) print_component_reasons(output, style, graph.component_assignment_reasons_for_path(path)) print_suppressions(output, style, file) print_facts(output, style, graph.edges.select { |edge| edge.from_path == path }) end |
.in_gutters(labels) ⇒ Object
Yields each label right-justified to the widest one, with the frame gutter bar appended, so columns line up like the check output.
104 105 106 107 108 109 110 |
# File 'lib/archspec/formatters/explanation.rb', line 104 def in_gutters(labels) width = labels.map(&:length).max labels.each_with_index do |label, index| yield "#{label.rjust(width)} │", index end end |
.line_range(suppression) ⇒ Object
112 113 114 115 116 117 118 119 120 |
# File 'lib/archspec/formatters/explanation.rb', line 112 def line_range(suppression) if suppression.end_line == Float::INFINITY "#{suppression.start_line}-EOF" elsif suppression.start_line == suppression.end_line suppression.start_line.to_s else "#{suppression.start_line}-#{suppression.end_line}" end end |
.print(output = $stdout, graph:, subject:) ⇒ Object
12 13 14 15 16 17 18 19 20 21 |
# File 'lib/archspec/formatters/explanation.rb', line 12 def print(output = $stdout, graph:, subject:) style = Style.new(output) path = File.(subject, graph.root) if graph.files.key?(path) explain_file(output, style, graph, path) else explain_constant(output, style, graph, subject) end end |
.print_component_reasons(output, style, assignments) ⇒ Object
54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/archspec/formatters/explanation.rb', line 54 def print_component_reasons(output, style, assignments) if assignments.empty? output.puts " #{style.note('components:')} (none)" return end output.puts " #{style.note('components:')}" assignments.sort_by { |name, _reasons| name.to_s }.each do |name, reasons| output.puts " #{name}: #{reasons.empty? ? '(no recorded reason)' : reasons.join('; ')}" end end |
.print_facts(output, style, facts) ⇒ Object
88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/archspec/formatters/explanation.rb', line 88 def print_facts(output, style, facts) if facts.empty? output.puts " #{style.note('outgoing facts:')} (none)" return end output.puts " #{style.note('outgoing facts:')}" locations = facts.map { |edge| "#{edge.location.line}:#{edge.location.column}" } in_gutters(locations) do |gutter, index| edge = facts[index] output.puts " #{style.faint(gutter)} #{edge.verb} #{edge.to}" end end |
.print_parse_errors(output, style, file) ⇒ Object
78 79 80 81 82 83 84 85 86 |
# File 'lib/archspec/formatters/explanation.rb', line 78 def print_parse_errors(output, style, file) return if file.parse_errors.empty? output.puts " #{style.note('parse errors:')}" locations = file.parse_errors.map { |error| "#{error.location.line}:#{error.location.column}" } in_gutters(locations) do |gutter, index| output.puts " #{style.faint(gutter)} #{file.parse_errors[index].}" end end |
.print_suppressions(output, style, file) ⇒ Object
66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/archspec/formatters/explanation.rb', line 66 def print_suppressions(output, style, file) return if file.suppressions.empty? output.puts " #{style.note('suppressions:')}" in_gutters(file.suppressions.map { |suppression| line_range(suppression) }) do |gutter, index| suppression = file.suppressions[index] rule = suppression.rule || '*' reason = suppression.reason ? " -- #{suppression.reason}" : '' output.puts " #{style.faint(gutter)} #{rule}#{reason}" end end |