attractor 2.1.0 → 2.2.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 +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
|
-
[](#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
|