roodi 0.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|