attractor 2.0.2 → 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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +15 -6
  3. data/Rakefile +13 -13
  4. data/app/assets/javascripts/index.pack.js +1 -1
  5. data/app/assets/stylesheets/main.css +1 -1
  6. data/exe/attractor +1 -1
  7. data/lib/attractor.rb +13 -13
  8. data/lib/attractor.rb~ +43 -0
  9. data/lib/attractor/#duration_parser.rb# +24 -0
  10. data/lib/attractor/cache.rb +67 -0
  11. data/lib/attractor/{reporters/.keep → cache.rb~} +0 -0
  12. data/lib/attractor/calculators/base_calculator.rb +28 -13
  13. data/lib/attractor/calculators/base_calculator.rb~ +52 -0
  14. data/lib/attractor/cli.rb +33 -44
  15. data/lib/attractor/detectors/base_detector.rb~ +4 -0
  16. data/lib/attractor/duration_parser.rb +4 -4
  17. data/lib/attractor/duration_parser.rb~ +23 -0
  18. data/lib/attractor/gem_names.rb +3 -1
  19. data/lib/attractor/gem_names.rb~ +23 -0
  20. data/lib/attractor/registry_entry.rb +0 -1
  21. data/lib/attractor/registry_entry.rb~ +2 -0
  22. data/lib/attractor/reporters/base_reporter.rb +22 -13
  23. data/lib/attractor/reporters/base_reporter.rb~ +54 -0
  24. data/lib/attractor/reporters/console_reporter.rb +4 -4
  25. data/lib/attractor/reporters/console_reporter.rb~ +21 -0
  26. data/lib/attractor/reporters/html_reporter.rb +23 -25
  27. data/lib/attractor/reporters/html_reporter.rb~ +48 -0
  28. data/lib/attractor/reporters/sinatra_reporter.rb +18 -24
  29. data/lib/attractor/suggester.rb +3 -1
  30. data/lib/attractor/tmp/churn/9f86bc9b7ec6bf59cbf7a111ce8b1159ae128347.json +1 -0
  31. data/lib/attractor/value.rb +8 -4
  32. data/lib/attractor/value.rb~ +30 -0
  33. data/lib/attractor/version.rb +1 -1
  34. data/lib/attractor/watcher.rb +7 -3
  35. data/lib/attractor/watcher.rb~ +24 -0
  36. metadata +29 -31
  37. data/.babelrc +0 -4
  38. data/.eslintrc.json +0 -25
  39. data/.gitignore +0 -20
  40. data/.rubocop.yml +0 -2
  41. data/CHANGELOG.md +0 -95
  42. data/Gemfile +0 -4
  43. data/Guardfile +0 -27
  44. data/attractor.gemspec +0 -65
  45. data/bin/console +0 -14
  46. data/bin/setup +0 -8
  47. data/bin/standardize +0 -4
  48. data/bin/test +0 -6
  49. data/dist/calculator.bundle.js +0 -1
  50. data/package-lock.json +0 -6906
  51. data/package.json +0 -53
  52. data/webpack.config.js +0 -23
data/lib/attractor/cli.rb CHANGED
@@ -1,95 +1,84 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'thor'
3
+ require "thor"
4
4
 
5
- require 'attractor'
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
- [:watch, aliases: :w, type: :boolean],
12
- [:minimum_churn, aliases: :c, type: :numeric, default: 3],
13
- [:start_ago, aliases: :s, type: :string, default: '5y'],
14
- [:type, aliases: :t]]
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: 'html'],
17
- [:no_open_browser, type: :boolean],
18
- [:ci, type: :boolean]]
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(-v --version) => :version
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 'calc', 'Calculates churn and complexity for all ruby files in current directory'
29
+ desc "calc", "Calculates churn and complexity for all ruby files in current directory"
29
30
  shared_options.each do |shared_option|
30
31
  option(*shared_option)
31
32
  end
32
33
  def calc
33
34
  file_prefix = options[:file_prefix]
34
- if options[:watch]
35
- puts 'Listening for file changes...'
36
- Attractor::ConsoleReporter.new(file_prefix: file_prefix, calculators: calculators(options)).watch
37
- else
38
- Attractor::ConsoleReporter.new(file_prefix: file_prefix, calculators: calculators(options)).report
39
- end
35
+
36
+ report! Attractor::ConsoleReporter.new(file_prefix: file_prefix, ignores: options[:ignore], calculators: calculators(options))
40
37
  rescue RuntimeError => e
41
38
  puts "Runtime error: #{e.message}"
42
39
  end
43
40
 
44
- desc 'report', 'Generates an HTML report'
41
+ desc "report", "Generates an HTML report"
45
42
  (shared_options + advanced_options).each do |option|
46
43
  option(*option)
47
44
  end
48
45
  def report
49
46
  file_prefix = options[:file_prefix]
50
47
  open_browser = !(options[:no_open_browser] || options[:ci])
51
- if options[:watch]
52
- puts 'Listening for file changes...'
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
48
+
49
+ report! Attractor::HtmlReporter.new(file_prefix: file_prefix, ignores: options[:ignore], calculators: calculators(options), open_browser: open_browser)
62
50
  rescue RuntimeError => e
63
51
  puts "Runtime error: #{e.message}"
64
52
  end
65
53
 
66
- desc 'serve', 'Serves the report on localhost'
54
+ desc "serve", "Serves the report on localhost"
67
55
  (shared_options + advanced_options).each do |option|
68
56
  option(*option)
69
57
  end
70
58
  def serve
71
59
  file_prefix = options[:file_prefix]
72
60
  open_browser = !(options[:no_open_browser] || options[:ci])
73
- if options[:watch]
74
- puts 'Listening for file changes...'
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
61
+
62
+ report! Attractor::SinatraReporter.new(file_prefix: file_prefix, ignores: options[:ignore], calculators: calculators(options), open_browser: open_browser)
84
63
  end
85
64
 
86
65
  private
87
66
 
88
67
  def calculators(options)
89
68
  Attractor.calculators_for_type(options[:type],
90
- file_prefix: options[:file_prefix],
91
- minimum_churn_count: options[:minimum_churn],
92
- start_ago: options[:start_ago])
69
+ file_prefix: options[:file_prefix],
70
+ minimum_churn_count: options[:minimum_churn],
71
+ ignores: options[:ignore],
72
+ start_ago: options[:start_ago])
73
+ end
74
+
75
+ def report!(reporter)
76
+ if options[:watch]
77
+ puts "Listening for file changes..."
78
+ reporter.watch
79
+ else
80
+ reporter.report
81
+ end
93
82
  end
94
83
  end
95
84
  end
@@ -0,0 +1,4 @@
1
+ module Attractor
2
+ class BaseDetector
3
+ end
4
+ end
@@ -4,10 +4,10 @@ module Attractor
4
4
  # converts a duration string into an amount of days
5
5
  class DurationParser
6
6
  TOKENS = {
7
- 'd' => 1,
8
- 'w' => 7,
9
- 'm' => 30,
10
- 'y' => 365
7
+ "d" => 1,
8
+ "w" => 7,
9
+ "m" => 30,
10
+ "y" => 365
11
11
  }.freeze
12
12
 
13
13
  attr_reader :duration
@@ -0,0 +1,23 @@
1
+ module Attractor
2
+ class DurationParser
3
+ TOKENS = {
4
+ "m" => (60),
5
+ "h" => (60 * 60),
6
+ "d" => (60 * 60 * 24)
7
+ }
8
+
9
+ attr_reader :time
10
+
11
+ def initialize(input)
12
+ @input = input
13
+ @time = 0
14
+ parse
15
+ end
16
+
17
+ def parse
18
+ @input.scan(/(\d+)(\w)/).each do |amount, measure|
19
+ @time += amount.to_i * TOKENS[measure]
20
+ end
21
+ end
22
+ end
23
+ end
@@ -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-/, '') }.uniq.sort
5
+ gems.map { |gem| gem.name.sub(/^attractor-/, "") }.uniq.sort
6
6
  end
7
7
 
8
8
  private
@@ -10,6 +10,8 @@ module Attractor
10
10
  def gems
11
11
  Gem::Specification.find_all.select do |gem|
12
12
  gem.name =~ /^attractor-/
13
+ end.reject do |gem|
14
+ gem.name =~ /attractor-rails/
13
15
  end
14
16
  end
15
17
  end
@@ -0,0 +1,23 @@
1
+ module Attractor
2
+
3
+ # from https://github.com/prontolabs/pronto/blob/master/lib/pronto/gem_names.rb
4
+ class GemNames
5
+ def to_a
6
+ gems.map { |gem| gem.name.sub(/^pronto-/, '') }.uniq.sort
7
+ end
8
+
9
+ private
10
+
11
+ def gems
12
+ Gem::Specification.find_all.select do |gem|
13
+ if gem.name =~ /^pronto-/
14
+ true
15
+ elsif gem.name != 'pronto'
16
+ runner_path = File.join(gem.full_gem_path,
17
+ "lib/pronto/#{gem.name}.rb")
18
+ File.exist?(runner_path)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -9,4 +9,3 @@ module Attractor
9
9
  end
10
10
  end
11
11
  end
12
-
@@ -0,0 +1,2 @@
1
+ RegistryEntry = Struct.new :type, :detector, :calculator
2
+
@@ -1,34 +1,36 @@
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
11
11
  class BaseReporter
12
12
  extend Forwardable
13
- attr_accessor :values, :file_prefix
13
+ attr_accessor :file_prefix
14
14
  attr_reader :types
15
+ attr_writer :values
15
16
  def_delegator :@watcher, :watch
16
17
 
17
- def initialize(file_prefix: '', calculators:, open_browser: true)
18
- @file_prefix = file_prefix
18
+ def initialize(calculators:, file_prefix: "", ignores: "", open_browser: true)
19
+ @file_prefix = file_prefix || ""
19
20
  @calculators = calculators
20
21
  @open_browser = open_browser
21
22
  @values = @calculators.first.last.calculate
22
23
  @suggester = Suggester.new(values)
23
24
 
24
- @watcher = Watcher.new(file_prefix, lambda do
25
+ @watcher = Watcher.new(@file_prefix, ignores, lambda do
25
26
  report
26
27
  end)
27
- rescue NoMethodError => e
28
- raise 'There was a problem gathering churn changes'
28
+ rescue NoMethodError => _e
29
+ raise "There was a problem gathering churn changes"
29
30
  end
30
31
 
31
- def suggestions(quantile)
32
+ def suggestions(quantile:, type: "rb")
33
+ @suggester.values = values(type: type)
32
34
  @suggestions = @suggester.suggest(quantile)
33
35
  @suggestions
34
36
  end
@@ -39,7 +41,14 @@ module Attractor
39
41
  end
40
42
 
41
43
  def render
42
- 'Attractor'
44
+ "Attractor"
45
+ end
46
+
47
+ def values(type: "rb")
48
+ @values = @calculators[type].calculate
49
+ @values
50
+ rescue NoMethodError => _e
51
+ puts "No calculator for type #{type}"
43
52
  end
44
53
  end
45
54
  end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "descriptive_statistics/safe"
4
+ require "fileutils"
5
+ require "forwardable"
6
+ require "launchy"
7
+ require "tilt"
8
+
9
+ module Attractor
10
+ # base reporter
11
+ class BaseReporter
12
+ extend Forwardable
13
+ attr_accessor :file_prefix
14
+ attr_reader :types
15
+ attr_writer :values
16
+ def_delegator :@watcher, :watch
17
+
18
+ def initialize(calculators:, file_prefix: "", ignores: "", open_browser: true)
19
+ @file_prefix = file_prefix || ""
20
+ @calculators = calculators
21
+ @open_browser = open_browser
22
+ @values = @calculators.first.last.calculate
23
+ @suggester = Suggester.new(values)
24
+
25
+ @watcher = Watcher.new(@file_prefix, ignores, lambda do
26
+ report
27
+ end)
28
+ rescue NoMethodError => _e
29
+ raise "There was a problem gathering churn changes"
30
+ end
31
+
32
+ def suggestions(quantile:, type: "rb")
33
+ @suggester.values = values(type: type)
34
+ @suggestions = @suggester.suggest(quantile)
35
+ @suggestions
36
+ end
37
+
38
+ def report
39
+ @suggestions = @suggester.suggest
40
+ @types = Hash[@calculators.map { |calc| [calc.first, calc.last.type] }]
41
+ end
42
+
43
+ def render
44
+ "Attractor"
45
+ end
46
+
47
+ def values(type: "rb")
48
+ @values = @calculators[type].calculate
49
+ @values
50
+ rescue NoMethodError => _e
51
+ puts "No calculator for type #{type}"
52
+ end
53
+ end
54
+ end
@@ -5,10 +5,10 @@ module Attractor
5
5
  class ConsoleReporter < BaseReporter
6
6
  def report
7
7
  super
8
- puts 'Calculated churn and complexity'
8
+ puts "Calculated churn and complexity"
9
9
  puts
10
- puts "file_path#{' ' * 53}complexity churn"
11
- puts '-' * 80
10
+ puts "file_path#{" " * 53}complexity churn"
11
+ puts "-" * 80
12
12
 
13
13
  @calculators.each do |calc|
14
14
  # e.g. ['js', JsCalculator']
@@ -19,7 +19,7 @@ module Attractor
19
19
 
20
20
  puts values&.map(&:to_s)
21
21
  puts
22
- puts 'Suggestions for refactorings:'
22
+ puts "Suggestions for refactorings:"
23
23
  suggester.suggest&.each { |sug| puts sug.file_path }
24
24
  puts
25
25
  end
@@ -0,0 +1,21 @@
1
+ require 'base_reporter'
2
+
3
+ module Attractor
4
+ # console reporter
5
+ class ConsoleReporter < BaseReporter
6
+ def report
7
+ super
8
+ puts 'Calculated churn and complexity'
9
+ puts
10
+ puts "file_path#{' ' * 53}complexity churn"
11
+ puts '-' * 80
12
+
13
+ puts @values.map(&:to_s)
14
+
15
+ puts
16
+ puts 'Suggestions for refactorings:'
17
+ @suggestions.each { |sug| puts sug.file_path }
18
+ puts
19
+ end
20
+ end
21
+ end
@@ -6,29 +6,28 @@ module Attractor
6
6
  def report
7
7
  super
8
8
 
9
- puts 'Generating an HTML report'
9
+ puts "Generating an HTML report"
10
10
  @serve_static = true
11
11
 
12
- FileUtils.mkdir_p './attractor_output'
13
- FileUtils.mkdir_p './attractor_output/stylesheets'
14
- FileUtils.mkdir_p './attractor_output/images'
15
- FileUtils.mkdir_p './attractor_output/javascripts'
12
+ FileUtils.mkdir_p "./attractor_output"
13
+ FileUtils.mkdir_p "./attractor_output/stylesheets"
14
+ FileUtils.mkdir_p "./attractor_output/images"
15
+ FileUtils.mkdir_p "./attractor_output/javascripts"
16
16
 
17
- File.open('./attractor_output/images/attractor_logo.svg', 'w') { |file| file.write(logo) }
18
- File.open('./attractor_output/images/attractor_favicon.png', 'w') { |file| file.write(favicon) }
19
- File.open('./attractor_output/stylesheets/main.css', 'w') { |file| file.write(css) }
20
- File.open('./attractor_output/javascripts/index.pack.js', 'w') { |file| file.write(javascript_pack) }
17
+ File.open("./attractor_output/images/attractor_logo.svg", "w") { |file| file.write(logo) }
18
+ File.open("./attractor_output/images/attractor_favicon.png", "w") { |file| file.write(favicon) }
19
+ File.open("./attractor_output/stylesheets/main.css", "w") { |file| file.write(css) }
20
+ File.open("./attractor_output/javascripts/index.pack.js", "w") { |file| file.write(javascript_pack) }
21
21
 
22
22
  if @calculators.size > 1
23
23
  @calculators.each do |calc|
24
24
  @short_type = calc.first
25
- @values = calc.last.calculate
26
- suggester = Suggester.new(values)
25
+ suggester = Suggester.new(values(type: @short_type))
27
26
  @suggestions = suggester.suggest
28
27
 
29
- File.open("./attractor_output/javascripts/index.#{@short_type}.js", 'w') { |file| file.write(javascript) }
30
- File.open("./attractor_output/index.#{@short_type}.html", 'w') { |file| file.write(render) }
31
- puts "Generated HTML report at #{File.expand_path './attractor_output/'}/index.#{@short_type}.html"
28
+ File.open("./attractor_output/javascripts/index.#{@short_type}.js", "w") { |file| file.write(javascript) }
29
+ File.open("./attractor_output/index.#{@short_type}.html", "w") { |file| file.write(render) }
30
+ puts "Generated HTML report at #{File.expand_path "./attractor_output/"}/index.#{@short_type}.html"
32
31
  end
33
32
 
34
33
  if @open_browser
@@ -36,41 +35,40 @@ module Attractor
36
35
  puts "Opening browser window..."
37
36
  end
38
37
  else
39
- File.open('./attractor_output/javascripts/index.js', 'w') { |file| file.write(javascript) }
40
- File.open('./attractor_output/index.html', 'w') { |file| file.write(render) }
41
- puts "Generated HTML report at #{File.expand_path './attractor_output/index.html'}"
38
+ File.open("./attractor_output/javascripts/index.js", "w") { |file| file.write(javascript) }
39
+ File.open("./attractor_output/index.html", "w") { |file| file.write(render) }
40
+ puts "Generated HTML report at #{File.expand_path "./attractor_output/index.html"}"
42
41
 
43
42
  if @open_browser
44
- Launchy.open(File.expand_path('./attractor_output/index.html'))
43
+ Launchy.open(File.expand_path("./attractor_output/index.html"))
45
44
  puts "Opening browser window..."
46
45
  end
47
46
  end
48
-
49
47
  end
50
48
 
51
49
  def logo
52
- File.read(File.expand_path('../../../app/assets/images/attractor_logo.svg', __dir__))
50
+ File.read(File.expand_path("../../../app/assets/images/attractor_logo.svg", __dir__))
53
51
  end
54
52
 
55
53
  def favicon
56
- File.read(File.expand_path('../../../app/assets/images/attractor_favicon.png', __dir__))
54
+ File.read(File.expand_path("../../../app/assets/images/attractor_favicon.png", __dir__))
57
55
  end
58
56
 
59
57
  def css
60
- File.read(File.expand_path('../../../app/assets/stylesheets/main.css', __dir__))
58
+ File.read(File.expand_path("../../../app/assets/stylesheets/main.css", __dir__))
61
59
  end
62
60
 
63
61
  def javascript_pack
64
- File.read(File.expand_path('../../../app/assets/javascripts/index.pack.js', __dir__))
62
+ File.read(File.expand_path("../../../app/assets/javascripts/index.pack.js", __dir__))
65
63
  end
66
64
 
67
65
  def javascript
68
- template = Tilt.new(File.expand_path('../../../app/assets/javascripts/index.js.erb', __dir__))
66
+ template = Tilt.new(File.expand_path("../../../app/assets/javascripts/index.js.erb", __dir__))
69
67
  template.render self
70
68
  end
71
69
 
72
70
  def render
73
- template = Tilt.new(File.expand_path('../../../app/views/index.html.erb', __dir__))
71
+ template = Tilt.new(File.expand_path("../../../app/views/index.html.erb", __dir__))
74
72
  template.render self
75
73
  end
76
74
  end