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 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