attractor 2.0.5 → 2.4.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 +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
|
-
[![All Contributors](https://img.shields.io/badge/all_contributors-
|
50
|
+
[![All Contributors](https://img.shields.io/badge/all_contributors-5-orange.svg?style=flat-square)](#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
|