attractor 2.3.0 → 2.4.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/lib/attractor/calculators/base_calculator.rb +4 -4
- data/lib/attractor/cli.rb +3 -1
- data/lib/attractor/reporters/base_reporter.rb +1 -1
- data/lib/attractor/reporters/console_reporter.rb +55 -16
- data/lib/attractor/suggester.rb +3 -3
- data/lib/attractor/value.rb +4 -0
- data/lib/attractor/version.rb +1 -1
- data/lib/attractor/version.rb~ +7 -0
- data/lib/attractor.rb +2 -2
- metadata +7 -20
- data/lib/attractor/cache.rb~ +0 -67
- data/lib/attractor/calculators/base_calculator.rb~ +0 -64
- data/lib/attractor/cli.rb~ +0 -84
- 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/base_reporter.rb~ +0 -54
- data/lib/attractor/reporters/console_reporter.rb~ +0 -21
- 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: c1b66250e293edf3d4298ee78c92a928b92128d0f8525a012ba4c99b8e16a77e
|
4
|
+
data.tar.gz: 354068cdc6c91978e80e3860ac65255811daf364d64c15dcd7221db2bac79f22
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 456416f564b3e7e5efa7874f901fea6a0c26c23e59bf157dd5c78cb58014f4bcee58f0172cebdc83061d94195d94631e1818eba905835799e1664527fc26caa9
|
7
|
+
data.tar.gz: 472072d37700e794de8ee98d48f2252186598f709094c9046acc97deb91ef34dfbc089a4c7b70f0965ce8fc4621877c63d41a167f4d2968f2b734b9f5e4e18de
|
@@ -40,10 +40,10 @@ module Attractor
|
|
40
40
|
complexity, details = yield(change)
|
41
41
|
|
42
42
|
value = Value.new(file_path: change[:file_path],
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
43
|
+
churn: change[:times_changed],
|
44
|
+
complexity: complexity,
|
45
|
+
details: details,
|
46
|
+
history: history)
|
47
47
|
Cache.write(file_path: change[:file_path], value: value)
|
48
48
|
end
|
49
49
|
|
data/lib/attractor/cli.rb
CHANGED
@@ -45,10 +45,12 @@ module Attractor
|
|
45
45
|
shared_options.each do |shared_option|
|
46
46
|
option(*shared_option)
|
47
47
|
end
|
48
|
+
option(:format, aliases: :f, default: :table)
|
48
49
|
def calc
|
49
50
|
file_prefix = options[:file_prefix]
|
51
|
+
output_format = options[:format]
|
50
52
|
|
51
|
-
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)
|
52
54
|
rescue RuntimeError => e
|
53
55
|
puts "Runtime error: #{e.message}"
|
54
56
|
end
|
@@ -3,26 +3,65 @@
|
|
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
|
25
26
|
end
|
26
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
|
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)
|
65
|
+
end
|
27
66
|
end
|
28
67
|
end
|
data/lib/attractor/suggester.rb
CHANGED
@@ -10,12 +10,12 @@ module Attractor
|
|
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
|
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.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Julian Rubisch
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-12-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: churn
|
@@ -371,35 +371,22 @@ 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
|
-
- lib/attractor/calculators/base_calculator.rb~
|
379
376
|
- lib/attractor/cli.rb
|
380
|
-
- lib/attractor/cli.rb~
|
381
377
|
- lib/attractor/detectors/base_detector.rb
|
382
|
-
- lib/attractor/detectors/base_detector.rb~
|
383
378
|
- lib/attractor/duration_parser.rb
|
384
|
-
- lib/attractor/duration_parser.rb~
|
385
379
|
- lib/attractor/gem_names.rb
|
386
|
-
- lib/attractor/gem_names.rb~
|
387
380
|
- lib/attractor/registry_entry.rb
|
388
|
-
- lib/attractor/registry_entry.rb~
|
389
381
|
- lib/attractor/reporters/base_reporter.rb
|
390
|
-
- lib/attractor/reporters/base_reporter.rb~
|
391
382
|
- lib/attractor/reporters/console_reporter.rb
|
392
|
-
- lib/attractor/reporters/console_reporter.rb~
|
393
383
|
- lib/attractor/reporters/html_reporter.rb
|
394
|
-
- lib/attractor/reporters/html_reporter.rb~
|
395
384
|
- lib/attractor/reporters/sinatra_reporter.rb
|
396
385
|
- lib/attractor/suggester.rb
|
397
|
-
- lib/attractor/tmp/churn/9f86bc9b7ec6bf59cbf7a111ce8b1159ae128347.json
|
398
386
|
- lib/attractor/value.rb
|
399
|
-
- lib/attractor/value.rb~
|
400
387
|
- lib/attractor/version.rb
|
388
|
+
- lib/attractor/version.rb~
|
401
389
|
- lib/attractor/watcher.rb
|
402
|
-
- lib/attractor/watcher.rb~
|
403
390
|
homepage: https://github.com/julianrubisch/attractor
|
404
391
|
licenses:
|
405
392
|
- MIT
|
@@ -408,7 +395,7 @@ metadata:
|
|
408
395
|
source_code_uri: https://github.com/julianrubisch/attractor
|
409
396
|
bug_tracker_uri: https://github.com/julianrubisch/attractor/issues
|
410
397
|
changelog_uri: https://github.com/julianrubisch/attractor/CHANGELOG.md
|
411
|
-
post_install_message:
|
398
|
+
post_install_message:
|
412
399
|
rdoc_options: []
|
413
400
|
require_paths:
|
414
401
|
- lib
|
@@ -423,8 +410,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
423
410
|
- !ruby/object:Gem::Version
|
424
411
|
version: '0'
|
425
412
|
requirements: []
|
426
|
-
rubygems_version: 3.
|
427
|
-
signing_key:
|
413
|
+
rubygems_version: 3.1.4
|
414
|
+
signing_key:
|
428
415
|
specification_version: 4
|
429
416
|
summary: Churn vs Complexity Chart Generator
|
430
417
|
test_files: []
|
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,64 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "churn/calculator"
|
4
|
-
|
5
|
-
require "attractor/value"
|
6
|
-
|
7
|
-
module Attractor
|
8
|
-
# calculates churn and complexity
|
9
|
-
class BaseCalculator
|
10
|
-
attr_reader :type
|
11
|
-
|
12
|
-
def initialize(file_prefix: "", ignores: "", file_extension: "rb", minimum_churn_count: 3, start_ago: "5y")
|
13
|
-
@file_prefix = file_prefix
|
14
|
-
@file_extension = file_extension
|
15
|
-
@minimum_churn_count = minimum_churn_count
|
16
|
-
@start_date = Date.today - Attractor::DurationParser.new(start_ago).duration
|
17
|
-
@ignores = ignores
|
18
|
-
end
|
19
|
-
|
20
|
-
def calculate
|
21
|
-
churn = ::Churn::ChurnCalculator.new(
|
22
|
-
file_extension: @file_extension,
|
23
|
-
file_prefix: @file_prefix,
|
24
|
-
minimum_churn_count: @minimum_churn_count,
|
25
|
-
start_date: @start_date,
|
26
|
-
ignores: @ignores
|
27
|
-
).report(false)
|
28
|
-
|
29
|
-
churn[:churn][:changes].map do |change|
|
30
|
-
history = git_history_for_file(file_path: change[:file_path])
|
31
|
-
commit = history&.first&.first
|
32
|
-
|
33
|
-
cached_value = Cache.read(file_path: change[:file_path])
|
34
|
-
|
35
|
-
if !cached_value.nil? && !cached_value.current_commit.nil? && cached_value.current_commit == commit
|
36
|
-
value = cached_value
|
37
|
-
else
|
38
|
-
complexity, details = yield(change)
|
39
|
-
|
40
|
-
value = Value.new(file_path: change[:file_path],
|
41
|
-
churn: change[:times_changed],
|
42
|
-
complexity: complexity,
|
43
|
-
details: details,
|
44
|
-
history: history)
|
45
|
-
Cache.write(file_path: change[:file_path], value: value)
|
46
|
-
end
|
47
|
-
|
48
|
-
value
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
private
|
53
|
-
|
54
|
-
def git_history_for_file(file_path:, limit: 10)
|
55
|
-
history = `git log --oneline -n #{limit} -- #{file_path}`
|
56
|
-
history.split("\n")
|
57
|
-
.map do |log_entry|
|
58
|
-
log_entry.partition(/\A(\S+)\s/)
|
59
|
-
.map(&:strip)
|
60
|
-
.reject(&:empty?)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
data/lib/attractor/cli.rb~
DELETED
@@ -1,84 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "thor"
|
4
|
-
|
5
|
-
require "attractor"
|
6
|
-
|
7
|
-
module Attractor
|
8
|
-
# contains methods implementing the CLI
|
9
|
-
class CLI < Thor
|
10
|
-
shared_options = [[:file_prefix, aliases: :p],
|
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]]
|
16
|
-
|
17
|
-
advanced_options = [[:format, aliases: :f, default: "html"],
|
18
|
-
[:no_open_browser, type: :boolean],
|
19
|
-
[:ci, type: :boolean]]
|
20
|
-
|
21
|
-
desc "version", "Prints Attractor's version information"
|
22
|
-
map %w[-v --version] => :version
|
23
|
-
def version
|
24
|
-
puts "Attractor version #{Attractor::VERSION}"
|
25
|
-
rescue RuntimeError => e
|
26
|
-
puts "Runtime error: #{e.message}"
|
27
|
-
end
|
28
|
-
|
29
|
-
desc "calc", "Calculates churn and complexity for all ruby files in current directory"
|
30
|
-
shared_options.each do |shared_option|
|
31
|
-
option(*shared_option)
|
32
|
-
end
|
33
|
-
def calc
|
34
|
-
file_prefix = options[:file_prefix]
|
35
|
-
|
36
|
-
report! Attractor::ConsoleReporter.new(file_prefix: file_prefix, ignores: options[:ignore], calculators: calculators(options))
|
37
|
-
rescue RuntimeError => e
|
38
|
-
puts "Runtime error: #{e.message}"
|
39
|
-
end
|
40
|
-
|
41
|
-
desc "report", "Generates an HTML report"
|
42
|
-
(shared_options + advanced_options).each do |option|
|
43
|
-
option(*option)
|
44
|
-
end
|
45
|
-
def report
|
46
|
-
file_prefix = options[:file_prefix]
|
47
|
-
open_browser = !(options[:no_open_browser] || options[:ci])
|
48
|
-
|
49
|
-
report! Attractor::HtmlReporter.new(file_prefix: file_prefix, ignores: options[:ignore], calculators: calculators(options), open_browser: open_browser)
|
50
|
-
rescue RuntimeError => e
|
51
|
-
puts "Runtime error: #{e.message}"
|
52
|
-
end
|
53
|
-
|
54
|
-
desc "serve", "Serves the report on localhost"
|
55
|
-
(shared_options + advanced_options).each do |option|
|
56
|
-
option(*option)
|
57
|
-
end
|
58
|
-
def serve
|
59
|
-
file_prefix = options[:file_prefix]
|
60
|
-
open_browser = !(options[:no_open_browser] || options[:ci])
|
61
|
-
|
62
|
-
report! Attractor::SinatraReporter.new(file_prefix: file_prefix, ignores: options[:ignore], calculators: calculators(options), open_browser: open_browser)
|
63
|
-
end
|
64
|
-
|
65
|
-
private
|
66
|
-
|
67
|
-
def calculators(options)
|
68
|
-
Attractor.calculators_for_type(options[:type],
|
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
|
82
|
-
end
|
83
|
-
end
|
84
|
-
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,54 +0,0 @@
|
|
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
|
@@ -1,21 +0,0 @@
|
|
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
|
@@ -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
|