attractor 2.1.0 → 2.2.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 +8 -6
- data/lib/attractor.rb +1 -0
- data/lib/attractor.rb~ +12 -13
- data/lib/attractor/cache.rb +67 -0
- data/lib/attractor/cache.rb~ +0 -0
- data/lib/attractor/calculators/base_calculator.rb +19 -6
- data/lib/attractor/calculators/base_calculator.rb~ +18 -20
- data/lib/attractor/reporters/base_reporter.rb~ +14 -14
- data/lib/attractor/value.rb +4 -0
- data/lib/attractor/value.rb~ +30 -0
- data/lib/attractor/version.rb +1 -1
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 38e9529df9d6404034cde6792aab2f1dec600e1d13269b819f71074003a4162b
|
4
|
+
data.tar.gz: 319ad98e23caed681ec07278c7d27402f080618810dab2532aa79ac9c0778c4c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aae211d1f8f4519a5470b43a6ceefdf3ea40580bfbb524e14e13c79ab8f99dc95269e28c089a114c60628ac58eb43ff299c87cc021ce72273a855ef0de21b4ef
|
7
|
+
data.tar.gz: 304a7d902b3fdb6440068ba2501dbe1b9b7cd1b7fea3b1a3590533ab6654a7575ecedd1109fe94e6afb62be0d734f3ae1292f56231a8a157eec79d8ecebbaa07
|
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>
|
@@ -248,15 +248,17 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
|
248
248
|
<!-- markdownlint-disable -->
|
249
249
|
<table>
|
250
250
|
<tr>
|
251
|
-
<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>
|
252
|
-
<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>
|
253
|
-
<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>
|
254
|
-
<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>
|
251
|
+
<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>
|
252
|
+
<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>
|
253
|
+
<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>
|
254
|
+
<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>
|
255
|
+
<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>
|
255
256
|
</tr>
|
256
257
|
</table>
|
257
258
|
|
258
|
-
<!-- markdownlint-
|
259
|
+
<!-- markdownlint-restore -->
|
259
260
|
<!-- prettier-ignore-end -->
|
261
|
+
|
260
262
|
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
261
263
|
|
262
264
|
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
|
data/lib/attractor.rb
CHANGED
@@ -8,6 +8,7 @@ require "attractor/detectors/base_detector"
|
|
8
8
|
require "attractor/reporters/base_reporter"
|
9
9
|
require "attractor/suggester"
|
10
10
|
require "attractor/watcher"
|
11
|
+
require "attractor/cache"
|
11
12
|
|
12
13
|
Dir[File.join(__dir__, "attractor", "reporters", "*.rb")].sort.each do |file|
|
13
14
|
next if file.start_with?("base")
|
data/lib/attractor.rb~
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
|
12
|
-
Dir[File.join(__dir__,
|
13
|
-
next if file.start_with?(
|
3
|
+
require "attractor/version"
|
4
|
+
require "attractor/gem_names"
|
5
|
+
require "attractor/duration_parser"
|
6
|
+
require "attractor/calculators/base_calculator"
|
7
|
+
require "attractor/detectors/base_detector"
|
8
|
+
require "attractor/reporters/base_reporter"
|
9
|
+
require "attractor/suggester"
|
10
|
+
require "attractor/watcher"
|
11
|
+
|
12
|
+
Dir[File.join(__dir__, "attractor", "reporters", "*.rb")].sort.each do |file|
|
13
|
+
next if file.start_with?("base")
|
14
14
|
|
15
15
|
require file
|
16
16
|
end
|
@@ -27,9 +27,8 @@ module Attractor
|
|
27
27
|
def calculators_for_type(type, **options)
|
28
28
|
registry_entry_for_type = @registry_entries[type]
|
29
29
|
|
30
|
-
return {
|
30
|
+
return {type => registry_entry_for_type.calculator_class.new(**options)} if type
|
31
31
|
|
32
|
-
|
33
32
|
Hash[@registry_entries.map do |type, entry|
|
34
33
|
[type, entry.calculator_class.new(**options)] if entry.detector_class.new.detect
|
35
34
|
end.compact]
|
@@ -0,0 +1,67 @@
|
|
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
|
File without changes
|
@@ -27,12 +27,25 @@ module Attractor
|
|
27
27
|
).report(false)
|
28
28
|
|
29
29
|
churn[:churn][:changes].map do |change|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
36
49
|
end
|
37
50
|
end
|
38
51
|
|
@@ -1,20 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require 'csv'
|
5
|
-
require 'date'
|
6
|
-
require 'flog'
|
7
|
-
require 'listen'
|
3
|
+
require "churn/calculator"
|
8
4
|
|
9
|
-
require
|
5
|
+
require "attractor/value"
|
10
6
|
|
11
7
|
module Attractor
|
12
8
|
# calculates churn and complexity
|
13
9
|
class BaseCalculator
|
14
|
-
|
10
|
+
attr_reader :type
|
11
|
+
|
12
|
+
def initialize(file_prefix: "", ignores: "", file_extension: "rb", minimum_churn_count: 3, start_ago: "5y")
|
15
13
|
@file_prefix = file_prefix
|
16
14
|
@file_extension = file_extension
|
17
15
|
@minimum_churn_count = minimum_churn_count
|
16
|
+
@start_date = Date.today - Attractor::DurationParser.new(start_ago).duration
|
17
|
+
@ignores = ignores
|
18
18
|
end
|
19
19
|
|
20
20
|
def calculate
|
@@ -22,20 +22,18 @@ module Attractor
|
|
22
22
|
file_extension: @file_extension,
|
23
23
|
file_prefix: @file_prefix,
|
24
24
|
minimum_churn_count: @minimum_churn_count,
|
25
|
-
start_date:
|
25
|
+
start_date: @start_date,
|
26
|
+
ignores: @ignores
|
26
27
|
).report(false)
|
27
28
|
|
28
29
|
churn[:churn][:changes].map do |change|
|
29
30
|
complexity, details = yield(change)
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
complexity: complexity,
|
37
|
-
details: details,
|
38
|
-
history: git_history_for_file(file_path: change[:file_path]))
|
31
|
+
value = Value.new(file_path: change[:file_path],
|
32
|
+
churn: change[:times_changed],
|
33
|
+
complexity: complexity,
|
34
|
+
details: details,
|
35
|
+
history: git_history_for_file(file_path: change[:file_path]))
|
36
|
+
value
|
39
37
|
end
|
40
38
|
end
|
41
39
|
|
@@ -44,10 +42,10 @@ module Attractor
|
|
44
42
|
def git_history_for_file(file_path:, limit: 10)
|
45
43
|
history = `git log --oneline -n #{limit} -- #{file_path}`
|
46
44
|
history.split("\n")
|
47
|
-
|
45
|
+
.map do |log_entry|
|
48
46
|
log_entry.partition(/\A(\S+)\s/)
|
49
|
-
|
50
|
-
|
47
|
+
.map(&:strip)
|
48
|
+
.reject(&:empty?)
|
51
49
|
end
|
52
50
|
end
|
53
51
|
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
3
|
+
require "descriptive_statistics/safe"
|
4
|
+
require "fileutils"
|
5
|
+
require "forwardable"
|
6
|
+
require "launchy"
|
7
|
+
require "tilt"
|
8
8
|
|
9
9
|
module Attractor
|
10
10
|
# base reporter
|
@@ -15,21 +15,21 @@ module Attractor
|
|
15
15
|
attr_writer :values
|
16
16
|
def_delegator :@watcher, :watch
|
17
17
|
|
18
|
-
def initialize(file_prefix:
|
19
|
-
@file_prefix = file_prefix
|
18
|
+
def initialize(calculators:, file_prefix: "", ignores: "", open_browser: true)
|
19
|
+
@file_prefix = file_prefix || ""
|
20
20
|
@calculators = calculators
|
21
21
|
@open_browser = open_browser
|
22
22
|
@values = @calculators.first.last.calculate
|
23
23
|
@suggester = Suggester.new(values)
|
24
24
|
|
25
|
-
@watcher = Watcher.new(file_prefix, lambda do
|
25
|
+
@watcher = Watcher.new(@file_prefix, ignores, lambda do
|
26
26
|
report
|
27
27
|
end)
|
28
|
-
rescue NoMethodError =>
|
29
|
-
raise
|
28
|
+
rescue NoMethodError => _e
|
29
|
+
raise "There was a problem gathering churn changes"
|
30
30
|
end
|
31
31
|
|
32
|
-
def suggestions(quantile:, type:
|
32
|
+
def suggestions(quantile:, type: "rb")
|
33
33
|
@suggester.values = values(type: type)
|
34
34
|
@suggestions = @suggester.suggest(quantile)
|
35
35
|
@suggestions
|
@@ -41,13 +41,13 @@ module Attractor
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def render
|
44
|
-
|
44
|
+
"Attractor"
|
45
45
|
end
|
46
46
|
|
47
|
-
def values(type:
|
47
|
+
def values(type: "rb")
|
48
48
|
@values = @calculators[type].calculate
|
49
49
|
@values
|
50
|
-
rescue NoMethodError =>
|
50
|
+
rescue NoMethodError => _e
|
51
51
|
puts "No calculator for type #{type}"
|
52
52
|
end
|
53
53
|
end
|
data/lib/attractor/value.rb
CHANGED
@@ -0,0 +1,30 @@
|
|
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/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.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Julian Rubisch
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-04-
|
11
|
+
date: 2021-04-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: churn
|
@@ -373,6 +373,8 @@ files:
|
|
373
373
|
- lib/attractor.rb
|
374
374
|
- lib/attractor.rb~
|
375
375
|
- lib/attractor/#duration_parser.rb#
|
376
|
+
- lib/attractor/cache.rb
|
377
|
+
- lib/attractor/cache.rb~
|
376
378
|
- lib/attractor/calculators/base_calculator.rb
|
377
379
|
- lib/attractor/calculators/base_calculator.rb~
|
378
380
|
- lib/attractor/cli.rb
|
@@ -394,6 +396,7 @@ files:
|
|
394
396
|
- lib/attractor/suggester.rb
|
395
397
|
- lib/attractor/tmp/churn/9f86bc9b7ec6bf59cbf7a111ce8b1159ae128347.json
|
396
398
|
- lib/attractor/value.rb
|
399
|
+
- lib/attractor/value.rb~
|
397
400
|
- lib/attractor/version.rb
|
398
401
|
- lib/attractor/watcher.rb
|
399
402
|
- lib/attractor/watcher.rb~
|
@@ -420,7 +423,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
420
423
|
- !ruby/object:Gem::Version
|
421
424
|
version: '0'
|
422
425
|
requirements: []
|
423
|
-
rubygems_version: 3.
|
426
|
+
rubygems_version: 3.2.3
|
424
427
|
signing_key:
|
425
428
|
specification_version: 4
|
426
429
|
summary: Churn vs Complexity Chart Generator
|