quality_report 1.4.0 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c36b81a22b99492d9995e4446769315fe3a01df58e39ed71a3a1050bc05f1791
4
- data.tar.gz: ebc3e1150b528d795fd72e926a462a93aca6311f4a232a3d94c04416bc0a7a7e
3
+ metadata.gz: eb26bd22c7b99d601f0eca7648de96e15dc06ac06b405c887cca9c92e96d353f
4
+ data.tar.gz: 03bc3e1613cc9bdb6b84d123164ec213051054d5f05b9627ab57394ca9df721f
5
5
  SHA512:
6
- metadata.gz: 1e9379b163149edcab1180868a320b7b5529f6c73b5a8c90cc2ffe3292fa818ccc1a48a1cfef9fb6a319fd9dbdf348fc9e6f66749d9b82e4886f2f4a42a50d4d
7
- data.tar.gz: f9911d996413da6cf9264b34749e318d01a1c64e85925c76dae7da609966495341e7fc9d5956866b32c477cc7adbf9f0fbbe50bd0422773242b69e16a122003f
6
+ metadata.gz: 4a35e38922d52d49f34d6e4269e76aa1e43820f7c3d22ab569aa92667397cf3a1b9b0bfeed46fbc188bf3fcf9911166c6ec8372f074c033272e378b21a9e3383
7
+ data.tar.gz: d841f2ecc4afc0bbefeacc3500be6132e976d82da5705759151527d8be64b03cfdb96083e2819eff1590d725d1f34e59b644c349bb6c08ef09146bf4e893211a
data/.rubocop.yml CHANGED
@@ -1,2 +1,2 @@
1
1
  AllCops:
2
- TargetRubyVersion: 3.1
2
+ TargetRubyVersion: 3.1
data/README.md CHANGED
@@ -3,11 +3,13 @@
3
3
  ## Installation
4
4
 
5
5
  ```sh
6
- gem install quality-report
6
+ gem install quality_report
7
7
  ```
8
8
 
9
9
  ## Usage
10
10
 
11
+ Change to a project directory that contains `*.rb` files. Then:
12
+
11
13
  ```sh
12
14
  ruby-quality-report
13
15
  ```
@@ -16,7 +18,7 @@ After a bit of a wait, it outputs a report in table form:
16
18
 
17
19
  ![Screenshot](screenshot-1@2x.webp)
18
20
 
19
- This is showing, for each author, the percentage of problematic lines of code. Lower is better.
21
+ This is showing, for each author, the percentage of problematic lines of code in the repo. Lower is better.
20
22
 
21
23
 
22
24
  ### For improved relevance, it has two filters.
@@ -44,7 +46,7 @@ It runs a subset of [Rubocop Metrics cops](https://docs.rubocop.org/rubocop/cops
44
46
  - [Method Length](https://docs.rubocop.org/rubocop/cops_metrics.html#metricsmethodlength)
45
47
  - [Perceived Complexity](https://docs.rubocop.org/rubocop/cops_metrics.html#metricsperceivedcomplexity)
46
48
 
47
- It then calculates the percentage of warnings per line written, per author. Each failing check is another warning.
49
+ Then, using git, it calculates the percentage of warnings per line written, per author. Each failing check is another warning.
48
50
 
49
51
 
50
52
  ## Intent
@@ -56,7 +58,7 @@ This is a team management tool to:
56
58
 
57
59
  ## Foundational Research
58
60
 
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.
61
+ 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 create a lot of commits. But they also create a lot of bugs. Their productivity is illusory.
60
62
 
61
63
  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
64
 
@@ -69,13 +71,13 @@ Complex code introduces bugs in a second, more subtle way. This is because code
69
71
 
70
72
  ## Roadmap
71
73
 
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.
74
+ - [x] Colorize the output to separate high, medium, and low warning percentages.
75
+ - [x] Fix the numerical alignment.
77
76
  - [ ] Add a progress bar.
77
+ - [ ] Speed up the scan.
78
78
  - [ ] Refactor from script-style coding to a more standard Ruby project.
79
+ - [ ] Make the filters configurable.
80
+ - [ ] Add a `--csv` option.
79
81
 
80
82
 
81
83
  ## Contributing
data/Rakefile CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "bundler/gem_tasks"
4
- require "rspec/core/rake_task"
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
5
 
6
6
  RSpec::Core::RakeTask.new(:spec)
7
7
 
@@ -1,6 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
+ require 'json'
5
+ require 'tty-option'
4
6
  require 'tty-table'
5
7
  require 'pastel'
6
8
 
@@ -43,17 +45,27 @@ def generate_csv(combined_stats)
43
45
  .join("\n")
44
46
  end
45
47
 
46
- def generate_table(combined_stats)
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) }
48
+ def generate_table(combined_stats, colspec: {}, skip: '')
49
+ default_headings = ['Author', 'Percent Flagged', 'Flagged Lines', 'All lines']
50
+ static_heading = colspec.keys&.first # Handle just one column for now
51
+ all_headings = (default_headings + [static_heading]).compact
52
+
53
+ table = TTY::Table.new(header: all_headings)
54
+ table_data = generate_data(combined_stats).reject { should_skip?(_1) }
55
+ static_values = colspec[static_heading] || {}
49
56
 
50
57
  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 }
58
+ next if skip.include?(stats[:label])
59
+
60
+ new_row = [
61
+ { value: stats[:label], alignment: :left },
62
+ { value: "#{stats[:percent]}%", alignment: :right },
63
+ { value: int_to_accounting(stats[:part_count]), alignment: :right },
64
+ { value: int_to_accounting(stats[:whole_count]), alignment: :right }
56
65
  ]
66
+ new_row << { value: static_values[stats[:label]], alignment: :left } if static_heading
67
+
68
+ table << new_row
57
69
  end
58
70
 
59
71
  render_table(table, table_data)
@@ -109,15 +121,99 @@ def no_commits_in_last_60_days?(stats)
109
121
  last_commit_time < sixty_days_ago
110
122
  end
111
123
 
124
+ ###
125
+ # Converts a float to an integer percentage
126
+ #
112
127
  def float_to_percent(a_float)
113
- (Float(a_float) * 100).round(1)
128
+ (Float(a_float) * 100).round
129
+ end
130
+
131
+ ##
132
+ # Converts an integer to a string with commas separating thousands.
133
+ #
134
+ # An alternative implementation is:
135
+ # an_int.to_s.gsub(/(\d)(?=(\d{3})+(?!\d))/, '\\1,')
136
+ #
137
+ def int_to_accounting(an_int)
138
+ an_int.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
139
+ end
140
+
141
+ #
142
+ # CLI Argument Parser
143
+ #
144
+ class Command
145
+ include TTY::Option
146
+
147
+ usage do
148
+ program 'ruby-quality-report'
149
+ no_command
150
+ header 'Ruby Quality Report'
151
+ desc <<~DESC
152
+ Generates a report of the percentage of flagged lines of code written by each author. For improved relevance, it excludes:
153
+
154
+ - authors with fewer than 200 lines of code
155
+ - authors with no commits in the previous 60 days
156
+
157
+ It runs a suite of Rubocop metrics on *.rb files recursively starting in the current directory. Then, using git, it calculates the percentage of warnings per line written, per author. Each failing check is another warning.
158
+ DESC
159
+
160
+ example <<~EXAMPLE1
161
+ Append a column showing office location. The order of the keys in the JSON does not matter:
162
+ $ ruby-quality-report -c '{"Location": {"Amy": "NYC", "Bob": "Remote"}}'
163
+ EXAMPLE1
164
+
165
+ example <<~EXAMPLE2
166
+ Skip one author:
167
+ $ ruby-quality-report --skip Cathy
168
+ EXAMPLE2
169
+ end
170
+
171
+ option :add_column do
172
+ long '--add-column JSON'
173
+ short '-c JSON'
174
+ desc 'Add a static column to the report. This is a simple way to add known information. See the examples.'
175
+ end
176
+
177
+ flag :skip do
178
+ long '--skip AUTHOR'
179
+ short '-s AUTHOR'
180
+ desc 'Filter out a git author'
181
+ end
182
+
183
+ flag :help do
184
+ short '-h'
185
+ long '--help'
186
+ desc 'Print this help'
187
+ end
188
+
189
+ def run
190
+ if params[:help]
191
+ print help
192
+ exit 1
193
+ elsif params.errors.any?
194
+ puts params.errors.summary
195
+ exit 1
196
+ end
197
+ end
114
198
  end
115
199
 
116
200
  #
117
201
  # Execution begins here
118
202
  #
203
+
204
+ command = Command.new
205
+ command.parse
206
+ command.run
207
+
208
+ add_column_param = command.params[:add_column]
209
+ colspec = add_column_param ? JSON.parse(add_column_param) : {}
210
+ puts "Adding column: #{colspec.keys.first}" unless colspec.empty?
211
+
212
+ skip = command.params[:skip] || ''
213
+ puts "Skipping author: #{skip}" if skip != ''
214
+
119
215
  PART_STATS = `ruby-author-warnings | frequency-list`.freeze
120
- WHOLE_STATS = `ruby-line-authors | frequency-list`.freeze
216
+ WHOLE_STATS = `ruby-line-authors | frequency-list`.freeze
121
217
  COMBINED_STATS = create_combined_stats(PART_STATS, WHOLE_STATS)
122
218
 
123
- puts generate_table(COMBINED_STATS)
219
+ puts generate_table(COMBINED_STATS, colspec:, skip:)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module QualityReport
4
- VERSION = '1.4.0'
4
+ VERSION = '1.6.0'
5
5
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "quality_report/version"
3
+ require_relative 'quality_report/version'
4
4
 
5
5
  module QualityReport
6
6
  class Error < StandardError; end
data/screenshot-1@2x.webp CHANGED
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.4.0
4
+ version: 1.6.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-09 00:00:00.000000000 Z
11
+ date: 2024-11-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubocop
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.68'
27
+ - !ruby/object:Gem::Dependency
28
+ name: tty-option
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.3.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.3.0
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: tty-table
29
43
  requirement: !ruby/object:Gem::Requirement