attractor 2.0.5 → 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/README.md +29 -6
- data/Rakefile +13 -13
- data/exe/attractor +1 -1
- data/lib/attractor/cache.rb +82 -0
- data/lib/attractor/calculators/base_calculator.rb +38 -14
- data/lib/attractor/cli.rb +50 -44
- data/lib/attractor/duration_parser.rb +7 -5
- data/lib/attractor/gem_names.rb +1 -1
- data/lib/attractor/registry_entry.rb +0 -1
- data/lib/attractor/reporters/base_reporter.rb +14 -15
- data/lib/attractor/reporters/console_reporter.rb +55 -16
- data/lib/attractor/reporters/html_reporter.rb +22 -23
- data/lib/attractor/reporters/sinatra_reporter.rb +17 -17
- data/lib/attractor/suggester.rb +4 -4
- data/lib/attractor/value.rb +12 -4
- data/lib/attractor/version.rb +1 -1
- data/lib/attractor/version.rb~ +7 -0
- data/lib/attractor/watcher.rb +5 -3
- data/lib/attractor.rb +31 -15
- metadata +15 -25
- data/lib/attractor/#duration_parser.rb# +0 -24
- data/lib/attractor/calculators/base_calculator.rb~ +0 -54
- data/lib/attractor/detectors/base_detector.rb~ +0 -4
- data/lib/attractor/duration_parser.rb~ +0 -23
- 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/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
|
data/README.md
CHANGED
@@ -47,7 +47,7 @@
|
|
47
47
|
[![MIT License][license-shield]][license-url]
|
48
48
|
[![Twitter follow][twitter-shield]][twitter-url]
|
49
49
|
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
50
|
-
[](#contributors-)
|
51
51
|
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
52
52
|
|
53
53
|
<a href="https://www.patreon.com/user?u=24747270"><img src="https://c5.patreon.com/external/logo/become_a_patron_button@2x.png" alt="Become a Patron!" width="160" /></a>
|
@@ -186,6 +186,17 @@ attractor:
|
|
186
186
|
|
187
187
|
## CLI Commands and Options
|
188
188
|
|
189
|
+
Initialize the local cache:
|
190
|
+
|
191
|
+
```sh
|
192
|
+
attractor init
|
193
|
+
--file_prefix|-p app/models
|
194
|
+
--type|-t rb|js
|
195
|
+
--start_ago|-s (e.g. 5y, 3m, 7w)
|
196
|
+
--minimum_churn|-c (minimum times a file must have changed to be processed)
|
197
|
+
--ignore|-i 'spec/*_spec.rb,db/schema.rb,tmp'
|
198
|
+
```
|
199
|
+
|
189
200
|
Print a simple output to console:
|
190
201
|
|
191
202
|
```sh
|
@@ -195,6 +206,7 @@ attractor calc
|
|
195
206
|
--watch|-w
|
196
207
|
--start_ago|-s (e.g. 5y, 3m, 7w)
|
197
208
|
--minimum_churn|-c (minimum times a file must have changed to be processed)
|
209
|
+
--ignore|-i 'spec/*_spec.rb,db/schema.rb,tmp'
|
198
210
|
```
|
199
211
|
|
200
212
|
Generate a full report
|
@@ -207,6 +219,7 @@ attractor report
|
|
207
219
|
--no-open-browser|--ci
|
208
220
|
--start_ago|-s (e.g. 5y, 3m, 7w)
|
209
221
|
--minimum_churn|-c (minimum times a file must have changed to be processed)
|
222
|
+
--ignore|-i 'spec/*_spec.rb,db/schema.rb,tmp'
|
210
223
|
```
|
211
224
|
|
212
225
|
Serve the output on `http://localhost:7890`
|
@@ -218,8 +231,16 @@ attractor serve
|
|
218
231
|
--no-open-browser|--ci
|
219
232
|
--start_ago|-s (e.g. 5y, 3m, 7w)
|
220
233
|
--minimum_churn|-c (minimum times a file must have changed to be processed)
|
234
|
+
--ignore|-i 'spec/*_spec.rb,db/schema.rb,tmp'
|
221
235
|
```
|
222
236
|
|
237
|
+
Clear the local cache:
|
238
|
+
|
239
|
+
```sh
|
240
|
+
attractor clean
|
241
|
+
```
|
242
|
+
|
243
|
+
|
223
244
|
## Development
|
224
245
|
|
225
246
|
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -245,15 +266,17 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
|
245
266
|
<!-- markdownlint-disable -->
|
246
267
|
<table>
|
247
268
|
<tr>
|
248
|
-
<td align="center"><a href="http://www.julianrubisch.at"><img src="https://avatars0.githubusercontent.com/u/4352208?v=4" width="100px;" alt=""/><br /><sub><b>Julian Rubisch</b></sub></a><br /><a href="https://github.com/julianrubisch/attractor/commits?author=julianrubisch" title="Code">💻</a> <a href="https://github.com/julianrubisch/attractor/commits?author=julianrubisch" title="Documentation">📖</a></td>
|
249
|
-
<td align="center"><a href="https://github.com/olimart"><img src="https://avatars3.githubusercontent.com/u/547754?v=4" width="100px;" alt=""/><br /><sub><b>Olivier</b></sub></a><br /><a href="#maintenance-olimart" title="Maintenance">🚧</a></td>
|
250
|
-
<td align="center"><a href="https://www.andrewmason.me/"><img src="https://avatars1.githubusercontent.com/u/18423853?v=4" width="100px;" alt=""/><br /><sub><b>Andrew Mason</b></sub></a><br /><a href="https://github.com/julianrubisch/attractor/commits?author=andrewmcodes" title="Code">💻</a> <a href="https://github.com/julianrubisch/attractor/pulls?q=is%3Apr+reviewed-by%3Aandrewmcodes" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/julianrubisch/attractor/commits?author=andrewmcodes" title="Documentation">📖</a></td>
|
251
|
-
<td align="center"><a href="https://www.ombulabs.com"><img src="https://avatars2.githubusercontent.com/u/17584?v=4" width="100px;" alt=""/><br /><sub><b>Ernesto Tagwerker</b></sub></a><br /><a href="https://github.com/julianrubisch/attractor/commits?author=etagwerker" title="Code">💻</a></td>
|
269
|
+
<td align="center"><a href="http://www.julianrubisch.at"><img src="https://avatars0.githubusercontent.com/u/4352208?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Julian Rubisch</b></sub></a><br /><a href="https://github.com/julianrubisch/attractor/commits?author=julianrubisch" title="Code">💻</a> <a href="https://github.com/julianrubisch/attractor/commits?author=julianrubisch" title="Documentation">📖</a></td>
|
270
|
+
<td align="center"><a href="https://github.com/olimart"><img src="https://avatars3.githubusercontent.com/u/547754?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Olivier</b></sub></a><br /><a href="#maintenance-olimart" title="Maintenance">🚧</a></td>
|
271
|
+
<td align="center"><a href="https://www.andrewmason.me/"><img src="https://avatars1.githubusercontent.com/u/18423853?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Andrew Mason</b></sub></a><br /><a href="https://github.com/julianrubisch/attractor/commits?author=andrewmcodes" title="Code">💻</a> <a href="https://github.com/julianrubisch/attractor/pulls?q=is%3Apr+reviewed-by%3Aandrewmcodes" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/julianrubisch/attractor/commits?author=andrewmcodes" title="Documentation">📖</a></td>
|
272
|
+
<td align="center"><a href="https://www.ombulabs.com"><img src="https://avatars2.githubusercontent.com/u/17584?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ernesto Tagwerker</b></sub></a><br /><a href="https://github.com/julianrubisch/attractor/commits?author=etagwerker" title="Code">💻</a></td>
|
273
|
+
<td align="center"><a href="https://experimentslabs.com"><img src="https://avatars.githubusercontent.com/u/1732268?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Manuel Tancoigne</b></sub></a><br /><a href="https://github.com/julianrubisch/attractor/commits?author=mtancoigne" title="Code">💻</a></td>
|
252
274
|
</tr>
|
253
275
|
</table>
|
254
276
|
|
255
|
-
<!-- markdownlint-
|
277
|
+
<!-- markdownlint-restore -->
|
256
278
|
<!-- prettier-ignore-end -->
|
279
|
+
|
257
280
|
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
258
281
|
|
259
282
|
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
|
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
@@ -0,0 +1,82 @@
|
|
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
|
+
def persist!
|
18
|
+
adapter.persist!
|
19
|
+
end
|
20
|
+
|
21
|
+
def clear
|
22
|
+
adapter.clear
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def adapter
|
28
|
+
@@adapter ||= CacheAdapter::JSON.instance
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
module CacheAdapter
|
34
|
+
class Base
|
35
|
+
include Singleton
|
36
|
+
end
|
37
|
+
|
38
|
+
class JSON < Base
|
39
|
+
def initialize
|
40
|
+
super
|
41
|
+
|
42
|
+
@data_directory = "tmp"
|
43
|
+
FileUtils.mkdir_p @data_directory
|
44
|
+
FileUtils.touch filename
|
45
|
+
|
46
|
+
begin
|
47
|
+
@store = ::JSON.parse(File.read(filename))
|
48
|
+
rescue ::JSON::ParserError
|
49
|
+
@store = {}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def read(file_path:)
|
54
|
+
value_hash = @store[file_path]
|
55
|
+
|
56
|
+
Value.new(**value_hash.values.first.transform_keys(&:to_sym)) unless value_hash.nil?
|
57
|
+
rescue ArgumentError => e
|
58
|
+
puts "Couldn't rehydrate value from cache: #{e.message}"
|
59
|
+
nil
|
60
|
+
end
|
61
|
+
|
62
|
+
def write(file_path:, value:)
|
63
|
+
mappings = {x: :churn, y: :complexity}
|
64
|
+
|
65
|
+
transformed_value = value.to_h.transform_keys { |k| mappings[k] || k }
|
66
|
+
@store[file_path] = {value.current_commit => transformed_value}
|
67
|
+
end
|
68
|
+
|
69
|
+
def persist!
|
70
|
+
File.write(filename, ::JSON.dump(@store))
|
71
|
+
end
|
72
|
+
|
73
|
+
def clear
|
74
|
+
FileUtils.rm filename
|
75
|
+
end
|
76
|
+
|
77
|
+
def filename
|
78
|
+
"#{@data_directory}/attractor-cache.json"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -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,17 +22,40 @@ 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
|
-
churn[:churn][:changes].
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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
|
34
52
|
end
|
53
|
+
|
54
|
+
Cache.persist!
|
55
|
+
|
56
|
+
print "\n\n"
|
57
|
+
|
58
|
+
values
|
35
59
|
end
|
36
60
|
|
37
61
|
private
|
@@ -39,10 +63,10 @@ module Attractor
|
|
39
63
|
def git_history_for_file(file_path:, limit: 10)
|
40
64
|
history = `git log --oneline -n #{limit} -- #{file_path}`
|
41
65
|
history.split("\n")
|
42
|
-
|
66
|
+
.map do |log_entry|
|
43
67
|
log_entry.partition(/\A(\S+)\s/)
|
44
|
-
|
45
|
-
|
68
|
+
.map(&:strip)
|
69
|
+
.reject(&:empty?)
|
46
70
|
end
|
47
71
|
end
|
48
72
|
end
|
data/lib/attractor/cli.rb
CHANGED
@@ -1,95 +1,101 @@
|
|
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 "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"
|
29
45
|
shared_options.each do |shared_option|
|
30
46
|
option(*shared_option)
|
31
47
|
end
|
48
|
+
option(:format, aliases: :f, default: :table)
|
32
49
|
def calc
|
33
50
|
file_prefix = options[:file_prefix]
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
else
|
38
|
-
Attractor::ConsoleReporter.new(file_prefix: file_prefix, calculators: calculators(options)).report
|
39
|
-
end
|
51
|
+
output_format = options[:format]
|
52
|
+
|
53
|
+
report! Attractor::ConsoleReporter.new(file_prefix: file_prefix, ignores: options[:ignore], calculators: calculators(options), format: output_format)
|
40
54
|
rescue RuntimeError => e
|
41
55
|
puts "Runtime error: #{e.message}"
|
42
56
|
end
|
43
57
|
|
44
|
-
desc
|
58
|
+
desc "report", "Generates an HTML report"
|
45
59
|
(shared_options + advanced_options).each do |option|
|
46
60
|
option(*option)
|
47
61
|
end
|
48
62
|
def report
|
49
63
|
file_prefix = options[:file_prefix]
|
50
64
|
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
|
65
|
+
|
66
|
+
report! Attractor::HtmlReporter.new(file_prefix: file_prefix, ignores: options[:ignore], calculators: calculators(options), open_browser: open_browser)
|
62
67
|
rescue RuntimeError => e
|
63
68
|
puts "Runtime error: #{e.message}"
|
64
69
|
end
|
65
70
|
|
66
|
-
desc
|
71
|
+
desc "serve", "Serves the report on localhost"
|
67
72
|
(shared_options + advanced_options).each do |option|
|
68
73
|
option(*option)
|
69
74
|
end
|
70
75
|
def serve
|
71
76
|
file_prefix = options[:file_prefix]
|
72
77
|
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
|
78
|
+
|
79
|
+
report! Attractor::SinatraReporter.new(file_prefix: file_prefix, ignores: options[:ignore], calculators: calculators(options), open_browser: open_browser)
|
84
80
|
end
|
85
81
|
|
86
82
|
private
|
87
83
|
|
88
84
|
def calculators(options)
|
89
85
|
Attractor.calculators_for_type(options[:type],
|
90
|
-
|
91
|
-
|
92
|
-
|
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
|
93
99
|
end
|
94
100
|
end
|
95
101
|
end
|
@@ -4,17 +4,19 @@ module Attractor
|
|
4
4
|
# converts a duration string into an amount of days
|
5
5
|
class DurationParser
|
6
6
|
TOKENS = {
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
"d" => 1,
|
8
|
+
"w" => 7,
|
9
|
+
"m" => 30,
|
10
|
+
"y" => 365
|
11
11
|
}.freeze
|
12
12
|
|
13
13
|
attr_reader :duration
|
14
14
|
|
15
15
|
def initialize(input)
|
16
16
|
@input = input
|
17
|
-
@duration = 0
|
17
|
+
@duration = @input.is_a?(Numeric) ? @input : 0
|
18
|
+
return if @duration > 0
|
19
|
+
|
18
20
|
parse
|
19
21
|
end
|
20
22
|
|
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,20 @@ 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
|
-
@values = @calculators.first.last.calculate
|
23
22
|
@suggester = Suggester.new(values)
|
24
23
|
|
25
|
-
@watcher = Watcher.new(@file_prefix, lambda do
|
24
|
+
@watcher = Watcher.new(@file_prefix, ignores, lambda do
|
26
25
|
report
|
27
26
|
end)
|
28
|
-
rescue NoMethodError =>
|
29
|
-
raise
|
27
|
+
rescue NoMethodError => _e
|
28
|
+
raise "There was a problem gathering churn changes"
|
30
29
|
end
|
31
30
|
|
32
|
-
def suggestions(quantile:, type:
|
31
|
+
def suggestions(quantile:, type: "rb")
|
33
32
|
@suggester.values = values(type: type)
|
34
33
|
@suggestions = @suggester.suggest(quantile)
|
35
34
|
@suggestions
|
@@ -37,17 +36,17 @@ 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
|
44
|
-
|
43
|
+
"Attractor"
|
45
44
|
end
|
46
45
|
|
47
|
-
def values(type:
|
46
|
+
def values(type: "rb")
|
48
47
|
@values = @calculators[type].calculate
|
49
48
|
@values
|
50
|
-
rescue NoMethodError =>
|
49
|
+
rescue NoMethodError => _e
|
51
50
|
puts "No calculator for type #{type}"
|
52
51
|
end
|
53
52
|
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
|