attractor 2.3.0 → 2.5.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/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
|