roodi 0.5
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.
- data/History.txt +27 -0
- data/Manifest.txt +32 -0
- data/README.txt +66 -0
- data/Rakefile +32 -0
- data/bin/roodi +21 -0
- data/bin/roodi-describe +7 -0
- data/lib/roodi.rb +6 -0
- data/lib/roodi/checks.rb +7 -0
- data/lib/roodi/checks/check.rb +26 -0
- data/lib/roodi/checks/class_name_check.rb +16 -0
- data/lib/roodi/checks/cyclomatic_complexity_block_check.rb +20 -0
- data/lib/roodi/checks/cyclomatic_complexity_check.rb +36 -0
- data/lib/roodi/checks/cyclomatic_complexity_method_check.rb +20 -0
- data/lib/roodi/checks/empty_rescue_body_check.rb +24 -0
- data/lib/roodi/checks/for_loop_check.rb +15 -0
- data/lib/roodi/checks/method_line_count_check.rb +30 -0
- data/lib/roodi/checks/method_name_check.rb +16 -0
- data/lib/roodi/core.rb +6 -0
- data/lib/roodi/core/checking_visitor.rb +24 -0
- data/lib/roodi/core/iterator_visitor.rb +19 -0
- data/lib/roodi/core/parse_tree_runner.rb +69 -0
- data/lib/roodi/core/parser.rb +26 -0
- data/lib/roodi/core/visitable_sexp.rb +24 -0
- data/roodi.yml +9 -0
- data/spec/checks/class_name_check_spec.rb +27 -0
- data/spec/checks/cyclomatic_complexity_block_check_spec.rb +36 -0
- data/spec/checks/cyclomatic_complexity_method_check_spec.rb +183 -0
- data/spec/checks/empty_rescue_body_check_spec.rb +57 -0
- data/spec/checks/for_loop_check_spec.rb +18 -0
- data/spec/checks/method_line_count_check_spec.rb +39 -0
- data/spec/checks/method_name_check_spec.rb +153 -0
- data/spec/spec_helper.rb +2 -0
- metadata +118 -0
data/History.txt
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
= 0.5
|
2
|
+
|
3
|
+
* expanded regex matching for method name check.
|
4
|
+
* suppressed noisy output from ParseTree using facets API.
|
5
|
+
* updated dependencies and version as a result of facets change.
|
6
|
+
* made Roodi tolerant of being asked to parse files which aren't really Ruby files.
|
7
|
+
* updated the documentation with usage examples.
|
8
|
+
|
9
|
+
= 0.4
|
10
|
+
|
11
|
+
* Added support back in for line numbers in error messages.
|
12
|
+
* Re-enabled MethodLineCountCheck as part of the default check set.
|
13
|
+
|
14
|
+
= 0.3
|
15
|
+
|
16
|
+
* First version of Roodi to be published to Rubyforge.
|
17
|
+
|
18
|
+
= 0.2
|
19
|
+
|
20
|
+
* Now use ParseTree instead of JRuby, which makes the tool much more accessible.
|
21
|
+
* Removed MagicNumberCheck
|
22
|
+
* Line numbers no longer supported as a result of the move.
|
23
|
+
|
24
|
+
= 0.1
|
25
|
+
|
26
|
+
* A first version of a design checking tool for Ruby, with a few checks built in to get started.
|
27
|
+
|
data/Manifest.txt
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.txt
|
4
|
+
Rakefile
|
5
|
+
bin/roodi
|
6
|
+
bin/roodi-describe
|
7
|
+
lib/roodi.rb
|
8
|
+
lib/roodi/checks.rb
|
9
|
+
lib/roodi/checks/check.rb
|
10
|
+
lib/roodi/checks/class_name_check.rb
|
11
|
+
lib/roodi/checks/cyclomatic_complexity_block_check.rb
|
12
|
+
lib/roodi/checks/cyclomatic_complexity_check.rb
|
13
|
+
lib/roodi/checks/cyclomatic_complexity_method_check.rb
|
14
|
+
lib/roodi/checks/empty_rescue_body_check.rb
|
15
|
+
lib/roodi/checks/for_loop_check.rb
|
16
|
+
lib/roodi/checks/method_line_count_check.rb
|
17
|
+
lib/roodi/checks/method_name_check.rb
|
18
|
+
lib/roodi/core.rb
|
19
|
+
lib/roodi/core/checking_visitor.rb
|
20
|
+
lib/roodi/core/iterator_visitor.rb
|
21
|
+
lib/roodi/core/parse_tree_runner.rb
|
22
|
+
lib/roodi/core/parser.rb
|
23
|
+
lib/roodi/core/visitable_sexp.rb
|
24
|
+
roodi.yml
|
25
|
+
spec/checks/class_name_check_spec.rb
|
26
|
+
spec/checks/cyclomatic_complexity_block_check_spec.rb
|
27
|
+
spec/checks/cyclomatic_complexity_method_check_spec.rb
|
28
|
+
spec/checks/empty_rescue_body_check_spec.rb
|
29
|
+
spec/checks/for_loop_check_spec.rb
|
30
|
+
spec/checks/method_line_count_check_spec.rb
|
31
|
+
spec/checks/method_name_check_spec.rb
|
32
|
+
spec/spec_helper.rb
|
data/README.txt
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
= roodi
|
2
|
+
|
3
|
+
* http://roodi.rubyforge.org
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
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
|
+
|
9
|
+
== INSTALL:
|
10
|
+
|
11
|
+
* sudo gem install roodi
|
12
|
+
|
13
|
+
== SYNOPSIS:
|
14
|
+
|
15
|
+
To check one or more files using the default configuration that comes with Roodi, use:
|
16
|
+
roodi [patterns]
|
17
|
+
|
18
|
+
=== EXAMPLE USAGE
|
19
|
+
|
20
|
+
Check all ruby files in a rails app:
|
21
|
+
roodi "rails_app/**/*.rb"
|
22
|
+
|
23
|
+
Check one controller and one model file in a rails app:
|
24
|
+
roodi app/controller/sample_controller.rb app/models/sample.rb
|
25
|
+
|
26
|
+
Check one controller and all model files in a rails app:
|
27
|
+
roodi app/controller/sample_controller.rb "app/models/*.rb"
|
28
|
+
|
29
|
+
|
30
|
+
If you're writing a check, it is useful to see the structure of a file the way that Roodi tokenizes it (via ParesTree). Use:
|
31
|
+
roodi-describe [filename]
|
32
|
+
|
33
|
+
== SUPPORTED CHECKS:
|
34
|
+
|
35
|
+
* ClassNameCheck - Check that class names match convention.
|
36
|
+
* CyclomaticComplexityBlockCheck - Check that the cyclomatic complexity of all blocks is below the threshold.
|
37
|
+
* CyclomaticComplexityMethodCheck - Check that the cyclomatic complexity of all methods is below the threshold.
|
38
|
+
* EmptyRescueBodyCheck - Check that there are no empty rescue blocks.
|
39
|
+
* ForLoopCheck - Check that for loops aren't used (Use Enumerable.each instead)
|
40
|
+
* MethodNameCheck - Check that method names match convention.
|
41
|
+
* MethodLineCountCheck - Check that the number of lines in a method is below the threshold.
|
42
|
+
|
43
|
+
== LICENSE:
|
44
|
+
|
45
|
+
(The MIT License)
|
46
|
+
|
47
|
+
Copyright (c) 2008 Marty Andrews
|
48
|
+
|
49
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
50
|
+
a copy of this software and associated documentation files (the
|
51
|
+
'Software'), to deal in the Software without restriction, including
|
52
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
53
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
54
|
+
permit persons to whom the Software is furnished to do so, subject to
|
55
|
+
the following conditions:
|
56
|
+
|
57
|
+
The above copyright notice and this permission notice shall be
|
58
|
+
included in all copies or substantial portions of the Software.
|
59
|
+
|
60
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
61
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
62
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
63
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
64
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
65
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
66
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
$:.unshift(File.join(File.dirname(__FILE__), 'lib'))
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
require 'rake'
|
6
|
+
require 'spec/rake/spectask'
|
7
|
+
require 'roodi'
|
8
|
+
|
9
|
+
Hoe.new('roodi', Roodi::VERSION) do |p|
|
10
|
+
p.developer('Marty Andrews', 'marty@cogentconsulting.com.au')
|
11
|
+
p.extra_deps = ['ParseTree', 'facets']
|
12
|
+
p.remote_rdoc_dir = ''
|
13
|
+
end
|
14
|
+
|
15
|
+
def roodi(ruby_files)
|
16
|
+
roodi = Roodi::Core::ParseTreeRunner.new
|
17
|
+
ruby_files.each { |file| roodi.check_file(file) }
|
18
|
+
roodi.errors.each {|error| puts error}
|
19
|
+
puts "\nFound #{roodi.errors.size} errors."
|
20
|
+
end
|
21
|
+
|
22
|
+
desc "Run all specs"
|
23
|
+
Spec::Rake::SpecTask.new('spec') do |t|
|
24
|
+
t.spec_files = FileList['spec/**/*spec.rb']
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "Run Roodi against all source files"
|
28
|
+
task :roodi do
|
29
|
+
pattern = File.join(File.dirname(__FILE__), "**", "*.rb")
|
30
|
+
roodi(Dir.glob(pattern))
|
31
|
+
end
|
32
|
+
|
data/bin/roodi
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
|
4
|
+
|
5
|
+
require 'roodi'
|
6
|
+
|
7
|
+
runner = Roodi::Core::ParseTreeRunner.new
|
8
|
+
|
9
|
+
config_param = ARGV.detect {|arg| arg =~ /-config=.*/}
|
10
|
+
runner.config = config_param.split("=")[1] if config_param
|
11
|
+
ARGV.delete config_param
|
12
|
+
|
13
|
+
ARGV.each do |arg|
|
14
|
+
Dir.glob(arg).each { |file| runner.check_file(file) }
|
15
|
+
end
|
16
|
+
|
17
|
+
runner.errors.each {|error| puts error}
|
18
|
+
|
19
|
+
puts "\nFound #{runner.errors.size} errors."
|
20
|
+
|
21
|
+
exit runner.errors.size
|
data/bin/roodi-describe
ADDED
data/lib/roodi.rb
ADDED
data/lib/roodi/checks.rb
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
require 'roodi/checks/class_name_check'
|
2
|
+
require 'roodi/checks/cyclomatic_complexity_block_check'
|
3
|
+
require 'roodi/checks/cyclomatic_complexity_method_check'
|
4
|
+
require 'roodi/checks/empty_rescue_body_check'
|
5
|
+
require 'roodi/checks/for_loop_check'
|
6
|
+
require 'roodi/checks/method_name_check'
|
7
|
+
require 'roodi/checks/method_line_count_check'
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Roodi
|
2
|
+
module Checks
|
3
|
+
class Check
|
4
|
+
def initialize
|
5
|
+
@errors = []
|
6
|
+
end
|
7
|
+
|
8
|
+
def position(offset = 0)
|
9
|
+
"#{@line[2]}:#{@line[1] + offset}"
|
10
|
+
end
|
11
|
+
|
12
|
+
def evaluate_node_at_line(node, line)
|
13
|
+
@line = line
|
14
|
+
evaluate(node)
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_error(error, offset = 0)
|
18
|
+
@errors << "#{position(offset)} - #{error}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def errors
|
22
|
+
@errors
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'roodi/checks/check'
|
2
|
+
|
3
|
+
module Roodi
|
4
|
+
module Checks
|
5
|
+
class ClassNameCheck < Check
|
6
|
+
def interesting_nodes
|
7
|
+
[:class]
|
8
|
+
end
|
9
|
+
|
10
|
+
def evaluate(node)
|
11
|
+
pattern = /^[A-Z][a-zA-Z0-9]*$/
|
12
|
+
add_error "Class name \"#{node[1]}\" should match pattern #{pattern.inspect}" unless node[1].to_s =~ pattern
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'roodi/checks/cyclomatic_complexity_check'
|
2
|
+
|
3
|
+
module Roodi
|
4
|
+
module Checks
|
5
|
+
class CyclomaticComplexityBlockCheck < CyclomaticComplexityCheck
|
6
|
+
def initialize(complexity = 4)
|
7
|
+
super(complexity)
|
8
|
+
end
|
9
|
+
|
10
|
+
def interesting_nodes
|
11
|
+
[:iter]
|
12
|
+
end
|
13
|
+
|
14
|
+
def evaluate(node)
|
15
|
+
complexity = count_complexity(node)
|
16
|
+
add_error "Block cyclomatic complexity is #{complexity}. It should be #{@complexity} or less." unless complexity <= @complexity
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'roodi/checks/check'
|
2
|
+
|
3
|
+
module Roodi
|
4
|
+
module Checks
|
5
|
+
# Checks cyclomatic complexity against a specified limit. The complexity is
|
6
|
+
# measured by the number of "if", "unless", "elsif", "?:", "while", "until",
|
7
|
+
# "for", "rescue", "case", "when", "&&", "and", "||" and "or" statements (plus
|
8
|
+
# one) in the body of the member. It is a measure of the minimum number of
|
9
|
+
# possible paths through the source and therefore the number of required tests.
|
10
|
+
# Generally 1-4 is considered good, 5-7 ok, 8-10 consider re-factoring, and
|
11
|
+
# 11+ re-factor now!
|
12
|
+
class CyclomaticComplexityCheck < Check
|
13
|
+
def initialize(complexity = 8)
|
14
|
+
super()
|
15
|
+
@complexity = complexity
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
def count_complexity(node)
|
21
|
+
count_branches(node) + 1
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def count_branches(node)
|
27
|
+
complexity_node_types = [:if, :while, :until, :for, :rescue, :case, :when, :and, :or]
|
28
|
+
|
29
|
+
count = 0
|
30
|
+
count = count + 1 if complexity_node_types.include? node.node_type
|
31
|
+
node.children.each {|node| count += count_branches(node)}
|
32
|
+
count
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'roodi/checks/cyclomatic_complexity_check'
|
2
|
+
|
3
|
+
module Roodi
|
4
|
+
module Checks
|
5
|
+
class CyclomaticComplexityMethodCheck < CyclomaticComplexityCheck
|
6
|
+
def initialize(complexity = 8)
|
7
|
+
super(complexity)
|
8
|
+
end
|
9
|
+
|
10
|
+
def interesting_nodes
|
11
|
+
[:defn]
|
12
|
+
end
|
13
|
+
|
14
|
+
def evaluate(node)
|
15
|
+
complexity = count_complexity(node)
|
16
|
+
add_error "Method name \"#{node[1]}\" has a cyclomatic complexity is #{complexity}. It should be #{@complexity} or less." unless complexity <= @complexity
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'roodi/checks/check'
|
2
|
+
|
3
|
+
module Roodi
|
4
|
+
module Checks
|
5
|
+
class EmptyRescueBodyCheck < Check
|
6
|
+
def interesting_nodes
|
7
|
+
[:resbody]
|
8
|
+
end
|
9
|
+
|
10
|
+
def evaluate(node)
|
11
|
+
add_error("Rescue block should not be empty.", 1) unless has_statement?(node)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def has_statement?(node)
|
17
|
+
found_statement = false
|
18
|
+
found_statement = found_statement || node.node_type == :fcall
|
19
|
+
node.children.each { |child| found_statement = found_statement || has_statement?(child) }
|
20
|
+
found_statement
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'roodi/checks/check'
|
2
|
+
|
3
|
+
module Roodi
|
4
|
+
module Checks
|
5
|
+
class MethodLineCountCheck < Check
|
6
|
+
def initialize(line_count = 20)
|
7
|
+
super()
|
8
|
+
@line_count = line_count
|
9
|
+
end
|
10
|
+
|
11
|
+
def interesting_nodes
|
12
|
+
[:defn]
|
13
|
+
end
|
14
|
+
|
15
|
+
def evaluate(node)
|
16
|
+
line_count = count_lines(node)
|
17
|
+
add_error "Method name \"#{node[1]}\" has #{line_count} lines. It should have #{@line_count} or less." unless line_count <= @line_count
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def count_lines(node)
|
23
|
+
count = 0
|
24
|
+
count = count + 1 if node.node_type == :newline
|
25
|
+
node.children.each {|node| count += count_lines(node)}
|
26
|
+
count
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'roodi/checks/check'
|
2
|
+
|
3
|
+
module Roodi
|
4
|
+
module Checks
|
5
|
+
class MethodNameCheck < Check
|
6
|
+
def interesting_nodes
|
7
|
+
[:defn]
|
8
|
+
end
|
9
|
+
|
10
|
+
def evaluate(node)
|
11
|
+
pattern = /^[_a-z<>=\[\]|+-\/\*`]+[_a-z0-9_<>=~@\[\]]*[=!\?]?$/
|
12
|
+
add_error "Method name \"#{node[1]}\" should match pattern #{pattern.inspect}" unless node[1].to_s =~ pattern
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/roodi/core.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
module Roodi
|
2
|
+
module Core
|
3
|
+
class CheckingVisitor
|
4
|
+
def initialize(*checks)
|
5
|
+
@checks ||= {}
|
6
|
+
checks.first.each do |check|
|
7
|
+
nodes = check.interesting_nodes
|
8
|
+
nodes.each do |node|
|
9
|
+
@checks[node] ||= []
|
10
|
+
@checks[node] << check
|
11
|
+
@checks[node].uniq!
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def visit(node)
|
17
|
+
@last_newline = node if node.node_type == :newline
|
18
|
+
checks = @checks[node.node_type]
|
19
|
+
checks.each {|check| check.evaluate_node_at_line(node, @last_newline)} unless checks.nil?
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Roodi
|
2
|
+
module Core
|
3
|
+
class IteratorVisitor
|
4
|
+
def initialize(payload)
|
5
|
+
@payload = payload
|
6
|
+
end
|
7
|
+
|
8
|
+
def visit(visited)
|
9
|
+
visited.accept(@payload)
|
10
|
+
visitable_nodes = visited.is_language_node? ? visited.sexp_body : visited
|
11
|
+
visitable_nodes.each do |child|
|
12
|
+
if child.class == VisitableSexp then
|
13
|
+
child.accept(self)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
require 'roodi/core/checking_visitor'
|
5
|
+
require 'roodi/core/iterator_visitor'
|
6
|
+
require 'roodi/core/parser'
|
7
|
+
require 'roodi/core/visitable_sexp'
|
8
|
+
|
9
|
+
module Roodi
|
10
|
+
module Core
|
11
|
+
class ParseTreeRunner
|
12
|
+
DEFAULT_CONFIG = File.join(File.dirname(__FILE__), "..", "..", "..", "roodi.yml")
|
13
|
+
|
14
|
+
def initialize(*checks)
|
15
|
+
@config = DEFAULT_CONFIG
|
16
|
+
@checks = checks unless checks.empty?
|
17
|
+
@parser = Parser.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def check(filename, content)
|
21
|
+
@checks ||= load_checks
|
22
|
+
begin
|
23
|
+
node = @parser.parse(content, filename)
|
24
|
+
node.accept(IteratorVisitor.new(CheckingVisitor.new(@checks)))
|
25
|
+
rescue Exception => e
|
26
|
+
# puts e
|
27
|
+
puts "#{filename} looks like it's not a valid Ruby file. Skipping..."
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def check_content(content)
|
32
|
+
check("dummy-file.rb", content)
|
33
|
+
end
|
34
|
+
|
35
|
+
def check_file(filename)
|
36
|
+
check(filename, File.read(filename))
|
37
|
+
end
|
38
|
+
|
39
|
+
def print(filename, content)
|
40
|
+
node = @parser.parse(content, filename)
|
41
|
+
pp node
|
42
|
+
end
|
43
|
+
|
44
|
+
def print_content(content)
|
45
|
+
print("dummy-file.rb", content)
|
46
|
+
end
|
47
|
+
|
48
|
+
def print_file(filename)
|
49
|
+
print(filename, File.read(filename))
|
50
|
+
end
|
51
|
+
|
52
|
+
def errors
|
53
|
+
@checks ||= []
|
54
|
+
all_errors = @checks.collect {|check| check.errors}
|
55
|
+
all_errors.flatten
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def load_checks
|
61
|
+
check_objects = []
|
62
|
+
check_config = YAML.load_file @config
|
63
|
+
checks = check_config["checks"]
|
64
|
+
checks.each { |check| check_objects << eval("Roodi::Checks::#{check['name']}.new") }
|
65
|
+
check_objects
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'parse_tree'
|
3
|
+
require 'facets'
|
4
|
+
|
5
|
+
|
6
|
+
module Roodi
|
7
|
+
module Core
|
8
|
+
class Parser
|
9
|
+
def parse(content, filename)
|
10
|
+
silence_stream(STDERR) do
|
11
|
+
return silent_parse(content, filename)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def silent_parse(content, filename)
|
18
|
+
@parser ||= ParseTree.new(true)
|
19
|
+
node = @parser.parse_tree_for_string(content, filename)
|
20
|
+
sexp = VisitableSexp.from_array node
|
21
|
+
sexp.filename = filename
|
22
|
+
sexp
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'sexp'
|
3
|
+
|
4
|
+
module Roodi
|
5
|
+
module Core
|
6
|
+
class VisitableSexp < Sexp
|
7
|
+
def accept(visitor)
|
8
|
+
visitor.visit(self)
|
9
|
+
end
|
10
|
+
|
11
|
+
def node_type
|
12
|
+
first
|
13
|
+
end
|
14
|
+
|
15
|
+
def children
|
16
|
+
sexp_body.select {|each| each.class == VisitableSexp }
|
17
|
+
end
|
18
|
+
|
19
|
+
def is_language_node?
|
20
|
+
first.class == Symbol
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/roodi.yml
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
checks:
|
2
|
+
- { name: ClassNameCheck }
|
3
|
+
- { name: CyclomaticComplexityBlockCheck, complexity: 2 }
|
4
|
+
- { name: CyclomaticComplexityMethodCheck, complexity: 5 }
|
5
|
+
- { name: EmptyRescueBodyCheck }
|
6
|
+
- { name: ForLoopCheck }
|
7
|
+
- { name: MethodLineCountCheck }
|
8
|
+
- { name: MethodNameCheck }
|
9
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Roodi::Checks::ClassNameCheck do
|
4
|
+
before(:each) do
|
5
|
+
@roodi = Roodi::Core::ParseTreeRunner.new(Roodi::Checks::ClassNameCheck.new)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should accept camel case class names starting in capitals" do
|
9
|
+
content = <<-END
|
10
|
+
class GoodClassName
|
11
|
+
end
|
12
|
+
END
|
13
|
+
@roodi.check_content(content)
|
14
|
+
@roodi.errors.should be_empty
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should reject class names with underscores" do
|
18
|
+
content = <<-END
|
19
|
+
class Bad_ClassName
|
20
|
+
end
|
21
|
+
END
|
22
|
+
@roodi.check_content(content)
|
23
|
+
errors = @roodi.errors
|
24
|
+
errors.should_not be_empty
|
25
|
+
errors[0].should eql("dummy-file.rb:1 - Class name \"Bad_ClassName\" should match pattern /^[A-Z][a-zA-Z0-9]*$/")
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Roodi::Checks::CyclomaticComplexityBlockCheck do
|
4
|
+
before(:each) do
|
5
|
+
@roodi = Roodi::Core::ParseTreeRunner.new(Roodi::Checks::CyclomaticComplexityBlockCheck.new(0))
|
6
|
+
end
|
7
|
+
|
8
|
+
def verify_content_complexity(content, complexity)
|
9
|
+
@roodi.check_content(content)
|
10
|
+
errors = @roodi.errors
|
11
|
+
errors.should_not be_empty
|
12
|
+
errors[0].should eql("dummy-file.rb:2 - Block cyclomatic complexity is #{complexity}. It should be 0 or less.")
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should find a simple block" do
|
16
|
+
content = <<-END
|
17
|
+
def method_name
|
18
|
+
it "should be a simple block" do
|
19
|
+
call_foo
|
20
|
+
end
|
21
|
+
end
|
22
|
+
END
|
23
|
+
verify_content_complexity(content, 1)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should find a block with multiple paths" do
|
27
|
+
content = <<-END
|
28
|
+
def method_name
|
29
|
+
it "should be a complex block" do
|
30
|
+
call_foo if some_condition
|
31
|
+
end
|
32
|
+
end
|
33
|
+
END
|
34
|
+
verify_content_complexity(content, 2)
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,183 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Roodi::Checks::CyclomaticComplexityMethodCheck do
|
4
|
+
before(:each) do
|
5
|
+
@roodi = Roodi::Core::ParseTreeRunner.new(Roodi::Checks::CyclomaticComplexityMethodCheck.new(0))
|
6
|
+
end
|
7
|
+
|
8
|
+
def verify_content_complexity(content, complexity)
|
9
|
+
@roodi.check_content(content)
|
10
|
+
errors = @roodi.errors
|
11
|
+
errors.should_not be_empty
|
12
|
+
errors[0].should eql("dummy-file.rb:1 - Method name \"method_name\" has a cyclomatic complexity is #{complexity}. It should be 0 or less.")
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should find an if block" do
|
16
|
+
content = <<-END
|
17
|
+
def method_name
|
18
|
+
call_foo if some_condition
|
19
|
+
end
|
20
|
+
END
|
21
|
+
verify_content_complexity(content, 2)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should find an unless block" do
|
25
|
+
content = <<-END
|
26
|
+
def method_name
|
27
|
+
call_foo unless some_condition
|
28
|
+
end
|
29
|
+
END
|
30
|
+
verify_content_complexity(content, 2)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should find an elsif block" do
|
34
|
+
content = <<-END
|
35
|
+
def method_name
|
36
|
+
if first_condition then
|
37
|
+
call_foo
|
38
|
+
elsif second_condition then
|
39
|
+
call_bar
|
40
|
+
else
|
41
|
+
call_bam
|
42
|
+
end
|
43
|
+
end
|
44
|
+
END
|
45
|
+
verify_content_complexity(content, 3)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should find a ternary operator" do
|
49
|
+
content = <<-END
|
50
|
+
def method_name
|
51
|
+
value = some_condition ? 1 : 2
|
52
|
+
end
|
53
|
+
END
|
54
|
+
verify_content_complexity(content, 2)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should find a while loop" do
|
58
|
+
content = <<-END
|
59
|
+
def method_name
|
60
|
+
while some_condition do
|
61
|
+
call_foo
|
62
|
+
end
|
63
|
+
end
|
64
|
+
END
|
65
|
+
verify_content_complexity(content, 2)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should find an until loop" do
|
69
|
+
content = <<-END
|
70
|
+
def method_name
|
71
|
+
until some_condition do
|
72
|
+
call_foo
|
73
|
+
end
|
74
|
+
end
|
75
|
+
END
|
76
|
+
verify_content_complexity(content, 2)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should find a for loop" do
|
80
|
+
content = <<-END
|
81
|
+
def method_name
|
82
|
+
for i in 1..2 do
|
83
|
+
call_method
|
84
|
+
end
|
85
|
+
end
|
86
|
+
END
|
87
|
+
verify_content_complexity(content, 2)
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should find a rescue block" do
|
91
|
+
content = <<-END
|
92
|
+
def method_name
|
93
|
+
begin
|
94
|
+
call_foo
|
95
|
+
rescue Exception
|
96
|
+
call_bar
|
97
|
+
end
|
98
|
+
end
|
99
|
+
END
|
100
|
+
verify_content_complexity(content, 2)
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should find a case and when block" do
|
104
|
+
content = <<-END
|
105
|
+
def method_name
|
106
|
+
case value
|
107
|
+
when 1
|
108
|
+
call_foo
|
109
|
+
when 2
|
110
|
+
call_bar
|
111
|
+
end
|
112
|
+
end
|
113
|
+
END
|
114
|
+
verify_content_complexity(content, 4)
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should find the && symbol" do
|
118
|
+
content = <<-END
|
119
|
+
def method_name
|
120
|
+
call_foo && call_bar
|
121
|
+
end
|
122
|
+
END
|
123
|
+
verify_content_complexity(content, 2)
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should find the and symbol" do
|
127
|
+
content = <<-END
|
128
|
+
def method_name
|
129
|
+
call_foo and call_bar
|
130
|
+
end
|
131
|
+
END
|
132
|
+
verify_content_complexity(content, 2)
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should find the || symbol" do
|
136
|
+
content = <<-END
|
137
|
+
def method_name
|
138
|
+
call_foo || call_bar
|
139
|
+
end
|
140
|
+
END
|
141
|
+
verify_content_complexity(content, 2)
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should find the or symbol" do
|
145
|
+
content = <<-END
|
146
|
+
def method_name
|
147
|
+
call_foo or call_bar
|
148
|
+
end
|
149
|
+
END
|
150
|
+
verify_content_complexity(content, 2)
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should deal with nested if blocks containing && and ||" do
|
154
|
+
content = <<-END
|
155
|
+
def method_name
|
156
|
+
if first_condition then
|
157
|
+
call_foo if second_condition && third_condition
|
158
|
+
call_bar if fourth_condition || fifth_condition
|
159
|
+
end
|
160
|
+
end
|
161
|
+
END
|
162
|
+
verify_content_complexity(content, 6)
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should count stupid nested if and else blocks" do
|
166
|
+
content = <<-END
|
167
|
+
def method_name
|
168
|
+
if first_condition then
|
169
|
+
call_foo
|
170
|
+
else
|
171
|
+
if second_condition then
|
172
|
+
call_bar
|
173
|
+
else
|
174
|
+
call_bam if third_condition
|
175
|
+
end
|
176
|
+
call_baz if fourth_condition
|
177
|
+
end
|
178
|
+
end
|
179
|
+
END
|
180
|
+
verify_content_complexity(content, 5)
|
181
|
+
end
|
182
|
+
|
183
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Roodi::Checks::EmptyRescueBodyCheck do
|
4
|
+
before(:each) do
|
5
|
+
@roodi = Roodi::Core::ParseTreeRunner.new(Roodi::Checks::EmptyRescueBodyCheck.new)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should accept a rescue body with content and no parameter" do
|
9
|
+
content = <<-END
|
10
|
+
begin
|
11
|
+
call_method
|
12
|
+
rescue
|
13
|
+
puts "Recover from the call"
|
14
|
+
end
|
15
|
+
END
|
16
|
+
@roodi.check_content(content)
|
17
|
+
@roodi.errors.should be_empty
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should accept a rescue body with content and a parameter" do
|
21
|
+
content = <<-END
|
22
|
+
begin
|
23
|
+
call_method
|
24
|
+
rescue Exception => e
|
25
|
+
puts "Recover from the call"
|
26
|
+
end
|
27
|
+
END
|
28
|
+
@roodi.check_content(content)
|
29
|
+
@roodi.errors.should be_empty
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should reject an empty rescue block with no parameter" do
|
33
|
+
content = <<-END
|
34
|
+
begin
|
35
|
+
call_method
|
36
|
+
rescue
|
37
|
+
end
|
38
|
+
END
|
39
|
+
@roodi.check_content(content)
|
40
|
+
errors = @roodi.errors
|
41
|
+
errors.should_not be_empty
|
42
|
+
errors[0].should eql("dummy-file.rb:3 - Rescue block should not be empty.")
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should reject an empty rescue block with a parameter" do
|
46
|
+
content = <<-END
|
47
|
+
begin
|
48
|
+
call_method
|
49
|
+
rescue Exception => e
|
50
|
+
end
|
51
|
+
END
|
52
|
+
@roodi.check_content(content)
|
53
|
+
errors = @roodi.errors
|
54
|
+
errors.should_not be_empty
|
55
|
+
errors[0].should eql("dummy-file.rb:3 - Rescue block should not be empty.")
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Roodi::Checks::ForLoopCheck do
|
4
|
+
before(:each) do
|
5
|
+
@roodi = Roodi::Core::ParseTreeRunner.new(Roodi::Checks::ForLoopCheck.new)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should reject for loops" do
|
9
|
+
content = <<-END
|
10
|
+
for i in 1..2
|
11
|
+
end
|
12
|
+
END
|
13
|
+
@roodi.check_content(content)
|
14
|
+
errors = @roodi.errors
|
15
|
+
errors.should_not be_empty
|
16
|
+
errors[0].should eql("dummy-file.rb:1 - Don't use 'for' loops. Use Enumerable.each instead.")
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Roodi::Checks::MethodLineCountCheck do
|
4
|
+
before(:each) do
|
5
|
+
@roodi = Roodi::Core::ParseTreeRunner.new(Roodi::Checks::MethodLineCountCheck.new(1))
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should accept methods with less lines than the threshold" do
|
9
|
+
content = <<-END
|
10
|
+
def zero_line_method
|
11
|
+
end
|
12
|
+
END
|
13
|
+
@roodi.check_content(content)
|
14
|
+
@roodi.errors.should be_empty
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should accept methods with the same number of lines as the threshold" do
|
18
|
+
content = <<-END
|
19
|
+
def one_line_method
|
20
|
+
1
|
21
|
+
end
|
22
|
+
END
|
23
|
+
@roodi.check_content(content)
|
24
|
+
@roodi.errors.should be_empty
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should reject methods with more lines than the threshold" do
|
28
|
+
content = <<-END
|
29
|
+
def two_line_method
|
30
|
+
puts 1
|
31
|
+
puts 2
|
32
|
+
end
|
33
|
+
END
|
34
|
+
@roodi.check_content(content)
|
35
|
+
errors = @roodi.errors
|
36
|
+
errors.should_not be_empty
|
37
|
+
errors[0].should eql("dummy-file.rb:1 - Method name \"two_line_method\" has 2 lines. It should have 1 or less.")
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Roodi::Checks::MethodNameCheck do
|
4
|
+
before(:each) do
|
5
|
+
@roodi = Roodi::Core::ParseTreeRunner.new(Roodi::Checks::MethodNameCheck.new)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should accept method names with underscores" do
|
9
|
+
content = <<-END
|
10
|
+
def good_method_name
|
11
|
+
end
|
12
|
+
END
|
13
|
+
@roodi.check_content(content)
|
14
|
+
@roodi.errors.should be_empty
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should accept method names with numbers" do
|
18
|
+
content = <<-END
|
19
|
+
def good_method_1_name
|
20
|
+
end
|
21
|
+
END
|
22
|
+
@roodi.check_content(content)
|
23
|
+
@roodi.errors.should be_empty
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should accept method names ending a question mark" do
|
27
|
+
content = <<-END
|
28
|
+
def good_method_name?
|
29
|
+
end
|
30
|
+
END
|
31
|
+
@roodi.check_content(content)
|
32
|
+
@roodi.errors.should be_empty
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should accept method names ending an exclamation mark" do
|
36
|
+
content = <<-END
|
37
|
+
def good_method_name!
|
38
|
+
end
|
39
|
+
END
|
40
|
+
@roodi.check_content(content)
|
41
|
+
@roodi.errors.should be_empty
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should accept method names ending an equals sign" do
|
45
|
+
content = <<-END
|
46
|
+
def good_method_name=
|
47
|
+
end
|
48
|
+
END
|
49
|
+
@roodi.check_content(content)
|
50
|
+
@roodi.errors.should be_empty
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should accept << as a method name" do
|
54
|
+
content = <<-END
|
55
|
+
def <<
|
56
|
+
end
|
57
|
+
END
|
58
|
+
@roodi.check_content(content)
|
59
|
+
@roodi.errors.should be_empty
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should accept >> as a method name" do
|
63
|
+
content = <<-END
|
64
|
+
def >>
|
65
|
+
end
|
66
|
+
END
|
67
|
+
@roodi.check_content(content)
|
68
|
+
@roodi.errors.should be_empty
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should accept == as a method name" do
|
72
|
+
content = <<-END
|
73
|
+
def ==
|
74
|
+
end
|
75
|
+
END
|
76
|
+
@roodi.check_content(content)
|
77
|
+
@roodi.errors.should be_empty
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should accept === as a method name" do
|
81
|
+
content = <<-END
|
82
|
+
def ===
|
83
|
+
end
|
84
|
+
END
|
85
|
+
@roodi.check_content(content)
|
86
|
+
@roodi.errors.should be_empty
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should accept < as a method name" do
|
90
|
+
content = <<-END
|
91
|
+
def <
|
92
|
+
end
|
93
|
+
END
|
94
|
+
@roodi.check_content(content)
|
95
|
+
@roodi.errors.should be_empty
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should accept <= as a method name" do
|
99
|
+
content = <<-END
|
100
|
+
def <=
|
101
|
+
end
|
102
|
+
END
|
103
|
+
@roodi.check_content(content)
|
104
|
+
@roodi.errors.should be_empty
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should accept > as a method name" do
|
108
|
+
content = <<-END
|
109
|
+
def >
|
110
|
+
end
|
111
|
+
END
|
112
|
+
@roodi.check_content(content)
|
113
|
+
@roodi.errors.should be_empty
|
114
|
+
end
|
115
|
+
|
116
|
+
it "should accept >= as a method name" do
|
117
|
+
content = <<-END
|
118
|
+
def >=
|
119
|
+
end
|
120
|
+
END
|
121
|
+
@roodi.check_content(content)
|
122
|
+
@roodi.errors.should be_empty
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should accept [] as a method name" do
|
126
|
+
content = <<-END
|
127
|
+
def []
|
128
|
+
end
|
129
|
+
END
|
130
|
+
@roodi.check_content(content)
|
131
|
+
@roodi.errors.should be_empty
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should accept []= as a method name" do
|
135
|
+
content = <<-END
|
136
|
+
def []=
|
137
|
+
end
|
138
|
+
END
|
139
|
+
@roodi.check_content(content)
|
140
|
+
@roodi.errors.should be_empty
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should reject camel case method names" do
|
144
|
+
content = <<-END
|
145
|
+
def badMethodName
|
146
|
+
end
|
147
|
+
END
|
148
|
+
@roodi.check_content(content)
|
149
|
+
errors = @roodi.errors
|
150
|
+
errors.should_not be_empty
|
151
|
+
errors[0].should eql("dummy-file.rb:1 - Method name \"badMethodName\" should match pattern /^[_a-z<>=\\[\\]|+-\\/\\*`]+[_a-z0-9_<>=~@\\[\\]]*[=!\\?]?$/")
|
152
|
+
end
|
153
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: roodi
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "0.5"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Marty Andrews
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-09-10 00:00:00 +10:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: ParseTree
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: facets
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: hoe
|
37
|
+
type: :development
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 1.7.0
|
44
|
+
version:
|
45
|
+
description: 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.
|
46
|
+
email:
|
47
|
+
- marty@cogentconsulting.com.au
|
48
|
+
executables:
|
49
|
+
- roodi
|
50
|
+
- roodi-describe
|
51
|
+
extensions: []
|
52
|
+
|
53
|
+
extra_rdoc_files:
|
54
|
+
- History.txt
|
55
|
+
- Manifest.txt
|
56
|
+
- README.txt
|
57
|
+
files:
|
58
|
+
- History.txt
|
59
|
+
- Manifest.txt
|
60
|
+
- README.txt
|
61
|
+
- Rakefile
|
62
|
+
- bin/roodi
|
63
|
+
- bin/roodi-describe
|
64
|
+
- lib/roodi.rb
|
65
|
+
- lib/roodi/checks.rb
|
66
|
+
- lib/roodi/checks/check.rb
|
67
|
+
- lib/roodi/checks/class_name_check.rb
|
68
|
+
- lib/roodi/checks/cyclomatic_complexity_block_check.rb
|
69
|
+
- lib/roodi/checks/cyclomatic_complexity_check.rb
|
70
|
+
- lib/roodi/checks/cyclomatic_complexity_method_check.rb
|
71
|
+
- lib/roodi/checks/empty_rescue_body_check.rb
|
72
|
+
- lib/roodi/checks/for_loop_check.rb
|
73
|
+
- lib/roodi/checks/method_line_count_check.rb
|
74
|
+
- lib/roodi/checks/method_name_check.rb
|
75
|
+
- lib/roodi/core.rb
|
76
|
+
- lib/roodi/core/checking_visitor.rb
|
77
|
+
- lib/roodi/core/iterator_visitor.rb
|
78
|
+
- lib/roodi/core/parse_tree_runner.rb
|
79
|
+
- lib/roodi/core/parser.rb
|
80
|
+
- lib/roodi/core/visitable_sexp.rb
|
81
|
+
- roodi.yml
|
82
|
+
- spec/checks/class_name_check_spec.rb
|
83
|
+
- spec/checks/cyclomatic_complexity_block_check_spec.rb
|
84
|
+
- spec/checks/cyclomatic_complexity_method_check_spec.rb
|
85
|
+
- spec/checks/empty_rescue_body_check_spec.rb
|
86
|
+
- spec/checks/for_loop_check_spec.rb
|
87
|
+
- spec/checks/method_line_count_check_spec.rb
|
88
|
+
- spec/checks/method_name_check_spec.rb
|
89
|
+
- spec/spec_helper.rb
|
90
|
+
has_rdoc: true
|
91
|
+
homepage: http://roodi.rubyforge.org
|
92
|
+
post_install_message:
|
93
|
+
rdoc_options:
|
94
|
+
- --main
|
95
|
+
- README.txt
|
96
|
+
require_paths:
|
97
|
+
- lib
|
98
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: "0"
|
103
|
+
version:
|
104
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: "0"
|
109
|
+
version:
|
110
|
+
requirements: []
|
111
|
+
|
112
|
+
rubyforge_project: roodi
|
113
|
+
rubygems_version: 1.2.0
|
114
|
+
signing_key:
|
115
|
+
specification_version: 2
|
116
|
+
summary: Roodi stands for Ruby Object Oriented Design Inferometer
|
117
|
+
test_files: []
|
118
|
+
|