roodi 3.0.0 → 3.0.1
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 +4 -4
- data/.gitignore +1 -0
- data/Gemfile +1 -0
- data/History.txt +12 -0
- data/README.md +17 -2
- data/Rakefile +1 -0
- data/lib/roodi/checks/abc_metric_method_check.rb +3 -7
- data/lib/roodi/checks/check.rb +8 -7
- data/lib/roodi/checks/class_name_check.rb +3 -7
- data/lib/roodi/checks/control_coupling_check.rb +5 -1
- data/lib/roodi/checks/cyclomatic_complexity_check.rb +12 -7
- data/lib/roodi/checks/line_count_check.rb +1 -0
- data/lib/roodi/checks/module_name_check.rb +2 -6
- data/lib/roodi/checks/name_check.rb +5 -0
- data/lib/roodi/checks/npath_complexity_check.rb +12 -7
- data/lib/roodi/checks/npath_complexity_method_check.rb +3 -3
- data/lib/roodi/core/runner.rb +9 -10
- data/lib/roodi/version.rb +1 -1
- data/roodi.gemspec +1 -1
- data/spec/spec_helper.rb +3 -0
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2eb90b5c384b0916cd61ec2cf8f5a8f950458f23
|
4
|
+
data.tar.gz: 76875cad67471324e5017ea7dd82e28c49307ba6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b68ac64dba9440ef8088cce40aaf71e4a3e2af8b7d9315255191cda7d786ca591862c13a319c5b109cc32c93f05eb17b81a4c3ef42b94962cfb9d31bbc6f92ee
|
7
|
+
data.tar.gz: 57ca381da9ccd33455286ec37a7a0798010ccc744ebba90f23668187470eae1a6d9d28181fcb1204450a59bfdfbae3bf4d5f402fe5b009c14438a6f16f8870c5
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/History.txt
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
= 3.0.1
|
2
|
+
|
3
|
+
* Added brief class level documentation on all checks
|
4
|
+
* No longer printing out "Line: 1" every time you run roodi-describe
|
5
|
+
* Added code coverage badge from Coverall.io
|
6
|
+
* Added code coverage through Coveralls.io
|
7
|
+
* Pulled duplicated find_name method up to super class
|
8
|
+
* Added Code Climate badge
|
9
|
+
* Added info about how to contribute
|
10
|
+
* Updated copyright info to be current
|
11
|
+
* Re-added tasks for releasing new versions of the gem
|
12
|
+
|
1
13
|
= 3.0.0
|
2
14
|
|
3
15
|
* Removed MissingForeignKeyIndexCheck, since it is specific to Rails/ActiveRecord and thus doesn't belong in Roodi
|
data/README.md
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
# roodi
|
2
2
|
|
3
3
|
[](https://travis-ci.org/roodi/roodi)
|
4
|
+
[](https://codeclimate.com/repos/5204c3fc7e00a47bf7015e4e/feed)
|
5
|
+
[](https://coveralls.io/r/roodi/roodi?branch=master)
|
4
6
|
|
5
7
|
## Description
|
6
8
|
|
7
|
-
Roodi stands for Ruby Object Oriented Design Inferometer.
|
9
|
+
Roodi stands for Ruby Object Oriented Design Inferometer. It parses your Ruby code and warns you about design issues you have based on the checks that is has configured.
|
8
10
|
|
9
11
|
## Install
|
10
12
|
|
@@ -86,11 +88,24 @@ To change the set of checks included, or to change the default values of the che
|
|
86
88
|
|
87
89
|
* BlockVariableShadowCheck - Check that a block variable does not have the same name as a method parameter or local variable. It may be mistakenly referenced within the block.
|
88
90
|
|
91
|
+
## Contributing
|
92
|
+
|
93
|
+
### Bug reporting
|
94
|
+
Please use the GitHub issue tracker.
|
95
|
+
|
96
|
+
### Want to submit some code?
|
97
|
+
Fantastic! Please follow this procedure:
|
98
|
+
- Fork the repository
|
99
|
+
- Create a well-named topic branch
|
100
|
+
- Add specs for any changes you make
|
101
|
+
- Write meaningful commit messages explaining why this change is needed
|
102
|
+
- Create a pull request.
|
103
|
+
|
89
104
|
## License
|
90
105
|
|
91
106
|
(The MIT License)
|
92
107
|
|
93
|
-
Copyright (c)
|
108
|
+
Copyright (c) 2013 Peter Evjan
|
94
109
|
|
95
110
|
Permission is hereby granted, free of charge, to any person obtaining
|
96
111
|
a copy of this software and associated documentation files (the
|
data/Rakefile
CHANGED
@@ -2,17 +2,13 @@ require 'roodi/checks/check'
|
|
2
2
|
|
3
3
|
module Roodi
|
4
4
|
module Checks
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
5
|
+
# The ABC metric method check calculates the number of Assignments,
|
6
|
+
# Branches and Conditionals in your code. It is similar to
|
7
|
+
# cyclomatic complexity, so the lower the better.
|
8
8
|
class AbcMetricMethodCheck < Check
|
9
|
-
# ASSIGNMENTS = [:attrasgn, :attrset, :dasgn_curr, :iasgn, :lasgn, :masgn]
|
10
9
|
ASSIGNMENTS = [:lasgn]
|
11
|
-
# BRANCHES = [:if, :else, :while, :until, :for, :rescue, :case, :when, :and, :or]
|
12
10
|
BRANCHES = [:vcall, :call]
|
13
|
-
# CONDITIONS = [:and, :or]
|
14
11
|
CONDITIONS = [:==, :"!=", :<=, :>=, :<, :>]
|
15
|
-
# = *= /= %= += <<= >>= &= |= ^=
|
16
12
|
OPERATORS = [:*, :/, :%, :+, :<<, :>>, :&, :|, :^]
|
17
13
|
DEFAULT_SCORE = 10
|
18
14
|
|
data/lib/roodi/checks/check.rb
CHANGED
@@ -2,12 +2,13 @@ require 'roodi/core/error'
|
|
2
2
|
|
3
3
|
module Roodi
|
4
4
|
module Checks
|
5
|
+
# Base class for all the checks
|
5
6
|
class Check
|
6
7
|
|
7
8
|
NODE_TYPES = [:defn, :module, :resbody, :lvar, :cvar, :class, :if, :while, :until, :for, :rescue, :case, :when, :and, :or]
|
8
9
|
|
9
10
|
class << self
|
10
|
-
|
11
|
+
|
11
12
|
def make(options = nil)
|
12
13
|
check = new
|
13
14
|
if options
|
@@ -23,7 +24,7 @@ module Roodi
|
|
23
24
|
def initialize
|
24
25
|
@errors = []
|
25
26
|
end
|
26
|
-
|
27
|
+
|
27
28
|
NODE_TYPES.each do |node|
|
28
29
|
start_node_method = "evaluate_start_#{node}"
|
29
30
|
end_node_method = "evaluate_end_#{node}"
|
@@ -37,10 +38,10 @@ module Roodi
|
|
37
38
|
|
38
39
|
def start_file(filename)
|
39
40
|
end
|
40
|
-
|
41
|
+
|
41
42
|
def end_file(filename)
|
42
43
|
end
|
43
|
-
|
44
|
+
|
44
45
|
def evaluate_start(node)
|
45
46
|
end
|
46
47
|
|
@@ -57,17 +58,17 @@ module Roodi
|
|
57
58
|
evaluate_node(:start, node)
|
58
59
|
evaluate_start(node)
|
59
60
|
end
|
60
|
-
|
61
|
+
|
61
62
|
def evaluate_node_end(node)
|
62
63
|
evaluate_node(:end, node)
|
63
64
|
evaluate_end(node)
|
64
65
|
end
|
65
|
-
|
66
|
+
|
66
67
|
def add_error(error, filename = @node.file, line = @node.line)
|
67
68
|
@errors ||= []
|
68
69
|
@errors << Roodi::Core::Error.new("#{filename}", "#{line}", error)
|
69
70
|
end
|
70
|
-
|
71
|
+
|
71
72
|
def errors
|
72
73
|
@errors
|
73
74
|
end
|
@@ -3,17 +3,17 @@ require 'roodi/checks/name_check'
|
|
3
3
|
module Roodi
|
4
4
|
module Checks
|
5
5
|
# Checks a class name to make sure it matches the specified pattern.
|
6
|
-
#
|
6
|
+
#
|
7
7
|
# Keeping to a consistent naming convention makes your code easier to read.
|
8
8
|
class ClassNameCheck < NameCheck
|
9
9
|
|
10
10
|
DEFAULT_PATTERN = /^[A-Z][a-zA-Z0-9]*$/
|
11
|
-
|
11
|
+
|
12
12
|
def initialize
|
13
13
|
super()
|
14
14
|
self.pattern = DEFAULT_PATTERN
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
def interesting_nodes
|
18
18
|
[:class]
|
19
19
|
end
|
@@ -22,10 +22,6 @@ module Roodi
|
|
22
22
|
'Class'
|
23
23
|
end
|
24
24
|
|
25
|
-
def find_name(node)
|
26
|
-
node[1].class == Symbol ? node[1] : node[1].last
|
27
|
-
end
|
28
|
-
|
29
25
|
end
|
30
26
|
end
|
31
27
|
end
|
@@ -2,6 +2,10 @@ require 'roodi/checks/check'
|
|
2
2
|
|
3
3
|
module Roodi
|
4
4
|
module Checks
|
5
|
+
# Checks if a method uses an argument to decide on what execution path to take
|
6
|
+
#
|
7
|
+
# It is a kind of duplication, since the caller knows what path should be taken.
|
8
|
+
# Also, it means the method has more than one responsibility.
|
5
9
|
class ControlCouplingCheck < Check
|
6
10
|
def interesting_nodes
|
7
11
|
[:defn, :lvar]
|
@@ -11,7 +15,7 @@ module Roodi
|
|
11
15
|
@method_name = node[1]
|
12
16
|
@arguments = node[2][1..-1]
|
13
17
|
end
|
14
|
-
|
18
|
+
|
15
19
|
def evaluate_start_lvar(node)
|
16
20
|
add_error "Method \"#{@method_name}\" uses the argument \"#{node[1]}\" for internal control." if @arguments.detect {|each| each == node[1]}
|
17
21
|
end
|
@@ -2,8 +2,13 @@ require 'roodi/checks/check'
|
|
2
2
|
|
3
3
|
module Roodi
|
4
4
|
module Checks
|
5
|
+
# Cyclomatic complexity counts the number of linearly independent paths in
|
6
|
+
# the code. The lower the score, the better.
|
7
|
+
#
|
8
|
+
# Read more in the inventor Thomas J. McCabe's original research
|
9
|
+
# paper: www.literateprogramming.com/mccabe.pdf
|
5
10
|
class CyclomaticComplexityCheck < Check
|
6
|
-
|
11
|
+
|
7
12
|
COMPLEXITY_NODE_TYPES = [:if, :while, :until, :for, :rescue, :case, :when, :and, :or]
|
8
13
|
|
9
14
|
attr_accessor :complexity
|
@@ -13,19 +18,19 @@ module Roodi
|
|
13
18
|
@count = 0
|
14
19
|
@counting = 0
|
15
20
|
end
|
16
|
-
|
21
|
+
|
17
22
|
COMPLEXITY_NODE_TYPES.each do |type|
|
18
23
|
define_method "evaluate_start_#{type}" do |node|
|
19
24
|
@count = @count + 1 if counting?
|
20
25
|
end
|
21
26
|
end
|
22
|
-
|
27
|
+
|
23
28
|
protected
|
24
|
-
|
29
|
+
|
25
30
|
def count_complexity(node)
|
26
31
|
count_branches(node) + 1
|
27
32
|
end
|
28
|
-
|
33
|
+
|
29
34
|
def increase_depth
|
30
35
|
@count = 1 unless counting?
|
31
36
|
@counting = @counting + 1
|
@@ -38,9 +43,9 @@ module Roodi
|
|
38
43
|
evaluate_matching_end
|
39
44
|
end
|
40
45
|
end
|
41
|
-
|
46
|
+
|
42
47
|
private
|
43
|
-
|
48
|
+
|
44
49
|
def counting?
|
45
50
|
@counting > 0
|
46
51
|
end
|
@@ -3,12 +3,12 @@ require 'roodi/checks/name_check'
|
|
3
3
|
module Roodi
|
4
4
|
module Checks
|
5
5
|
# Checks a module name to make sure it matches the specified pattern.
|
6
|
-
#
|
6
|
+
#
|
7
7
|
# Keeping to a consistent nameing convention makes your code easier to read.
|
8
8
|
class ModuleNameCheck < NameCheck
|
9
9
|
|
10
10
|
DEFAULT_PATTERN = /^[A-Z][a-zA-Z0-9]*$/
|
11
|
-
|
11
|
+
|
12
12
|
def initialize
|
13
13
|
super()
|
14
14
|
self.pattern = DEFAULT_PATTERN
|
@@ -22,10 +22,6 @@ module Roodi
|
|
22
22
|
'Module'
|
23
23
|
end
|
24
24
|
|
25
|
-
def find_name(node)
|
26
|
-
node[1].class == Symbol ? node[1] : node[1].last
|
27
|
-
end
|
28
|
-
|
29
25
|
end
|
30
26
|
end
|
31
27
|
end
|
@@ -2,6 +2,7 @@ require 'roodi/checks/check'
|
|
2
2
|
|
3
3
|
module Roodi
|
4
4
|
module Checks
|
5
|
+
# Checking that a node's name matches a pattern
|
5
6
|
class NameCheck < Check
|
6
7
|
|
7
8
|
attr_accessor :pattern
|
@@ -11,6 +12,10 @@ module Roodi
|
|
11
12
|
add_error "#{message_prefix} name \"#{name}\" should match pattern #{@pattern.inspect}" unless name.to_s =~ @pattern
|
12
13
|
end
|
13
14
|
|
15
|
+
def find_name(node)
|
16
|
+
node[1].class == Symbol ? node[1] : node[1].last
|
17
|
+
end
|
18
|
+
|
14
19
|
end
|
15
20
|
end
|
16
21
|
end
|
@@ -2,6 +2,11 @@ require 'roodi/checks/check'
|
|
2
2
|
|
3
3
|
module Roodi
|
4
4
|
module Checks
|
5
|
+
# Checks NPATH complexity of a node
|
6
|
+
#
|
7
|
+
# NPATH counts acyclic execution paths in a piece of code.
|
8
|
+
# Here is an article by Brian A. Nejmeh that explains it in more detail:
|
9
|
+
# http://www.accessmylibrary.com/article-1G1-6242192/npath-measure-execution-path.html
|
5
10
|
class NpathComplexityCheck < Check
|
6
11
|
# , :when, :and, :or
|
7
12
|
MULTIPLYING_NODE_TYPES = [:if, :while, :until, :for, :case]
|
@@ -9,18 +14,18 @@ module Roodi
|
|
9
14
|
COMPLEXITY_NODE_TYPES = MULTIPLYING_NODE_TYPES + ADDING_NODE_TYPES
|
10
15
|
|
11
16
|
attr_accessor :complexity
|
12
|
-
|
17
|
+
|
13
18
|
def initialize(complexity)
|
14
19
|
super()
|
15
20
|
@complexity = complexity
|
16
21
|
@value_stack = []
|
17
22
|
@current_value = 1
|
18
23
|
end
|
19
|
-
|
24
|
+
|
20
25
|
def evalute_start_if(node)
|
21
26
|
push_value
|
22
27
|
end
|
23
|
-
|
28
|
+
|
24
29
|
def evalute_start_while(node)
|
25
30
|
push_value
|
26
31
|
end
|
@@ -40,7 +45,7 @@ module Roodi
|
|
40
45
|
def evalute_start_rescue(node)
|
41
46
|
push_value
|
42
47
|
end
|
43
|
-
|
48
|
+
|
44
49
|
MULTIPLYING_NODE_TYPES.each do |type|
|
45
50
|
define_method "evaluate_end_#{type}" do |node|
|
46
51
|
leave_multiplying_conditional
|
@@ -52,9 +57,9 @@ module Roodi
|
|
52
57
|
leave_multiplying_conditional
|
53
58
|
end
|
54
59
|
end
|
55
|
-
|
60
|
+
|
56
61
|
protected
|
57
|
-
|
62
|
+
|
58
63
|
def push_value
|
59
64
|
@value_stack.push @current_value
|
60
65
|
@current_value = 1
|
@@ -64,7 +69,7 @@ module Roodi
|
|
64
69
|
pop = @value_stack.pop
|
65
70
|
@current_value = (@current_value + 1) * pop
|
66
71
|
end
|
67
|
-
|
72
|
+
|
68
73
|
def leave_adding_conditional
|
69
74
|
pop = @value_stack.pop
|
70
75
|
puts "#{type}, so adding #{pop}"
|
@@ -2,15 +2,15 @@ require 'roodi/checks/npath_complexity_check'
|
|
2
2
|
|
3
3
|
module Roodi
|
4
4
|
module Checks
|
5
|
-
# Checks
|
5
|
+
# Checks NPATH complexity of a method against a specified limit.
|
6
6
|
class NpathComplexityMethodCheck < NpathComplexityCheck
|
7
7
|
|
8
8
|
DEFAULT_COMPLEXITY = 8
|
9
|
-
|
9
|
+
|
10
10
|
def initialize
|
11
11
|
super(DEFAULT_COMPLEXITY)
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
def interesting_nodes
|
15
15
|
[:defn] + COMPLEXITY_NODE_TYPES
|
16
16
|
end
|
data/lib/roodi/core/runner.rb
CHANGED
@@ -9,14 +9,14 @@ module Roodi
|
|
9
9
|
module Core
|
10
10
|
class Runner
|
11
11
|
DEFAULT_CONFIG = File.join(File.dirname(__FILE__), "..", "..", "..", "roodi.yml")
|
12
|
-
|
12
|
+
|
13
13
|
attr_writer :config
|
14
|
-
|
14
|
+
|
15
15
|
def initialize(*checks)
|
16
16
|
@config = DEFAULT_CONFIG
|
17
17
|
@checks = checks unless checks.empty?
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
def check(filename, content)
|
21
21
|
@checks ||= load_checks
|
22
22
|
@checker ||= CheckingVisitor.new(@checks)
|
@@ -29,34 +29,33 @@ module Roodi
|
|
29
29
|
def check_content(content, filename = "dummy-file.rb")
|
30
30
|
check(filename, content)
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
33
|
def check_file(filename)
|
34
34
|
return unless File.exists?(filename)
|
35
35
|
check(filename, File.read(filename))
|
36
36
|
end
|
37
|
-
|
37
|
+
|
38
38
|
def print(filename, content)
|
39
39
|
node = parse(filename, content)
|
40
|
-
puts "Line: #{node.line}"
|
41
40
|
pp node
|
42
41
|
end
|
43
42
|
|
44
43
|
def print_content(content)
|
45
44
|
print("dummy-file.rb", content)
|
46
45
|
end
|
47
|
-
|
46
|
+
|
48
47
|
def print_file(filename)
|
49
48
|
print(filename, File.read(filename))
|
50
49
|
end
|
51
|
-
|
50
|
+
|
52
51
|
def errors
|
53
52
|
@checks ||= []
|
54
53
|
all_errors = @checks.collect {|check| check.errors}
|
55
54
|
all_errors.flatten
|
56
55
|
end
|
57
|
-
|
56
|
+
|
58
57
|
private
|
59
|
-
|
58
|
+
|
60
59
|
def parse(filename, content)
|
61
60
|
begin
|
62
61
|
Parser.new.parse(content, filename)
|
data/lib/roodi/version.rb
CHANGED
data/roodi.gemspec
CHANGED
@@ -5,7 +5,7 @@ Gem::Specification.new do |gem|
|
|
5
5
|
|
6
6
|
gem.name = "roodi"
|
7
7
|
gem.summary = "Roodi stands for Ruby Object Oriented Design Inferometer"
|
8
|
-
gem.description = "Roodi
|
8
|
+
gem.description = "Roodi parses your Ruby code and warns you about design issues you have based on the checks that is has configured"
|
9
9
|
gem.homepage = "http://github.com/roodi/roodi"
|
10
10
|
gem.authors = ["Marty Andrews", "Peter Evjan"]
|
11
11
|
gem.email = "peter.evjan@gmail.com"
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: roodi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.
|
4
|
+
version: 3.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marty Andrews
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-08-
|
12
|
+
date: 2013-08-16 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ruby_parser
|
@@ -25,7 +25,8 @@ dependencies:
|
|
25
25
|
- - ~>
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
version: 3.2.2
|
28
|
-
description: Roodi
|
28
|
+
description: Roodi parses your Ruby code and warns you about design issues you have
|
29
|
+
based on the checks that is has configured
|
29
30
|
email: peter.evjan@gmail.com
|
30
31
|
executables:
|
31
32
|
- roodi
|