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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a044632a13376f395112a65b938be9c3173a16499f4eab725e97afffc1e83925
4
- data.tar.gz: 20aafed307f3cf4f047130aa6aafc2395838ba50ecf48dedc0bd205fe5e6c391
3
+ metadata.gz: 38e9529df9d6404034cde6792aab2f1dec600e1d13269b819f71074003a4162b
4
+ data.tar.gz: 319ad98e23caed681ec07278c7d27402f080618810dab2532aa79ac9c0778c4c
5
5
  SHA512:
6
- metadata.gz: 12a4dd82ff8d50a7d5e7c3663b6b013e93d6575a706581c7b64a5bbb75714d9d8126c3f24344f57e2c5c79905ea549e82370def04fb6643ff25fbc38f365c846
7
- data.tar.gz: f27f15c33b573ab2cccb9be34d59f89c12575e19fe18454d5380c4fd500f98e63e2b0e8be2bd4e7a86c18512124fb6792e5f625101ef4e8333b54bf59a03a165
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-4-orange.svg?style=flat-square)](#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-enable -->
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 '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')].each do |file|
13
- next if file.start_with?('base')
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 { type => registry_entry_for_type.calculator_class.new(**options) } if type
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
- complexity, details = yield(change)
31
- 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]))
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 'churn/calculator'
4
- require 'csv'
5
- require 'date'
6
- require 'flog'
7
- require 'listen'
3
+ require "churn/calculator"
8
4
 
9
- require 'attractor/value'
5
+ require "attractor/value"
10
6
 
11
7
  module Attractor
12
8
  # calculates churn and complexity
13
9
  class BaseCalculator
14
- def initialize(file_prefix: '', file_extension: 'rb', minimum_churn_count: 3)
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: Date.today - 365 * 5
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
- # flogger = Flog.new(all: true)
31
- # flogger.flog(change[:file_path])
32
- # complexity = flogger.total_score
33
- # details = flogger.totals
34
- Value.new(file_path: change[:file_path],
35
- churn: change[:times_changed],
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
- .map do |log_entry|
45
+ .map do |log_entry|
48
46
  log_entry.partition(/\A(\S+)\s/)
49
- .map(&:strip)
50
- .reject(&:empty?)
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 'descriptive_statistics/safe'
4
- require 'fileutils'
5
- require 'forwardable'
6
- require 'launchy'
7
- require 'tilt'
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: '', calculators:, open_browser: true)
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 => e
29
- raise 'There was a problem gathering churn changes'
28
+ rescue NoMethodError => _e
29
+ raise "There was a problem gathering churn changes"
30
30
  end
31
31
 
32
- def suggestions(quantile:, type: 'rb')
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
- 'Attractor'
44
+ "Attractor"
45
45
  end
46
46
 
47
- def values(type: 'rb')
47
+ def values(type: "rb")
48
48
  @values = @calculators[type].calculate
49
49
  @values
50
- rescue NoMethodError => e
50
+ rescue NoMethodError => _e
51
51
  puts "No calculator for type #{type}"
52
52
  end
53
53
  end
@@ -15,6 +15,10 @@ module Attractor
15
15
  @history = history
16
16
  end
17
17
 
18
+ def current_commit
19
+ history&.first&.first
20
+ end
21
+
18
22
  def to_s
19
23
  format("%-64s%8.1f%8i", @file_path, @complexity, @churn)
20
24
  end
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Attractor
2
- VERSION = "2.1.0"
2
+ VERSION = "2.2.0"
3
3
  end
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.1.0
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-01 00:00:00.000000000 Z
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.1.4
426
+ rubygems_version: 3.2.3
424
427
  signing_key:
425
428
  specification_version: 4
426
429
  summary: Churn vs Complexity Chart Generator