churn_vs_complexity 1.0.0 → 1.2.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 +4 -4
- data/CHANGELOG.md +12 -1
- data/README.md +6 -1
- data/lib/churn_vs_complexity/churn.rb +8 -10
- data/lib/churn_vs_complexity/cli.rb +23 -16
- data/lib/churn_vs_complexity/concurrent_calculator.rb +16 -6
- data/lib/churn_vs_complexity/config.rb +31 -6
- data/lib/churn_vs_complexity/engine.rb +6 -5
- data/lib/churn_vs_complexity/file_selector.rb +11 -3
- data/lib/churn_vs_complexity/git_date.rb +59 -0
- data/lib/churn_vs_complexity/serializer.rb +50 -7
- data/lib/churn_vs_complexity/version.rb +1 -1
- data/lib/churn_vs_complexity.rb +1 -0
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d748bfb7d21087a5fbcb360dbc05dcd46b5ec05bf2c2fcc542e2e25368084d78
|
4
|
+
data.tar.gz: 0d160c582aee2696823061dd7623cf17e9b537d798ae31502eb30b065a61391d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f521d39fddad8e8adf05ab0f21d8b8adcc2460f9a57c553b90c73eb60451aafed19ab11977768d494987ff72838c856dd04c3910781ec0da34f53d82d18d9d4e
|
7
|
+
data.tar.gz: 3ab17db013540128bd428edb54cf0615f47ce50caa19d1ad418261c20efde494bb36ea8e84408e0ca330f7721013a9e8958f3cd5a16565636186508e59bf7360
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
1
|
## [1.0.0] - 2024-06-07
|
2
2
|
|
3
|
-
- Initial release
|
3
|
+
- Initial release
|
4
|
+
|
5
|
+
## [1.1.0] - 2024-09-20
|
6
|
+
|
7
|
+
- Introduce `--summary` flag to output summary statistics for churn and complexity
|
8
|
+
- Introduce `--month`, `--quarter`, and `--year` short-hand flags to calculate churn for different time periods relative to the most recent commit
|
9
|
+
|
10
|
+
## [1.2.0] - 2024-09-20
|
11
|
+
|
12
|
+
- Fix bug in CLI where new flags and `--since` would not be recognized
|
13
|
+
- Improve selection of observations included in the output
|
14
|
+
- Fixed calculation of churn that would never be zero
|
data/README.md
CHANGED
@@ -31,13 +31,17 @@ In order to use the `--java` flag, you must first install PMD manually, and the
|
|
31
31
|
Execute the `churn_vs_complexity` with the applicable arguments. Output in the requested format will be directed to stdout.
|
32
32
|
|
33
33
|
```
|
34
|
-
churn_vs_complexity [options] folder
|
34
|
+
Usage: churn_vs_complexity [options] folder
|
35
35
|
--java Check complexity of java classes
|
36
36
|
--ruby Check complexity of ruby files
|
37
37
|
--csv Format output as CSV
|
38
38
|
--graph Format output as HTML page with Churn vs Complexity graph
|
39
|
+
--summary Output summary statistics (mean and median) for churn and complexity
|
39
40
|
--excluded PATTERN Exclude file paths including this string. Can be used multiple times.
|
40
41
|
--since YYYY-MM-DD Calculate churn after this date
|
42
|
+
-m, --month Calculate churn for the month leading up to the most recent commit
|
43
|
+
-q, --quarter Calculate churn for the quarter leading up to the most recent commit
|
44
|
+
-y, --year Calculate churn for the year leading up to the most recent commit
|
41
45
|
-h, --help Display help
|
42
46
|
```
|
43
47
|
|
@@ -47,6 +51,7 @@ churn_vs_complexity [options] folder
|
|
47
51
|
|
48
52
|
`churn_vs_complexity --java --graph --exclude generated-sources --exclude generated-test-sources --since 2023-01-01 my_java_project > ~/Desktop/java-demo.html`
|
49
53
|
|
54
|
+
`churn_vs_complexity --ruby --summary -m my_ruby_project >> ~/Desktop/monthly-report.txt`
|
50
55
|
|
51
56
|
|
52
57
|
## Development
|
@@ -7,20 +7,18 @@ module ChurnVsComplexity
|
|
7
7
|
module GitCalculator
|
8
8
|
class << self
|
9
9
|
def calculate(folder:, file:, since:)
|
10
|
-
|
11
|
-
|
10
|
+
git_dir = File.join(folder, '.git')
|
11
|
+
formatted_date = since.strftime('%Y-%m-%d')
|
12
|
+
cmd = %Q(git --git-dir #{git_dir} --work-tree #{folder} log --format="%H" --follow --since="#{formatted_date}" -- #{file} | wc -l)
|
13
|
+
`#{cmd}`.to_i
|
12
14
|
end
|
13
15
|
|
14
|
-
|
15
|
-
|
16
|
-
def calculate_with_follow(folder, file, since)
|
17
|
-
# Format the date as "YYYY-MM-DD"
|
18
|
-
formatted_date = since.strftime('%Y-%m-%d')
|
19
|
-
# git log --follow --oneline --since="YYYY-MM-DD" <file_path> | wc -l
|
20
|
-
`git --git-dir #{File.join(folder,
|
21
|
-
'.git',)} --work-tree #{folder} log --follow --oneline --since=#{formatted_date} #{file} | wc -l`.to_i
|
16
|
+
def date_of_latest_commit(folder:)
|
17
|
+
repo(folder).log.first.date
|
22
18
|
end
|
23
19
|
|
20
|
+
private
|
21
|
+
|
24
22
|
def repo(folder)
|
25
23
|
repos[folder] ||= Git.open(folder)
|
26
24
|
end
|
@@ -9,7 +9,6 @@ module ChurnVsComplexity
|
|
9
9
|
def self.run!
|
10
10
|
# Create an options hash to store parsed options
|
11
11
|
options = { excluded: [] }
|
12
|
-
since = nil
|
13
12
|
|
14
13
|
# Initialize OptionParser
|
15
14
|
OptionParser.new do |opts|
|
@@ -31,13 +30,34 @@ module ChurnVsComplexity
|
|
31
30
|
options[:serializer] = :graph
|
32
31
|
end
|
33
32
|
|
33
|
+
opts.on('--summary', 'Output summary statistics (mean and median) for churn and complexity') do
|
34
|
+
options[:serializer] = :summary
|
35
|
+
end
|
36
|
+
|
34
37
|
opts.on('--excluded PATTERN',
|
35
38
|
'Exclude file paths including this string. Can be used multiple times.',) do |value|
|
36
39
|
options[:excluded] << value
|
37
40
|
end
|
38
41
|
|
39
42
|
opts.on('--since YYYY-MM-DD', 'Calculate churn after this date') do |value|
|
40
|
-
since = value
|
43
|
+
options[:since] = value
|
44
|
+
end
|
45
|
+
|
46
|
+
opts.on('-m', '--month', 'Calculate churn for the month leading up to the most recent commit') do
|
47
|
+
options[:since] = :month
|
48
|
+
end
|
49
|
+
|
50
|
+
opts.on('-q', '--quarter', 'Calculate churn for the quarter leading up to the most recent commit') do
|
51
|
+
options[:since] = :quarter
|
52
|
+
end
|
53
|
+
|
54
|
+
opts.on('-y', '--year', 'Calculate churn for the year leading up to the most recent commit') do
|
55
|
+
options[:since] = :year
|
56
|
+
end
|
57
|
+
|
58
|
+
opts.on('--dry-run', 'Echo the chosen options from the CLI') do
|
59
|
+
puts options
|
60
|
+
exit
|
41
61
|
end
|
42
62
|
|
43
63
|
opts.on('-h', '--help', 'Display help') do
|
@@ -56,24 +76,11 @@ module ChurnVsComplexity
|
|
56
76
|
|
57
77
|
raise Error, 'No options selected. Use --help for usage information.' if options.empty?
|
58
78
|
|
59
|
-
begin
|
60
|
-
if since.nil?
|
61
|
-
since = Time.at(0).to_date
|
62
|
-
options[:graph_title] = 'Churn vs Complexity'
|
63
|
-
else
|
64
|
-
date_string = since
|
65
|
-
since = Date.strptime(since, '%Y-%m-%d')
|
66
|
-
options[:graph_title] = "Churn vs Complexity since #{date_string}"
|
67
|
-
end
|
68
|
-
rescue StandardError
|
69
|
-
raise Error, "Invalid date #{since}, please use correct format, YYYY-MM-DD"
|
70
|
-
end
|
71
|
-
|
72
79
|
config = Config.new(**options)
|
73
80
|
|
74
81
|
config.validate!
|
75
82
|
|
76
|
-
puts config.to_engine.check(folder
|
83
|
+
puts config.to_engine.check(folder:)
|
77
84
|
end
|
78
85
|
end
|
79
86
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'etc'
|
4
|
+
|
3
5
|
module ChurnVsComplexity
|
4
6
|
class ConcurrentCalculator
|
5
7
|
CONCURRENCY = Etc.nprocessors
|
@@ -11,7 +13,9 @@ module ChurnVsComplexity
|
|
11
13
|
end
|
12
14
|
|
13
15
|
def calculate(folder:, files:, since:)
|
14
|
-
|
16
|
+
latest_commit_date = @churn.date_of_latest_commit(folder:)
|
17
|
+
@git_period = GitDate.git_period(since, latest_commit_date)
|
18
|
+
schedule_churn_calculation(folder, files[:included], @git_period.effective_start_date)
|
15
19
|
calculate_complexity(folder, files)
|
16
20
|
await_results
|
17
21
|
combine_results
|
@@ -22,9 +26,11 @@ module ChurnVsComplexity
|
|
22
26
|
def calculate_complexity(folder, files)
|
23
27
|
@complexity_results =
|
24
28
|
if @complexity.folder_based?
|
25
|
-
@complexity.calculate(folder:)
|
29
|
+
result = @complexity.calculate(folder:)
|
30
|
+
files[:explicitly_excluded].each { |file| result.delete(file) }
|
31
|
+
result
|
26
32
|
else
|
27
|
-
files.each_with_object({}) do |file, acc|
|
33
|
+
files[:included].each_with_object({}) do |file, acc|
|
28
34
|
acc.merge!(@complexity.calculate(file:))
|
29
35
|
end
|
30
36
|
end
|
@@ -51,10 +57,14 @@ module ChurnVsComplexity
|
|
51
57
|
end
|
52
58
|
|
53
59
|
def combine_results
|
54
|
-
|
55
|
-
|
56
|
-
|
60
|
+
result = {}
|
61
|
+
result[:values_by_file] = @complexity_results.keys.each_with_object({}) do |file, acc|
|
62
|
+
# File with complexity score might not have churned in queried period,
|
63
|
+
# set zero churn on miss
|
64
|
+
acc[file] = [@churn_results[file] || 0, @complexity_results[file]]
|
57
65
|
end
|
66
|
+
result[:git_period] = @git_period
|
67
|
+
result
|
58
68
|
end
|
59
69
|
end
|
60
70
|
end
|
@@ -6,21 +6,23 @@ module ChurnVsComplexity
|
|
6
6
|
language:,
|
7
7
|
serializer:,
|
8
8
|
excluded: [],
|
9
|
-
|
10
|
-
complexity_validator: ComplexityValidator
|
9
|
+
since: nil,
|
10
|
+
complexity_validator: ComplexityValidator,
|
11
|
+
since_validator: SinceValidator
|
11
12
|
)
|
12
13
|
@language = language
|
13
14
|
@serializer = serializer
|
14
15
|
@excluded = excluded
|
16
|
+
@since = since
|
15
17
|
@complexity_validator = complexity_validator
|
16
|
-
@
|
18
|
+
@since_validator = since_validator
|
17
19
|
end
|
18
20
|
|
19
21
|
def validate!
|
20
22
|
raise Error, "Unsupported language: #{@language}" unless %i[java ruby].include?(@language)
|
21
|
-
raise Error, "Unsupported serializer: #{@serializer}" unless %i[none csv graph].include?(@serializer)
|
22
|
-
raise Error, 'Please provide a title for the graph' if @serializer == :graph && @graph_title.nil?
|
23
|
+
raise Error, "Unsupported serializer: #{@serializer}" unless %i[none csv graph summary].include?(@serializer)
|
23
24
|
|
25
|
+
@since_validator.validate!(@since)
|
24
26
|
@complexity_validator.validate!(@language)
|
25
27
|
end
|
26
28
|
|
@@ -32,6 +34,7 @@ module ChurnVsComplexity
|
|
32
34
|
churn:,
|
33
35
|
file_selector: FileSelector::Java.excluding(@excluded),
|
34
36
|
serializer:,
|
37
|
+
since: @since,
|
35
38
|
)
|
36
39
|
when :ruby
|
37
40
|
Engine.concurrent(
|
@@ -39,6 +42,7 @@ module ChurnVsComplexity
|
|
39
42
|
churn:,
|
40
43
|
file_selector: FileSelector::Ruby.excluding(@excluded),
|
41
44
|
serializer:,
|
45
|
+
since: @since,
|
42
46
|
)
|
43
47
|
end
|
44
48
|
end
|
@@ -54,7 +58,9 @@ module ChurnVsComplexity
|
|
54
58
|
when :csv
|
55
59
|
Serializer::CSV
|
56
60
|
when :graph
|
57
|
-
Serializer::Graph.new
|
61
|
+
Serializer::Graph.new
|
62
|
+
when :summary
|
63
|
+
Serializer::Summary
|
58
64
|
end
|
59
65
|
end
|
60
66
|
|
@@ -66,5 +72,24 @@ module ChurnVsComplexity
|
|
66
72
|
end
|
67
73
|
end
|
68
74
|
end
|
75
|
+
|
76
|
+
module SinceValidator
|
77
|
+
def self.validate!(since)
|
78
|
+
# since can be nil, a date string or a keyword (:month, :quarter, :year)
|
79
|
+
return if since.nil?
|
80
|
+
|
81
|
+
if since.is_a?(Symbol)
|
82
|
+
raise Error, "Invalid since value #{since}" unless %i[month quarter year].include?(since)
|
83
|
+
elsif since.is_a?(String)
|
84
|
+
begin
|
85
|
+
Date.strptime(since, '%Y-%m-%d')
|
86
|
+
rescue StandardError
|
87
|
+
raise Error, "Invalid date #{since}, please use correct format, YYYY-MM-DD"
|
88
|
+
end
|
89
|
+
else
|
90
|
+
raise Error, "Invalid since value #{since}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
69
94
|
end
|
70
95
|
end
|
@@ -2,20 +2,21 @@
|
|
2
2
|
|
3
3
|
module ChurnVsComplexity
|
4
4
|
class Engine
|
5
|
-
def initialize(file_selector:, calculator:, serializer:)
|
5
|
+
def initialize(file_selector:, calculator:, serializer:, since:)
|
6
6
|
@file_selector = file_selector
|
7
7
|
@calculator = calculator
|
8
8
|
@serializer = serializer
|
9
|
+
@since = since
|
9
10
|
end
|
10
11
|
|
11
|
-
def check(folder
|
12
|
+
def check(folder:)
|
12
13
|
files = @file_selector.select_files(folder)
|
13
|
-
result = @calculator.calculate(folder:, files:, since:)
|
14
|
+
result = @calculator.calculate(folder:, files:, since: @since)
|
14
15
|
@serializer.serialize(result)
|
15
16
|
end
|
16
17
|
|
17
|
-
def self.concurrent(complexity:, churn:, serializer: Serializer::None, file_selector: FileSelector::Any)
|
18
|
-
Engine.new(file_selector:, serializer:, calculator: ConcurrentCalculator.new(complexity:, churn:))
|
18
|
+
def self.concurrent(since:, complexity:, churn:, serializer: Serializer::None, file_selector: FileSelector::Any)
|
19
|
+
Engine.new(since:, file_selector:, serializer:, calculator: ConcurrentCalculator.new(complexity:, churn:))
|
19
20
|
end
|
20
21
|
end
|
21
22
|
end
|
@@ -4,7 +4,8 @@ module ChurnVsComplexity
|
|
4
4
|
module FileSelector
|
5
5
|
module Any
|
6
6
|
def self.select_files(folder)
|
7
|
-
Dir.glob("#{folder}/**/*").select { |f| File.file?(f) }
|
7
|
+
included = Dir.glob("#{folder}/**/*").select { |f| File.file?(f) }
|
8
|
+
{ explicitly_excluded: [], included: }
|
8
9
|
end
|
9
10
|
end
|
10
11
|
|
@@ -15,9 +16,16 @@ module ChurnVsComplexity
|
|
15
16
|
end
|
16
17
|
|
17
18
|
def select_files(folder)
|
18
|
-
|
19
|
-
|
19
|
+
were_excluded = []
|
20
|
+
were_included = []
|
21
|
+
Dir.glob("#{folder}/**/*").each do |f|
|
22
|
+
if has_excluded_pattern?(f)
|
23
|
+
were_excluded << f
|
24
|
+
elsif has_correct_extension?(f) && File.file?(f)
|
25
|
+
were_included << f
|
26
|
+
end
|
20
27
|
end
|
28
|
+
{ explicitly_excluded: were_excluded, included: were_included }
|
21
29
|
end
|
22
30
|
|
23
31
|
private
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ChurnVsComplexity
|
4
|
+
module GitDate
|
5
|
+
def self.git_period(cli_arg_since, latest_commit_date)
|
6
|
+
latest_commit_date = latest_commit_date.to_date
|
7
|
+
if cli_arg_since.nil?
|
8
|
+
NoStartGitPeriod.new(latest_commit_date)
|
9
|
+
elsif cli_arg_since.is_a?(Symbol)
|
10
|
+
AbsoluteGitPeriod.looking_back(relative_period: cli_arg_since, from: latest_commit_date)
|
11
|
+
elsif cli_arg_since.is_a?(String)
|
12
|
+
AbsoluteGitPeriod.between(cli_arg_since, latest_commit_date)
|
13
|
+
else
|
14
|
+
raise Error, "Unexpected since value #{cli_arg_since}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class NoStartGitPeriod
|
19
|
+
attr_reader :end_date
|
20
|
+
|
21
|
+
def initialize(end_date)
|
22
|
+
@end_date = end_date
|
23
|
+
end
|
24
|
+
|
25
|
+
def effective_start_date = Time.at(0)
|
26
|
+
|
27
|
+
def requested_start_date = nil
|
28
|
+
end
|
29
|
+
|
30
|
+
class AbsoluteGitPeriod
|
31
|
+
attr_reader :end_date
|
32
|
+
|
33
|
+
def self.between(cli_arg_since, latest_commit_date)
|
34
|
+
start_date = Date.strptime(cli_arg_since, '%Y-%m-%d')
|
35
|
+
new(start_date, latest_commit_date)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.looking_back(relative_period:, from:)
|
39
|
+
shifter = case relative_period
|
40
|
+
when :month then 1
|
41
|
+
when :quarter then 3
|
42
|
+
when :year then 12
|
43
|
+
else raise Error, "Unexpected since value #{relative_period}"
|
44
|
+
end
|
45
|
+
start_date = from << shifter
|
46
|
+
new(start_date, from)
|
47
|
+
end
|
48
|
+
|
49
|
+
def initialize(start_date, end_date)
|
50
|
+
@start_date = start_date
|
51
|
+
@end_date = end_date
|
52
|
+
end
|
53
|
+
|
54
|
+
def effective_start_date = @start_date
|
55
|
+
|
56
|
+
def requested_start_date = @start_date
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -2,12 +2,55 @@
|
|
2
2
|
|
3
3
|
module ChurnVsComplexity
|
4
4
|
module Serializer
|
5
|
+
def self.title(result)
|
6
|
+
requested_start_date = result[:git_period].requested_start_date
|
7
|
+
end_date = result[:git_period].end_date
|
8
|
+
if requested_start_date.nil?
|
9
|
+
"Churn until #{end_date.strftime('%Y-%m-%d')} vs complexity"
|
10
|
+
else
|
11
|
+
"Churn between #{requested_start_date.strftime('%Y-%m-%d')} and #{end_date.strftime('%Y-%m-%d')} vs complexity"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
5
15
|
module None
|
6
|
-
def self.serialize(
|
16
|
+
def self.serialize(result) = result
|
17
|
+
end
|
18
|
+
|
19
|
+
module Summary
|
20
|
+
def self.serialize(result)
|
21
|
+
values_by_file = result[:values_by_file]
|
22
|
+
churn_values = values_by_file.map { |_, values| values[0].to_f }
|
23
|
+
complexity_values = values_by_file.map { |_, values| values[1].to_f }
|
24
|
+
|
25
|
+
mean_churn = churn_values.sum / churn_values.size
|
26
|
+
median_churn = churn_values.sort[churn_values.size / 2]
|
27
|
+
mean_complexity = complexity_values.sum / complexity_values.size
|
28
|
+
median_complexity = complexity_values.sort[complexity_values.size / 2]
|
29
|
+
|
30
|
+
product = values_by_file.map { |_, values| values[0].to_f * values[1].to_f }
|
31
|
+
mean_product = product.sum / product.size
|
32
|
+
median_product = product.sort[product.size / 2]
|
33
|
+
|
34
|
+
<<~SUMMARY
|
35
|
+
#{Serializer.title(result)}
|
36
|
+
|
37
|
+
Number of observations: #{values_by_file.size}
|
38
|
+
|
39
|
+
Churn:
|
40
|
+
Mean #{mean_churn}, Median #{median_churn}
|
41
|
+
|
42
|
+
Complexity:
|
43
|
+
Mean #{mean_complexity}, Median #{median_complexity}
|
44
|
+
|
45
|
+
Product of churn and complexity:
|
46
|
+
Mean #{mean_product}, Median #{median_product}
|
47
|
+
SUMMARY
|
48
|
+
end
|
7
49
|
end
|
8
50
|
|
9
51
|
module CSV
|
10
|
-
def self.serialize(
|
52
|
+
def self.serialize(result)
|
53
|
+
values_by_file = result[:values_by_file]
|
11
54
|
values_by_file.map do |file, values|
|
12
55
|
"#{file},#{values[0]},#{values[1]}\n"
|
13
56
|
end.join
|
@@ -15,16 +58,16 @@ module ChurnVsComplexity
|
|
15
58
|
end
|
16
59
|
|
17
60
|
class Graph
|
18
|
-
def initialize(
|
61
|
+
def initialize(template: Graph.load_template_file)
|
19
62
|
@template = template
|
20
|
-
@title = title
|
21
63
|
end
|
22
64
|
|
23
|
-
def serialize(
|
24
|
-
data = values_by_file.map do |file, values|
|
65
|
+
def serialize(result)
|
66
|
+
data = result[:values_by_file].map do |file, values|
|
25
67
|
"{ file_path: '#{file}', churn: #{values[0]}, complexity: #{values[1]} }"
|
26
68
|
end.join(",\n") + "\n"
|
27
|
-
|
69
|
+
title = Serializer.title(result)
|
70
|
+
@template.gsub("// INSERT DATA\n", data).gsub('INSERT TITLE', title)
|
28
71
|
end
|
29
72
|
|
30
73
|
def self.load_template_file
|
data/lib/churn_vs_complexity.rb
CHANGED
@@ -12,6 +12,7 @@ require_relative 'churn_vs_complexity/churn'
|
|
12
12
|
require_relative 'churn_vs_complexity/cli'
|
13
13
|
require_relative 'churn_vs_complexity/config'
|
14
14
|
require_relative 'churn_vs_complexity/serializer'
|
15
|
+
require_relative 'churn_vs_complexity/git_date'
|
15
16
|
|
16
17
|
module ChurnVsComplexity
|
17
18
|
class Error < StandardError; end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: churn_vs_complexity
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Erik T. Madsen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-09-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: flog
|
@@ -68,6 +68,7 @@ files:
|
|
68
68
|
- lib/churn_vs_complexity/config.rb
|
69
69
|
- lib/churn_vs_complexity/engine.rb
|
70
70
|
- lib/churn_vs_complexity/file_selector.rb
|
71
|
+
- lib/churn_vs_complexity/git_date.rb
|
71
72
|
- lib/churn_vs_complexity/serializer.rb
|
72
73
|
- lib/churn_vs_complexity/version.rb
|
73
74
|
- tmp/pmd-support/ruleset.xml
|
@@ -89,7 +90,7 @@ licenses:
|
|
89
90
|
- MIT
|
90
91
|
metadata:
|
91
92
|
source_code_uri: https://github.com/beatmadsen/churn_vs_complexity
|
92
|
-
changelog_uri: https://github.com/beatmadsen/churn_vs_complexity/CHANGELOG.md
|
93
|
+
changelog_uri: https://github.com/beatmadsen/churn_vs_complexity/blob/main/CHANGELOG.md
|
93
94
|
rubygems_mfa_required: 'true'
|
94
95
|
post_install_message:
|
95
96
|
rdoc_options: []
|