rubycritic-simplecov 4.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 +7 -0
- data/CHANGELOG.md +290 -0
- data/CONTRIBUTING.md +93 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +259 -0
- data/ROADMAP.md +56 -0
- data/Rakefile +28 -0
- data/bin/rubycritic +10 -0
- data/lib/rubycritic/analysers/attributes.rb +29 -0
- data/lib/rubycritic/analysers/churn.rb +30 -0
- data/lib/rubycritic/analysers/complexity.rb +30 -0
- data/lib/rubycritic/analysers/coverage.rb +118 -0
- data/lib/rubycritic/analysers/helpers/ast_node.rb +76 -0
- data/lib/rubycritic/analysers/helpers/flay.rb +13 -0
- data/lib/rubycritic/analysers/helpers/flog.rb +17 -0
- data/lib/rubycritic/analysers/helpers/methods_counter.rb +25 -0
- data/lib/rubycritic/analysers/helpers/modules_locator.rb +42 -0
- data/lib/rubycritic/analysers/helpers/parser.rb +14 -0
- data/lib/rubycritic/analysers/helpers/reek.rb +11 -0
- data/lib/rubycritic/analysers/smells/flay.rb +79 -0
- data/lib/rubycritic/analysers/smells/flog.rb +69 -0
- data/lib/rubycritic/analysers/smells/reek.rb +53 -0
- data/lib/rubycritic/analysers_runner.rb +41 -0
- data/lib/rubycritic/analysis_summary.rb +40 -0
- data/lib/rubycritic/browser.rb +19 -0
- data/lib/rubycritic/cli/application.rb +34 -0
- data/lib/rubycritic/cli/options/argv.rb +137 -0
- data/lib/rubycritic/cli/options/file.rb +100 -0
- data/lib/rubycritic/cli/options.rb +33 -0
- data/lib/rubycritic/colorize.rb +17 -0
- data/lib/rubycritic/command_factory.rb +25 -0
- data/lib/rubycritic/commands/base.rb +18 -0
- data/lib/rubycritic/commands/ci.rb +14 -0
- data/lib/rubycritic/commands/compare.rb +106 -0
- data/lib/rubycritic/commands/default.rb +38 -0
- data/lib/rubycritic/commands/help.rb +18 -0
- data/lib/rubycritic/commands/status_reporter.rb +45 -0
- data/lib/rubycritic/commands/utils/build_number_file.rb +37 -0
- data/lib/rubycritic/commands/version.rb +16 -0
- data/lib/rubycritic/configuration.rb +69 -0
- data/lib/rubycritic/core/analysed_module.rb +93 -0
- data/lib/rubycritic/core/analysed_modules_collection.rb +95 -0
- data/lib/rubycritic/core/location.rb +47 -0
- data/lib/rubycritic/core/rating.rb +30 -0
- data/lib/rubycritic/core/smell.rb +84 -0
- data/lib/rubycritic/generators/console_report.rb +20 -0
- data/lib/rubycritic/generators/html/assets/fonts/FontAwesome.otf +0 -0
- data/lib/rubycritic/generators/html/assets/fonts/Roboto-Medium.ttf +0 -0
- data/lib/rubycritic/generators/html/assets/fonts/Roboto-Regular.ttf +0 -0
- data/lib/rubycritic/generators/html/assets/fonts/fontawesome-webfont.eot +0 -0
- data/lib/rubycritic/generators/html/assets/fonts/fontawesome-webfont.svg +2671 -0
- data/lib/rubycritic/generators/html/assets/fonts/fontawesome-webfont.ttf +0 -0
- data/lib/rubycritic/generators/html/assets/fonts/fontawesome-webfont.woff +0 -0
- data/lib/rubycritic/generators/html/assets/fonts/fontawesome-webfont.woff2 +0 -0
- data/lib/rubycritic/generators/html/assets/fonts/glyphicons-halflings-regular.eot +0 -0
- data/lib/rubycritic/generators/html/assets/fonts/glyphicons-halflings-regular.svg +288 -0
- data/lib/rubycritic/generators/html/assets/fonts/glyphicons-halflings-regular.ttf +0 -0
- data/lib/rubycritic/generators/html/assets/fonts/glyphicons-halflings-regular.woff +0 -0
- data/lib/rubycritic/generators/html/assets/fonts/glyphicons-halflings-regular.woff2 +0 -0
- data/lib/rubycritic/generators/html/assets/images/logo.png +0 -0
- data/lib/rubycritic/generators/html/assets/javascripts/application.js +281 -0
- data/lib/rubycritic/generators/html/assets/javascripts/bootstrap.min.js +7 -0
- data/lib/rubycritic/generators/html/assets/javascripts/highcharts.src-4.0.1.js +17672 -0
- data/lib/rubycritic/generators/html/assets/javascripts/jquery.filtertable.min.js +13 -0
- data/lib/rubycritic/generators/html/assets/javascripts/jquery.min.js +4 -0
- data/lib/rubycritic/generators/html/assets/javascripts/jquery.scrollTo.min.js +7 -0
- data/lib/rubycritic/generators/html/assets/javascripts/jquery.tablesorter.js +1031 -0
- data/lib/rubycritic/generators/html/assets/javascripts/jquery.tablesorter.min.js +4 -0
- data/lib/rubycritic/generators/html/assets/javascripts/jquery.timeago.js +231 -0
- data/lib/rubycritic/generators/html/assets/javascripts/prettify.js +46 -0
- data/lib/rubycritic/generators/html/assets/stylesheets/application.css +570 -0
- data/lib/rubycritic/generators/html/assets/stylesheets/bootstrap.min.css +6 -0
- data/lib/rubycritic/generators/html/assets/stylesheets/font-awesome.min.css +4 -0
- data/lib/rubycritic/generators/html/assets/stylesheets/prettify.css +1 -0
- data/lib/rubycritic/generators/html/assets/stylesheets/prettify.custom_theme.css +69 -0
- data/lib/rubycritic/generators/html/base.rb +54 -0
- data/lib/rubycritic/generators/html/code_file.rb +51 -0
- data/lib/rubycritic/generators/html/code_index.rb +33 -0
- data/lib/rubycritic/generators/html/line.rb +37 -0
- data/lib/rubycritic/generators/html/overview.rb +38 -0
- data/lib/rubycritic/generators/html/simple_cov_index.rb +44 -0
- data/lib/rubycritic/generators/html/smells_index.rb +45 -0
- data/lib/rubycritic/generators/html/templates/code_file.html.erb +61 -0
- data/lib/rubycritic/generators/html/templates/code_index.html.erb +55 -0
- data/lib/rubycritic/generators/html/templates/layouts/application.html.erb +66 -0
- data/lib/rubycritic/generators/html/templates/line.html.erb +1 -0
- data/lib/rubycritic/generators/html/templates/overview.html.erb +68 -0
- data/lib/rubycritic/generators/html/templates/simple_cov_index.html.erb +44 -0
- data/lib/rubycritic/generators/html/templates/smells_index.html.erb +47 -0
- data/lib/rubycritic/generators/html/templates/smelly_line.html.erb +23 -0
- data/lib/rubycritic/generators/html/turbulence.rb +17 -0
- data/lib/rubycritic/generators/html/view_helpers.rb +58 -0
- data/lib/rubycritic/generators/html_report.rb +77 -0
- data/lib/rubycritic/generators/json/simple.rb +43 -0
- data/lib/rubycritic/generators/json_report.rb +26 -0
- data/lib/rubycritic/generators/lint_report.rb +30 -0
- data/lib/rubycritic/generators/text/lint.rb +41 -0
- data/lib/rubycritic/generators/text/list.rb +45 -0
- data/lib/rubycritic/generators/text/templates/lint.erb +3 -0
- data/lib/rubycritic/generators/text/templates/list.erb +12 -0
- data/lib/rubycritic/rake_task.rb +71 -0
- data/lib/rubycritic/reporter.rb +39 -0
- data/lib/rubycritic/revision_comparator.rb +45 -0
- data/lib/rubycritic/serializer.rb +32 -0
- data/lib/rubycritic/smells_status_setter.rb +18 -0
- data/lib/rubycritic/source_control_systems/base.rb +41 -0
- data/lib/rubycritic/source_control_systems/double.rb +19 -0
- data/lib/rubycritic/source_control_systems/git.rb +95 -0
- data/lib/rubycritic/source_control_systems/mercurial.rb +29 -0
- data/lib/rubycritic/source_control_systems/perforce.rb +116 -0
- data/lib/rubycritic/source_locator.rb +52 -0
- data/lib/rubycritic/version.rb +5 -0
- data/lib/rubycritic.rb +6 -0
- metadata +528 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rubycritic/commands/base'
|
|
4
|
+
|
|
5
|
+
module RubyCritic
|
|
6
|
+
module Command
|
|
7
|
+
class Help < Base
|
|
8
|
+
def execute
|
|
9
|
+
puts options[:help_text]
|
|
10
|
+
status_reporter
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
attr_reader :options, :status_reporter
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyCritic
|
|
4
|
+
module Command
|
|
5
|
+
class StatusReporter
|
|
6
|
+
attr_reader :status, :status_message, :score
|
|
7
|
+
SUCCESS = 0
|
|
8
|
+
SCORE_BELOW_MINIMUM = 1
|
|
9
|
+
|
|
10
|
+
def initialize(options)
|
|
11
|
+
@options = options
|
|
12
|
+
@status = SUCCESS
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def score=(input_score)
|
|
16
|
+
@score = input_score.round(2)
|
|
17
|
+
update_status
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def update_status
|
|
23
|
+
@status = current_status
|
|
24
|
+
update_status_message
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def current_status
|
|
28
|
+
satisfy_minimum_score_rule ? SUCCESS : SCORE_BELOW_MINIMUM
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def satisfy_minimum_score_rule
|
|
32
|
+
score >= @options[:minimum_score].to_f
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def update_status_message
|
|
36
|
+
case @status
|
|
37
|
+
when SUCCESS
|
|
38
|
+
@status_message = "Score: #{score}"
|
|
39
|
+
when SCORE_BELOW_MINIMUM
|
|
40
|
+
@status_message = "Score (#{score}) is below the minimum #{@options[:minimum_score]}"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyCritic
|
|
4
|
+
module Command
|
|
5
|
+
module Utils
|
|
6
|
+
class BuildNumberFile
|
|
7
|
+
attr_reader :file, :build_number
|
|
8
|
+
|
|
9
|
+
def initialize
|
|
10
|
+
open_build_number_file
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# keep track of the number of builds and
|
|
14
|
+
# use this build number to create separate directory for each build
|
|
15
|
+
def update_build_number
|
|
16
|
+
@build_number = file.read.to_i + 1
|
|
17
|
+
write_build_number
|
|
18
|
+
build_number
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def write_build_number
|
|
22
|
+
file.rewind
|
|
23
|
+
file.write(build_number)
|
|
24
|
+
file.close
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def open_build_number_file
|
|
28
|
+
root = Config.root
|
|
29
|
+
FileUtils.mkdir_p(root) unless File.directory?(root)
|
|
30
|
+
location = "#{root}/build_number.txt"
|
|
31
|
+
File.new(location, 'a') unless File.exist?(location)
|
|
32
|
+
@file = File.open(location, 'r+')
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rubycritic/version'
|
|
4
|
+
require 'rubycritic/commands/base'
|
|
5
|
+
|
|
6
|
+
module RubyCritic
|
|
7
|
+
module Command
|
|
8
|
+
class Version < Base
|
|
9
|
+
attr_reader :status_reporter
|
|
10
|
+
def execute
|
|
11
|
+
puts "RubyCritic #{VERSION}"
|
|
12
|
+
status_reporter
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rubycritic/source_control_systems/base'
|
|
4
|
+
|
|
5
|
+
module RubyCritic
|
|
6
|
+
class Configuration
|
|
7
|
+
attr_reader :root
|
|
8
|
+
attr_accessor :source_control_system, :mode, :formats, :formatters, :deduplicate_symlinks,
|
|
9
|
+
:suppress_ratings, :open_with, :no_browser, :base_branch,
|
|
10
|
+
:feature_branch, :base_branch_score, :feature_branch_score,
|
|
11
|
+
:base_root_directory, :feature_root_directory,
|
|
12
|
+
:compare_root_directory, :threshold_score, :base_branch_collection,
|
|
13
|
+
:feature_branch_collection
|
|
14
|
+
|
|
15
|
+
def set(options)
|
|
16
|
+
self.mode = options[:mode] || :default
|
|
17
|
+
self.root = options[:root] || 'tmp/rubycritic'
|
|
18
|
+
self.deduplicate_symlinks = options[:deduplicate_symlinks]
|
|
19
|
+
self.suppress_ratings = options[:suppress_ratings]
|
|
20
|
+
self.open_with = options[:open_with]
|
|
21
|
+
self.no_browser = options[:no_browser]
|
|
22
|
+
self.base_branch = options[:base_branch]
|
|
23
|
+
self.feature_branch = options[:feature_branch]
|
|
24
|
+
self.threshold_score = options[:threshold_score].to_i
|
|
25
|
+
setup_formats(options)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def setup_formats(options)
|
|
29
|
+
formats = options[:formats].to_a
|
|
30
|
+
self.formats = formats.empty? ? [:html] : formats
|
|
31
|
+
self.formatters = options[:formatters] || []
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def root=(path)
|
|
35
|
+
@root = File.expand_path(path)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def source_control_present?
|
|
39
|
+
source_control_system &&
|
|
40
|
+
!source_control_system.is_a?(SourceControlSystem::Double)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
module Config
|
|
45
|
+
def self.configuration
|
|
46
|
+
@configuration ||= Configuration.new
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def self.set(options = {})
|
|
50
|
+
configuration.set(options)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.compare_branches_mode?
|
|
54
|
+
%i[compare_branches ci].include?(Config.mode)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def self.build_mode?
|
|
58
|
+
!Config.no_browser && %i[compare_branches ci].include?(Config.mode)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def self.method_missing(method, *args, &block)
|
|
62
|
+
configuration.public_send(method, *args, &block)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def self.respond_to_missing?(symbol, include_all = false)
|
|
66
|
+
configuration.respond_to_missing?(symbol) || super
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'virtus'
|
|
4
|
+
require 'rubycritic/core/rating'
|
|
5
|
+
|
|
6
|
+
module RubyCritic
|
|
7
|
+
class AnalysedModule
|
|
8
|
+
include Virtus.model
|
|
9
|
+
|
|
10
|
+
# Complexity is reduced by a factor of 25 when calculating cost
|
|
11
|
+
COMPLEXITY_FACTOR = 25.0
|
|
12
|
+
|
|
13
|
+
attribute :coverage
|
|
14
|
+
attribute :name
|
|
15
|
+
attribute :smells_count
|
|
16
|
+
attribute :file_location
|
|
17
|
+
attribute :file_name
|
|
18
|
+
attribute :line_count
|
|
19
|
+
attribute :pathname
|
|
20
|
+
attribute :smells, Array, default: []
|
|
21
|
+
attribute :churn
|
|
22
|
+
attribute :committed_at
|
|
23
|
+
attribute :complexity, Float, default: Float::INFINITY
|
|
24
|
+
attribute :duplication, Integer, default: 0
|
|
25
|
+
attribute :methods_count
|
|
26
|
+
|
|
27
|
+
def path
|
|
28
|
+
@path ||= pathname.to_s
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def file_location
|
|
32
|
+
pathname.dirname
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def file_name
|
|
36
|
+
pathname.basename
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def line_count
|
|
40
|
+
File.read(path).each_line.count
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def cost
|
|
44
|
+
@cost ||= smells.map(&:cost).inject(0.0, :+) +
|
|
45
|
+
(complexity / COMPLEXITY_FACTOR)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def coverage_rating
|
|
49
|
+
@coverage_rating ||= Rating.from_cost(100 - coverage)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def rating
|
|
53
|
+
@rating ||= Rating.from_cost(cost)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def complexity_per_method
|
|
57
|
+
if methods_count.zero?
|
|
58
|
+
'N/A'
|
|
59
|
+
else
|
|
60
|
+
complexity.fdiv(methods_count).round(1)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def smells_count
|
|
65
|
+
smells.count
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def smells?
|
|
69
|
+
!smells.empty?
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def smells_at_location(location)
|
|
73
|
+
smells.select { |smell| smell.at_location?(location) }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def <=>(other)
|
|
77
|
+
[rating.to_s, name] <=> [other.rating.to_s, other.name]
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def to_h
|
|
81
|
+
{
|
|
82
|
+
name: name, path: path, smells: smells,
|
|
83
|
+
churn: churn, committed_at: committed_at, complexity: complexity,
|
|
84
|
+
duplication: duplication, methods_count: methods_count, cost: cost,
|
|
85
|
+
rating: rating
|
|
86
|
+
}
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def to_json(*options)
|
|
90
|
+
to_h.to_json(*options)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rubycritic/source_locator'
|
|
4
|
+
require 'rubycritic/core/analysed_module'
|
|
5
|
+
|
|
6
|
+
module RubyCritic
|
|
7
|
+
class AnalysedModulesCollection
|
|
8
|
+
include Enumerable
|
|
9
|
+
|
|
10
|
+
# Limit used to prevent very bad modules to have excessive impact in the
|
|
11
|
+
# overall result. See #limited_cost_for
|
|
12
|
+
COST_LIMIT = 32.0
|
|
13
|
+
# Score goes from 0 (worst) to 100 (perfect)
|
|
14
|
+
MAX_SCORE = 100.0
|
|
15
|
+
# Projects with an average cost of 16 (or above) will score 0, since 16
|
|
16
|
+
# is where the worst possible rating (F) starts
|
|
17
|
+
ZERO_SCORE_COST = 16.0
|
|
18
|
+
COST_MULTIPLIER = MAX_SCORE / ZERO_SCORE_COST
|
|
19
|
+
|
|
20
|
+
def initialize(paths, modules = nil)
|
|
21
|
+
@modules = SourceLocator.new(paths).pathnames.map do |pathname|
|
|
22
|
+
if modules
|
|
23
|
+
analysed_module = modules.find { |mod| mod.pathname == pathname }
|
|
24
|
+
build_analysed_module(analysed_module)
|
|
25
|
+
else
|
|
26
|
+
AnalysedModule.new(pathname: pathname)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def each(&block)
|
|
32
|
+
@modules.each(&block)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def where(module_paths)
|
|
36
|
+
@modules.find_all { |mod| module_paths.include? mod.path }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def find(module_path)
|
|
40
|
+
@modules.find { |mod| mod.pathname == module_path }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def to_json(*options)
|
|
44
|
+
@modules.to_json(*options)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def score
|
|
48
|
+
if @modules.any?
|
|
49
|
+
(MAX_SCORE - average_limited_cost * COST_MULTIPLIER).round(2)
|
|
50
|
+
else
|
|
51
|
+
0.0
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def summary
|
|
56
|
+
AnalysisSummary.generate(self)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def for_rating(rating)
|
|
60
|
+
find_all { |mod| mod.rating.to_s == rating }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def average_limited_cost
|
|
66
|
+
[average_cost, ZERO_SCORE_COST].min
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def average_cost
|
|
70
|
+
num_modules = @modules.size
|
|
71
|
+
if num_modules.positive?
|
|
72
|
+
map { |mod| limited_cost_for(mod) }.reduce(:+) / num_modules.to_f
|
|
73
|
+
else
|
|
74
|
+
0.0
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def limited_cost_for(mod)
|
|
79
|
+
[mod.cost, COST_LIMIT].min
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def build_analysed_module(analysed_module)
|
|
83
|
+
AnalysedModule.new(
|
|
84
|
+
pathname: analysed_module.pathname,
|
|
85
|
+
name: analysed_module.name,
|
|
86
|
+
smells: analysed_module.smells,
|
|
87
|
+
churn: analysed_module.churn,
|
|
88
|
+
committed_at: analysed_module.committed_at,
|
|
89
|
+
complexity: analysed_module.complexity,
|
|
90
|
+
duplication: analysed_module.duplication,
|
|
91
|
+
methods_count: analysed_module.methods_count
|
|
92
|
+
)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'pathname'
|
|
4
|
+
|
|
5
|
+
module RubyCritic
|
|
6
|
+
class Location
|
|
7
|
+
attr_reader :pathname, :line
|
|
8
|
+
|
|
9
|
+
def initialize(path, line)
|
|
10
|
+
@pathname = Pathname.new(path)
|
|
11
|
+
@line = line.to_i
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def file_name
|
|
15
|
+
@pathname.basename.sub_ext('').to_s
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def to_s
|
|
19
|
+
"#{pathname}:#{line}"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def to_h
|
|
23
|
+
{
|
|
24
|
+
path: pathname.to_s,
|
|
25
|
+
line: line
|
|
26
|
+
}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def to_json(*options)
|
|
30
|
+
to_h.to_json(*options)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def ==(other)
|
|
34
|
+
state == other.state
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def <=>(other)
|
|
38
|
+
state <=> other.state
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
protected
|
|
42
|
+
|
|
43
|
+
def state
|
|
44
|
+
[@pathname, @line]
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyCritic
|
|
4
|
+
class Rating
|
|
5
|
+
def self.from_cost(cost)
|
|
6
|
+
if cost <= 2 then new('A')
|
|
7
|
+
elsif cost <= 4 then new('B')
|
|
8
|
+
elsif cost <= 8 then new('C')
|
|
9
|
+
elsif cost <= 16 then new('D')
|
|
10
|
+
else new('F')
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def initialize(letter)
|
|
15
|
+
@letter = letter
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def to_s
|
|
19
|
+
@letter
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def to_h
|
|
23
|
+
@letter
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def to_json(*options)
|
|
27
|
+
to_h.to_json(*options)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'virtus'
|
|
4
|
+
require 'rubycritic/core/location'
|
|
5
|
+
|
|
6
|
+
module RubyCritic
|
|
7
|
+
class Smell
|
|
8
|
+
include Virtus.model
|
|
9
|
+
|
|
10
|
+
attribute :context
|
|
11
|
+
attribute :cost
|
|
12
|
+
attribute :locations, Array, default: []
|
|
13
|
+
attribute :message
|
|
14
|
+
attribute :score
|
|
15
|
+
attribute :status
|
|
16
|
+
attribute :type
|
|
17
|
+
attribute :analyser
|
|
18
|
+
|
|
19
|
+
FLAY_DOCS_URL = 'http://docs.seattlerb.org/flay/'.freeze
|
|
20
|
+
FLOG_DOCS_URL = 'http://docs.seattlerb.org/flog/'.freeze
|
|
21
|
+
|
|
22
|
+
def at_location?(other_location)
|
|
23
|
+
locations.any? { |location| location == other_location }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def multiple_locations?
|
|
27
|
+
locations.length > 1
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def ==(other)
|
|
31
|
+
state == other.state
|
|
32
|
+
end
|
|
33
|
+
alias eql? ==
|
|
34
|
+
|
|
35
|
+
def to_s
|
|
36
|
+
"(#{type}) #{context} #{message}"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def to_h
|
|
40
|
+
{
|
|
41
|
+
context: context,
|
|
42
|
+
cost: cost,
|
|
43
|
+
locations: locations,
|
|
44
|
+
message: message,
|
|
45
|
+
score: score,
|
|
46
|
+
status: status,
|
|
47
|
+
type: type
|
|
48
|
+
}
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def to_json(*options)
|
|
52
|
+
to_h.to_json(*options)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def doc_url
|
|
56
|
+
case analyser
|
|
57
|
+
when 'reek'
|
|
58
|
+
"https://github.com/troessner/reek/blob/master/docs/#{dasherized_type}.md"
|
|
59
|
+
when 'flay'
|
|
60
|
+
FLAY_DOCS_URL
|
|
61
|
+
when 'flog'
|
|
62
|
+
FLOG_DOCS_URL
|
|
63
|
+
else
|
|
64
|
+
raise "No documentation URL set for analyser '#{analyser}' smells"
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def hash
|
|
69
|
+
state.hash
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
protected
|
|
73
|
+
|
|
74
|
+
def state
|
|
75
|
+
[context, message, score, type]
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
private
|
|
79
|
+
|
|
80
|
+
def dasherized_type
|
|
81
|
+
type.gsub(/(?<!^)([A-Z])/, '-\1')
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rubycritic/generators/text/list'
|
|
4
|
+
|
|
5
|
+
module RubyCritic
|
|
6
|
+
module Generator
|
|
7
|
+
class ConsoleReport
|
|
8
|
+
def initialize(analysed_modules)
|
|
9
|
+
@analysed_modules = analysed_modules
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def generate_report
|
|
13
|
+
reports = @analysed_modules.sort.map do |mod|
|
|
14
|
+
Text::List.new(mod).render
|
|
15
|
+
end
|
|
16
|
+
puts reports.join("\n")
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|