attractor 2.0.5 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -0
- data/Rakefile +13 -13
- data/exe/attractor +1 -1
- data/lib/attractor.rb +12 -13
- data/lib/attractor/calculators/base_calculator.rb +9 -7
- data/lib/attractor/cli.rb +33 -44
- data/lib/attractor/duration_parser.rb +4 -4
- data/lib/attractor/gem_names.rb +1 -1
- data/lib/attractor/registry_entry.rb +0 -1
- data/lib/attractor/reporters/base_reporter.rb +13 -13
- data/lib/attractor/reporters/console_reporter.rb +4 -4
- data/lib/attractor/reporters/html_reporter.rb +22 -23
- data/lib/attractor/reporters/sinatra_reporter.rb +17 -17
- data/lib/attractor/suggester.rb +2 -2
- data/lib/attractor/value.rb +4 -4
- data/lib/attractor/version.rb +1 -1
- data/lib/attractor/watcher.rb +5 -3
- metadata +10 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a044632a13376f395112a65b938be9c3173a16499f4eab725e97afffc1e83925
|
4
|
+
data.tar.gz: 20aafed307f3cf4f047130aa6aafc2395838ba50ecf48dedc0bd205fe5e6c391
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 12a4dd82ff8d50a7d5e7c3663b6b013e93d6575a706581c7b64a5bbb75714d9d8126c3f24344f57e2c5c79905ea549e82370def04fb6643ff25fbc38f365c846
|
7
|
+
data.tar.gz: f27f15c33b573ab2cccb9be34d59f89c12575e19fe18454d5380c4fd500f98e63e2b0e8be2bd4e7a86c18512124fb6792e5f625101ef4e8333b54bf59a03a165
|
data/README.md
CHANGED
@@ -195,6 +195,7 @@ attractor calc
|
|
195
195
|
--watch|-w
|
196
196
|
--start_ago|-s (e.g. 5y, 3m, 7w)
|
197
197
|
--minimum_churn|-c (minimum times a file must have changed to be processed)
|
198
|
+
--ignore|-i 'spec/*_spec.rb,db/schema.rb,tmp'
|
198
199
|
```
|
199
200
|
|
200
201
|
Generate a full report
|
@@ -207,6 +208,7 @@ attractor report
|
|
207
208
|
--no-open-browser|--ci
|
208
209
|
--start_ago|-s (e.g. 5y, 3m, 7w)
|
209
210
|
--minimum_churn|-c (minimum times a file must have changed to be processed)
|
211
|
+
--ignore|-i 'spec/*_spec.rb,db/schema.rb,tmp'
|
210
212
|
```
|
211
213
|
|
212
214
|
Serve the output on `http://localhost:7890`
|
@@ -218,6 +220,7 @@ attractor serve
|
|
218
220
|
--no-open-browser|--ci
|
219
221
|
--start_ago|-s (e.g. 5y, 3m, 7w)
|
220
222
|
--minimum_churn|-c (minimum times a file must have changed to be processed)
|
223
|
+
--ignore|-i 'spec/*_spec.rb,db/schema.rb,tmp'
|
221
224
|
```
|
222
225
|
|
223
226
|
## Development
|
data/Rakefile
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
3
|
+
require "autoprefixer-rails"
|
4
|
+
require "bootstrap"
|
5
|
+
require "bundler/gem_tasks"
|
6
|
+
require "fileutils"
|
7
|
+
require "rspec/core/rake_task"
|
8
|
+
require "sassc"
|
9
|
+
require "structured_changelog/tasks"
|
10
10
|
|
11
11
|
RSpec::Core::RakeTask.new(:spec)
|
12
12
|
|
@@ -14,18 +14,18 @@ task default: :spec
|
|
14
14
|
|
15
15
|
task build: :assets
|
16
16
|
|
17
|
-
desc
|
17
|
+
desc "Preprocess assets"
|
18
18
|
task :assets do
|
19
|
-
puts
|
19
|
+
puts "Preprocessing SCSS and JS files"
|
20
20
|
|
21
|
-
puts
|
21
|
+
puts "Copying over bootstrap"
|
22
22
|
|
23
|
-
FileUtils.cp_r Gem::Specification.find_by_name(
|
23
|
+
FileUtils.cp_r Gem::Specification.find_by_name("bootstrap").gem_dir, "tmp"
|
24
24
|
|
25
|
-
sass = File.read(File.expand_path(
|
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.open(File.expand_path(
|
28
|
+
File.open(File.expand_path("./app/assets/stylesheets/main.css"), "w") { |file| file.write(prefixed) }
|
29
29
|
|
30
30
|
npm_output = `npm run build`
|
31
31
|
puts npm_output
|
data/exe/attractor
CHANGED
data/lib/attractor.rb
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
|
12
|
-
Dir[File.join(__dir__,
|
13
|
-
next if file.start_with?(
|
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
|
+
|
12
|
+
Dir[File.join(__dir__, "attractor", "reporters", "*.rb")].sort.each do |file|
|
13
|
+
next if file.start_with?("base")
|
14
14
|
|
15
15
|
require file
|
16
16
|
end
|
@@ -27,9 +27,8 @@ module Attractor
|
|
27
27
|
def calculators_for_type(type, **options)
|
28
28
|
registry_entry_for_type = @registry_entries[type]
|
29
29
|
|
30
|
-
return {
|
30
|
+
return {type => registry_entry_for_type.calculator_class.new(**options)} if type
|
31
31
|
|
32
|
-
|
33
32
|
Hash[@registry_entries.map do |type, entry|
|
34
33
|
[type, entry.calculator_class.new(**options)] if entry.detector_class.new.detect
|
35
34
|
end.compact]
|
@@ -1,19 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "churn/calculator"
|
4
4
|
|
5
|
-
require
|
5
|
+
require "attractor/value"
|
6
6
|
|
7
7
|
module Attractor
|
8
8
|
# calculates churn and complexity
|
9
9
|
class BaseCalculator
|
10
10
|
attr_reader :type
|
11
11
|
|
12
|
-
def initialize(file_prefix:
|
12
|
+
def initialize(file_prefix: "", ignores: "", file_extension: "rb", minimum_churn_count: 3, start_ago: "5y")
|
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
|
+
@ignores = ignores
|
17
18
|
end
|
18
19
|
|
19
20
|
def calculate
|
@@ -21,7 +22,8 @@ module Attractor
|
|
21
22
|
file_extension: @file_extension,
|
22
23
|
file_prefix: @file_prefix,
|
23
24
|
minimum_churn_count: @minimum_churn_count,
|
24
|
-
start_date: @start_date
|
25
|
+
start_date: @start_date,
|
26
|
+
ignores: @ignores
|
25
27
|
).report(false)
|
26
28
|
|
27
29
|
churn[:churn][:changes].map do |change|
|
@@ -39,10 +41,10 @@ module Attractor
|
|
39
41
|
def git_history_for_file(file_path:, limit: 10)
|
40
42
|
history = `git log --oneline -n #{limit} -- #{file_path}`
|
41
43
|
history.split("\n")
|
42
|
-
|
44
|
+
.map do |log_entry|
|
43
45
|
log_entry.partition(/\A(\S+)\s/)
|
44
|
-
|
45
|
-
|
46
|
+
.map(&:strip)
|
47
|
+
.reject(&:empty?)
|
46
48
|
end
|
47
49
|
end
|
48
50
|
end
|
data/lib/attractor/cli.rb
CHANGED
@@ -1,95 +1,84 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "thor"
|
4
4
|
|
5
|
-
require
|
5
|
+
require "attractor"
|
6
6
|
|
7
7
|
module Attractor
|
8
8
|
# contains methods implementing the CLI
|
9
9
|
class CLI < Thor
|
10
10
|
shared_options = [[:file_prefix, aliases: :p],
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
[:ignore, aliases: :i, default: ""],
|
12
|
+
[:watch, aliases: :w, type: :boolean],
|
13
|
+
[:minimum_churn, aliases: :c, type: :numeric, default: 3],
|
14
|
+
[:start_ago, aliases: :s, type: :string, default: "5y"],
|
15
|
+
[:type, aliases: :t]]
|
15
16
|
|
16
|
-
advanced_options = [[:format, aliases: :f, default:
|
17
|
-
|
18
|
-
|
17
|
+
advanced_options = [[:format, aliases: :f, default: "html"],
|
18
|
+
[:no_open_browser, type: :boolean],
|
19
|
+
[:ci, type: :boolean]]
|
19
20
|
|
20
21
|
desc "version", "Prints Attractor's version information"
|
21
|
-
map %w
|
22
|
+
map %w[-v --version] => :version
|
22
23
|
def version
|
23
24
|
puts "Attractor version #{Attractor::VERSION}"
|
24
25
|
rescue RuntimeError => e
|
25
26
|
puts "Runtime error: #{e.message}"
|
26
27
|
end
|
27
28
|
|
28
|
-
desc
|
29
|
+
desc "calc", "Calculates churn and complexity for all ruby files in current directory"
|
29
30
|
shared_options.each do |shared_option|
|
30
31
|
option(*shared_option)
|
31
32
|
end
|
32
33
|
def calc
|
33
34
|
file_prefix = options[:file_prefix]
|
34
|
-
|
35
|
-
|
36
|
-
Attractor::ConsoleReporter.new(file_prefix: file_prefix, calculators: calculators(options)).watch
|
37
|
-
else
|
38
|
-
Attractor::ConsoleReporter.new(file_prefix: file_prefix, calculators: calculators(options)).report
|
39
|
-
end
|
35
|
+
|
36
|
+
report! Attractor::ConsoleReporter.new(file_prefix: file_prefix, ignores: options[:ignore], calculators: calculators(options))
|
40
37
|
rescue RuntimeError => e
|
41
38
|
puts "Runtime error: #{e.message}"
|
42
39
|
end
|
43
40
|
|
44
|
-
desc
|
41
|
+
desc "report", "Generates an HTML report"
|
45
42
|
(shared_options + advanced_options).each do |option|
|
46
43
|
option(*option)
|
47
44
|
end
|
48
45
|
def report
|
49
46
|
file_prefix = options[:file_prefix]
|
50
47
|
open_browser = !(options[:no_open_browser] || options[:ci])
|
51
|
-
|
52
|
-
|
53
|
-
Attractor::HtmlReporter.new(file_prefix: file_prefix, calculators: calculators(options), open_browser: open_browser).watch
|
54
|
-
else
|
55
|
-
case options[:format]
|
56
|
-
when 'html'
|
57
|
-
Attractor::HtmlReporter.new(file_prefix: file_prefix, calculators: calculators(options), open_browser: open_browser).report
|
58
|
-
else
|
59
|
-
Attractor::HtmlReporter.new(file_prefix: file_prefix, calculators: calculators(options), open_browser: open_browser).report
|
60
|
-
end
|
61
|
-
end
|
48
|
+
|
49
|
+
report! Attractor::HtmlReporter.new(file_prefix: file_prefix, ignores: options[:ignore], calculators: calculators(options), open_browser: open_browser)
|
62
50
|
rescue RuntimeError => e
|
63
51
|
puts "Runtime error: #{e.message}"
|
64
52
|
end
|
65
53
|
|
66
|
-
desc
|
54
|
+
desc "serve", "Serves the report on localhost"
|
67
55
|
(shared_options + advanced_options).each do |option|
|
68
56
|
option(*option)
|
69
57
|
end
|
70
58
|
def serve
|
71
59
|
file_prefix = options[:file_prefix]
|
72
60
|
open_browser = !(options[:no_open_browser] || options[:ci])
|
73
|
-
|
74
|
-
|
75
|
-
Attractor::SinatraReporter.new(file_prefix: file_prefix, calculators: calculators(options), open_browser: open_browser).watch
|
76
|
-
else
|
77
|
-
case options[:format]
|
78
|
-
when 'html'
|
79
|
-
Attractor::SinatraReporter.new(file_prefix: file_prefix, calculators: calculators(options), open_browser: open_browser).report
|
80
|
-
else
|
81
|
-
Attractor::SinatraReporter.new(file_prefix: file_prefix, calculators: calculators(options), open_browser: open_browser).report
|
82
|
-
end
|
83
|
-
end
|
61
|
+
|
62
|
+
report! Attractor::SinatraReporter.new(file_prefix: file_prefix, ignores: options[:ignore], calculators: calculators(options), open_browser: open_browser)
|
84
63
|
end
|
85
64
|
|
86
65
|
private
|
87
66
|
|
88
67
|
def calculators(options)
|
89
68
|
Attractor.calculators_for_type(options[:type],
|
90
|
-
|
91
|
-
|
92
|
-
|
69
|
+
file_prefix: options[:file_prefix],
|
70
|
+
minimum_churn_count: options[:minimum_churn],
|
71
|
+
ignores: options[:ignore],
|
72
|
+
start_ago: options[:start_ago])
|
73
|
+
end
|
74
|
+
|
75
|
+
def report!(reporter)
|
76
|
+
if options[:watch]
|
77
|
+
puts "Listening for file changes..."
|
78
|
+
reporter.watch
|
79
|
+
else
|
80
|
+
reporter.report
|
81
|
+
end
|
93
82
|
end
|
94
83
|
end
|
95
84
|
end
|
data/lib/attractor/gem_names.rb
CHANGED
@@ -2,7 +2,7 @@ module Attractor
|
|
2
2
|
# from https://github.com/prontolabs/pronto/blob/master/lib/pronto/gem_names.rb
|
3
3
|
class GemNames
|
4
4
|
def to_a
|
5
|
-
gems.map { |gem| gem.name.sub(/^attractor-/,
|
5
|
+
gems.map { |gem| gem.name.sub(/^attractor-/, "") }.uniq.sort
|
6
6
|
end
|
7
7
|
|
8
8
|
private
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
3
|
+
require "descriptive_statistics/safe"
|
4
|
+
require "fileutils"
|
5
|
+
require "forwardable"
|
6
|
+
require "launchy"
|
7
|
+
require "tilt"
|
8
8
|
|
9
9
|
module Attractor
|
10
10
|
# base reporter
|
@@ -15,21 +15,21 @@ module Attractor
|
|
15
15
|
attr_writer :values
|
16
16
|
def_delegator :@watcher, :watch
|
17
17
|
|
18
|
-
def initialize(
|
18
|
+
def initialize(calculators:, file_prefix: "", ignores: "", open_browser: true)
|
19
19
|
@file_prefix = file_prefix || ""
|
20
20
|
@calculators = calculators
|
21
21
|
@open_browser = open_browser
|
22
22
|
@values = @calculators.first.last.calculate
|
23
23
|
@suggester = Suggester.new(values)
|
24
24
|
|
25
|
-
@watcher = Watcher.new(@file_prefix, lambda do
|
25
|
+
@watcher = Watcher.new(@file_prefix, ignores, lambda do
|
26
26
|
report
|
27
27
|
end)
|
28
|
-
rescue NoMethodError =>
|
29
|
-
raise
|
28
|
+
rescue NoMethodError => _e
|
29
|
+
raise "There was a problem gathering churn changes"
|
30
30
|
end
|
31
31
|
|
32
|
-
def suggestions(quantile:, type:
|
32
|
+
def suggestions(quantile:, type: "rb")
|
33
33
|
@suggester.values = values(type: type)
|
34
34
|
@suggestions = @suggester.suggest(quantile)
|
35
35
|
@suggestions
|
@@ -41,13 +41,13 @@ module Attractor
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def render
|
44
|
-
|
44
|
+
"Attractor"
|
45
45
|
end
|
46
46
|
|
47
|
-
def values(type:
|
47
|
+
def values(type: "rb")
|
48
48
|
@values = @calculators[type].calculate
|
49
49
|
@values
|
50
|
-
rescue NoMethodError =>
|
50
|
+
rescue NoMethodError => _e
|
51
51
|
puts "No calculator for type #{type}"
|
52
52
|
end
|
53
53
|
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
|
@@ -6,18 +6,18 @@ 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|
|
@@ -25,9 +25,9 @@ module Attractor
|
|
25
25
|
suggester = Suggester.new(values(type: @short_type))
|
26
26
|
@suggestions = suggester.suggest
|
27
27
|
|
28
|
-
File.open("./attractor_output/javascripts/index.#{@short_type}.js",
|
29
|
-
File.open("./attractor_output/index.#{@short_type}.html",
|
30
|
-
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"
|
31
31
|
end
|
32
32
|
|
33
33
|
if @open_browser
|
@@ -35,41 +35,40 @@ module Attractor
|
|
35
35
|
puts "Opening browser window..."
|
36
36
|
end
|
37
37
|
else
|
38
|
-
File.open(
|
39
|
-
File.open(
|
40
|
-
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"}"
|
41
41
|
|
42
42
|
if @open_browser
|
43
|
-
Launchy.open(File.expand_path(
|
43
|
+
Launchy.open(File.expand_path("./attractor_output/index.html"))
|
44
44
|
puts "Opening browser window..."
|
45
45
|
end
|
46
46
|
end
|
47
|
-
|
48
47
|
end
|
49
48
|
|
50
49
|
def logo
|
51
|
-
File.read(File.expand_path(
|
50
|
+
File.read(File.expand_path("../../../app/assets/images/attractor_logo.svg", __dir__))
|
52
51
|
end
|
53
52
|
|
54
53
|
def favicon
|
55
|
-
File.read(File.expand_path(
|
54
|
+
File.read(File.expand_path("../../../app/assets/images/attractor_favicon.png", __dir__))
|
56
55
|
end
|
57
56
|
|
58
57
|
def css
|
59
|
-
File.read(File.expand_path(
|
58
|
+
File.read(File.expand_path("../../../app/assets/stylesheets/main.css", __dir__))
|
60
59
|
end
|
61
60
|
|
62
61
|
def javascript_pack
|
63
|
-
File.read(File.expand_path(
|
62
|
+
File.read(File.expand_path("../../../app/assets/javascripts/index.pack.js", __dir__))
|
64
63
|
end
|
65
64
|
|
66
65
|
def javascript
|
67
|
-
template = Tilt.new(File.expand_path(
|
66
|
+
template = Tilt.new(File.expand_path("../../../app/assets/javascripts/index.js.erb", __dir__))
|
68
67
|
template.render self
|
69
68
|
end
|
70
69
|
|
71
70
|
def render
|
72
|
-
template = Tilt.new(File.expand_path(
|
71
|
+
template = Tilt.new(File.expand_path("../../../app/views/index.html.erb", __dir__))
|
73
72
|
template.render self
|
74
73
|
end
|
75
74
|
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,31 +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
|
-
type = params[:type] ||
|
35
|
+
type = params[:type] || "rb"
|
36
36
|
@reporter.suggestions(quantile: threshold, type: type).to_json
|
37
37
|
end
|
38
38
|
|
39
39
|
error NoMethodError do
|
40
|
-
{
|
40
|
+
{error: env["sinatra.error"].message}.to_json
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
@@ -48,10 +48,10 @@ module Attractor
|
|
48
48
|
|
49
49
|
app = AttractorApp.new(self)
|
50
50
|
|
51
|
-
puts
|
51
|
+
puts "Serving attractor at http://localhost:7890"
|
52
52
|
|
53
53
|
if @open_browser
|
54
|
-
Launchy.open(
|
54
|
+
Launchy.open("http://localhost:7890") if @open_browser
|
55
55
|
puts "Opening browser window..."
|
56
56
|
end
|
57
57
|
|
@@ -63,9 +63,9 @@ module Attractor
|
|
63
63
|
|
64
64
|
app = AttractorApp.new(self)
|
65
65
|
|
66
|
-
puts
|
66
|
+
puts "Serving attractor at http://localhost:7890"
|
67
67
|
if @open_browser
|
68
|
-
Launchy.open(
|
68
|
+
Launchy.open("http://localhost:7890")
|
69
69
|
puts "Opening browser window..."
|
70
70
|
end
|
71
71
|
|
data/lib/attractor/suggester.rb
CHANGED
@@ -4,7 +4,7 @@ module Attractor
|
|
4
4
|
# makes suggestions for refactorings
|
5
5
|
class Suggester
|
6
6
|
attr_accessor :values
|
7
|
-
|
7
|
+
|
8
8
|
def initialize(values)
|
9
9
|
@values = values || []
|
10
10
|
end
|
@@ -15,7 +15,7 @@ module Attractor
|
|
15
15
|
quantile = products.percentile(threshold.to_i)
|
16
16
|
|
17
17
|
@values.select { |val| val.churn * val.complexity > quantile }
|
18
|
-
|
18
|
+
.sort_by { |val| val.churn * val.complexity }.reverse
|
19
19
|
end
|
20
20
|
end
|
21
21
|
end
|
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
|
@@ -16,11 +16,11 @@ module Attractor
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def to_s
|
19
|
-
format(
|
19
|
+
format("%-64s%8.1f%8i", @file_path, @complexity, @churn)
|
20
20
|
end
|
21
21
|
|
22
22
|
def to_h
|
23
|
-
{
|
23
|
+
{file_path: file_path, x: churn, y: complexity, details: details, history: history}
|
24
24
|
end
|
25
25
|
|
26
26
|
def to_json(_opt)
|
data/lib/attractor/version.rb
CHANGED
data/lib/attractor/watcher.rb
CHANGED
@@ -5,17 +5,19 @@ require "listen"
|
|
5
5
|
module Attractor
|
6
6
|
# functionality for watching file system changes
|
7
7
|
class Watcher
|
8
|
-
def initialize(file_prefix, callback)
|
8
|
+
def initialize(file_prefix, ignores, callback)
|
9
9
|
@file_prefix = file_prefix
|
10
10
|
@callback = callback
|
11
|
+
@ignores = ignores.split(",").map(&:strip)
|
11
12
|
end
|
12
13
|
|
13
14
|
def watch
|
14
15
|
@callback.call
|
16
|
+
ignore = @ignores + [/^attractor_output/]
|
15
17
|
|
16
|
-
listener = Listen.to(File.absolute_path(@file_prefix), ignore:
|
18
|
+
listener = Listen.to(File.absolute_path(@file_prefix), ignore: ignore) do |modified, _added, _removed|
|
17
19
|
if modified
|
18
|
-
puts "#{modified.map(&:to_s).join(
|
20
|
+
puts "#{modified.map(&:to_s).join(", ")} modified, recalculating..."
|
19
21
|
@callback.call
|
20
22
|
end
|
21
23
|
end
|
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.0
|
4
|
+
version: 2.1.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: 2021-
|
11
|
+
date: 2021-04-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: churn
|
@@ -154,30 +154,30 @@ dependencies:
|
|
154
154
|
name: attractor-javascript
|
155
155
|
requirement: !ruby/object:Gem::Requirement
|
156
156
|
requirements:
|
157
|
-
- - "
|
157
|
+
- - "~>"
|
158
158
|
- !ruby/object:Gem::Version
|
159
|
-
version:
|
159
|
+
version: 0.2.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:
|
166
|
+
version: 0.2.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:
|
173
|
+
version: 0.2.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:
|
180
|
+
version: 0.2.0
|
181
181
|
- !ruby/object:Gem::Dependency
|
182
182
|
name: autoprefixer-rails
|
183
183
|
requirement: !ruby/object:Gem::Requirement
|