attractor 1.1.0 → 1.1.1

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: 3a0acbe6a12975ea7a7aef0aceb983b61b45b3ec6bb7f3da95f2aaa992761011
4
- data.tar.gz: 9541fe95c00fd300b2ce44bb9c19eb5c0819bb5eb69959bbe8270d56dd031fb3
3
+ metadata.gz: d67da9392099763d432b8bb74551259f5b6105cbbc4d7b370c41c1fe851102bf
4
+ data.tar.gz: 4fdd7358b4c073b52f517bd6933b2fcfc134549685dc42320ff055358971df86
5
5
  SHA512:
6
- metadata.gz: d25be5b596d5af43eee4ba6827d483f3c9daa6a4be37c9dcb740906b81ea30c9dd8c4a5f7610ff994f38385ce1346f1cccb3c4892fd68214f99057e71b11b057
7
- data.tar.gz: ee49bd39b824ce6918514ecbb9ef913a7bed6d7fdb2dd79982cd097755121ba1af49e94764aafbd2511ed827b085a202f222c187d52162a003c6ae15eb4f4002
6
+ metadata.gz: cd928f5288a2330156ef222277b34a3cfa9554529a8fcdd3fbf85e00ef38c140b6e3eaee990e8d9923a92da922546d0daaf7c139567ba7227245f9fb04d399c6
7
+ data.tar.gz: 341c7ba41ef4ac89021594d534f1c27f30d16e0f381a8e9106d09b68fdbec66c0ac8b738d916ff35323987d898a8a63043f4e74f19c186685c9d548ae58ff6c3
@@ -1,3 +1,7 @@
1
+ ## RELEASE 1.1.1
2
+
3
+ * ENHANCEMENT: `--minimum_churn` and `--start_ago` CLI options
4
+
1
5
  ## RELEASE 1.1.0
2
6
 
3
7
  * FEATURE: Auto-detect ruby and js projects
data/README.md CHANGED
@@ -7,12 +7,15 @@
7
7
 
8
8
  Many authors ([Michael Feathers](https://www.agileconnection.com/article/getting-empirical-about-refactoring), [Sandi Metz](https://www.sandimetz.com/blog/2017/9/13/breaking-up-the-behemoth)) have shown that an evaluation of churn vs complexity of files in software projects provide a valuable metric towards code quality. This is another take on the matter, for ruby code, using the `churn` and `flog` projects.
9
9
 
10
+ Here's an [article on medium](https://medium.com/better-programming/why-i-made-my-own-code-quality-tool-c44b40ceaafd) explaining the approach in greater detail.
11
+
10
12
  ## Table of Contents
11
13
 
12
14
  * [Installation](#installation)
13
15
  * [Usage](#usage)
14
16
  + [Live Reloading](#live-reloading)
15
17
  * [CI Usage](#ci-usage)
18
+ + [Github Action](#github-action)
16
19
  + [Gitlab Example](#gitlab-example)
17
20
  * [CLI Commands and Options](#cli-commands-and-options)
18
21
  * [Development](#development)
@@ -74,6 +77,10 @@ If you have `guard-livereload` (or a similar service) running on your project, y
74
77
 
75
78
  To use this CLI in a CI environment, use the `--ci` option, which will suppress automatic opening of a browser window.
76
79
 
80
+ ### Github Action
81
+
82
+ There is a dedicated [Github Action](https://github.com/julianrubisch/attractor-action) that will compile Attractor's output. Here's the action on the [Marketplace](https://github.com/marketplace/actions/attractor-action).
83
+
77
84
  ### Gitlab Example
78
85
 
79
86
  The simplest use case is to store the `attractor_output` directory as an artifact.
@@ -101,6 +108,8 @@ Print a simple output to console:
101
108
  $ --file_prefix|-p app/models
102
109
  $ --type|-t rb|js
103
110
  $ --watch|-w
111
+ $ --start_ago|-s (e.g. 5y, 3m, 7w)
112
+ $ --minimum_churn|-c (minimum times a file must have changed to be processed)
104
113
 
105
114
  Generate a full report
106
115
 
@@ -109,6 +118,8 @@ Generate a full report
109
118
  $ --type|-t rb|js
110
119
  $ --watch|-w
111
120
  $ --no-open-browser|--ci
121
+ $ --start_ago|-s (e.g. 5y, 3m, 7w)
122
+ $ --minimum_churn|-c (minimum times a file must have changed to be processed)
112
123
 
113
124
  Serve the output on http://localhost:7890
114
125
 
@@ -116,6 +127,8 @@ Serve the output on http://localhost:7890
116
127
  $ --file_prefix|-p app/models
117
128
  $ --watch|-w
118
129
  $ --no-open-browser|--ci
130
+ $ --start_ago|-s (e.g. 5y, 3m, 7w)
131
+ $ --minimum_churn|-c (minimum times a file must have changed to be processed)
119
132
 
120
133
  ## Development
121
134
 
@@ -153,4 +166,4 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
153
166
  <!-- prettier-ignore-end -->
154
167
  <!-- ALL-CONTRIBUTORS-LIST:END -->
155
168
 
156
- This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
169
+ This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
@@ -28,7 +28,10 @@ Gem::Specification.new do |spec|
28
28
  # Specify which files should be added to the gem when it is released.
29
29
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
30
30
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
31
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(src|tmp|test|spec|features)/}) }
31
+ `git ls-files -z`.split("\x0").reject do |f|
32
+ f.match(%r{^(src|tmp|test|spec|features|\.github)/}) ||
33
+ %w[.all-contributorsrc .rspec .rspec_status .travis.yml].include?(f)
34
+ end
32
35
  end
33
36
  spec.bindir = 'exe'
34
37
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
@@ -55,5 +58,6 @@ Gem::Specification.new do |spec|
55
58
  spec.add_development_dependency 'rake', '~> 10.0'
56
59
  spec.add_development_dependency 'rspec', '~> 3.0'
57
60
  spec.add_development_dependency 'sassc'
61
+ spec.add_development_dependency 'standard'
58
62
  spec.add_development_dependency 'structured_changelog'
59
63
  end
@@ -1,32 +1,44 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'attractor/version'
4
+ require 'attractor/duration_parser'
4
5
  require 'attractor/calculators/base_calculator'
5
- require 'attractor/calculators/ruby_calculator'
6
- require 'attractor/calculators/js_calculator'
7
6
  require 'attractor/detectors/base_detector'
8
- require 'attractor/detectors/ruby_detector'
9
- require 'attractor/detectors/js_detector'
10
7
  require 'attractor/reporters/base_reporter'
11
- require 'attractor/reporters/console_reporter'
12
- require 'attractor/reporters/html_reporter'
13
- require 'attractor/reporters/sinatra_reporter'
14
8
  require 'attractor/suggester'
15
9
  require 'attractor/watcher'
16
10
 
11
+ Dir[File.join(__dir__, 'attractor', 'calculators', '*.rb')].each do |file|
12
+ next if file.start_with?('base')
13
+
14
+ require file
15
+ end
16
+
17
+ Dir[File.join(__dir__, 'attractor', 'detectors', '*.rb')].each do |file|
18
+ next if file.start_with?('base')
19
+
20
+ require file
21
+ end
22
+
23
+ Dir[File.join(__dir__, 'attractor', 'reporters', '*.rb')].each do |file|
24
+ next if file.start_with?('base')
25
+
26
+ require file
27
+ end
28
+
17
29
  module Attractor
18
30
  class Error < StandardError; end
19
31
 
20
- def calculators_for_type(type, file_prefix)
32
+ def calculators_for_type(type, **options)
21
33
  case type
22
34
  when 'js'
23
- { 'js' => JsCalculator.new(file_prefix: file_prefix) }
35
+ { 'js' => JsCalculator.new(**options) }
24
36
  when 'rb'
25
- { 'rb' => RubyCalculator.new(file_prefix: file_prefix) }
37
+ { 'rb' => RubyCalculator.new(**options) }
26
38
  else
27
39
  {}.tap do |hash|
28
- hash['rb'] = RubyCalculator.new(file_prefix: file_prefix) if RubyDetector.new.detect
29
- hash['js'] = JsCalculator.new(file_prefix: file_prefix) if JsDetector.new.detect
40
+ hash['rb'] = RubyCalculator.new(**options) if RubyDetector.new.detect
41
+ hash['js'] = JsCalculator.new(**options) if JsDetector.new.detect
30
42
  end
31
43
  end
32
44
  end
@@ -9,10 +9,11 @@ module Attractor
9
9
  class BaseCalculator
10
10
  attr_reader :type
11
11
 
12
- def initialize(file_prefix: '', file_extension: 'rb', minimum_churn_count: 3)
12
+ def initialize(file_prefix: '', file_extension: 'rb', minimum_churn_count: 3, start_ago: '5y')
13
13
  @file_prefix = file_prefix
14
14
  @file_extension = file_extension
15
15
  @minimum_churn_count = minimum_churn_count
16
+ @start_date = Date.today - Attractor::DurationParser.new(start_ago).duration
16
17
  end
17
18
 
18
19
  def calculate
@@ -20,7 +21,7 @@ module Attractor
20
21
  file_extension: @file_extension,
21
22
  file_prefix: @file_prefix,
22
23
  minimum_churn_count: @minimum_churn_count,
23
- start_date: Date.today - 365 * 5
24
+ start_date: @start_date
24
25
  ).report(false)
25
26
 
26
27
  churn[:churn][:changes].map do |change|
@@ -2,8 +2,8 @@
2
2
 
3
3
  module Attractor
4
4
  class JsCalculator < BaseCalculator
5
- def initialize(file_prefix: '', minimum_churn_count: 3)
6
- super(file_prefix: file_prefix, file_extension: '(js|jsx)', minimum_churn_count: minimum_churn_count)
5
+ def initialize(file_prefix: '', minimum_churn_count: 3, start_ago: 365 * 5)
6
+ super(file_prefix: file_prefix, file_extension: '(js|jsx)', minimum_churn_count: minimum_churn_count, start_ago: start_ago)
7
7
  @type = "JavaScript"
8
8
  end
9
9
 
@@ -4,8 +4,8 @@ require 'flog'
4
4
 
5
5
  module Attractor
6
6
  class RubyCalculator < BaseCalculator
7
- def initialize(file_prefix: '', minimum_churn_count: 3)
8
- super(file_prefix: file_prefix, file_extension: 'rb', minimum_churn_count: minimum_churn_count)
7
+ def initialize(file_prefix: '', minimum_churn_count: 3, start_ago: 365 * 5)
8
+ super(file_prefix: file_prefix, file_extension: 'rb', minimum_churn_count: minimum_churn_count, start_ago: start_ago)
9
9
  @type = "Ruby"
10
10
  end
11
11
 
@@ -7,43 +7,48 @@ require 'attractor'
7
7
  module Attractor
8
8
  # contains methods implementing the CLI
9
9
  class CLI < Thor
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]]
15
+
16
+ advanced_options = [[:format, aliases: :f, default: 'html'],
17
+ [:no_open_browser, type: :boolean],
18
+ [:ci, type: :boolean]]
19
+
10
20
  desc 'calc', 'Calculates churn and complexity for all ruby files in current directory'
11
- option :file_prefix, aliases: :p
12
- option :watch, aliases: :w, type: :boolean
13
- option :type, aliases: :t
21
+ shared_options.each do |shared_option|
22
+ option(*shared_option)
23
+ end
14
24
  def calc
15
25
  file_prefix = options[:file_prefix]
16
- calculators = Attractor.calculators_for_type(options[:type], file_prefix)
17
26
  if options[:watch]
18
27
  puts 'Listening for file changes...'
19
- Attractor::ConsoleReporter.new(file_prefix: file_prefix, calculators: calculators).watch
28
+ Attractor::ConsoleReporter.new(file_prefix: file_prefix, calculators: calculators(options)).watch
20
29
  else
21
- Attractor::ConsoleReporter.new(file_prefix: file_prefix, calculators: calculators).report
30
+ Attractor::ConsoleReporter.new(file_prefix: file_prefix, calculators: calculators(options)).report
22
31
  end
23
32
  rescue RuntimeError => e
24
33
  puts "Runtime error: #{e.message}"
25
34
  end
26
35
 
27
36
  desc 'report', 'Generates an HTML report'
28
- option :format, aliases: :f, default: 'html'
29
- option :file_prefix, aliases: :p
30
- option :watch, aliases: :w, type: :boolean
31
- option :type, aliases: :t
32
- option :no_open_browser, type: :boolean
33
- option :ci, type: :boolean
37
+ (shared_options + advanced_options).each do |option|
38
+ option(*option)
39
+ end
34
40
  def report
35
41
  file_prefix = options[:file_prefix]
36
- calculators = Attractor.calculators_for_type(options[:type], file_prefix)
37
- open_browser = !(options[:no_open_browser] || options[:ci])
42
+ open_browser = !(options[:no_open_browser] || options[:ci])
38
43
  if options[:watch]
39
44
  puts 'Listening for file changes...'
40
- Attractor::HtmlReporter.new(file_prefix: file_prefix, calculators: calculators, open_browser: open_browser).watch
45
+ Attractor::HtmlReporter.new(file_prefix: file_prefix, calculators: calculators(options), open_browser: open_browser).watch
41
46
  else
42
47
  case options[:format]
43
48
  when 'html'
44
- Attractor::HtmlReporter.new(file_prefix: file_prefix, calculators: calculators, open_browser: open_browser).report
49
+ Attractor::HtmlReporter.new(file_prefix: file_prefix, calculators: calculators(options), open_browser: open_browser).report
45
50
  else
46
- Attractor::HtmlReporter.new(file_prefix: file_prefix, calculators: calculators, open_browser: open_browser).report
51
+ Attractor::HtmlReporter.new(file_prefix: file_prefix, calculators: calculators(options), open_browser: open_browser).report
47
52
  end
48
53
  end
49
54
  rescue RuntimeError => e
@@ -51,27 +56,32 @@ module Attractor
51
56
  end
52
57
 
53
58
  desc 'serve', 'Serves the report on localhost'
54
- option :format, aliases: :f, default: 'html'
55
- option :file_prefix, aliases: :p
56
- option :watch, aliases: :w, type: :boolean
57
- option :type, aliases: :t
58
- option :no_open_browser, type: :boolean
59
- option :ci, type: :boolean
59
+ (shared_options + advanced_options).each do |option|
60
+ option(*option)
61
+ end
60
62
  def serve
61
63
  file_prefix = options[:file_prefix]
62
- open_browser = !(options[:no_open_browser] || options[:ci])
63
- calculators = Attractor.calculators_for_type(options[:type], file_prefix)
64
+ open_browser = !(options[:no_open_browser] || options[:ci])
64
65
  if options[:watch]
65
66
  puts 'Listening for file changes...'
66
- Attractor::SinatraReporter.new(file_prefix: file_prefix, calculators: calculators, open_browser: open_browser).watch
67
+ Attractor::SinatraReporter.new(file_prefix: file_prefix, calculators: calculators(options), open_browser: open_browser).watch
67
68
  else
68
69
  case options[:format]
69
70
  when 'html'
70
- Attractor::SinatraReporter.new(file_prefix: file_prefix, calculators: calculators, open_browser: open_browser).report
71
+ Attractor::SinatraReporter.new(file_prefix: file_prefix, calculators: calculators(options), open_browser: open_browser).report
71
72
  else
72
- Attractor::SinatraReporter.new(file_prefix: file_prefix, calculators: calculators, open_browser: open_browser).report
73
+ Attractor::SinatraReporter.new(file_prefix: file_prefix, calculators: calculators(options), open_browser: open_browser).report
73
74
  end
74
75
  end
75
76
  end
77
+
78
+ private
79
+
80
+ def calculators(options)
81
+ Attractor.calculators_for_type(options[:type],
82
+ file_prefix: options[:file_prefix],
83
+ minimum_churn_count: options[:minimum_churn],
84
+ start_ago: options[:start_ago])
85
+ end
76
86
  end
77
87
  end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Attractor
4
+ # converts a duration string into an amount of days
5
+ class DurationParser
6
+ TOKENS = {
7
+ 'd' => 1,
8
+ 'w' => 7,
9
+ 'm' => 30,
10
+ 'y' => 365
11
+ }.freeze
12
+
13
+ attr_reader :duration
14
+
15
+ def initialize(input)
16
+ @input = input
17
+ @duration = 0
18
+ parse
19
+ end
20
+
21
+ def parse
22
+ @input.scan(/(\d+)(\w)/).each do |amount, measure|
23
+ @duration += amount.to_i * TOKENS[measure]
24
+ end
25
+ end
26
+ end
27
+ end
@@ -74,6 +74,8 @@ module Attractor
74
74
  def values(type: 'rb')
75
75
  @values = @calculators[type].calculate
76
76
  @values
77
+ rescue NoMethodError => e
78
+ puts "No calculator for type #{type}"
77
79
  end
78
80
  end
79
81
  end
@@ -4,7 +4,7 @@ module Attractor
4
4
  # makes suggestions for refactorings
5
5
  class Suggester
6
6
  def initialize(values)
7
- @values = values
7
+ @values = values || []
8
8
  end
9
9
 
10
10
  def suggest(threshold = 95)
@@ -1,3 +1,3 @@
1
1
  module Attractor
2
- VERSION = "1.1.0"
2
+ VERSION = "1.1.1"
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: 1.1.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julian Rubisch
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-12-23 00:00:00.000000000 Z
11
+ date: 2019-12-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: churn
@@ -290,6 +290,20 @@ dependencies:
290
290
  - - ">="
291
291
  - !ruby/object:Gem::Version
292
292
  version: '0'
293
+ - !ruby/object:Gem::Dependency
294
+ name: standard
295
+ requirement: !ruby/object:Gem::Requirement
296
+ requirements:
297
+ - - ">="
298
+ - !ruby/object:Gem::Version
299
+ version: '0'
300
+ type: :development
301
+ prerelease: false
302
+ version_requirements: !ruby/object:Gem::Requirement
303
+ requirements:
304
+ - - ">="
305
+ - !ruby/object:Gem::Version
306
+ version: '0'
293
307
  - !ruby/object:Gem::Dependency
294
308
  name: structured_changelog
295
309
  requirement: !ruby/object:Gem::Requirement
@@ -316,14 +330,10 @@ executables:
316
330
  extensions: []
317
331
  extra_rdoc_files: []
318
332
  files:
319
- - ".all-contributorsrc"
320
333
  - ".babelrc"
321
334
  - ".eslintrc.json"
322
- - ".github/workflows/ruby.yml"
323
335
  - ".gitignore"
324
- - ".rspec"
325
336
  - ".rubocop.yml"
326
- - ".travis.yml"
327
337
  - CHANGELOG.md
328
338
  - Gemfile
329
339
  - Guardfile
@@ -350,6 +360,7 @@ files:
350
360
  - lib/attractor/detectors/base_detector.rb
351
361
  - lib/attractor/detectors/js_detector.rb
352
362
  - lib/attractor/detectors/ruby_detector.rb
363
+ - lib/attractor/duration_parser.rb
353
364
  - lib/attractor/reporters/.keep
354
365
  - lib/attractor/reporters/base_reporter.rb
355
366
  - lib/attractor/reporters/console_reporter.rb
@@ -1,25 +0,0 @@
1
- {
2
- "files": [
3
- "README.md"
4
- ],
5
- "imageSize": 100,
6
- "commit": false,
7
- "contributors": [
8
- {
9
- "login": "julianrubisch",
10
- "name": "Julian Rubisch",
11
- "avatar_url": "https://avatars0.githubusercontent.com/u/4352208?v=4",
12
- "profile": "http://www.julianrubisch.at",
13
- "contributions": [
14
- "code",
15
- "doc"
16
- ]
17
- }
18
- ],
19
- "contributorsPerLine": 7,
20
- "projectName": "attractor",
21
- "projectOwner": "julianrubisch",
22
- "repoType": "github",
23
- "repoHost": "https://github.com",
24
- "skipCi": true
25
- }
@@ -1,25 +0,0 @@
1
- name: Ruby
2
-
3
- on: [push]
4
-
5
- jobs:
6
- build:
7
-
8
- runs-on: ubuntu-latest
9
-
10
- strategy:
11
- matrix:
12
- ruby-version: [2.4.x, 2.5.x, 2.6.x]
13
-
14
- steps:
15
- - uses: actions/checkout@v1
16
- - name: Set up Ruby ${{ matrix.ruby-version }}
17
- uses: actions/setup-ruby@v1
18
- with:
19
- ruby-version: ${{ matrix.ruby-version }}
20
- - name: Build and test with Rake
21
- run: |
22
- gem install bundler
23
- bundle install --jobs 4 --retry 3
24
- bundle exec rspec spec
25
- bundle exec cucumber features
data/.rspec DELETED
@@ -1,3 +0,0 @@
1
- --format documentation
2
- --color
3
- --require spec_helper
@@ -1,10 +0,0 @@
1
- ---
2
- sudo: false
3
- language: ruby
4
- cache: bundler
5
- rvm:
6
- - 2.6.3
7
- before_install: gem install bundler -v 2.0.2
8
- script:
9
- - bundle exec cucumber features
10
- - bundle exec rspec spec