attractor 2.4.0 → 2.6.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 +0 -2
- data/Rakefile +1 -1
- data/lib/attractor/calculators/base_calculator.rb +5 -4
- data/lib/attractor/calculators/base_calculator.rb~ +73 -0
- data/lib/attractor/cli.rb +3 -1
- data/lib/attractor/cli.rb~ +101 -0
- data/lib/attractor/reporters/base_reporter.rb +1 -1
- data/lib/attractor/reporters/base_reporter.rb~ +53 -0
- data/lib/attractor/reporters/console_reporter.rb +33 -4
- data/lib/attractor/reporters/console_reporter.rb~ +94 -0
- data/lib/attractor/reporters/html_reporter.rb +8 -8
- data/lib/attractor/suggester.rb +2 -2
- data/lib/attractor/suggester.rb~ +21 -0
- data/lib/attractor/version.rb +1 -1
- data/lib/attractor/version.rb~ +1 -5
- metadata +15 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 31bd7aa261b4e29b98a2a0af93e653d6dac08d0187505b57ff6df1dc60ad37c4
|
4
|
+
data.tar.gz: 602324b18e247c6410f9d6206b089c0ae11cfe2d6a99feb5d0a0cf3d9a77f88e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 04a0fb63d5f198b8a5f412615e0fcc48ccb3cf6ee89edc1448ce0213cd9bc5f224bd4d435bf1c114c19789461cc6d4ccea13f55dc7cd39a6132bc0fe3d388342
|
7
|
+
data.tar.gz: bb2aa36a13ca5a1615853e6eb29bc13e2f5b49e46e18242fc10ef78ea4d57cd5dbbd5ed2c9bd01e5139c191a36d61ff5d7ca5f1645bcdce98783c194740425a1
|
data/README.md
CHANGED
@@ -10,7 +10,6 @@
|
|
10
10
|
[license-url]: https://github.com/julianrubisch/attractor/blob/master/LICENSE
|
11
11
|
[twitter-url]: https://twitter.com/AttractorGem
|
12
12
|
[twitter-shield]: https://img.shields.io/twitter/follow/AttractorGem?style=social
|
13
|
-
[build-status]: https://travis-ci.org/julianrubisch/attractor.svg?branch=master
|
14
13
|
[twitter-shield]: https://img.shields.io/twitter/follow/AttractorGem?style=social
|
15
14
|
[ruby-tests-action-shield]: https://github.com/julianrubisch/attractor/workflows/Ruby%20Tests/badge.svg
|
16
15
|
<!-- Media -->
|
@@ -39,7 +38,6 @@
|
|
39
38
|
---
|
40
39
|
|
41
40
|
<!-- PROJECT SHIELDS -->
|
42
|
-
![Build Status][build-status]
|
43
41
|
![Ruby Tests Action Status][ruby-tests-action-shield]
|
44
42
|
[![Forks][forks-shield]][forks-url]
|
45
43
|
[![Stargazers][stars-shield]][stars-url]
|
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])
|
@@ -47,13 +48,13 @@ module Attractor
|
|
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
|
@@ -0,0 +1,73 @@
|
|
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
|
+
puts "Calculating churn and complexity values for #{churn[:churn][:changes].size} #{type} files"
|
30
|
+
|
31
|
+
values = churn[:churn][:changes].map do |change|
|
32
|
+
history = git_history_for_file(file_path: change[:file_path])
|
33
|
+
commit = history&.first&.first
|
34
|
+
|
35
|
+
cached_value = Cache.read(file_path: change[:file_path])
|
36
|
+
|
37
|
+
if !cached_value.nil? && !cached_value.current_commit.nil? && cached_value.current_commit == commit
|
38
|
+
value = cached_value
|
39
|
+
else
|
40
|
+
complexity, details = yield(change)
|
41
|
+
|
42
|
+
value = Value.new(file_path: change[:file_path],
|
43
|
+
churn: change[:times_changed],
|
44
|
+
complexity: complexity,
|
45
|
+
details: details,
|
46
|
+
history: history)
|
47
|
+
Cache.write(file_path: change[:file_path], value: value)
|
48
|
+
end
|
49
|
+
|
50
|
+
print "."
|
51
|
+
value
|
52
|
+
end
|
53
|
+
|
54
|
+
Cache.persist!
|
55
|
+
|
56
|
+
print "\n\n"
|
57
|
+
|
58
|
+
values
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def git_history_for_file(file_path:, limit: 10)
|
64
|
+
history = `git log --oneline -n #{limit} -- #{file_path}`
|
65
|
+
history.split("\n")
|
66
|
+
.map do |log_entry|
|
67
|
+
log_entry.partition(/\A(\S+)\s/)
|
68
|
+
.map(&:strip)
|
69
|
+
.reject(&:empty?)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
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],
|
@@ -86,7 +87,8 @@ module Attractor
|
|
86
87
|
file_prefix: options[:file_prefix],
|
87
88
|
minimum_churn_count: options[:minimum_churn],
|
88
89
|
ignores: options[:ignore],
|
89
|
-
start_ago: options[:start_ago]
|
90
|
+
start_ago: options[:start_ago],
|
91
|
+
verbose: options[:verbose])
|
90
92
|
end
|
91
93
|
|
92
94
|
def report!(reporter)
|
@@ -0,0 +1,101 @@
|
|
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 "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
|
+
|
44
|
+
desc "calc", "Calculates churn and complexity for all ruby files in current directory"
|
45
|
+
shared_options.each do |shared_option|
|
46
|
+
option(*shared_option)
|
47
|
+
end
|
48
|
+
option(:format, aliases: :f, default: :table)
|
49
|
+
def calc
|
50
|
+
file_prefix = options[:file_prefix]
|
51
|
+
output_format = options[:format]
|
52
|
+
|
53
|
+
report! Attractor::ConsoleReporter.new(file_prefix: file_prefix, ignores: options[:ignore], calculators: calculators(options), format: output_format)
|
54
|
+
rescue RuntimeError => e
|
55
|
+
puts "Runtime error: #{e.message}"
|
56
|
+
end
|
57
|
+
|
58
|
+
desc "report", "Generates an HTML report"
|
59
|
+
(shared_options + advanced_options).each do |option|
|
60
|
+
option(*option)
|
61
|
+
end
|
62
|
+
def report
|
63
|
+
file_prefix = options[:file_prefix]
|
64
|
+
open_browser = !(options[:no_open_browser] || options[:ci])
|
65
|
+
|
66
|
+
report! Attractor::HtmlReporter.new(file_prefix: file_prefix, ignores: options[:ignore], calculators: calculators(options), open_browser: open_browser)
|
67
|
+
rescue RuntimeError => e
|
68
|
+
puts "Runtime error: #{e.message}"
|
69
|
+
end
|
70
|
+
|
71
|
+
desc "serve", "Serves the report on localhost"
|
72
|
+
(shared_options + advanced_options).each do |option|
|
73
|
+
option(*option)
|
74
|
+
end
|
75
|
+
def serve
|
76
|
+
file_prefix = options[:file_prefix]
|
77
|
+
open_browser = !(options[:no_open_browser] || options[:ci])
|
78
|
+
|
79
|
+
report! Attractor::SinatraReporter.new(file_prefix: file_prefix, ignores: options[:ignore], calculators: calculators(options), open_browser: open_browser)
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def calculators(options)
|
85
|
+
Attractor.calculators_for_type(options[:type],
|
86
|
+
file_prefix: options[:file_prefix],
|
87
|
+
minimum_churn_count: options[:minimum_churn],
|
88
|
+
ignores: options[:ignore],
|
89
|
+
start_ago: options[:start_ago])
|
90
|
+
end
|
91
|
+
|
92
|
+
def report!(reporter)
|
93
|
+
if options[:watch]
|
94
|
+
puts "Listening for file changes..."
|
95
|
+
reporter.watch
|
96
|
+
else
|
97
|
+
reporter.report
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,53 @@
|
|
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
|
+
@suggester = Suggester.new(values)
|
23
|
+
|
24
|
+
@watcher = Watcher.new(@file_prefix, ignores, lambda do
|
25
|
+
report
|
26
|
+
end)
|
27
|
+
rescue NoMethodError => _e
|
28
|
+
raise "There was a problem gathering churn changes"
|
29
|
+
end
|
30
|
+
|
31
|
+
def suggestions(quantile:, type: "rb")
|
32
|
+
@suggester.values = values(type: type)
|
33
|
+
@suggestions = @suggester.suggest(quantile)
|
34
|
+
@suggestions
|
35
|
+
end
|
36
|
+
|
37
|
+
def report
|
38
|
+
@suggestions = @suggester.suggest
|
39
|
+
@types = @calculators.map { |calc| [calc.first, calc.last.type] }.to_h
|
40
|
+
end
|
41
|
+
|
42
|
+
def render
|
43
|
+
"Attractor"
|
44
|
+
end
|
45
|
+
|
46
|
+
def values(type: "rb")
|
47
|
+
@values = @calculators[type].calculate
|
48
|
+
@values
|
49
|
+
rescue NoMethodError => _e
|
50
|
+
puts "No calculator for type #{type}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -49,13 +49,42 @@ module Attractor
|
|
49
49
|
end
|
50
50
|
end
|
51
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
|
+
details: value.details,
|
69
|
+
history: value.history
|
70
|
+
}
|
71
|
+
end
|
72
|
+
]
|
73
|
+
end
|
74
|
+
|
75
|
+
puts result.to_h.to_json
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
52
79
|
def initialize(format:, **other)
|
53
80
|
super(**other)
|
54
81
|
@formatter = case format.to_sym
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
82
|
+
when :csv
|
83
|
+
CSVFormatter.new
|
84
|
+
when :json
|
85
|
+
JSONFormatter.new
|
86
|
+
else
|
87
|
+
TableFormatter.new
|
59
88
|
end
|
60
89
|
end
|
61
90
|
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Attractor
|
4
|
+
# console reporter
|
5
|
+
class ConsoleReporter < BaseReporter
|
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]
|
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
|
+
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
|
93
|
+
end
|
94
|
+
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
@@ -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/version.rb
CHANGED
data/lib/attractor/version.rb~
CHANGED
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.6.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:
|
11
|
+
date: 2023-12-03 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
|
@@ -373,16 +373,21 @@ files:
|
|
373
373
|
- lib/attractor.rb
|
374
374
|
- lib/attractor/cache.rb
|
375
375
|
- lib/attractor/calculators/base_calculator.rb
|
376
|
+
- lib/attractor/calculators/base_calculator.rb~
|
376
377
|
- lib/attractor/cli.rb
|
378
|
+
- lib/attractor/cli.rb~
|
377
379
|
- lib/attractor/detectors/base_detector.rb
|
378
380
|
- lib/attractor/duration_parser.rb
|
379
381
|
- lib/attractor/gem_names.rb
|
380
382
|
- lib/attractor/registry_entry.rb
|
381
383
|
- lib/attractor/reporters/base_reporter.rb
|
384
|
+
- lib/attractor/reporters/base_reporter.rb~
|
382
385
|
- lib/attractor/reporters/console_reporter.rb
|
386
|
+
- lib/attractor/reporters/console_reporter.rb~
|
383
387
|
- lib/attractor/reporters/html_reporter.rb
|
384
388
|
- lib/attractor/reporters/sinatra_reporter.rb
|
385
389
|
- lib/attractor/suggester.rb
|
390
|
+
- lib/attractor/suggester.rb~
|
386
391
|
- lib/attractor/value.rb
|
387
392
|
- lib/attractor/version.rb
|
388
393
|
- lib/attractor/version.rb~
|
@@ -395,7 +400,7 @@ metadata:
|
|
395
400
|
source_code_uri: https://github.com/julianrubisch/attractor
|
396
401
|
bug_tracker_uri: https://github.com/julianrubisch/attractor/issues
|
397
402
|
changelog_uri: https://github.com/julianrubisch/attractor/CHANGELOG.md
|
398
|
-
post_install_message:
|
403
|
+
post_install_message:
|
399
404
|
rdoc_options: []
|
400
405
|
require_paths:
|
401
406
|
- lib
|
@@ -410,8 +415,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
410
415
|
- !ruby/object:Gem::Version
|
411
416
|
version: '0'
|
412
417
|
requirements: []
|
413
|
-
rubygems_version: 3.
|
414
|
-
signing_key:
|
418
|
+
rubygems_version: 3.4.19
|
419
|
+
signing_key:
|
415
420
|
specification_version: 4
|
416
421
|
summary: Churn vs Complexity Chart Generator
|
417
422
|
test_files: []
|