metric_fu-roodi 2.2.2 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/metric_fu-roodi +2 -19
- data/bin/metric_fu-roodi-describe +2 -5
- data/lib/roodi.rb +6 -2
- data/lib/roodi/version.rb +1 -1
- metadata +13 -119
- data/.gitignore +0 -3
- data/.rspec +0 -2
- data/Gemfile +0 -6
- data/History.txt +0 -111
- data/Manifest.txt +0 -56
- data/README.txt +0 -98
- data/Rakefile +0 -31
- data/TODO.md +0 -3
- data/lib/roodi/checks.rb +0 -18
- data/lib/roodi/checks/abc_metric_method_check.rb +0 -79
- data/lib/roodi/checks/assignment_in_conditional_check.rb +0 -32
- data/lib/roodi/checks/case_missing_else_check.rb +0 -20
- data/lib/roodi/checks/check.rb +0 -76
- data/lib/roodi/checks/class_line_count_check.rb +0 -28
- data/lib/roodi/checks/class_name_check.rb +0 -31
- data/lib/roodi/checks/class_variable_check.rb +0 -24
- data/lib/roodi/checks/control_coupling_check.rb +0 -20
- data/lib/roodi/checks/cyclomatic_complexity_block_check.rb +0 -41
- data/lib/roodi/checks/cyclomatic_complexity_check.rb +0 -50
- data/lib/roodi/checks/cyclomatic_complexity_method_check.rb +0 -42
- data/lib/roodi/checks/empty_rescue_body_check.rb +0 -32
- data/lib/roodi/checks/for_loop_check.rb +0 -20
- data/lib/roodi/checks/line_count_check.rb +0 -28
- data/lib/roodi/checks/method_line_count_check.rb +0 -29
- data/lib/roodi/checks/method_name_check.rb +0 -31
- data/lib/roodi/checks/missing_foreign_key_index_check.rb +0 -99
- data/lib/roodi/checks/module_line_count_check.rb +0 -28
- data/lib/roodi/checks/module_name_check.rb +0 -31
- data/lib/roodi/checks/name_check.rb +0 -16
- data/lib/roodi/checks/npath_complexity_check.rb +0 -75
- data/lib/roodi/checks/npath_complexity_method_check.rb +0 -29
- data/lib/roodi/checks/parameter_number_check.rb +0 -34
- data/lib/roodi/core.rb +0 -1
- data/lib/roodi/core/checking_visitor.rb +0 -26
- data/lib/roodi/core/error.rb +0 -17
- data/lib/roodi/core/parser.rb +0 -48
- data/lib/roodi/core/runner.rb +0 -81
- data/lib/roodi/core/visitable_sexp.rb +0 -25
- data/lib/roodi_task.rb +0 -35
- data/roodi.gemspec +0 -26
- data/roodi.yml +0 -25
- data/spec/roodi/checks/abc_metric_method_check_spec.rb +0 -89
- data/spec/roodi/checks/assignment_in_conditional_check_spec.rb +0 -105
- data/spec/roodi/checks/case_missing_else_check_spec.rb +0 -32
- data/spec/roodi/checks/class_line_count_check_spec.rb +0 -39
- data/spec/roodi/checks/class_name_check_spec.rb +0 -39
- data/spec/roodi/checks/class_variable_check_spec.rb +0 -17
- data/spec/roodi/checks/control_coupling_check_spec.rb +0 -23
- data/spec/roodi/checks/cyclomatic_complexity_block_check_spec.rb +0 -67
- data/spec/roodi/checks/cyclomatic_complexity_method_check_spec.rb +0 -200
- data/spec/roodi/checks/empty_rescue_body_check_spec.rb +0 -154
- data/spec/roodi/checks/for_loop_check_spec.rb +0 -18
- data/spec/roodi/checks/method_line_count_check_spec.rb +0 -56
- data/spec/roodi/checks/method_name_check_spec.rb +0 -76
- data/spec/roodi/checks/missing_foreign_key_index_check_spec.rb +0 -33
- data/spec/roodi/checks/module_line_count_check_spec.rb +0 -39
- data/spec/roodi/checks/module_name_check_spec.rb +0 -27
- data/spec/roodi/checks/npath_complexity_method_check_spec.rb +0 -53
- data/spec/roodi/checks/parameter_number_check_spec.rb +0 -47
- data/spec/roodi/core/runner_spec.rb +0 -25
- data/spec/roodi/roodi.yml +0 -2
- data/spec/spec_helper.rb +0 -3
data/Rakefile
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
#!/usr/bin/env rake
|
2
|
-
require 'bundler/setup'
|
3
|
-
require 'bundler/gem_tasks'
|
4
|
-
begin
|
5
|
-
require 'spec/rake/spectask'
|
6
|
-
desc "Run all specs in spec directory"
|
7
|
-
Spec::Rake::SpecTask.new(:spec) do |t|
|
8
|
-
t.spec_files = FileList['spec/**/*_spec.rb']
|
9
|
-
end
|
10
|
-
rescue LoadError
|
11
|
-
require 'rspec/core/rake_task'
|
12
|
-
desc "Run all specs in spec directory"
|
13
|
-
RSpec::Core::RakeTask.new(:spec)
|
14
|
-
end
|
15
|
-
|
16
|
-
require File.expand_path('lib/roodi',File.dirname(__FILE__))
|
17
|
-
|
18
|
-
def roodi(ruby_files)
|
19
|
-
roodi = Roodi::Core::Runner.new
|
20
|
-
ruby_files.each { |file| roodi.check_file(file) }
|
21
|
-
roodi.errors.each {|error| puts error}
|
22
|
-
puts "\nFound #{roodi.errors.size} errors."
|
23
|
-
end
|
24
|
-
|
25
|
-
desc "Run Roodi against all source files"
|
26
|
-
task :roodi do
|
27
|
-
pattern = File.join(File.dirname(__FILE__), "**", "*.rb")
|
28
|
-
roodi(Dir.glob(pattern))
|
29
|
-
end
|
30
|
-
|
31
|
-
task :default => :spec
|
data/TODO.md
DELETED
data/lib/roodi/checks.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
require 'roodi/checks/abc_metric_method_check'
|
2
|
-
require 'roodi/checks/assignment_in_conditional_check'
|
3
|
-
require 'roodi/checks/case_missing_else_check'
|
4
|
-
require 'roodi/checks/class_line_count_check'
|
5
|
-
require 'roodi/checks/class_name_check'
|
6
|
-
require 'roodi/checks/class_variable_check'
|
7
|
-
require 'roodi/checks/control_coupling_check'
|
8
|
-
require 'roodi/checks/cyclomatic_complexity_block_check'
|
9
|
-
require 'roodi/checks/cyclomatic_complexity_method_check'
|
10
|
-
require 'roodi/checks/empty_rescue_body_check'
|
11
|
-
require 'roodi/checks/for_loop_check'
|
12
|
-
require 'roodi/checks/method_line_count_check'
|
13
|
-
require 'roodi/checks/method_name_check'
|
14
|
-
require 'roodi/checks/missing_foreign_key_index_check'
|
15
|
-
require 'roodi/checks/module_line_count_check'
|
16
|
-
require 'roodi/checks/module_name_check'
|
17
|
-
require 'roodi/checks/npath_complexity_method_check'
|
18
|
-
require 'roodi/checks/parameter_number_check'
|
@@ -1,79 +0,0 @@
|
|
1
|
-
require 'roodi/checks/check'
|
2
|
-
|
3
|
-
module Roodi
|
4
|
-
module Checks
|
5
|
-
# TODO: Add summary
|
6
|
-
#
|
7
|
-
# TODO: Add detail
|
8
|
-
class AbcMetricMethodCheck < Check
|
9
|
-
# ASSIGNMENTS = [:attrasgn, :attrset, :dasgn_curr, :iasgn, :lasgn, :masgn]
|
10
|
-
ASSIGNMENTS = [:lasgn]
|
11
|
-
# BRANCHES = [:if, :else, :while, :until, :for, :rescue, :case, :when, :and, :or]
|
12
|
-
BRANCHES = [:vcall, :call]
|
13
|
-
# CONDITIONS = [:and, :or]
|
14
|
-
CONDITIONS = [:==, :<=, :>=, :<, :>]
|
15
|
-
# = *= /= %= += <<= >>= &= |= ^=
|
16
|
-
OPERATORS = [:*, :/, :%, :+, :<<, :>>, :&, :|, :^]
|
17
|
-
DEFAULT_SCORE = 10
|
18
|
-
|
19
|
-
attr_accessor :score
|
20
|
-
|
21
|
-
def initialize
|
22
|
-
super()
|
23
|
-
self.score = DEFAULT_SCORE
|
24
|
-
end
|
25
|
-
|
26
|
-
def interesting_nodes
|
27
|
-
[:defn]
|
28
|
-
end
|
29
|
-
|
30
|
-
def evaluate_start(node)
|
31
|
-
method_name = node[1]
|
32
|
-
a = count_assignments(node)
|
33
|
-
b = count_branches(node)
|
34
|
-
c = count_conditionals(node)
|
35
|
-
score = Math.sqrt(a*a + b*b + c*c)
|
36
|
-
add_error "Method name \"#{method_name}\" has an ABC metric score of <#{a},#{b},#{c}> = #{score}. It should be #{@score} or less." unless score <= @score
|
37
|
-
end
|
38
|
-
|
39
|
-
private
|
40
|
-
|
41
|
-
def count_assignments(node)
|
42
|
-
count = 0
|
43
|
-
count = count + 1 if assignment?(node)
|
44
|
-
node.children.each {|node| count += count_assignments(node)}
|
45
|
-
count
|
46
|
-
end
|
47
|
-
|
48
|
-
def count_branches(node)
|
49
|
-
count = 0
|
50
|
-
count = count + 1 if branch?(node)
|
51
|
-
node.children.each {|node| count += count_branches(node)}
|
52
|
-
count
|
53
|
-
end
|
54
|
-
|
55
|
-
def count_conditionals(node)
|
56
|
-
count = 0
|
57
|
-
count = count + 1 if conditional?(node)
|
58
|
-
node.children.each {|node| count += count_conditionals(node)}
|
59
|
-
count
|
60
|
-
end
|
61
|
-
|
62
|
-
def assignment?(node)
|
63
|
-
ASSIGNMENTS.include?(node.node_type)
|
64
|
-
end
|
65
|
-
|
66
|
-
def branch?(node)
|
67
|
-
BRANCHES.include?(node.node_type) && !conditional?(node) && !operator?(node)
|
68
|
-
end
|
69
|
-
|
70
|
-
def conditional?(node)
|
71
|
-
(:call == node.node_type) && CONDITIONS.include?(node[2])
|
72
|
-
end
|
73
|
-
|
74
|
-
def operator?(node)
|
75
|
-
(:call == node.node_type) && OPERATORS.include?(node[2])
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
@@ -1,32 +0,0 @@
|
|
1
|
-
require 'roodi/checks/check'
|
2
|
-
|
3
|
-
module Roodi
|
4
|
-
module Checks
|
5
|
-
# Checks a conditional to see if it contains an assignment.
|
6
|
-
#
|
7
|
-
# A conditional containing an assignment is likely to be a mistyped equality check. You
|
8
|
-
# should either fix the typo or factor out the assignment so that the code is clearer.
|
9
|
-
class AssignmentInConditionalCheck < Check
|
10
|
-
|
11
|
-
def interesting_nodes
|
12
|
-
[:if, :while]
|
13
|
-
end
|
14
|
-
|
15
|
-
def evaluate_start(node)
|
16
|
-
add_error("Found = in conditional. It should probably be an ==") if has_assignment?(node[1])
|
17
|
-
end
|
18
|
-
|
19
|
-
private
|
20
|
-
|
21
|
-
def has_assignment?(node)
|
22
|
-
found_assignment = false
|
23
|
-
found_assignment = found_assignment || node.node_type == :lasgn
|
24
|
-
if (node.node_type == :and or node.node_type == :or)
|
25
|
-
node.children.each { |child| found_assignment = found_assignment || has_assignment?(child) }
|
26
|
-
end
|
27
|
-
found_assignment
|
28
|
-
end
|
29
|
-
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
require 'roodi/checks/check'
|
2
|
-
|
3
|
-
module Roodi
|
4
|
-
module Checks
|
5
|
-
# Checks a case statement to make sure it has an 'else' clause.
|
6
|
-
#
|
7
|
-
# It's usually a good idea to have an else clause in every case statement. Even if the
|
8
|
-
# developer is sure that all currently possible cases are covered, this should be
|
9
|
-
# expressed in the else clause. This way the code is protected aginst later changes,
|
10
|
-
class CaseMissingElseCheck < Check
|
11
|
-
def interesting_nodes
|
12
|
-
[:case]
|
13
|
-
end
|
14
|
-
|
15
|
-
def evaluate_start(node)
|
16
|
-
add_error "Case statement is missing an else clause." unless node.last
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
data/lib/roodi/checks/check.rb
DELETED
@@ -1,76 +0,0 @@
|
|
1
|
-
require 'roodi/core/error'
|
2
|
-
|
3
|
-
module Roodi
|
4
|
-
module Checks
|
5
|
-
class Check
|
6
|
-
|
7
|
-
NODE_TYPES = [:defn, :module, :resbody, :lvar, :cvar, :class, :if, :while, :until, :for, :rescue, :case, :when, :and, :or]
|
8
|
-
|
9
|
-
class << self
|
10
|
-
|
11
|
-
def make(options = nil)
|
12
|
-
check = new
|
13
|
-
if options
|
14
|
-
options.each do |name, value|
|
15
|
-
check.send("#{name}=", value)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
check
|
19
|
-
end
|
20
|
-
|
21
|
-
end
|
22
|
-
|
23
|
-
def initialize
|
24
|
-
@errors = []
|
25
|
-
end
|
26
|
-
|
27
|
-
NODE_TYPES.each do |node|
|
28
|
-
start_node_method = "evaluate_start_#{node}"
|
29
|
-
end_node_method = "evaluate_end_#{node}"
|
30
|
-
define_method(start_node_method) { |node| return } unless self.respond_to?(start_node_method)
|
31
|
-
define_method(end_node_method) { |node| return } unless self.respond_to?(end_node_method)
|
32
|
-
end
|
33
|
-
|
34
|
-
def position(offset = 0)
|
35
|
-
"#{@line[2]}:#{@line[1] + offset}"
|
36
|
-
end
|
37
|
-
|
38
|
-
def start_file(filename)
|
39
|
-
end
|
40
|
-
|
41
|
-
def end_file(filename)
|
42
|
-
end
|
43
|
-
|
44
|
-
def evaluate_start(node)
|
45
|
-
end
|
46
|
-
|
47
|
-
def evaluate_end(node)
|
48
|
-
end
|
49
|
-
|
50
|
-
def evaluate_node(position, node)
|
51
|
-
@node = node
|
52
|
-
eval_method = "evaluate_#{position}_#{node.node_type}"
|
53
|
-
self.send(eval_method, node)
|
54
|
-
end
|
55
|
-
|
56
|
-
def evaluate_node_start(node)
|
57
|
-
evaluate_node(:start, node)
|
58
|
-
evaluate_start(node)
|
59
|
-
end
|
60
|
-
|
61
|
-
def evaluate_node_end(node)
|
62
|
-
evaluate_node(:end, node)
|
63
|
-
evaluate_end(node)
|
64
|
-
end
|
65
|
-
|
66
|
-
def add_error(error, filename = @node.file, line = @node.line)
|
67
|
-
@errors ||= []
|
68
|
-
@errors << Roodi::Core::Error.new("#{filename}", "#{line}", error)
|
69
|
-
end
|
70
|
-
|
71
|
-
def errors
|
72
|
-
@errors
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
require 'roodi/checks/line_count_check'
|
2
|
-
|
3
|
-
module Roodi
|
4
|
-
module Checks
|
5
|
-
# Checks a class to make sure the number of lines it has is under the specified limit.
|
6
|
-
#
|
7
|
-
# A class getting too large is a code smell that indicates it might be taking on too many
|
8
|
-
# responsibilities. It should probably be refactored into multiple smaller classes.
|
9
|
-
class ClassLineCountCheck < LineCountCheck
|
10
|
-
|
11
|
-
DEFAULT_LINE_COUNT = 300
|
12
|
-
|
13
|
-
def initialize
|
14
|
-
super()
|
15
|
-
self.line_count = DEFAULT_LINE_COUNT
|
16
|
-
end
|
17
|
-
|
18
|
-
def interesting_nodes
|
19
|
-
[:class]
|
20
|
-
end
|
21
|
-
|
22
|
-
def message_prefix
|
23
|
-
'Class'
|
24
|
-
end
|
25
|
-
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
@@ -1,31 +0,0 @@
|
|
1
|
-
require 'roodi/checks/name_check'
|
2
|
-
|
3
|
-
module Roodi
|
4
|
-
module Checks
|
5
|
-
# Checks a class name to make sure it matches the specified pattern.
|
6
|
-
#
|
7
|
-
# Keeping to a consistent naming convention makes your code easier to read.
|
8
|
-
class ClassNameCheck < NameCheck
|
9
|
-
|
10
|
-
DEFAULT_PATTERN = /^[A-Z][a-zA-Z0-9]*$/
|
11
|
-
|
12
|
-
def initialize
|
13
|
-
super()
|
14
|
-
self.pattern = DEFAULT_PATTERN
|
15
|
-
end
|
16
|
-
|
17
|
-
def interesting_nodes
|
18
|
-
[:class]
|
19
|
-
end
|
20
|
-
|
21
|
-
def message_prefix
|
22
|
-
'Class'
|
23
|
-
end
|
24
|
-
|
25
|
-
def find_name(node)
|
26
|
-
node[1].class == Symbol ? node[1] : node[1].last
|
27
|
-
end
|
28
|
-
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
@@ -1,24 +0,0 @@
|
|
1
|
-
require 'roodi/checks/check'
|
2
|
-
|
3
|
-
module Roodi
|
4
|
-
module Checks
|
5
|
-
# Checks to make sure class variables are not being used..
|
6
|
-
#
|
7
|
-
# Class variables in Ruby have a complicated inheritance policy, and their use
|
8
|
-
# can lead to mistakes. Often an alternate design can be used to solve the
|
9
|
-
# problem instead.
|
10
|
-
#
|
11
|
-
# This check is looking for a code smell rather than a definite error. If you're
|
12
|
-
# sure that you're doing the right thing, try turning this check off in your
|
13
|
-
# config file.
|
14
|
-
class ClassVariableCheck < Check
|
15
|
-
def interesting_nodes
|
16
|
-
[:cvar]
|
17
|
-
end
|
18
|
-
|
19
|
-
def evaluate_start(node)
|
20
|
-
add_error "Don't use class variables. You might want to try a different design."
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
require 'roodi/checks/check'
|
2
|
-
|
3
|
-
module Roodi
|
4
|
-
module Checks
|
5
|
-
class ControlCouplingCheck < Check
|
6
|
-
def interesting_nodes
|
7
|
-
[:defn, :lvar]
|
8
|
-
end
|
9
|
-
|
10
|
-
def evaluate_start_defn(node)
|
11
|
-
@method_name = node[1]
|
12
|
-
@arguments = node[2][1..-1]
|
13
|
-
end
|
14
|
-
|
15
|
-
def evaluate_start_lvar(node)
|
16
|
-
add_error "Method \"#{@method_name}\" uses the argument \"#{node[1]}\" for internal control." if @arguments.detect {|each| each == node[1]}
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
@@ -1,41 +0,0 @@
|
|
1
|
-
require 'roodi/checks/cyclomatic_complexity_check'
|
2
|
-
|
3
|
-
module Roodi
|
4
|
-
module Checks
|
5
|
-
# Checks cyclomatic complexity of a block against a specified limit.
|
6
|
-
#
|
7
|
-
# The cyclomatic complexity is measured by the number of "if", "unless", "elsif", "?:",
|
8
|
-
# "while", "until", "for", "rescue", "case", "when", "&&", "and", "||" and "or"
|
9
|
-
# statements (plus one) in the body of the member. It is a measure of the minimum
|
10
|
-
# number of possible paths through the source and therefore the number of required tests.
|
11
|
-
#
|
12
|
-
# Generally, for a block, 1-2 is considered good, 3-4 ok, 5-8 consider re-factoring, and 8+
|
13
|
-
# re-factor now!
|
14
|
-
class CyclomaticComplexityBlockCheck < CyclomaticComplexityCheck
|
15
|
-
|
16
|
-
DEFAULT_COMPLEXITY = 4
|
17
|
-
|
18
|
-
def initialize
|
19
|
-
super()
|
20
|
-
self.complexity = DEFAULT_COMPLEXITY
|
21
|
-
end
|
22
|
-
|
23
|
-
def interesting_nodes
|
24
|
-
[:iter] + COMPLEXITY_NODE_TYPES
|
25
|
-
end
|
26
|
-
|
27
|
-
def evaluate_start_iter(node)
|
28
|
-
increase_depth
|
29
|
-
end
|
30
|
-
|
31
|
-
def evaluate_end_iter(node)
|
32
|
-
decrease_depth
|
33
|
-
end
|
34
|
-
|
35
|
-
def evaluate_matching_end
|
36
|
-
add_error "Block cyclomatic complexity is #{@count}. It should be #{@complexity} or less." unless @count <= @complexity
|
37
|
-
end
|
38
|
-
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
@@ -1,50 +0,0 @@
|
|
1
|
-
require 'roodi/checks/check'
|
2
|
-
|
3
|
-
module Roodi
|
4
|
-
module Checks
|
5
|
-
class CyclomaticComplexityCheck < Check
|
6
|
-
|
7
|
-
COMPLEXITY_NODE_TYPES = [:if, :while, :until, :for, :rescue, :case, :when, :and, :or]
|
8
|
-
|
9
|
-
attr_accessor :complexity
|
10
|
-
|
11
|
-
def initialize
|
12
|
-
super()
|
13
|
-
@count = 0
|
14
|
-
@counting = 0
|
15
|
-
end
|
16
|
-
|
17
|
-
COMPLEXITY_NODE_TYPES.each do |type|
|
18
|
-
define_method "evaluate_start_#{type}" do |node|
|
19
|
-
@count = @count + 1 if counting?
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
protected
|
24
|
-
|
25
|
-
def count_complexity(node)
|
26
|
-
count_branches(node) + 1
|
27
|
-
end
|
28
|
-
|
29
|
-
def increase_depth
|
30
|
-
@count = 1 unless counting?
|
31
|
-
@counting = @counting + 1
|
32
|
-
end
|
33
|
-
|
34
|
-
def decrease_depth
|
35
|
-
@counting = @counting - 1
|
36
|
-
if @counting <= 0
|
37
|
-
@counting = 0
|
38
|
-
evaluate_matching_end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
|
44
|
-
def counting?
|
45
|
-
@counting > 0
|
46
|
-
end
|
47
|
-
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|