skunk 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9792e73da1c20854f3eb7f9f96af486132bf2e2cca8a7f3aadd6038487ec7ef0
4
+ data.tar.gz: 5cb6a6c3bfd1b009db448b3584a7871452753a124540da96d5b448eec20191e2
5
+ SHA512:
6
+ metadata.gz: 333bf77a14154f09e8ed7f205bfc3b93845788da9ac0088e66b2a3269c858184234cd5efa5eec92371ee74d22e568bff86f360d371622f24e446153bd5d7c89b
7
+ data.tar.gz: 997bad01c5e50c8b936f9364e497dbff9f99cff76c352c51f44f43922f4073ae1378d21177f52c786b3555cb09296eb8daed6b492ddbabfe808c2bcecb9db980
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ .byebug_history
11
+
12
+ .ruby-version
data/.reek.yml ADDED
@@ -0,0 +1,28 @@
1
+ # Auto generated by Reeks --todo flag
2
+ ---
3
+ detectors:
4
+ UtilityFunction:
5
+ exclude:
6
+ - capture_output_streams
7
+ InstanceVariableAssumption:
8
+ exclude:
9
+ - Skunk::Cli::Application
10
+ - Skunk::Cli::Command::Default
11
+ IrresponsibleModule:
12
+ exclude:
13
+ - Skunk::Cli::Application
14
+ - Skunk::Cli::Command::Default
15
+ - Skunk::Cli::Command::Help
16
+ - Skunk::Command::StatusReporter
17
+ - Skunk::Cli::Options
18
+ - RubyCritic::AnalysedModule
19
+ TooManyStatements:
20
+ exclude:
21
+ - initialize
22
+ - Skunk::Cli::Application#execute
23
+ Attribute:
24
+ exclude:
25
+ - Skunk::Command::StatusReporter#analysed_modules
26
+ FeatureEnvy:
27
+ exclude:
28
+ - Skunk::Command::StatusReporter#table
data/.rubocop.yml ADDED
@@ -0,0 +1,4 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ Style/StringLiterals:
4
+ EnforcedStyle: double_quotes
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,27 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config --exclude-limit 500`
3
+ # on 2019-10-15 15:20:00 -0400 using RuboCop version 0.75.1.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 1
10
+ # Configuration parameters: CountComments, ExcludedMethods.
11
+ # ExcludedMethods: refine
12
+ Metrics/BlockLength:
13
+ Max: 34
14
+
15
+ # Offense count: 1
16
+ # Cop supports --auto-correct.
17
+ # Configuration parameters: PreferredName.
18
+ Naming/RescuedExceptionsVariableName:
19
+ Exclude:
20
+ - 'lib/skunk/cli/application.rb'
21
+
22
+ # Offense count: 8
23
+ # Cop supports --auto-correct.
24
+ # Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
25
+ # URISchemes: http, https
26
+ Metrics/LineLength:
27
+ Max: 107
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
+
7
+ # Specify your gem's dependencies in skunk.gemspec
8
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,119 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ skunk (0.1.0)
5
+ rubycritic-simplecov (~> 4.1.1)
6
+ terminal-table (~> 1.8.0)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ addressable (2.7.0)
12
+ public_suffix (>= 2.0.2, < 5.0)
13
+ ansi (1.5.0)
14
+ ast (2.4.0)
15
+ axiom-types (0.1.1)
16
+ descendants_tracker (~> 0.0.4)
17
+ ice_nine (~> 0.11.0)
18
+ thread_safe (~> 0.3, >= 0.3.1)
19
+ byebug (11.0.1)
20
+ codeclimate-engine-rb (0.4.1)
21
+ virtus (~> 1.0)
22
+ coercible (1.0.0)
23
+ descendants_tracker (~> 0.0.1)
24
+ descendants_tracker (0.0.4)
25
+ thread_safe (~> 0.3, >= 0.3.1)
26
+ docile (1.3.2)
27
+ equalizer (0.0.11)
28
+ erubis (2.7.0)
29
+ flay (2.12.1)
30
+ erubis (~> 2.7.0)
31
+ path_expander (~> 1.0)
32
+ ruby_parser (~> 3.0)
33
+ sexp_processor (~> 4.0)
34
+ flog (4.6.3)
35
+ path_expander (~> 1.0)
36
+ ruby_parser (~> 3.1, > 3.1.0)
37
+ sexp_processor (~> 4.8)
38
+ ice_nine (0.11.2)
39
+ jaro_winkler (1.5.3)
40
+ json (2.2.0)
41
+ kwalify (0.7.2)
42
+ launchy (2.4.3)
43
+ addressable (~> 2.3)
44
+ minitest (5.8.5)
45
+ minitest-around (0.5.0)
46
+ minitest (~> 5.0)
47
+ parallel (1.18.0)
48
+ parser (2.6.5.0)
49
+ ast (~> 2.4.0)
50
+ path_expander (1.1.0)
51
+ psych (3.1.0)
52
+ public_suffix (4.0.1)
53
+ rainbow (3.0.0)
54
+ rake (10.5.0)
55
+ reek (5.4.0)
56
+ codeclimate-engine-rb (~> 0.4.0)
57
+ kwalify (~> 0.7.0)
58
+ parser (>= 2.5.0.0, < 2.7, != 2.5.1.1)
59
+ psych (~> 3.1.0)
60
+ rainbow (>= 2.0, < 4.0)
61
+ rubocop (0.75.1)
62
+ jaro_winkler (~> 1.5.1)
63
+ parallel (~> 1.10)
64
+ parser (>= 2.6)
65
+ rainbow (>= 2.2.2, < 4.0)
66
+ ruby-progressbar (~> 1.7)
67
+ unicode-display_width (>= 1.4.0, < 1.7)
68
+ ruby-progressbar (1.10.1)
69
+ ruby_parser (3.14.0)
70
+ sexp_processor (~> 4.9)
71
+ rubycritic-simplecov (4.1.1)
72
+ flay (~> 2.8)
73
+ flog (~> 4.4)
74
+ launchy (= 2.4.3)
75
+ parser (~> 2.6.0)
76
+ rainbow (~> 3.0)
77
+ reek (~> 5.0, < 6.0)
78
+ ruby_parser (~> 3.8)
79
+ simplecov (~> 0.17.0)
80
+ tty-which (~> 0.4.0)
81
+ virtus (~> 1.0)
82
+ sexp_processor (4.13.0)
83
+ simplecov (0.17.1)
84
+ docile (~> 1.1)
85
+ json (>= 1.8, < 3)
86
+ simplecov-html (~> 0.10.0)
87
+ simplecov-console (0.5.0)
88
+ ansi
89
+ simplecov
90
+ terminal-table
91
+ simplecov-html (0.10.2)
92
+ terminal-table (1.8.0)
93
+ unicode-display_width (~> 1.1, >= 1.1.1)
94
+ thread_safe (0.3.6)
95
+ tty-which (0.4.1)
96
+ unicode-display_width (1.6.0)
97
+ virtus (1.0.5)
98
+ axiom-types (~> 0.1)
99
+ coercible (~> 1.0)
100
+ descendants_tracker (~> 0.0, >= 0.0.3)
101
+ equalizer (~> 0.0, >= 0.0.9)
102
+
103
+ PLATFORMS
104
+ ruby
105
+
106
+ DEPENDENCIES
107
+ bundler (~> 2.0)
108
+ byebug (~> 11)
109
+ minitest (~> 5.8.4)
110
+ minitest-around (~> 0.5.0)
111
+ rake (~> 10.0)
112
+ reek (~> 5.4.0)
113
+ rubocop (< 1.0)
114
+ simplecov (~> 0.17.1)
115
+ simplecov-console (= 0.5.0)
116
+ skunk!
117
+
118
+ BUNDLED WITH
119
+ 2.0.2
data/README.md ADDED
@@ -0,0 +1,116 @@
1
+ # Skunk
2
+
3
+ A RubyCritic extension to calculate StinkScore for a file or project.
4
+
5
+ ## What is the StinkScore?
6
+
7
+ The StinkScore is a value that assess the quality of a module. It takes into
8
+ account:
9
+
10
+ - Code Quality
11
+ - Code Smells
12
+ - Churn
13
+ - Code Coverage
14
+
15
+ The formula is not perfect and it is certainly controversial.
16
+
17
+ ## Installation
18
+
19
+ Add this line to your application's Gemfile:
20
+
21
+ ```ruby
22
+ gem 'skunk'
23
+ ```
24
+
25
+ And then execute:
26
+
27
+ $ bundle
28
+
29
+ Or install it yourself as:
30
+
31
+ $ gem install skunk
32
+
33
+ ## Usage
34
+
35
+ Simply run:
36
+
37
+ ```
38
+ skunk
39
+ ```
40
+
41
+ Then get a list of stinky files:
42
+
43
+ ```
44
+ $ skunk
45
+ warning: parser/current is loading parser/ruby26, which recognizes
46
+ warning: 2.6.5-compliant syntax, but you are running 2.6.2.
47
+ warning: please see https://github.com/whitequark/parser#compatibility-with-ruby-mri.
48
+ running flay smells
49
+
50
+ running flog smells
51
+ .............
52
+ running reek smells
53
+ .............
54
+ running complexity
55
+ .............
56
+ running attributes
57
+ .............
58
+ running churn
59
+ .............
60
+ running simple_cov
61
+ .............
62
+ New critique at file:////Users/etagwerker/Projects/fastruby/skunk/tmp/rubycritic/overview.html
63
+ +-----------------------------------------------------+----------------------------+----------------------------+----------------------------+----------------------------+----------------------------+
64
+ | file | stink_score | churn_times_cost | churn | cost | coverage |
65
+ +-----------------------------------------------------+----------------------------+----------------------------+----------------------------+----------------------------+----------------------------+
66
+ | lib/skunk/cli/commands/default.rb | 166.44 | 1.6643999999999999 | 3 | 0.5548 | 0 |
67
+ | lib/skunk/cli/application.rb | 139.2 | 1.392 | 3 | 0.46399999999999997 | 0 |
68
+ | lib/skunk/cli/command_factory.rb | 97.6 | 0.976 | 2 | 0.488 | 0 |
69
+ | test/test_helper.rb | 75.2 | 0.752 | 2 | 0.376 | 0 |
70
+ | lib/skunk/rubycritic/analysed_module.rb | 48.12 | 1.7184 | 2 | 0.8592 | 72.72727272727273 |
71
+ | test/lib/skunk/cli/commands/status_reporter_test.rb | 45.6 | 0.456 | 1 | 0.456 | 0 |
72
+ | lib/skunk/cli/commands/base.rb | 29.52 | 0.2952 | 3 | 0.0984 | 0 |
73
+ | lib/skunk/cli/commands/status_reporter.rb | 8.0 | 7.9956 | 3 | 2.6652 | 100.0 |
74
+ | test/lib/skunk/rubycritic/analysed_module_test.rb | 2.63 | 2.6312 | 2 | 1.3156 | 100.0 |
75
+ | lib/skunk.rb | 0.0 | 0.0 | 2 | 0.0 | 0 |
76
+ | lib/skunk/cli/options.rb | 0.0 | 0.0 | 2 | 0.0 | 0 |
77
+ | lib/skunk/version.rb | 0.0 | 0.0 | 2 | 0.0 | 0 |
78
+ | lib/skunk/cli/commands/help.rb | 0.0 | 0.0 | 2 | 0.0 | 0 |
79
+ +-----------------------------------------------------+----------------------------+----------------------------+----------------------------+----------------------------+----------------------------+
80
+
81
+ StinkScore Total: 612.31
82
+ Modules Analysed: 13
83
+ StinkScore Average: 0.47100769230769230769230769231e2
84
+ Worst StinkScore: 166.44 (lib/skunk/cli/commands/default.rb)
85
+ ```
86
+
87
+ The command will run `rubycritic`, so that will generate a RubyCritic overview
88
+ HTML report as well.
89
+
90
+ Skunk's report will be in the console. Use it wisely. :)
91
+
92
+ ## Known Issues
93
+
94
+ The StinkScore should be calculated per method. This would provide a more accurate
95
+ representation of the average StinkScore in a module.
96
+
97
+ I think that the StinkScore of a module should be the average of the StinkScores of
98
+ all of its methods.
99
+
100
+ Right now the StinkScore is calculated using the totals for a module:
101
+
102
+ - Total Code Coverage Percentage per Module
103
+ - Total Churn per Module
104
+ - Total Cost per Module
105
+
106
+ For more details, feel free to review and improve this method: [RubyCritic::AnalysedModule#stink_score]
107
+
108
+ ## Development
109
+
110
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
111
+
112
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
113
+
114
+ ## Contributing
115
+
116
+ Bug reports and pull requests are welcome on GitHub at https://github.com/fastruby/skunk.
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+ require "rubocop/rake_task"
6
+ require "reek/rake/task"
7
+ require "rubycritic/rake_task"
8
+
9
+ Rake::TestTask.new do |task|
10
+ task.libs.push "lib"
11
+ task.libs.push "test"
12
+ task.pattern = "test/**/*_test.rb"
13
+ end
14
+
15
+ RuboCop::RakeTask.new
16
+
17
+ Reek::Rake::Task.new
18
+
19
+ RubyCritic::RakeTask.new do |task|
20
+ task.paths = FileList["lib/**/*.rb"]
21
+ end
22
+
23
+ task default: %i[test reek rubocop]
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "skunk"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/exe/skunk ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Always look in the lib directory of this gem
5
+ # first when searching the load path
6
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
7
+
8
+ require "skunk/cli/application"
9
+
10
+ exit Skunk::Cli::Application.new(ARGV).execute
data/lib/skunk.rb ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "skunk/version"
4
+
5
+ # Knows how to calculate the `StinkScore` for each file analyzed by `RubyCritic`
6
+ # and `SimpleCov`
7
+ module Skunk
8
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "skunk"
4
+ require "skunk/rubycritic/analysed_module"
5
+ require "skunk/cli/options"
6
+ require "skunk/cli/command_factory"
7
+
8
+ require "rubycritic/cli/application"
9
+
10
+ module Skunk
11
+ module Cli
12
+ # Knows how to execute command line commands
13
+ class Application < RubyCritic::Cli::Application
14
+ def execute
15
+ parsed_options = @options.parse.to_h
16
+
17
+ reporter = Skunk::Cli::CommandFactory.create(parsed_options).execute
18
+ print(reporter.status_message)
19
+ reporter.status
20
+ rescue OptionParser::InvalidOption => error
21
+ warn "Error: #{error}"
22
+ STATUS_ERROR
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubycritic/command_factory"
4
+
5
+ module Skunk
6
+ module Cli
7
+ # Knows how to calculate the command that was request by the CLI user
8
+ class CommandFactory < RubyCritic::CommandFactory
9
+ COMMAND_CLASS_MODES = %i[version help default].freeze
10
+
11
+ # Returns the command class based on the command that was executed
12
+ #
13
+ # @param mode
14
+ # @return [Class]
15
+ def self.command_class(mode)
16
+ mode = mode.to_s.split("_").first.to_sym
17
+ if COMMAND_CLASS_MODES.include? mode
18
+ require "skunk/cli/commands/#{mode}"
19
+ Command.const_get(mode.capitalize)
20
+ else
21
+ require "skunk/cli/commands/default"
22
+ Command::Default
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubycritic/commands/base"
4
+ require "skunk/cli/commands/status_reporter"
5
+
6
+ module Skunk
7
+ module Cli
8
+ module Command
9
+ # Base class for `Skunk` commands. It knows how to build a command with
10
+ # options. It always uses a [Skunk::Command::StatusReporter] as its
11
+ # reporter object.
12
+ class Base < RubyCritic::Command::Base
13
+ def initialize(options)
14
+ @options = options
15
+ @status_reporter = Skunk::Command::StatusReporter.new(@options)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubycritic/commands/default"
4
+ require "rubycritic/analysers_runner"
5
+ require "rubycritic/revision_comparator"
6
+ require "rubycritic/reporter"
7
+
8
+ require "skunk/cli/commands/base"
9
+ require "skunk/cli/commands/status_reporter"
10
+
11
+ module Skunk
12
+ module Cli
13
+ module Command
14
+ # Default command runs a critique using RubyCritic and uses
15
+ # Skunk::Command::StatusReporter to report status
16
+ class Default < RubyCritic::Command::Default
17
+ def initialize(options)
18
+ super
19
+ @status_reporter = Skunk::Command::StatusReporter.new(@options)
20
+ end
21
+
22
+ def execute
23
+ report(critique)
24
+ status_reporter
25
+ end
26
+
27
+ def critique
28
+ RubyCritic::AnalysersRunner.new(paths).run
29
+ end
30
+
31
+ def report(analysed_modules)
32
+ RubyCritic::Reporter.generate_report(analysed_modules)
33
+ status_reporter.analysed_modules = analysed_modules
34
+ status_reporter.score = analysed_modules.score
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "skunk/cli/commands/base"
4
+
5
+ module Skunk
6
+ module Cli
7
+ module Command
8
+ class Help < RubyCritic::Command::Help
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubycritic/commands/status_reporter"
4
+ require "terminal-table"
5
+
6
+ module Skunk
7
+ module Command
8
+ # Knows how to report status for stinky files
9
+ class StatusReporter < RubyCritic::Command::StatusReporter
10
+ attr_accessor :analysed_modules
11
+
12
+ HEADINGS = %w[file stink_score churn_times_cost churn cost coverage].freeze
13
+
14
+ # Returns a status message with a table of all analysed_modules and
15
+ # a stink score average
16
+ def update_status_message
17
+ opts = table_options.merge(headings: HEADINGS, rows: table)
18
+
19
+ ttable = Terminal::Table.new(opts)
20
+
21
+ @status_message = "#{ttable}\n\n"
22
+
23
+ @status_message += "StinkScore Total: #{total_stink_score}\n"
24
+ @status_message += "Modules Analysed: #{analysed_modules_count}\n"
25
+ @status_message += "StinkScore Average: #{stink_score}\n"
26
+ @status_message += "Worst StinkScore: #{worst.stink_score} (#{worst.pathname})\n" if worst
27
+ end
28
+
29
+ private
30
+
31
+ def analysed_modules_count
32
+ @analysed_modules_count ||= analysed_modules.count
33
+ end
34
+
35
+ def worst
36
+ @worst ||= sorted_modules.first
37
+ end
38
+
39
+ def sorted_modules
40
+ @sorted_modules ||= analysed_modules.sort_by(&:stink_score).reverse!
41
+ end
42
+
43
+ def total_stink_score
44
+ @total_stink_score ||= analysed_modules.map(&:stink_score).inject(0.0, :+)
45
+ end
46
+
47
+ def total_churn_times_cost
48
+ analysed_modules.map(&:churn_times_cost).sum
49
+ end
50
+
51
+ def stink_score
52
+ return 0 if analysed_modules_count == 0
53
+
54
+ total_stink_score.to_d / analysed_modules_count
55
+ end
56
+
57
+ def table_options
58
+ {
59
+ style: {
60
+ width: 200
61
+ }
62
+ }
63
+ end
64
+
65
+ def table
66
+ sorted_modules.map do |a_mod|
67
+ [
68
+ a_mod.pathname,
69
+ a_mod.stink_score,
70
+ a_mod.churn_times_cost,
71
+ a_mod.churn,
72
+ a_mod.cost,
73
+ a_mod.coverage
74
+ ]
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubycritic/cli/options"
4
+
5
+ module Skunk
6
+ module Cli
7
+ class Options < RubyCritic::Cli::Options
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubycritic/core/analysed_module"
4
+
5
+ module RubyCritic
6
+ # Monkey-patches RubyCritic::AnalysedModule to add a stink_score method
7
+ class AnalysedModule
8
+ PERFECT_COVERAGE = 100
9
+
10
+ # Returns a numeric value that represents the stink_score of a module:
11
+ #
12
+ # If module is perfectly covered, stink score is the same as the
13
+ # `churn_times_cost`
14
+ #
15
+ # If module has no coverage, stink score is a penalized value of
16
+ # `churn_times_cost`
17
+ #
18
+ # For now the stink_score is calculated by multiplying `churn_times_cost`
19
+ # times the lack of coverage.
20
+ #
21
+ # For example:
22
+ #
23
+ # When `churn_times_cost` is 100 and module is perfectly covered:
24
+ # stink_score => 100
25
+ #
26
+ # When `churn_times_cost` is 100 and module is not covered at all:
27
+ # stink_score => 100 * 100 = 10_000
28
+ #
29
+ # When `churn_times_cost` is 100 and module is covered at 75%:
30
+ # stink_score => 100 * 25 (percentage uncovered) = 2_500
31
+ #
32
+ # @return [Float]
33
+ def stink_score
34
+ return churn_times_cost.round(2) if coverage == PERFECT_COVERAGE
35
+
36
+ (churn_times_cost * (PERFECT_COVERAGE - coverage.to_i)).round(2)
37
+ end
38
+
39
+ # Returns the value of churn times cost.
40
+ #
41
+ # @return [Integer]
42
+ def churn_times_cost
43
+ safe_churn = churn > 0 ? churn : 1
44
+ @churn_times_cost ||= safe_churn * cost
45
+ end
46
+
47
+ def method_name
48
+
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Skunk
4
+ VERSION = "0.1.0"
5
+ end
data/skunk.gemspec ADDED
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path("lib", __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require "skunk/version"
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "skunk"
9
+ spec.version = Skunk::VERSION
10
+ spec.authors = ["Ernesto Tagwerker"]
11
+ spec.email = ["ernesto+github@ombulabs.com"]
12
+
13
+ spec.summary = "A library to assess code quality vs. code coverage"
14
+ spec.description = "Knows how to calculate the StinkScore for a Ruby file"
15
+ spec.homepage = "https://github.com/fastruby/skunk"
16
+
17
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
19
+ if spec.respond_to?(:metadata)
20
+ spec.metadata["allowed_push_host"] = "https://www.rubygems.org"
21
+
22
+ spec.metadata["homepage_uri"] = spec.homepage
23
+ spec.metadata["source_code_uri"] = "https://github.com/fastruby/skunk"
24
+ spec.metadata["changelog_uri"] = "https://github.com/fastruby/skunk/changelog"
25
+ else
26
+ raise "RubyGems 2.0 or newer is required to protect against " \
27
+ "public gem pushes."
28
+ end
29
+
30
+ # Specify which files should be added to the gem when it is released.
31
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
32
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
33
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
34
+ end
35
+ spec.bindir = "exe"
36
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
37
+ spec.require_paths = ["lib"]
38
+
39
+ spec.add_dependency "rubycritic-simplecov", "~> 4.1.1"
40
+ spec.add_dependency "terminal-table", "~> 1.8.0"
41
+
42
+ spec.add_development_dependency "bundler", "~> 2.0"
43
+ spec.add_development_dependency "byebug", "~> 11"
44
+ spec.add_development_dependency "minitest", "~> 5.8.4"
45
+ spec.add_development_dependency "minitest-around", "~> 0.5.0"
46
+ spec.add_development_dependency "rake", "~> 10.0"
47
+ spec.add_development_dependency "reek", "~> 5.4.0"
48
+ spec.add_development_dependency "rubocop", "< 1.0"
49
+ spec.add_development_dependency "simplecov", "~> 0.17.1"
50
+ spec.add_development_dependency "simplecov-console", "0.5.0"
51
+ end
metadata ADDED
@@ -0,0 +1,223 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: skunk
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ernesto Tagwerker
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-10-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rubycritic-simplecov
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 4.1.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 4.1.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: terminal-table
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 1.8.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 1.8.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: byebug
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '11'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '11'
69
+ - !ruby/object:Gem::Dependency
70
+ name: minitest
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 5.8.4
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 5.8.4
83
+ - !ruby/object:Gem::Dependency
84
+ name: minitest-around
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.5.0
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.5.0
97
+ - !ruby/object:Gem::Dependency
98
+ name: rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '10.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '10.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: reek
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 5.4.0
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 5.4.0
125
+ - !ruby/object:Gem::Dependency
126
+ name: rubocop
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "<"
130
+ - !ruby/object:Gem::Version
131
+ version: '1.0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "<"
137
+ - !ruby/object:Gem::Version
138
+ version: '1.0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: simplecov
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: 0.17.1
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: 0.17.1
153
+ - !ruby/object:Gem::Dependency
154
+ name: simplecov-console
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - '='
158
+ - !ruby/object:Gem::Version
159
+ version: 0.5.0
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - '='
165
+ - !ruby/object:Gem::Version
166
+ version: 0.5.0
167
+ description: Knows how to calculate the StinkScore for a Ruby file
168
+ email:
169
+ - ernesto+github@ombulabs.com
170
+ executables:
171
+ - skunk
172
+ extensions: []
173
+ extra_rdoc_files: []
174
+ files:
175
+ - ".gitignore"
176
+ - ".reek.yml"
177
+ - ".rubocop.yml"
178
+ - ".rubocop_todo.yml"
179
+ - Gemfile
180
+ - Gemfile.lock
181
+ - README.md
182
+ - Rakefile
183
+ - bin/console
184
+ - bin/setup
185
+ - exe/skunk
186
+ - lib/skunk.rb
187
+ - lib/skunk/cli/application.rb
188
+ - lib/skunk/cli/command_factory.rb
189
+ - lib/skunk/cli/commands/base.rb
190
+ - lib/skunk/cli/commands/default.rb
191
+ - lib/skunk/cli/commands/help.rb
192
+ - lib/skunk/cli/commands/status_reporter.rb
193
+ - lib/skunk/cli/options.rb
194
+ - lib/skunk/rubycritic/analysed_module.rb
195
+ - lib/skunk/version.rb
196
+ - skunk.gemspec
197
+ homepage: https://github.com/fastruby/skunk
198
+ licenses: []
199
+ metadata:
200
+ allowed_push_host: https://www.rubygems.org
201
+ homepage_uri: https://github.com/fastruby/skunk
202
+ source_code_uri: https://github.com/fastruby/skunk
203
+ changelog_uri: https://github.com/fastruby/skunk/changelog
204
+ post_install_message:
205
+ rdoc_options: []
206
+ require_paths:
207
+ - lib
208
+ required_ruby_version: !ruby/object:Gem::Requirement
209
+ requirements:
210
+ - - ">="
211
+ - !ruby/object:Gem::Version
212
+ version: '0'
213
+ required_rubygems_version: !ruby/object:Gem::Requirement
214
+ requirements:
215
+ - - ">="
216
+ - !ruby/object:Gem::Version
217
+ version: '0'
218
+ requirements: []
219
+ rubygems_version: 3.0.3
220
+ signing_key:
221
+ specification_version: 4
222
+ summary: A library to assess code quality vs. code coverage
223
+ test_files: []