quality_report 1.2.2 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a54ad55acc9cb09976954e805eb8c7136d5f81e76ca0d23b732739b629b43b77
4
- data.tar.gz: 4ba83e201032c764ef7077187d267f9fba8f29c682e4087b893eb0ab92ecfae6
3
+ metadata.gz: c36b81a22b99492d9995e4446769315fe3a01df58e39ed71a3a1050bc05f1791
4
+ data.tar.gz: ebc3e1150b528d795fd72e926a462a93aca6311f4a232a3d94c04416bc0a7a7e
5
5
  SHA512:
6
- metadata.gz: 295733e297b006cca1f68d5689f44422060f689aa44f10ecca0903fa4acca34fbd098df986edbceae908aa90b228e3e076eda2c5e324a8542015d2688e9118c4
7
- data.tar.gz: acfe8e8f47c38ec7fe118a6ead137b11083ac7288b94882cf1b9980c1ae8b797ec4fc124dc470374591ba3871c471039fb9894481e2bfc4c91b34d5ff94cc160
6
+ metadata.gz: 1e9379b163149edcab1180868a320b7b5529f6c73b5a8c90cc2ffe3292fa818ccc1a48a1cfef9fb6a319fd9dbdf348fc9e6f66749d9b82e4886f2f4a42a50d4d
7
+ data.tar.gz: f9911d996413da6cf9264b34749e318d01a1c64e85925c76dae7da609966495341e7fc9d5956866b32c477cc7adbf9f0fbbe50bd0422773242b69e16a122003f
data/README.md CHANGED
@@ -1,12 +1,82 @@
1
1
  # Ruby Quality Report
2
2
 
3
+ ## Installation
4
+
5
+ ```sh
6
+ gem install quality-report
7
+ ```
8
+
3
9
  ## Usage
4
10
 
5
11
  ```sh
6
12
  ruby-quality-report
7
13
  ```
8
14
 
9
- This outputs a CSV report to stdout.
15
+ After a bit of a wait, it outputs a report in table form:
16
+
17
+ ![Screenshot](screenshot-1@2x.webp)
18
+
19
+ This is showing, for each author, the percentage of problematic lines of code. Lower is better.
20
+
21
+
22
+ ### For improved relevance, it has two filters.
23
+
24
+ It excludes:
25
+
26
+ - authors with fewer than 200 lines of code
27
+ - authors with no commits in the previous 60 days
28
+
29
+
30
+ ### In Rails Projects
31
+
32
+ This works great in Rails projects. It examines code recursively starting in the current directory. In my testing,
33
+ I like the results best when run from `/app`. This is how I generated the report, above.
34
+
35
+
36
+ ## How it works
37
+
38
+ It runs a subset of [Rubocop Metrics cops](https://docs.rubocop.org/rubocop/cops_metrics.html) on `*.rb` files that flag single lines or methods:
39
+
40
+ - [ABC Size](https://docs.rubocop.org/rubocop/cops_metrics.html#metricsabcsize)
41
+ - [Block Length](https://docs.rubocop.org/rubocop/cops_metrics.html#metricsblocklength)
42
+ - [Block Nesting](https://docs.rubocop.org/rubocop/cops_metrics.html#metricsblocknesting)
43
+ - [Cyclomatic Complexity](https://docs.rubocop.org/rubocop/cops_metrics.html#metricscyclomaticcomplexity)
44
+ - [Method Length](https://docs.rubocop.org/rubocop/cops_metrics.html#metricsmethodlength)
45
+ - [Perceived Complexity](https://docs.rubocop.org/rubocop/cops_metrics.html#metricsperceivedcomplexity)
46
+
47
+ It then calculates the percentage of warnings per line written, per author. Each failing check is another warning.
48
+
49
+
50
+ ## Intent
51
+
52
+ This is a team management tool to:
53
+
54
+ - understand the quality of the code your team is producing
55
+ - identify programmers who'd benefit from mentorship and education
56
+
57
+ ## Foundational Research
58
+
59
+ Diving a little deeper, I've seen the phenomenon of micro-economies of bug-creation and bug-fixing develop within teams. Some developers appear to be extremely productive. They write a lot of code. But they also introduce a lot of bugs. The productivity is illusory.
60
+
61
+ This code quality report doesn't track **bugs** per se. But it does report **quality and complexity**. Researchers have found a strong correlation between complexity and bug rate [1]. This link is reflected, _e.g.,_ in international safety standards that mandate low software complexity to reduce failures [2].
62
+
63
+ Complex code introduces bugs in a second, more subtle way. This is because code complexity is the killer of understandability. Studies have found that developers devote 64% of their time to understanding code, while only 5% is spent on actually modifying it [3].
64
+
65
+ 1. De Silva, Dilshan, et al. The Relationship between Code Complexity and Software Quality: An Empirical Study. 2023, https://www.researchgate.net/publication/370761578_The_Relationship_between_Code_Complexity_and_Software_Quality_An_Empirical_Study.
66
+ 2. See e.g., ISO 26262-1:2018(En), Road Vehicles — Functional Safety — Part 1: Vocabulary. https://www.iso.org/obp/ui/en/#iso:std:iso:26262:-1:ed-2:v1:en. Accessed 29 Sept. 2024.
67
+ 3. Feitelson, Dror G. “From Code Complexity Metrics to Program Comprehension.” Communications of the ACM, vol. 66, no. 5, May 2023, pp. 52–61. DOI.org (Crossref), https://doi.org/10.1145/3546576.
68
+
69
+
70
+ ## Roadmap
71
+
72
+ - [ ] Colorize the output to separate high, medium, and low warning percentages.
73
+ - [ ] Fix the numerical alignment.
74
+ - [ ] Add a `--csv` option.
75
+ - [ ] Make the filters configurable.
76
+ - [ ] Speed up the scan.
77
+ - [ ] Add a progress bar.
78
+ - [ ] Refactor from script-style coding to a more standard Ruby project.
79
+
10
80
 
11
81
  ## Contributing
12
82
 
@@ -2,6 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'tty-table'
5
+ require 'pastel'
5
6
 
6
7
  #
7
8
  # ruby-quality-report
@@ -10,6 +11,9 @@ require 'tty-table'
10
11
  # of lines of code written by each author that have been flagged as
11
12
  # having too high of a complexity.
12
13
  #
14
+ #
15
+
16
+ SIXTY_DAYS_IN_SECONDS = 60 * 24 * 60 * 60
13
17
 
14
18
  def create_combined_stats(part_stats, whole_stats)
15
19
  part_data = make_data_set(part_stats)
@@ -17,13 +21,9 @@ def create_combined_stats(part_stats, whole_stats)
17
21
 
18
22
  whole_data.map do |label, whole_count|
19
23
  part_count = part_data[label] || 0
24
+ percent = float_to_percent(part_count.to_f / whole_count)
20
25
 
21
- {
22
- label:,
23
- part_count:,
24
- whole_count:,
25
- percent: float_to_percent(part_count.to_f / whole_count)
26
- }
26
+ { label:, part_count:, whole_count:, percent: }
27
27
  end
28
28
  end
29
29
 
@@ -44,21 +44,47 @@ def generate_csv(combined_stats)
44
44
  end
45
45
 
46
46
  def generate_table(combined_stats)
47
- table = TTY::Table.new(header: ['Author', 'Percent Flagged', 'Flagged Lines', 'All Lines'])
47
+ table = TTY::Table.new(header: %W[Author Percent\nFlagged Flagged\nLines All\nLines])
48
+ table_data = generate_data(combined_stats).reject { should_skip?(_1) }
49
+
50
+ table_data.each do |stats|
51
+ table << [
52
+ { value: stats[:label], alignment: :left },
53
+ { value: "#{stats[:percent]}%", alignment: :right },
54
+ { value: stats[:part_count], alignment: :right },
55
+ { value: stats[:whole_count], alignment: :right }
56
+ ]
57
+ end
48
58
 
49
- generate_data(combined_stats).each do |stats|
50
- next if should_skip?(stats)
59
+ render_table(table, table_data)
60
+ end
51
61
 
52
- table << [stats[:label], "#{stats[:percent]}%", stats[:part_count], stats[:whole_count]]
62
+ def render_table(table, table_data)
63
+ pastel = Pastel.new
64
+
65
+ table.render(:unicode, alignment: :center, padding: [0, 1], multiline: true) do |renderer|
66
+ renderer.border.style = :dim
67
+ renderer.filter = lambda { |val, row_index, _col_index|
68
+ if row_index <= 0
69
+ # Header row
70
+ val
71
+ else
72
+ case table_data[row_index - 1][:percent]
73
+ when 0...40
74
+ val
75
+ when 40...50
76
+ pastel.bold.yellow(val)
77
+ else
78
+ pastel.bold.red(val)
79
+ end
80
+ end
81
+ }
53
82
  end
54
-
55
- table.render(:unicode)
56
83
  end
57
84
 
58
85
  def generate_data(combined_stats)
59
86
  combined_stats
60
87
  .sort_by { |s| s[:percent] }
61
- .reverse
62
88
  end
63
89
 
64
90
  def should_skip?(stats)
@@ -78,7 +104,7 @@ def no_commits_in_last_60_days?(stats)
78
104
 
79
105
  # Convert Unix timestamp to Time object
80
106
  last_commit_time = Time.at(last_commit.to_i)
81
- sixty_days_ago = Time.now - (60 * 24 * 60 * 60) # 60 days in seconds
107
+ sixty_days_ago = Time.now - SIXTY_DAYS_IN_SECONDS
82
108
 
83
109
  last_commit_time < sixty_days_ago
84
110
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module QualityReport
4
- VERSION = '1.2.2'
4
+ VERSION = '1.4.0'
5
5
  end
Binary file
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: quality_report
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.2
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robb Shecter
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-11-08 00:00:00.000000000 Z
11
+ date: 2024-11-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubocop
@@ -75,6 +75,7 @@ files:
75
75
  - exe/ruby-quality-report
76
76
  - lib/quality_report.rb
77
77
  - lib/quality_report/version.rb
78
+ - screenshot-1@2x.webp
78
79
  - sig/quality_report.rbs
79
80
  homepage: https://github.com/dogweather/ruby-quality-report
80
81
  licenses: