attractor 2.3.0 → 2.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +1 -1
- data/lib/attractor/calculators/base_calculator.rb +9 -8
- data/lib/attractor/calculators/base_calculator.rb~ +14 -5
- data/lib/attractor/cli.rb +6 -2
- data/lib/attractor/cli.rb~ +18 -1
- data/lib/attractor/reporters/base_reporter.rb +2 -2
- data/lib/attractor/reporters/base_reporter.rb~ +1 -2
- data/lib/attractor/reporters/console_reporter.rb +82 -16
- data/lib/attractor/reporters/console_reporter.rb~ +58 -12
- data/lib/attractor/reporters/html_reporter.rb +8 -8
- data/lib/attractor/suggester.rb +5 -5
- data/lib/attractor/suggester.rb~ +21 -0
- data/lib/attractor/value.rb +4 -0
- data/lib/attractor/version.rb +1 -1
- data/lib/attractor/version.rb~ +3 -0
- data/lib/attractor.rb +2 -2
- metadata +9 -17
- data/lib/attractor/cache.rb~ +0 -67
- data/lib/attractor/detectors/base_detector.rb~ +0 -4
- data/lib/attractor/duration_parser.rb~ +0 -27
- data/lib/attractor/gem_names.rb~ +0 -23
- data/lib/attractor/registry_entry.rb~ +0 -2
- data/lib/attractor/reporters/html_reporter.rb~ +0 -48
- data/lib/attractor/tmp/churn/9f86bc9b7ec6bf59cbf7a111ce8b1159ae128347.json +0 -1
- data/lib/attractor/value.rb~ +0 -30
- data/lib/attractor/watcher.rb~ +0 -24
- data/lib/attractor.rb~ +0 -44
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1eef3d6e8104428fdc8240ae5161654990b53d5351962bc474f11e450db9cbc2
|
4
|
+
data.tar.gz: 6ec7109f7c6aa6e4b5b594863e5bfeff08652f2fdf6c8c2a86a208357c084713
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 244a2d549ba546424c7a4c25aaa59c17b4bd1a8fed73cbdd09851c4296631c4b42d907d4b31a76a8a4f6c584143d8f086764299e00e690bce1f0f19cc69d8bcb
|
7
|
+
data.tar.gz: d508623821df269d803f3fdb0440fbd76f14a5d24cbd169c23cfabf01e1423171b201b1c3ee2c92a986ada20700c790c67ff75d3928bfbf9e4e1d2419e60be65
|
data/Rakefile
CHANGED
@@ -25,7 +25,7 @@ task :assets do
|
|
25
25
|
sass = File.read(File.expand_path("./src/stylesheets/main.scss"))
|
26
26
|
css = SassC::Engine.new(sass, style: :compressed).render
|
27
27
|
prefixed = AutoprefixerRails.process(css)
|
28
|
-
File.
|
28
|
+
File.write(File.expand_path("./app/assets/stylesheets/main.css"), prefixed)
|
29
29
|
|
30
30
|
npm_output = `npm run build`
|
31
31
|
puts npm_output
|
@@ -9,12 +9,13 @@ module Attractor
|
|
9
9
|
class BaseCalculator
|
10
10
|
attr_reader :type
|
11
11
|
|
12
|
-
def initialize(file_prefix: "", ignores: "", file_extension: "rb", minimum_churn_count: 3, start_ago: "5y")
|
12
|
+
def initialize(file_prefix: "", ignores: "", file_extension: "rb", minimum_churn_count: 3, start_ago: "5y", verbose: false)
|
13
13
|
@file_prefix = file_prefix
|
14
14
|
@file_extension = file_extension
|
15
15
|
@minimum_churn_count = minimum_churn_count
|
16
16
|
@start_date = Date.today - Attractor::DurationParser.new(start_ago).duration
|
17
17
|
@ignores = ignores
|
18
|
+
@verbose = verbose
|
18
19
|
end
|
19
20
|
|
20
21
|
def calculate
|
@@ -26,7 +27,7 @@ module Attractor
|
|
26
27
|
ignores: @ignores
|
27
28
|
).report(false)
|
28
29
|
|
29
|
-
puts "Calculating churn and complexity values for #{churn[:churn][:changes].size} #{type} files"
|
30
|
+
puts "Calculating churn and complexity values for #{churn[:churn][:changes].size} #{type} files" if @verbose
|
30
31
|
|
31
32
|
values = churn[:churn][:changes].map do |change|
|
32
33
|
history = git_history_for_file(file_path: change[:file_path])
|
@@ -40,20 +41,20 @@ module Attractor
|
|
40
41
|
complexity, details = yield(change)
|
41
42
|
|
42
43
|
value = Value.new(file_path: change[:file_path],
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
44
|
+
churn: change[:times_changed],
|
45
|
+
complexity: complexity,
|
46
|
+
details: details,
|
47
|
+
history: history)
|
47
48
|
Cache.write(file_path: change[:file_path], value: value)
|
48
49
|
end
|
49
50
|
|
50
|
-
print "."
|
51
|
+
print "." if @verbose
|
51
52
|
value
|
52
53
|
end
|
53
54
|
|
54
55
|
Cache.persist!
|
55
56
|
|
56
|
-
print "\n\n"
|
57
|
+
print "\n\n" if @verbose
|
57
58
|
|
58
59
|
values
|
59
60
|
end
|
@@ -26,7 +26,9 @@ module Attractor
|
|
26
26
|
ignores: @ignores
|
27
27
|
).report(false)
|
28
28
|
|
29
|
-
churn[:churn][:changes].
|
29
|
+
puts "Calculating churn and complexity values for #{churn[:churn][:changes].size} #{type} files"
|
30
|
+
|
31
|
+
values = churn[:churn][:changes].map do |change|
|
30
32
|
history = git_history_for_file(file_path: change[:file_path])
|
31
33
|
commit = history&.first&.first
|
32
34
|
|
@@ -38,15 +40,22 @@ module Attractor
|
|
38
40
|
complexity, details = yield(change)
|
39
41
|
|
40
42
|
value = Value.new(file_path: change[:file_path],
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
43
|
+
churn: change[:times_changed],
|
44
|
+
complexity: complexity,
|
45
|
+
details: details,
|
46
|
+
history: history)
|
45
47
|
Cache.write(file_path: change[:file_path], value: value)
|
46
48
|
end
|
47
49
|
|
50
|
+
print "."
|
48
51
|
value
|
49
52
|
end
|
53
|
+
|
54
|
+
Cache.persist!
|
55
|
+
|
56
|
+
print "\n\n"
|
57
|
+
|
58
|
+
values
|
50
59
|
end
|
51
60
|
|
52
61
|
private
|
data/lib/attractor/cli.rb
CHANGED
@@ -8,6 +8,7 @@ module Attractor
|
|
8
8
|
# contains methods implementing the CLI
|
9
9
|
class CLI < Thor
|
10
10
|
shared_options = [[:file_prefix, aliases: :p],
|
11
|
+
[:verbose, aliases: :v, type: :boolean],
|
11
12
|
[:ignore, aliases: :i, default: ""],
|
12
13
|
[:watch, aliases: :w, type: :boolean],
|
13
14
|
[:minimum_churn, aliases: :c, type: :numeric, default: 3],
|
@@ -45,10 +46,12 @@ module Attractor
|
|
45
46
|
shared_options.each do |shared_option|
|
46
47
|
option(*shared_option)
|
47
48
|
end
|
49
|
+
option(:format, aliases: :f, default: :table)
|
48
50
|
def calc
|
49
51
|
file_prefix = options[:file_prefix]
|
52
|
+
output_format = options[:format]
|
50
53
|
|
51
|
-
report! Attractor::ConsoleReporter.new(file_prefix: file_prefix, ignores: options[:ignore], calculators: calculators(options))
|
54
|
+
report! Attractor::ConsoleReporter.new(file_prefix: file_prefix, ignores: options[:ignore], calculators: calculators(options), format: output_format)
|
52
55
|
rescue RuntimeError => e
|
53
56
|
puts "Runtime error: #{e.message}"
|
54
57
|
end
|
@@ -84,7 +87,8 @@ module Attractor
|
|
84
87
|
file_prefix: options[:file_prefix],
|
85
88
|
minimum_churn_count: options[:minimum_churn],
|
86
89
|
ignores: options[:ignore],
|
87
|
-
start_ago: options[:start_ago]
|
90
|
+
start_ago: options[:start_ago],
|
91
|
+
verbose: options[:verbose])
|
88
92
|
end
|
89
93
|
|
90
94
|
def report!(reporter)
|
data/lib/attractor/cli.rb~
CHANGED
@@ -26,14 +26,31 @@ module Attractor
|
|
26
26
|
puts "Runtime error: #{e.message}"
|
27
27
|
end
|
28
28
|
|
29
|
+
desc "clean", "Clears attractor's cache"
|
30
|
+
def clean
|
31
|
+
puts "Clearing attractor cache"
|
32
|
+
Attractor.clear
|
33
|
+
end
|
34
|
+
|
35
|
+
desc "init", "Initializes attractor's cache"
|
36
|
+
shared_options.each do |shared_option|
|
37
|
+
option(*shared_option)
|
38
|
+
end
|
39
|
+
def init
|
40
|
+
puts "Warming attractor cache"
|
41
|
+
Attractor.init(calculators(options))
|
42
|
+
end
|
43
|
+
|
29
44
|
desc "calc", "Calculates churn and complexity for all ruby files in current directory"
|
30
45
|
shared_options.each do |shared_option|
|
31
46
|
option(*shared_option)
|
32
47
|
end
|
48
|
+
option(:format, aliases: :f, default: :table)
|
33
49
|
def calc
|
34
50
|
file_prefix = options[:file_prefix]
|
51
|
+
output_format = options[:format]
|
35
52
|
|
36
|
-
report! Attractor::ConsoleReporter.new(file_prefix: file_prefix, ignores: options[:ignore], calculators: calculators(options))
|
53
|
+
report! Attractor::ConsoleReporter.new(file_prefix: file_prefix, ignores: options[:ignore], calculators: calculators(options), format: output_format)
|
37
54
|
rescue RuntimeError => e
|
38
55
|
puts "Runtime error: #{e.message}"
|
39
56
|
end
|
@@ -19,7 +19,7 @@ module Attractor
|
|
19
19
|
@file_prefix = file_prefix || ""
|
20
20
|
@calculators = calculators
|
21
21
|
@open_browser = open_browser
|
22
|
-
@suggester = Suggester.new
|
22
|
+
@suggester = Suggester.new
|
23
23
|
|
24
24
|
@watcher = Watcher.new(@file_prefix, ignores, lambda do
|
25
25
|
report
|
@@ -36,7 +36,7 @@ module Attractor
|
|
36
36
|
|
37
37
|
def report
|
38
38
|
@suggestions = @suggester.suggest
|
39
|
-
@types =
|
39
|
+
@types = @calculators.map { |calc| [calc.first, calc.last.type] }.to_h
|
40
40
|
end
|
41
41
|
|
42
42
|
def render
|
@@ -19,7 +19,6 @@ module Attractor
|
|
19
19
|
@file_prefix = file_prefix || ""
|
20
20
|
@calculators = calculators
|
21
21
|
@open_browser = open_browser
|
22
|
-
@values = @calculators.first.last.calculate
|
23
22
|
@suggester = Suggester.new(values)
|
24
23
|
|
25
24
|
@watcher = Watcher.new(@file_prefix, ignores, lambda do
|
@@ -37,7 +36,7 @@ module Attractor
|
|
37
36
|
|
38
37
|
def report
|
39
38
|
@suggestions = @suggester.suggest
|
40
|
-
@types =
|
39
|
+
@types = @calculators.map { |calc| [calc.first, calc.last.type] }.to_h
|
41
40
|
end
|
42
41
|
|
43
42
|
def render
|
@@ -3,26 +3,92 @@
|
|
3
3
|
module Attractor
|
4
4
|
# console reporter
|
5
5
|
class ConsoleReporter < BaseReporter
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
class TableFormatter
|
7
|
+
def call(calculators)
|
8
|
+
puts "Calculated churn and complexity"
|
9
|
+
puts
|
10
|
+
puts "file_path#{" " * 53}complexity churn"
|
11
|
+
puts "-" * 80
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
calculators.each do |calc|
|
14
|
+
# e.g. ['js', JsCalculator']
|
15
|
+
puts calc.last.type
|
16
16
|
|
17
|
-
|
18
|
-
|
17
|
+
values = calc.last.calculate
|
18
|
+
suggester = Suggester.new(values)
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
20
|
+
puts values&.map(&:to_s)
|
21
|
+
puts
|
22
|
+
puts "Suggestions for refactorings:"
|
23
|
+
suggester.suggest&.each { |sug| puts sug.file_path }
|
24
|
+
puts
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class CSVFormatter
|
30
|
+
def call(calculators)
|
31
|
+
require "csv"
|
32
|
+
|
33
|
+
result = CSV.generate do |csv|
|
34
|
+
csv << %w[file_path score complexity churn type refactor]
|
35
|
+
|
36
|
+
calculators.each do |calc|
|
37
|
+
type = calc.last.type
|
38
|
+
values = calc.last.calculate
|
39
|
+
suggester = Suggester.new(values)
|
40
|
+
to_be_refactored = suggester.suggest.map(&:file_path)
|
41
|
+
|
42
|
+
values.each do |value|
|
43
|
+
csv << [value.file_path, value.score, value.complexity, value.churn, type, to_be_refactored.include?(value.file_path)]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
puts result
|
25
49
|
end
|
26
50
|
end
|
51
|
+
|
52
|
+
class JSONFormatter
|
53
|
+
def call(calculators)
|
54
|
+
result = calculators.map do |calc|
|
55
|
+
type = calc.last.type
|
56
|
+
values = calc.last.calculate
|
57
|
+
suggester = Suggester.new(values)
|
58
|
+
to_be_refactored = suggester.suggest.map(&:file_path)
|
59
|
+
|
60
|
+
[
|
61
|
+
type, values.map do |value|
|
62
|
+
{
|
63
|
+
file_path: value.file_path,
|
64
|
+
score: value.score,
|
65
|
+
complexity: value.complexity,
|
66
|
+
churn: value.churn,
|
67
|
+
refactor: to_be_refactored.include?(value.file_path)
|
68
|
+
}
|
69
|
+
end
|
70
|
+
]
|
71
|
+
end
|
72
|
+
|
73
|
+
puts result.to_h.to_json
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def initialize(format:, **other)
|
78
|
+
super(**other)
|
79
|
+
@formatter = case format.to_sym
|
80
|
+
when :csv
|
81
|
+
CSVFormatter.new
|
82
|
+
when :json
|
83
|
+
JSONFormatter.new
|
84
|
+
else
|
85
|
+
TableFormatter.new
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def report
|
90
|
+
super
|
91
|
+
@formatter.call(@calculators)
|
92
|
+
end
|
27
93
|
end
|
28
94
|
end
|
@@ -1,21 +1,67 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Attractor
|
4
4
|
# console reporter
|
5
5
|
class ConsoleReporter < BaseReporter
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
class TableFormatter
|
7
|
+
def call(calculators)
|
8
|
+
puts "Calculated churn and complexity"
|
9
|
+
puts
|
10
|
+
puts "file_path#{" " * 53}complexity churn"
|
11
|
+
puts "-" * 80
|
12
|
+
|
13
|
+
calculators.each do |calc|
|
14
|
+
# e.g. ['js', JsCalculator']
|
15
|
+
puts calc.last.type
|
16
|
+
|
17
|
+
values = calc.last.calculate
|
18
|
+
suggester = Suggester.new(values)
|
19
|
+
|
20
|
+
puts values&.map(&:to_s)
|
21
|
+
puts
|
22
|
+
puts "Suggestions for refactorings:"
|
23
|
+
suggester.suggest&.each { |sug| puts sug.file_path }
|
24
|
+
puts
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class CSVFormatter
|
30
|
+
def call(calculators)
|
31
|
+
require "csv"
|
32
|
+
|
33
|
+
result = CSV.generate do |csv|
|
34
|
+
csv << %w[file_path score complexity churn type refactor]
|
12
35
|
|
13
|
-
|
36
|
+
calculators.each do |calc|
|
37
|
+
type = calc.last.type
|
38
|
+
values = calc.last.calculate
|
39
|
+
suggester = Suggester.new(values)
|
40
|
+
to_be_refactored = suggester.suggest.map(&:file_path)
|
14
41
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
42
|
+
values.each do |value|
|
43
|
+
csv << [value.file_path, value.score, value.complexity, value.churn, type, to_be_refactored.include?(value.file_path)]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
puts result
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def initialize(format:, **other)
|
53
|
+
super(**other)
|
54
|
+
@formatter = case format.to_sym
|
55
|
+
when :csv
|
56
|
+
CSVFormatter.new
|
57
|
+
else
|
58
|
+
TableFormatter.new
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def report
|
63
|
+
super
|
64
|
+
@formatter.call(@calculators)
|
19
65
|
end
|
20
66
|
end
|
21
67
|
end
|
@@ -14,10 +14,10 @@ module Attractor
|
|
14
14
|
FileUtils.mkdir_p "./attractor_output/images"
|
15
15
|
FileUtils.mkdir_p "./attractor_output/javascripts"
|
16
16
|
|
17
|
-
File.
|
18
|
-
File.
|
19
|
-
File.
|
20
|
-
File.
|
17
|
+
File.write("./attractor_output/images/attractor_logo.svg", logo)
|
18
|
+
File.write("./attractor_output/images/attractor_favicon.png", favicon)
|
19
|
+
File.write("./attractor_output/stylesheets/main.css", css)
|
20
|
+
File.write("./attractor_output/javascripts/index.pack.js", javascript_pack)
|
21
21
|
|
22
22
|
if @calculators.size > 1
|
23
23
|
@calculators.each do |calc|
|
@@ -25,8 +25,8 @@ module Attractor
|
|
25
25
|
suggester = Suggester.new(values(type: @short_type))
|
26
26
|
@suggestions = suggester.suggest
|
27
27
|
|
28
|
-
File.
|
29
|
-
File.
|
28
|
+
File.write("./attractor_output/javascripts/index.#{@short_type}.js", javascript)
|
29
|
+
File.write("./attractor_output/index.#{@short_type}.html", render)
|
30
30
|
puts "Generated HTML report at #{File.expand_path "./attractor_output/"}/index.#{@short_type}.html"
|
31
31
|
end
|
32
32
|
|
@@ -35,8 +35,8 @@ module Attractor
|
|
35
35
|
puts "Opening browser window..."
|
36
36
|
end
|
37
37
|
else
|
38
|
-
File.
|
39
|
-
File.
|
38
|
+
File.write("./attractor_output/javascripts/index.js", javascript)
|
39
|
+
File.write("./attractor_output/index.html", render)
|
40
40
|
puts "Generated HTML report at #{File.expand_path "./attractor_output/index.html"}"
|
41
41
|
|
42
42
|
if @open_browser
|
data/lib/attractor/suggester.rb
CHANGED
@@ -5,17 +5,17 @@ module Attractor
|
|
5
5
|
class Suggester
|
6
6
|
attr_accessor :values
|
7
7
|
|
8
|
-
def initialize(values)
|
9
|
-
@values = values
|
8
|
+
def initialize(values = [])
|
9
|
+
@values = values
|
10
10
|
end
|
11
11
|
|
12
12
|
def suggest(threshold = 95)
|
13
|
-
products = @values.map
|
13
|
+
products = @values.map(&:score)
|
14
14
|
products.extend(DescriptiveStatistics)
|
15
15
|
quantile = products.percentile(threshold.to_i)
|
16
16
|
|
17
|
-
@values.select { |val| val.
|
18
|
-
.sort_by { |val| val.
|
17
|
+
@values.select { |val| val.score > quantile }
|
18
|
+
.sort_by { |val| val.score }.reverse
|
19
19
|
end
|
20
20
|
end
|
21
21
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Attractor
|
4
|
+
# makes suggestions for refactorings
|
5
|
+
class Suggester
|
6
|
+
attr_accessor :values
|
7
|
+
|
8
|
+
def initialize(values)
|
9
|
+
@values = values || []
|
10
|
+
end
|
11
|
+
|
12
|
+
def suggest(threshold = 95)
|
13
|
+
products = @values.map(&:score)
|
14
|
+
products.extend(DescriptiveStatistics)
|
15
|
+
quantile = products.percentile(threshold.to_i)
|
16
|
+
|
17
|
+
@values.select { |val| val.score > quantile }
|
18
|
+
.sort_by { |val| val.score }.reverse
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/attractor/value.rb
CHANGED
data/lib/attractor/version.rb
CHANGED
data/lib/attractor.rb
CHANGED
@@ -43,9 +43,9 @@ module Attractor
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def all_registered_calculators(options = {})
|
46
|
-
|
46
|
+
@registry_entries.map do |type, entry|
|
47
47
|
[type, entry.calculator_class.new(**options)] if entry.detector_class.new.detect
|
48
|
-
end.compact
|
48
|
+
end.compact.to_h
|
49
49
|
end
|
50
50
|
|
51
51
|
module_function :calculators_for_type
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: attractor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Julian Rubisch
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-04-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: churn
|
@@ -156,28 +156,28 @@ dependencies:
|
|
156
156
|
requirements:
|
157
157
|
- - "~>"
|
158
158
|
- !ruby/object:Gem::Version
|
159
|
-
version: 0.
|
159
|
+
version: 0.3.0
|
160
160
|
type: :development
|
161
161
|
prerelease: false
|
162
162
|
version_requirements: !ruby/object:Gem::Requirement
|
163
163
|
requirements:
|
164
164
|
- - "~>"
|
165
165
|
- !ruby/object:Gem::Version
|
166
|
-
version: 0.
|
166
|
+
version: 0.3.0
|
167
167
|
- !ruby/object:Gem::Dependency
|
168
168
|
name: attractor-ruby
|
169
169
|
requirement: !ruby/object:Gem::Requirement
|
170
170
|
requirements:
|
171
171
|
- - "~>"
|
172
172
|
- !ruby/object:Gem::Version
|
173
|
-
version: 0.
|
173
|
+
version: 0.3.0
|
174
174
|
type: :development
|
175
175
|
prerelease: false
|
176
176
|
version_requirements: !ruby/object:Gem::Requirement
|
177
177
|
requirements:
|
178
178
|
- - "~>"
|
179
179
|
- !ruby/object:Gem::Version
|
180
|
-
version: 0.
|
180
|
+
version: 0.3.0
|
181
181
|
- !ruby/object:Gem::Dependency
|
182
182
|
name: autoprefixer-rails
|
183
183
|
requirement: !ruby/object:Gem::Requirement
|
@@ -371,35 +371,27 @@ files:
|
|
371
371
|
- app/views/index.html.erb
|
372
372
|
- exe/attractor
|
373
373
|
- lib/attractor.rb
|
374
|
-
- lib/attractor.rb~
|
375
374
|
- lib/attractor/cache.rb
|
376
|
-
- lib/attractor/cache.rb~
|
377
375
|
- lib/attractor/calculators/base_calculator.rb
|
378
376
|
- lib/attractor/calculators/base_calculator.rb~
|
379
377
|
- lib/attractor/cli.rb
|
380
378
|
- lib/attractor/cli.rb~
|
381
379
|
- lib/attractor/detectors/base_detector.rb
|
382
|
-
- lib/attractor/detectors/base_detector.rb~
|
383
380
|
- lib/attractor/duration_parser.rb
|
384
|
-
- lib/attractor/duration_parser.rb~
|
385
381
|
- lib/attractor/gem_names.rb
|
386
|
-
- lib/attractor/gem_names.rb~
|
387
382
|
- lib/attractor/registry_entry.rb
|
388
|
-
- lib/attractor/registry_entry.rb~
|
389
383
|
- lib/attractor/reporters/base_reporter.rb
|
390
384
|
- lib/attractor/reporters/base_reporter.rb~
|
391
385
|
- lib/attractor/reporters/console_reporter.rb
|
392
386
|
- lib/attractor/reporters/console_reporter.rb~
|
393
387
|
- lib/attractor/reporters/html_reporter.rb
|
394
|
-
- lib/attractor/reporters/html_reporter.rb~
|
395
388
|
- lib/attractor/reporters/sinatra_reporter.rb
|
396
389
|
- lib/attractor/suggester.rb
|
397
|
-
- lib/attractor/
|
390
|
+
- lib/attractor/suggester.rb~
|
398
391
|
- lib/attractor/value.rb
|
399
|
-
- lib/attractor/value.rb~
|
400
392
|
- lib/attractor/version.rb
|
393
|
+
- lib/attractor/version.rb~
|
401
394
|
- lib/attractor/watcher.rb
|
402
|
-
- lib/attractor/watcher.rb~
|
403
395
|
homepage: https://github.com/julianrubisch/attractor
|
404
396
|
licenses:
|
405
397
|
- MIT
|
@@ -423,7 +415,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
423
415
|
- !ruby/object:Gem::Version
|
424
416
|
version: '0'
|
425
417
|
requirements: []
|
426
|
-
rubygems_version: 3.
|
418
|
+
rubygems_version: 3.4.12
|
427
419
|
signing_key:
|
428
420
|
specification_version: 4
|
429
421
|
summary: Churn vs Complexity Chart Generator
|
data/lib/attractor/cache.rb~
DELETED
@@ -1,67 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "fileutils"
|
4
|
-
require "psych"
|
5
|
-
|
6
|
-
module Attractor
|
7
|
-
class Cache
|
8
|
-
class << self
|
9
|
-
def read(file_path:)
|
10
|
-
adapter.read(file_path: file_path)
|
11
|
-
end
|
12
|
-
|
13
|
-
def write(file_path:, value:)
|
14
|
-
adapter.write(file_path: file_path, value: value)
|
15
|
-
end
|
16
|
-
|
17
|
-
private
|
18
|
-
|
19
|
-
def adapter
|
20
|
-
@@adapter ||= CacheAdapter::JSON.instance
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
module CacheAdapter
|
26
|
-
class Base
|
27
|
-
include Singleton
|
28
|
-
end
|
29
|
-
|
30
|
-
class JSON < Base
|
31
|
-
def initialize
|
32
|
-
super
|
33
|
-
|
34
|
-
@data_directory = "tmp"
|
35
|
-
FileUtils.mkdir_p @data_directory
|
36
|
-
FileUtils.touch filename
|
37
|
-
|
38
|
-
begin
|
39
|
-
@store = ::JSON.parse(File.read(filename))
|
40
|
-
rescue ::JSON::ParserError
|
41
|
-
@store = {}
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def read(file_path:)
|
46
|
-
value_hash = @store[file_path]
|
47
|
-
|
48
|
-
Value.new(**value_hash.values.first.transform_keys(&:to_sym)) unless value_hash.nil?
|
49
|
-
rescue ArgumentError => e
|
50
|
-
puts "Couldn't rehydrate value from cache: #{e.message}"
|
51
|
-
nil
|
52
|
-
end
|
53
|
-
|
54
|
-
def write(file_path:, value:)
|
55
|
-
mappings = {x: :churn, y: :complexity}
|
56
|
-
|
57
|
-
transformed_value = value.to_h.transform_keys { |k| mappings[k] || k }
|
58
|
-
@store[file_path] = {value.current_commit => transformed_value}
|
59
|
-
File.write(filename, ::JSON.dump(@store))
|
60
|
-
end
|
61
|
-
|
62
|
-
def filename
|
63
|
-
"#{@data_directory}/attractor-cache.json"
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
@@ -1,27 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Attractor
|
4
|
-
# converts a duration string into an amount of days
|
5
|
-
class DurationParser
|
6
|
-
TOKENS = {
|
7
|
-
"d" => 1,
|
8
|
-
"w" => 7,
|
9
|
-
"m" => 30,
|
10
|
-
"y" => 365
|
11
|
-
}.freeze
|
12
|
-
|
13
|
-
attr_reader :duration
|
14
|
-
|
15
|
-
def initialize(input)
|
16
|
-
@input = input
|
17
|
-
@duration = 0
|
18
|
-
parse
|
19
|
-
end
|
20
|
-
|
21
|
-
def parse
|
22
|
-
@input.scan(/(\d+)(\w)/).each do |amount, measure|
|
23
|
-
@duration += amount.to_i * TOKENS[measure]
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
data/lib/attractor/gem_names.rb~
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
module Attractor
|
2
|
-
|
3
|
-
# from https://github.com/prontolabs/pronto/blob/master/lib/pronto/gem_names.rb
|
4
|
-
class GemNames
|
5
|
-
def to_a
|
6
|
-
gems.map { |gem| gem.name.sub(/^pronto-/, '') }.uniq.sort
|
7
|
-
end
|
8
|
-
|
9
|
-
private
|
10
|
-
|
11
|
-
def gems
|
12
|
-
Gem::Specification.find_all.select do |gem|
|
13
|
-
if gem.name =~ /^pronto-/
|
14
|
-
true
|
15
|
-
elsif gem.name != 'pronto'
|
16
|
-
runner_path = File.join(gem.full_gem_path,
|
17
|
-
"lib/pronto/#{gem.name}.rb")
|
18
|
-
File.exist?(runner_path)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
@@ -1,48 +0,0 @@
|
|
1
|
-
module Attractor
|
2
|
-
# HTML reporter
|
3
|
-
class HtmlReporter < BaseReporter
|
4
|
-
def report
|
5
|
-
super
|
6
|
-
|
7
|
-
puts 'Generating an HTML report'
|
8
|
-
@serve_static = true
|
9
|
-
|
10
|
-
FileUtils.mkdir_p './attractor_output'
|
11
|
-
FileUtils.mkdir_p './attractor_output/stylesheets'
|
12
|
-
FileUtils.mkdir_p './attractor_output/images'
|
13
|
-
FileUtils.mkdir_p './attractor_output/javascripts'
|
14
|
-
|
15
|
-
File.open('./attractor_output/images/attractor_logo.svg', 'w') { |file| file.write(logo) }
|
16
|
-
File.open('./attractor_output/stylesheets/main.css', 'w') { |file| file.write(css) }
|
17
|
-
File.open('./attractor_output/javascripts/index.js', 'w') { |file| file.write(javascript) }
|
18
|
-
File.open('./attractor_output/javascripts/index.pack.js', 'w') { |file| file.write(javascript_pack) }
|
19
|
-
|
20
|
-
File.open('./attractor_output/index.html', 'w') { |file| file.write(render) }
|
21
|
-
puts "Generated HTML report at #{File.expand_path './attractor_output/index.html'}"
|
22
|
-
|
23
|
-
Launchy.open(File.expand_path('./attractor_output/index.html'))
|
24
|
-
end
|
25
|
-
|
26
|
-
def logo
|
27
|
-
File.read(File.expand_path('../../../app/assets/images/attractor_logo.svg', __dir__))
|
28
|
-
end
|
29
|
-
|
30
|
-
def css
|
31
|
-
File.read(File.expand_path('../../../app/assets/stylesheets/main.css', __dir__))
|
32
|
-
end
|
33
|
-
|
34
|
-
def javascript_pack
|
35
|
-
File.read(File.expand_path('../../../app/assets/javascripts/index.pack.js', __dir__))
|
36
|
-
end
|
37
|
-
|
38
|
-
def javascript
|
39
|
-
template = Tilt.new(File.expand_path('../../../app/assets/javascripts/index.js.erb', __dir__))
|
40
|
-
template.render self
|
41
|
-
end
|
42
|
-
|
43
|
-
def render
|
44
|
-
template = Tilt.new(File.expand_path('../../../app/views/index.html.erb', __dir__))
|
45
|
-
template.render self
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
@@ -1 +0,0 @@
|
|
1
|
-
{"churn":{"changes":[{"file_path":"app/assets/javascripts/index.pack.js","times_changed":21},{"file_path":"src/javascript/functions.js","times_changed":16},{"file_path":"src/javascript/index.js","times_changed":4}],"class_churn":[],"method_churn":[],"changed_files":[".gitignore","Rakefile","attractor.gemspec","lib/attractor.rb","lib/attractor/calculators/js_calculator.rb","lib/attractor/cli.rb","src/javascript/calculator/index.js","src/javascript/calculator/package-lock.json","src/javascript/calculator/package.json","src/javascript/calculator/webpack.config.js"],"changed_classes":[],"changed_methods":[]}}
|
data/lib/attractor/value.rb~
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "json"
|
4
|
-
|
5
|
-
module Attractor
|
6
|
-
# holds a churn/complexity value
|
7
|
-
class Value
|
8
|
-
attr_reader :file_path, :churn, :complexity, :details, :history
|
9
|
-
|
10
|
-
def initialize(file_path: "", churn: 1, complexity: 0, details: [], history: [])
|
11
|
-
@file_path = file_path
|
12
|
-
@churn = churn
|
13
|
-
@complexity = complexity
|
14
|
-
@details = details
|
15
|
-
@history = history
|
16
|
-
end
|
17
|
-
|
18
|
-
def to_s
|
19
|
-
format("%-64s%8.1f%8i", @file_path, @complexity, @churn)
|
20
|
-
end
|
21
|
-
|
22
|
-
def to_h
|
23
|
-
{file_path: file_path, x: churn, y: complexity, details: details, history: history}
|
24
|
-
end
|
25
|
-
|
26
|
-
def to_json(_opt)
|
27
|
-
to_h.to_json
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
data/lib/attractor/watcher.rb~
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Attractor
|
4
|
-
# functionality for watching file system changes
|
5
|
-
class Watcher
|
6
|
-
def initialize(file_prefix, callback)
|
7
|
-
@file_prefix = file_prefix
|
8
|
-
@callback = callback
|
9
|
-
end
|
10
|
-
|
11
|
-
def watch
|
12
|
-
@callback.call
|
13
|
-
|
14
|
-
listener = Listen.to(@file_prefix, ignore: /^attractor_output/) do |modified, _added, _removed|
|
15
|
-
if modified
|
16
|
-
puts "#{modified.map(&:to_s).join(', ')} modified, recalculating..."
|
17
|
-
@callback.call
|
18
|
-
end
|
19
|
-
end
|
20
|
-
listener.start
|
21
|
-
sleep
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
data/lib/attractor.rb~
DELETED
@@ -1,44 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "attractor/version"
|
4
|
-
require "attractor/gem_names"
|
5
|
-
require "attractor/duration_parser"
|
6
|
-
require "attractor/calculators/base_calculator"
|
7
|
-
require "attractor/detectors/base_detector"
|
8
|
-
require "attractor/reporters/base_reporter"
|
9
|
-
require "attractor/suggester"
|
10
|
-
require "attractor/watcher"
|
11
|
-
require "attractor/cache"
|
12
|
-
|
13
|
-
Dir[File.join(__dir__, "attractor", "reporters", "*.rb")].sort.each do |file|
|
14
|
-
next if file.start_with?("base")
|
15
|
-
|
16
|
-
require file
|
17
|
-
end
|
18
|
-
|
19
|
-
module Attractor
|
20
|
-
class Error < StandardError; end
|
21
|
-
|
22
|
-
@registry_entries = {}
|
23
|
-
|
24
|
-
def register(registry_entry)
|
25
|
-
@registry_entries[registry_entry.type] = registry_entry
|
26
|
-
end
|
27
|
-
|
28
|
-
def calculators_for_type(type, **options)
|
29
|
-
registry_entry_for_type = @registry_entries[type]
|
30
|
-
|
31
|
-
return {type => registry_entry_for_type.calculator_class.new(**options)} if type
|
32
|
-
|
33
|
-
Hash[@registry_entries.map do |type, entry|
|
34
|
-
[type, entry.calculator_class.new(**options)] if entry.detector_class.new.detect
|
35
|
-
end.compact]
|
36
|
-
end
|
37
|
-
|
38
|
-
module_function :calculators_for_type
|
39
|
-
module_function :register
|
40
|
-
end
|
41
|
-
|
42
|
-
Attractor::GemNames.new.to_a.each do |gem_name|
|
43
|
-
require "attractor/#{gem_name}"
|
44
|
-
end
|