Skip to content

Commit 9e9aae8

Browse files
committed
Extract console report
Closing #103
1 parent f6f63c8 commit 9e9aae8

File tree

10 files changed

+261
-165
lines changed

10 files changed

+261
-165
lines changed

.reek.yml

+5-1
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,21 @@ detectors:
66
- Skunk::Command::StatusReporter#analysed_modules
77
FeatureEnvy:
88
exclude:
9-
- Skunk::Command::StatusReporter#table
9+
- Skunk::Generator::ConsoleReport#table
1010
InstanceVariableAssumption:
1111
exclude:
1212
- Skunk::Cli::Options::Argv
13+
- Skunk::Scorer
1314
- RubyCritic::AnalysedModulesCollection
1415
IrresponsibleModule:
1516
exclude:
1617
- RubyCritic::AnalysedModulesCollection
1718
NestedIterators:
1819
exclude:
1920
- Skunk::Cli::Options::Argv#parse
21+
TooManyMethods:
22+
exclude:
23+
- Skunk::Command::StatusSharer
2024
TooManyStatements:
2125
exclude:
2226
- initialize

lib/skunk/cli/application.rb

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ def execute
2626

2727
# :reek:NilCheck
2828
@parsed_options = @options.parse.to_h
29+
2930
command = Skunk::CommandFactory.create(@parsed_options)
3031
reporter = command.execute
3132

lib/skunk/commands/default.rb

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
# frozen_string_literal: true
22

33
require "rubycritic/commands/default"
4-
require "rubycritic/analysers_runner"
5-
require "rubycritic/revision_comparator"
6-
require "rubycritic/reporter"
74

8-
require "skunk/commands/base"
9-
require "skunk/commands/shareable"
105
require "skunk/commands/status_reporter"
6+
require "skunk/commands/shareable"
7+
require "skunk/generators/console_report"
8+
require "skunk/core/scorer"
119

1210
module Skunk
1311
module Command
@@ -39,6 +37,9 @@ def execute
3937
#
4038
# @param [RubyCritic::AnalysedModulesCollection] A collection of analysed modules
4139
def report(analysed_modules)
40+
skunk_scorer = Skunk::Scorer.new(analysed_modules)
41+
Skunk::Generator::ConsoleReport.new(skunk_scorer).generate_report
42+
4243
status_reporter.analysed_modules = analysed_modules
4344
status_reporter.score = analysed_modules.score
4445
end

lib/skunk/commands/status_reporter.rb

+1-86
Original file line numberDiff line numberDiff line change
@@ -1,97 +1,12 @@
11
# frozen_string_literal: true
22

3-
require "erb"
43
require "rubycritic/commands/status_reporter"
5-
require "terminal-table"
64

75
module Skunk
86
module Command
9-
# Knows how to report status for stinky files
7+
# Implements analysed_modules to share it when SHARE is true
108
class StatusReporter < RubyCritic::Command::StatusReporter
119
attr_accessor :analysed_modules
12-
13-
HEADINGS = %w[file skunk_score churn_times_cost churn cost coverage].freeze
14-
HEADINGS_WITHOUT_FILE = HEADINGS - %w[file]
15-
HEADINGS_WITHOUT_FILE_WIDTH = HEADINGS_WITHOUT_FILE.size * 17 # padding
16-
17-
TEMPLATE = ERB.new(<<-TEMPL
18-
<%= _ttable %>\n
19-
SkunkScore Total: <%= total_skunk_score %>
20-
Modules Analysed: <%= analysed_modules_count %>
21-
SkunkScore Average: <%= skunk_score_average %>
22-
<% if worst %>Worst SkunkScore: <%= worst.skunk_score %> (<%= worst.pathname %>)<% end %>
23-
24-
Generated with Skunk v<%= Skunk::VERSION %>
25-
TEMPL
26-
)
27-
28-
# Returns a status message with a table of all analysed_modules and
29-
# a skunk score average
30-
def update_status_message
31-
opts = table_options.merge(headings: HEADINGS, rows: table)
32-
33-
_ttable = Terminal::Table.new(opts)
34-
35-
@status_message = TEMPLATE.result(binding)
36-
end
37-
38-
private
39-
40-
def analysed_modules_count
41-
@analysed_modules_count ||= non_test_modules.count
42-
end
43-
44-
def non_test_modules
45-
@non_test_modules ||= analysed_modules.reject do |a_module|
46-
module_path = a_module.pathname.dirname.to_s
47-
module_path.start_with?("test", "spec") || module_path.end_with?("test", "spec")
48-
end
49-
end
50-
51-
def worst
52-
@worst ||= sorted_modules.first
53-
end
54-
55-
def sorted_modules
56-
@sorted_modules ||= non_test_modules.sort_by(&:skunk_score).reverse!
57-
end
58-
59-
def total_skunk_score
60-
@total_skunk_score ||= non_test_modules.sum(&:skunk_score)
61-
end
62-
63-
def total_churn_times_cost
64-
non_test_modules.sum(&:churn_times_cost)
65-
end
66-
67-
def skunk_score_average
68-
return 0 if analysed_modules_count.zero?
69-
70-
(total_skunk_score.to_d / analysed_modules_count).to_f.round(2)
71-
end
72-
73-
def table_options
74-
max = sorted_modules.max_by { |a_mod| a_mod.pathname.to_s.length }
75-
width = max.pathname.to_s.length + HEADINGS_WITHOUT_FILE_WIDTH
76-
{
77-
style: {
78-
width: width
79-
}
80-
}
81-
end
82-
83-
def table
84-
sorted_modules.map do |a_mod|
85-
[
86-
a_mod.pathname,
87-
a_mod.skunk_score,
88-
a_mod.churn_times_cost,
89-
a_mod.churn,
90-
a_mod.cost.round(2),
91-
a_mod.coverage.round(2)
92-
]
93-
end
94-
end
9510
end
9611
end
9712
end

lib/skunk/commands/status_sharer.rb

+36-7
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
require "json"
66

77
require "skunk/commands/status_reporter"
8+
require "skunk/core/scorer"
89

910
module Skunk
1011
module Command
@@ -40,26 +41,22 @@ def base_url
4041

4142
def json_summary
4243
result = {
43-
total_skunk_score: total_skunk_score,
44+
total_skunk_score: total_score,
4445
analysed_modules_count: analysed_modules_count,
4546
skunk_score_average: skunk_score_average,
4647
skunk_version: Skunk::VERSION
4748
}
4849

4950
if worst
5051
result[:worst_skunk_score] = {
51-
file: worst.pathname.to_s,
52-
skunk_score: worst.skunk_score
52+
file: worst_pathname.to_s,
53+
skunk_score: worst_skunk_score
5354
}
5455
end
5556

5657
result
5758
end
5859

59-
def json_results
60-
sorted_modules.map(&:to_hash)
61-
end
62-
6360
# :reek:UtilityFunction
6461
def not_sharing?
6562
ENV["SHARE"] != "true" && ENV["SHARE_URL"].to_s == ""
@@ -94,6 +91,38 @@ def post_payload
9491
def url
9592
URI(File.join(base_url, "reports"))
9693
end
94+
95+
def skunk_score_average
96+
skunk_scorer.average
97+
end
98+
99+
def total_score
100+
skunk_scorer.total_score
101+
end
102+
103+
def worst
104+
skunk_scorer.worst
105+
end
106+
107+
def worst_pathname
108+
skunk_scorer.worst_pathname
109+
end
110+
111+
def worst_skunk_score
112+
skunk_scorer.worst_skunk_score
113+
end
114+
115+
def analysed_modules_count
116+
skunk_scorer.analysed_modules_count
117+
end
118+
119+
def json_results
120+
skunk_scorer.sorted_modules.map(&:to_hash)
121+
end
122+
123+
def skunk_scorer
124+
@skunk_scorer ||= Skunk::Scorer.new(analysed_modules)
125+
end
97126
end
98127
end
99128
end

lib/skunk/core/scorer.rb

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# frozen_string_literal: true
2+
3+
module Skunk
4+
# Skunk Score to share with generators and sharers
5+
class Scorer
6+
attr_reader :analysed_modules
7+
8+
def initialize(analysed_modules)
9+
@analysed_modules = analysed_modules
10+
end
11+
12+
def score
13+
analysed_modules.score
14+
end
15+
16+
def analysed_modules_count
17+
@analysed_modules_count ||= non_test_modules.count
18+
end
19+
20+
def non_test_modules
21+
@non_test_modules ||= analysed_modules.reject do |a_module|
22+
module_path = a_module.pathname.dirname.to_s
23+
module_path.start_with?("test", "spec") || module_path.end_with?("test", "spec")
24+
end
25+
end
26+
27+
def total_score
28+
@total_score ||= non_test_modules.sum(&:skunk_score)
29+
end
30+
31+
def total_churn_times_cost
32+
non_test_modules.sum(&:churn_times_cost)
33+
end
34+
35+
def average
36+
return 0 if analysed_modules_count.zero?
37+
38+
(total_score.to_d / analysed_modules_count).to_f.round(2)
39+
end
40+
41+
def worst
42+
@worst ||= sorted_modules.first
43+
end
44+
45+
def worst_skunk_score
46+
@worst.skunk_score
47+
end
48+
49+
def worst_pathname
50+
@worst.pathname
51+
end
52+
53+
def sorted_modules
54+
@sorted_modules ||= non_test_modules.sort_by(&:skunk_score).reverse!
55+
end
56+
end
57+
end
+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# frozen_string_literal: true
2+
3+
require "erb"
4+
require "terminal-table"
5+
require "rubycritic/generators/console_report"
6+
7+
module Skunk
8+
module Generator
9+
# Print SkunkScore at the console in plain text
10+
class ConsoleReport < RubyCritic::Generator::ConsoleReport
11+
def initialize(skunk_scorer)
12+
@skunk_scorer = skunk_scorer
13+
super
14+
end
15+
16+
HEADINGS = %w[file skunk_score churn_times_cost churn cost coverage].freeze
17+
HEADINGS_WITHOUT_FILE = HEADINGS - %w[file]
18+
HEADINGS_WITHOUT_FILE_WIDTH = HEADINGS_WITHOUT_FILE.size * 17 # padding
19+
20+
TEMPLATE = ERB.new(<<~TEMPL
21+
<%= _ttable %>\n
22+
SkunkScore Total: <%= total_skunk_score %>
23+
Modules Analysed: <%= analysed_modules_count %>
24+
SkunkScore Average: <%= skunk_score_average %>
25+
<% if worst %>Worst SkunkScore: <%= worst_skunk_score %> (<%= worst_pathname %>)<% end %>
26+
27+
Generated with Skunk v<%= Skunk::VERSION %>
28+
TEMPL
29+
)
30+
31+
def generate_report
32+
opts = table_options.merge(headings: HEADINGS, rows: table)
33+
34+
_ttable = Terminal::Table.new(opts)
35+
36+
puts TEMPLATE.result(binding)
37+
end
38+
39+
private
40+
41+
def total_skunk_score
42+
@skunk_scorer.total_score
43+
end
44+
45+
def analysed_modules_count
46+
@skunk_scorer.analysed_modules_count
47+
end
48+
49+
def skunk_score_average
50+
@skunk_scorer.average
51+
end
52+
53+
def worst
54+
@skunk_scorer.worst
55+
end
56+
57+
def worst_skunk_score
58+
@skunk_scorer.worst_skunk_score
59+
end
60+
61+
def worst_pathname
62+
@skunk_scorer.worst_pathname
63+
end
64+
65+
def sorted_modules
66+
@skunk_scorer.sorted_modules
67+
end
68+
69+
def table_options
70+
max = sorted_modules.max_by { |a_mod| a_mod.pathname.to_s.length }
71+
width = max.pathname.to_s.length + HEADINGS_WITHOUT_FILE_WIDTH
72+
{
73+
style: {
74+
width: width
75+
}
76+
}
77+
end
78+
79+
def table
80+
sorted_modules.map do |a_mod|
81+
[
82+
a_mod.pathname,
83+
a_mod.skunk_score,
84+
a_mod.churn_times_cost,
85+
a_mod.churn,
86+
a_mod.cost.round(2),
87+
a_mod.coverage.round(2)
88+
]
89+
end
90+
end
91+
end
92+
end
93+
end

0 commit comments

Comments
 (0)