attractor 2.0.3 → 2.3.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/README.md +33 -6
- data/Rakefile +13 -13
- data/app/assets/javascripts/index.pack.js +1 -1
- data/app/assets/stylesheets/main.css +1 -1
- data/exe/attractor +1 -1
- data/lib/attractor.rb +29 -13
- data/lib/attractor.rb~ +44 -0
- data/lib/attractor/cache.rb +82 -0
- data/lib/attractor/cache.rb~ +67 -0
- data/lib/attractor/calculators/base_calculator.rb +38 -14
- data/lib/attractor/calculators/base_calculator.rb~ +64 -0
- data/lib/attractor/cli.rb +48 -44
- data/lib/attractor/cli.rb~ +84 -0
- data/lib/attractor/detectors/base_detector.rb~ +4 -0
- data/lib/attractor/duration_parser.rb +7 -5
- data/lib/attractor/duration_parser.rb~ +27 -0
- data/lib/attractor/gem_names.rb +3 -1
- data/lib/attractor/gem_names.rb~ +23 -0
- data/lib/attractor/registry_entry.rb +0 -1
- data/lib/attractor/registry_entry.rb~ +2 -0
- data/lib/attractor/reporters/base_reporter.rb +22 -14
- data/lib/attractor/reporters/base_reporter.rb~ +54 -0
- data/lib/attractor/reporters/console_reporter.rb +4 -4
- data/lib/attractor/reporters/console_reporter.rb~ +21 -0
- data/lib/attractor/reporters/html_reporter.rb +23 -25
- data/lib/attractor/reporters/html_reporter.rb~ +48 -0
- data/lib/attractor/reporters/sinatra_reporter.rb +18 -24
- data/lib/attractor/suggester.rb +3 -1
- data/lib/attractor/tmp/churn/9f86bc9b7ec6bf59cbf7a111ce8b1159ae128347.json +1 -0
- data/lib/attractor/value.rb +8 -4
- data/lib/attractor/value.rb~ +30 -0
- data/lib/attractor/version.rb +1 -1
- data/lib/attractor/watcher.rb +7 -3
- data/lib/attractor/watcher.rb~ +24 -0
- metadata +29 -31
- data/.babelrc +0 -4
- data/.eslintrc.json +0 -25
- data/.gitignore +0 -20
- data/.rubocop.yml +0 -2
- data/CHANGELOG.md +0 -99
- data/Gemfile +0 -4
- data/Guardfile +0 -27
- data/attractor.gemspec +0 -65
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/bin/standardize +0 -4
- data/bin/test +0 -6
- data/dist/calculator.bundle.js +0 -1
- data/lib/attractor/reporters/.keep +0 -0
- data/package-lock.json +0 -6906
- data/package.json +0 -53
- data/webpack.config.js +0 -23
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "descriptive_statistics/safe"
|
4
|
+
require "fileutils"
|
5
|
+
require "forwardable"
|
6
|
+
require "launchy"
|
7
|
+
require "tilt"
|
8
|
+
|
9
|
+
module Attractor
|
10
|
+
# base reporter
|
11
|
+
class BaseReporter
|
12
|
+
extend Forwardable
|
13
|
+
attr_accessor :file_prefix
|
14
|
+
attr_reader :types
|
15
|
+
attr_writer :values
|
16
|
+
def_delegator :@watcher, :watch
|
17
|
+
|
18
|
+
def initialize(calculators:, file_prefix: "", ignores: "", open_browser: true)
|
19
|
+
@file_prefix = file_prefix || ""
|
20
|
+
@calculators = calculators
|
21
|
+
@open_browser = open_browser
|
22
|
+
@values = @calculators.first.last.calculate
|
23
|
+
@suggester = Suggester.new(values)
|
24
|
+
|
25
|
+
@watcher = Watcher.new(@file_prefix, ignores, lambda do
|
26
|
+
report
|
27
|
+
end)
|
28
|
+
rescue NoMethodError => _e
|
29
|
+
raise "There was a problem gathering churn changes"
|
30
|
+
end
|
31
|
+
|
32
|
+
def suggestions(quantile:, type: "rb")
|
33
|
+
@suggester.values = values(type: type)
|
34
|
+
@suggestions = @suggester.suggest(quantile)
|
35
|
+
@suggestions
|
36
|
+
end
|
37
|
+
|
38
|
+
def report
|
39
|
+
@suggestions = @suggester.suggest
|
40
|
+
@types = Hash[@calculators.map { |calc| [calc.first, calc.last.type] }]
|
41
|
+
end
|
42
|
+
|
43
|
+
def render
|
44
|
+
"Attractor"
|
45
|
+
end
|
46
|
+
|
47
|
+
def values(type: "rb")
|
48
|
+
@values = @calculators[type].calculate
|
49
|
+
@values
|
50
|
+
rescue NoMethodError => _e
|
51
|
+
puts "No calculator for type #{type}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -5,10 +5,10 @@ module Attractor
|
|
5
5
|
class ConsoleReporter < BaseReporter
|
6
6
|
def report
|
7
7
|
super
|
8
|
-
puts
|
8
|
+
puts "Calculated churn and complexity"
|
9
9
|
puts
|
10
|
-
puts "file_path#{
|
11
|
-
puts
|
10
|
+
puts "file_path#{" " * 53}complexity churn"
|
11
|
+
puts "-" * 80
|
12
12
|
|
13
13
|
@calculators.each do |calc|
|
14
14
|
# e.g. ['js', JsCalculator']
|
@@ -19,7 +19,7 @@ module Attractor
|
|
19
19
|
|
20
20
|
puts values&.map(&:to_s)
|
21
21
|
puts
|
22
|
-
puts
|
22
|
+
puts "Suggestions for refactorings:"
|
23
23
|
suggester.suggest&.each { |sug| puts sug.file_path }
|
24
24
|
puts
|
25
25
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'base_reporter'
|
2
|
+
|
3
|
+
module Attractor
|
4
|
+
# console reporter
|
5
|
+
class ConsoleReporter < BaseReporter
|
6
|
+
def report
|
7
|
+
super
|
8
|
+
puts 'Calculated churn and complexity'
|
9
|
+
puts
|
10
|
+
puts "file_path#{' ' * 53}complexity churn"
|
11
|
+
puts '-' * 80
|
12
|
+
|
13
|
+
puts @values.map(&:to_s)
|
14
|
+
|
15
|
+
puts
|
16
|
+
puts 'Suggestions for refactorings:'
|
17
|
+
@suggestions.each { |sug| puts sug.file_path }
|
18
|
+
puts
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -6,29 +6,28 @@ module Attractor
|
|
6
6
|
def report
|
7
7
|
super
|
8
8
|
|
9
|
-
puts
|
9
|
+
puts "Generating an HTML report"
|
10
10
|
@serve_static = true
|
11
11
|
|
12
|
-
FileUtils.mkdir_p
|
13
|
-
FileUtils.mkdir_p
|
14
|
-
FileUtils.mkdir_p
|
15
|
-
FileUtils.mkdir_p
|
12
|
+
FileUtils.mkdir_p "./attractor_output"
|
13
|
+
FileUtils.mkdir_p "./attractor_output/stylesheets"
|
14
|
+
FileUtils.mkdir_p "./attractor_output/images"
|
15
|
+
FileUtils.mkdir_p "./attractor_output/javascripts"
|
16
16
|
|
17
|
-
File.open(
|
18
|
-
File.open(
|
19
|
-
File.open(
|
20
|
-
File.open(
|
17
|
+
File.open("./attractor_output/images/attractor_logo.svg", "w") { |file| file.write(logo) }
|
18
|
+
File.open("./attractor_output/images/attractor_favicon.png", "w") { |file| file.write(favicon) }
|
19
|
+
File.open("./attractor_output/stylesheets/main.css", "w") { |file| file.write(css) }
|
20
|
+
File.open("./attractor_output/javascripts/index.pack.js", "w") { |file| file.write(javascript_pack) }
|
21
21
|
|
22
22
|
if @calculators.size > 1
|
23
23
|
@calculators.each do |calc|
|
24
24
|
@short_type = calc.first
|
25
|
-
|
26
|
-
suggester = Suggester.new(values)
|
25
|
+
suggester = Suggester.new(values(type: @short_type))
|
27
26
|
@suggestions = suggester.suggest
|
28
27
|
|
29
|
-
File.open("./attractor_output/javascripts/index.#{@short_type}.js",
|
30
|
-
File.open("./attractor_output/index.#{@short_type}.html",
|
31
|
-
puts "Generated HTML report at #{File.expand_path
|
28
|
+
File.open("./attractor_output/javascripts/index.#{@short_type}.js", "w") { |file| file.write(javascript) }
|
29
|
+
File.open("./attractor_output/index.#{@short_type}.html", "w") { |file| file.write(render) }
|
30
|
+
puts "Generated HTML report at #{File.expand_path "./attractor_output/"}/index.#{@short_type}.html"
|
32
31
|
end
|
33
32
|
|
34
33
|
if @open_browser
|
@@ -36,41 +35,40 @@ module Attractor
|
|
36
35
|
puts "Opening browser window..."
|
37
36
|
end
|
38
37
|
else
|
39
|
-
File.open(
|
40
|
-
File.open(
|
41
|
-
puts "Generated HTML report at #{File.expand_path
|
38
|
+
File.open("./attractor_output/javascripts/index.js", "w") { |file| file.write(javascript) }
|
39
|
+
File.open("./attractor_output/index.html", "w") { |file| file.write(render) }
|
40
|
+
puts "Generated HTML report at #{File.expand_path "./attractor_output/index.html"}"
|
42
41
|
|
43
42
|
if @open_browser
|
44
|
-
Launchy.open(File.expand_path(
|
43
|
+
Launchy.open(File.expand_path("./attractor_output/index.html"))
|
45
44
|
puts "Opening browser window..."
|
46
45
|
end
|
47
46
|
end
|
48
|
-
|
49
47
|
end
|
50
48
|
|
51
49
|
def logo
|
52
|
-
File.read(File.expand_path(
|
50
|
+
File.read(File.expand_path("../../../app/assets/images/attractor_logo.svg", __dir__))
|
53
51
|
end
|
54
52
|
|
55
53
|
def favicon
|
56
|
-
File.read(File.expand_path(
|
54
|
+
File.read(File.expand_path("../../../app/assets/images/attractor_favicon.png", __dir__))
|
57
55
|
end
|
58
56
|
|
59
57
|
def css
|
60
|
-
File.read(File.expand_path(
|
58
|
+
File.read(File.expand_path("../../../app/assets/stylesheets/main.css", __dir__))
|
61
59
|
end
|
62
60
|
|
63
61
|
def javascript_pack
|
64
|
-
File.read(File.expand_path(
|
62
|
+
File.read(File.expand_path("../../../app/assets/javascripts/index.pack.js", __dir__))
|
65
63
|
end
|
66
64
|
|
67
65
|
def javascript
|
68
|
-
template = Tilt.new(File.expand_path(
|
66
|
+
template = Tilt.new(File.expand_path("../../../app/assets/javascripts/index.js.erb", __dir__))
|
69
67
|
template.render self
|
70
68
|
end
|
71
69
|
|
72
70
|
def render
|
73
|
-
template = Tilt.new(File.expand_path(
|
71
|
+
template = Tilt.new(File.expand_path("../../../app/views/index.html.erb", __dir__))
|
74
72
|
template.render self
|
75
73
|
end
|
76
74
|
end
|
@@ -0,0 +1,48 @@
|
|
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,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
3
|
+
require "rack/livereload"
|
4
|
+
require "rack"
|
5
|
+
require "sinatra/base"
|
6
6
|
|
7
7
|
module Attractor
|
8
8
|
# skeleton sinatra app
|
@@ -13,30 +13,31 @@ module Attractor
|
|
13
13
|
end
|
14
14
|
|
15
15
|
enable :static
|
16
|
-
set :public_folder, File.expand_path(
|
16
|
+
set :public_folder, File.expand_path("../../../app/assets", __dir__)
|
17
17
|
set :show_exceptions, :after_handler
|
18
18
|
|
19
|
-
get
|
19
|
+
get "/" do
|
20
20
|
@types = @reporter.types
|
21
|
-
erb File.read(File.expand_path(
|
21
|
+
erb File.read(File.expand_path("../../../app/views/index.html.erb", __dir__))
|
22
22
|
end
|
23
23
|
|
24
|
-
get
|
25
|
-
{
|
24
|
+
get "/file_prefix" do
|
25
|
+
{file_prefix: @reporter.file_prefix}.to_json
|
26
26
|
end
|
27
27
|
|
28
|
-
get
|
29
|
-
type = params[:type] ||
|
28
|
+
get "/values" do
|
29
|
+
type = params[:type] || "rb"
|
30
30
|
@reporter.values(type: type).to_json
|
31
31
|
end
|
32
32
|
|
33
|
-
get
|
33
|
+
get "/suggestions" do
|
34
34
|
threshold = params[:t] || 95
|
35
|
-
|
35
|
+
type = params[:type] || "rb"
|
36
|
+
@reporter.suggestions(quantile: threshold, type: type).to_json
|
36
37
|
end
|
37
38
|
|
38
39
|
error NoMethodError do
|
39
|
-
{
|
40
|
+
{error: env["sinatra.error"].message}.to_json
|
40
41
|
end
|
41
42
|
end
|
42
43
|
|
@@ -47,10 +48,10 @@ module Attractor
|
|
47
48
|
|
48
49
|
app = AttractorApp.new(self)
|
49
50
|
|
50
|
-
puts
|
51
|
+
puts "Serving attractor at http://localhost:7890"
|
51
52
|
|
52
53
|
if @open_browser
|
53
|
-
Launchy.open(
|
54
|
+
Launchy.open("http://localhost:7890") if @open_browser
|
54
55
|
puts "Opening browser window..."
|
55
56
|
end
|
56
57
|
|
@@ -62,20 +63,13 @@ module Attractor
|
|
62
63
|
|
63
64
|
app = AttractorApp.new(self)
|
64
65
|
|
65
|
-
puts
|
66
|
+
puts "Serving attractor at http://localhost:7890"
|
66
67
|
if @open_browser
|
67
|
-
Launchy.open(
|
68
|
+
Launchy.open("http://localhost:7890")
|
68
69
|
puts "Opening browser window..."
|
69
70
|
end
|
70
71
|
|
71
72
|
Rack::Handler::WEBrick.run Rack::LiveReload.new(app), Port: 7890
|
72
73
|
end
|
73
|
-
|
74
|
-
def values(type: 'rb')
|
75
|
-
@values = @calculators[type].calculate
|
76
|
-
@values
|
77
|
-
rescue NoMethodError => e
|
78
|
-
puts "No calculator for type #{type}"
|
79
|
-
end
|
80
74
|
end
|
81
75
|
end
|
data/lib/attractor/suggester.rb
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
module Attractor
|
4
4
|
# makes suggestions for refactorings
|
5
5
|
class Suggester
|
6
|
+
attr_accessor :values
|
7
|
+
|
6
8
|
def initialize(values)
|
7
9
|
@values = values || []
|
8
10
|
end
|
@@ -13,7 +15,7 @@ module Attractor
|
|
13
15
|
quantile = products.percentile(threshold.to_i)
|
14
16
|
|
15
17
|
@values.select { |val| val.churn * val.complexity > quantile }
|
16
|
-
|
18
|
+
.sort_by { |val| val.churn * val.complexity }.reverse
|
17
19
|
end
|
18
20
|
end
|
19
21
|
end
|
@@ -0,0 +1 @@
|
|
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
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "json"
|
4
4
|
|
5
5
|
module Attractor
|
6
6
|
# holds a churn/complexity value
|
7
7
|
class Value
|
8
8
|
attr_reader :file_path, :churn, :complexity, :details, :history
|
9
9
|
|
10
|
-
def initialize(file_path:
|
10
|
+
def initialize(file_path: "", churn: 1, complexity: 0, details: [], history: [])
|
11
11
|
@file_path = file_path
|
12
12
|
@churn = churn
|
13
13
|
@complexity = complexity
|
@@ -15,12 +15,16 @@ module Attractor
|
|
15
15
|
@history = history
|
16
16
|
end
|
17
17
|
|
18
|
+
def current_commit
|
19
|
+
history&.first&.first
|
20
|
+
end
|
21
|
+
|
18
22
|
def to_s
|
19
|
-
format(
|
23
|
+
format("%-64s%8.1f%8i", @file_path, @complexity, @churn)
|
20
24
|
end
|
21
25
|
|
22
26
|
def to_h
|
23
|
-
{
|
27
|
+
{file_path: file_path, x: churn, y: complexity, details: details, history: history}
|
24
28
|
end
|
25
29
|
|
26
30
|
def to_json(_opt)
|
@@ -0,0 +1,30 @@
|
|
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/version.rb
CHANGED
data/lib/attractor/watcher.rb
CHANGED
@@ -1,19 +1,23 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "listen"
|
4
|
+
|
3
5
|
module Attractor
|
4
6
|
# functionality for watching file system changes
|
5
7
|
class Watcher
|
6
|
-
def initialize(file_prefix, callback)
|
8
|
+
def initialize(file_prefix, ignores, callback)
|
7
9
|
@file_prefix = file_prefix
|
8
10
|
@callback = callback
|
11
|
+
@ignores = ignores.split(",").map(&:strip)
|
9
12
|
end
|
10
13
|
|
11
14
|
def watch
|
12
15
|
@callback.call
|
16
|
+
ignore = @ignores + [/^attractor_output/]
|
13
17
|
|
14
|
-
listener = Listen.to(@file_prefix, ignore:
|
18
|
+
listener = Listen.to(File.absolute_path(@file_prefix), ignore: ignore) do |modified, _added, _removed|
|
15
19
|
if modified
|
16
|
-
puts "#{modified.map(&:to_s).join(
|
20
|
+
puts "#{modified.map(&:to_s).join(", ")} modified, recalculating..."
|
17
21
|
@callback.call
|
18
22
|
end
|
19
23
|
end
|