roodi 2.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +6 -0
- data/Manifest.txt +4 -0
- data/lib/roodi.rb +1 -1
- data/lib/roodi/checks.rb +1 -0
- data/lib/roodi/checks/check.rb +11 -4
- data/lib/roodi/checks/cyclomatic_complexity_check.rb +1 -1
- data/lib/roodi/checks/empty_rescue_body_check.rb +1 -1
- data/lib/roodi/checks/missing_foreign_key_index_check.rb +98 -0
- data/lib/roodi/core/checking_visitor.rb +3 -3
- data/lib/roodi/core/runner.rb +4 -2
- data/roodi.yml +1 -0
- data/spec/roodi/checks/empty_rescue_body_check_spec.rb +26 -0
- data/spec/roodi/checks/missing_foreign_key_index_check_spec.rb +33 -0
- data/spec/roodi/core/runner_spec.rb +25 -0
- data/spec/roodi/roodi.yml +1 -0
- metadata +7 -3
data/History.txt
CHANGED
data/Manifest.txt
CHANGED
@@ -22,6 +22,7 @@ lib/roodi/checks/for_loop_check.rb
|
|
22
22
|
lib/roodi/checks/line_count_check.rb
|
23
23
|
lib/roodi/checks/method_line_count_check.rb
|
24
24
|
lib/roodi/checks/method_name_check.rb
|
25
|
+
lib/roodi/checks/missing_foreign_key_index_check.rb
|
25
26
|
lib/roodi/checks/module_line_count_check.rb
|
26
27
|
lib/roodi/checks/module_name_check.rb
|
27
28
|
lib/roodi/checks/name_check.rb
|
@@ -49,8 +50,11 @@ spec/roodi/checks/empty_rescue_body_check_spec.rb
|
|
49
50
|
spec/roodi/checks/for_loop_check_spec.rb
|
50
51
|
spec/roodi/checks/method_line_count_check_spec.rb
|
51
52
|
spec/roodi/checks/method_name_check_spec.rb
|
53
|
+
spec/roodi/checks/missing_foreign_key_index_check_spec.rb
|
52
54
|
spec/roodi/checks/module_line_count_check_spec.rb
|
53
55
|
spec/roodi/checks/module_name_check_spec.rb
|
54
56
|
spec/roodi/checks/npath_complexity_method_check_spec.rb
|
55
57
|
spec/roodi/checks/parameter_number_check_spec.rb
|
58
|
+
spec/roodi/core/runner_spec.rb
|
59
|
+
spec/roodi/roodi.yml
|
56
60
|
spec/spec_helper.rb
|
data/lib/roodi.rb
CHANGED
data/lib/roodi/checks.rb
CHANGED
@@ -11,6 +11,7 @@ require 'roodi/checks/empty_rescue_body_check'
|
|
11
11
|
require 'roodi/checks/for_loop_check'
|
12
12
|
require 'roodi/checks/method_line_count_check'
|
13
13
|
require 'roodi/checks/method_name_check'
|
14
|
+
require 'roodi/checks/missing_foreign_key_index_check'
|
14
15
|
require 'roodi/checks/module_line_count_check'
|
15
16
|
require 'roodi/checks/module_name_check'
|
16
17
|
require 'roodi/checks/npath_complexity_method_check'
|
data/lib/roodi/checks/check.rb
CHANGED
@@ -12,13 +12,19 @@ module Roodi
|
|
12
12
|
NODE_TYPES.each do |node|
|
13
13
|
start_node_method = "evaluate_start_#{node}"
|
14
14
|
end_node_method = "evaluate_end_#{node}"
|
15
|
-
define_method(start_node_method) { } unless self.respond_to?(start_node_method)
|
16
|
-
define_method(end_node_method) { } unless self.respond_to?(end_node_method)
|
15
|
+
define_method(start_node_method) { |node| return } unless self.respond_to?(start_node_method)
|
16
|
+
define_method(end_node_method) { |node| return } unless self.respond_to?(end_node_method)
|
17
17
|
end
|
18
18
|
|
19
19
|
def position(offset = 0)
|
20
20
|
"#{@line[2]}:#{@line[1] + offset}"
|
21
21
|
end
|
22
|
+
|
23
|
+
def start_file(filename)
|
24
|
+
end
|
25
|
+
|
26
|
+
def end_file(filename)
|
27
|
+
end
|
22
28
|
|
23
29
|
def evaluate_start(node)
|
24
30
|
end
|
@@ -42,8 +48,9 @@ module Roodi
|
|
42
48
|
evaluate_end(node)
|
43
49
|
end
|
44
50
|
|
45
|
-
def add_error(error)
|
46
|
-
@errors
|
51
|
+
def add_error(error, filename = @node.file, line = @node.line)
|
52
|
+
@errors ||= []
|
53
|
+
@errors << Roodi::Core::Error.new("#{filename}", "#{line}", error)
|
47
54
|
end
|
48
55
|
|
49
56
|
def errors
|
@@ -7,7 +7,7 @@ module Roodi
|
|
7
7
|
# When the body of a rescue block is empty, exceptions can get caught and swallowed without
|
8
8
|
# any feedback to the user.
|
9
9
|
class EmptyRescueBodyCheck < Check
|
10
|
-
STATEMENT_NODES = [:fcall, :return, :attrasgn, :vcall, :nil, :call, :lasgn]
|
10
|
+
STATEMENT_NODES = [:fcall, :return, :attrasgn, :vcall, :nil, :call, :lasgn, :true, :false]
|
11
11
|
|
12
12
|
def interesting_nodes
|
13
13
|
[:resbody]
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'roodi/checks/check'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
module Roodi
|
5
|
+
module Checks
|
6
|
+
# Checks to make sure for loops are not being used..
|
7
|
+
#
|
8
|
+
# Using a for loop is not idiomatic use of Ruby, and is usually a sign that someone with
|
9
|
+
# more experience in a different programming language is trying out Ruby. Use
|
10
|
+
# Enumerable.each with a block instead.
|
11
|
+
class MissingForeignKeyIndexCheck < Check
|
12
|
+
def initialize
|
13
|
+
@foreign_keys = {}
|
14
|
+
@indexes = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def interesting_nodes
|
18
|
+
[:call]
|
19
|
+
end
|
20
|
+
|
21
|
+
def evaluate_start_call(node)
|
22
|
+
if analyzing_schema(node)
|
23
|
+
if creating_table(node)
|
24
|
+
@current_table = create_table_name(node)
|
25
|
+
end
|
26
|
+
|
27
|
+
if creating_foreign_key(node)
|
28
|
+
@foreign_keys[@current_table] ||= []
|
29
|
+
@foreign_keys[@current_table] << foreign_key_column_name(node)
|
30
|
+
end
|
31
|
+
|
32
|
+
if adding_index(node)
|
33
|
+
@indexes[index_table_name(node)] ||= []
|
34
|
+
@indexes[index_table_name(node)] << index_column_name(node)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def evaluate_end_call(node)
|
40
|
+
#ignored
|
41
|
+
end
|
42
|
+
|
43
|
+
def analyzing_schema(node)
|
44
|
+
pathname = Pathname.new(node.file)
|
45
|
+
@analyzing_schema ||= ("schema.rb" == pathname.basename.to_s)
|
46
|
+
end
|
47
|
+
|
48
|
+
def creating_table(node)
|
49
|
+
:create_table == node[2]
|
50
|
+
end
|
51
|
+
|
52
|
+
def create_table_name(node)
|
53
|
+
# Get table name out of this:
|
54
|
+
# s(:call, nil, :create_table, s(:arglist, s(:str, "duplicate_blocks"), s(:hash, s(:lit, :force), s(:true))))
|
55
|
+
node[3][1][1]
|
56
|
+
end
|
57
|
+
|
58
|
+
def creating_foreign_key(node)
|
59
|
+
#s(:call, s(:lvar, :t), :integer, s(:arglist, s(:str, "duplicate_set_id"), s(:hash, s(:lit, :null), s(:false))))
|
60
|
+
column_type = node[2]
|
61
|
+
column_name = node[3][1][1]
|
62
|
+
:integer == column_type && "_id" == column_name[-3,3]
|
63
|
+
end
|
64
|
+
|
65
|
+
def foreign_key_column_name(node)
|
66
|
+
#s(:call, s(:lvar, :t), :integer, s(:arglist, s(:str, "duplicate_set_id"), s(:hash, s(:lit, :null), s(:false))))
|
67
|
+
column_name = node[3][1][1]
|
68
|
+
end
|
69
|
+
|
70
|
+
def adding_index(node)
|
71
|
+
:add_index == node[2]
|
72
|
+
end
|
73
|
+
|
74
|
+
def index_table_name(node)
|
75
|
+
# Get table name out of this:
|
76
|
+
# s(:call, nil, :add_index, s(:arglist, s(:str, "duplicate_blocks"), s(:array, s(:str, "duplicate_set_id")), s(:hash, s(:lit, :name), s(:str, "index_duplicate_blocks_on_duplicate_set_id"))))
|
77
|
+
node[3][1][1]
|
78
|
+
end
|
79
|
+
|
80
|
+
def index_column_name(node)
|
81
|
+
# Get index column name out of this:
|
82
|
+
# s(:call, nil, :add_index, s(:arglist, s(:str, "duplicate_blocks"), s(:array, s(:str, "duplicate_set_id")), s(:hash, s(:lit, :name), s(:str, "index_duplicate_blocks_on_duplicate_set_id"))))
|
83
|
+
node[3][2][1][1]
|
84
|
+
end
|
85
|
+
|
86
|
+
def end_file(filename)
|
87
|
+
@foreign_keys.keys.each do |table|
|
88
|
+
foreign_keys = @foreign_keys[table] || []
|
89
|
+
indexes = @indexes[table] || []
|
90
|
+
missing_indexes = foreign_keys - indexes
|
91
|
+
missing_indexes.each do |fkey|
|
92
|
+
add_error("Table '#{table}' is missing an index on the foreign key '#{fkey}'", filename, 1)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -13,14 +13,14 @@ module Roodi
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
|
16
|
+
def visit(node)
|
17
17
|
checks = @checks[node.node_type]
|
18
18
|
checks.each {|check| check.evaluate_node_start(node)} unless checks.nil?
|
19
19
|
|
20
|
-
|
20
|
+
node.visitable_children.each {|sexp| sexp.accept(self)}
|
21
21
|
|
22
22
|
checks.each {|check| check.evaluate_node_end(node)} unless checks.nil?
|
23
|
-
|
23
|
+
end
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
data/lib/roodi/core/runner.rb
CHANGED
@@ -21,12 +21,14 @@ module Roodi
|
|
21
21
|
def check(filename, content)
|
22
22
|
@checks ||= load_checks
|
23
23
|
@checker ||= CheckingVisitor.new(@checks)
|
24
|
+
@checks.each {|check| check.start_file(filename)}
|
24
25
|
node = parse(filename, content)
|
25
26
|
node.accept(@checker) if node
|
27
|
+
@checks.each {|check| check.end_file(filename)}
|
26
28
|
end
|
27
29
|
|
28
|
-
def check_content(content)
|
29
|
-
check(
|
30
|
+
def check_content(content, filename = "dummy-file.rb")
|
31
|
+
check(filename, content)
|
30
32
|
end
|
31
33
|
|
32
34
|
def check_file(filename)
|
data/roodi.yml
CHANGED
@@ -9,6 +9,7 @@ EmptyRescueBodyCheck: { }
|
|
9
9
|
ForLoopCheck: { }
|
10
10
|
MethodLineCountCheck: { line_count: 20 }
|
11
11
|
MethodNameCheck: { pattern: !ruby/regexp /^[_a-z<>=\[|+-\/\*`]+[_a-z0-9_<>=~@\[\]]*[=!\?]?$/ }
|
12
|
+
# MissingForeignKeyIndexCheck: { }
|
12
13
|
ModuleLineCountCheck: { line_count: 300 }
|
13
14
|
ModuleNameCheck: { pattern: !ruby/regexp /^[A-Z][a-zA-Z0-9]*$/ }
|
14
15
|
ParameterNumberCheck: { parameter_count: 5 }
|
@@ -111,4 +111,30 @@ describe Roodi::Checks::EmptyRescueBodyCheck do
|
|
111
111
|
errors.should_not be_empty
|
112
112
|
errors[0].to_s.should match(/dummy-file.rb:[3-4] - Rescue block should not be empty./)
|
113
113
|
end
|
114
|
+
|
115
|
+
it "should accept a rescue block that returns true" do
|
116
|
+
content = <<-END
|
117
|
+
begin
|
118
|
+
call_method
|
119
|
+
rescue Exception => e
|
120
|
+
true
|
121
|
+
end
|
122
|
+
END
|
123
|
+
@roodi.check_content(content)
|
124
|
+
errors = @roodi.errors
|
125
|
+
errors.should be_empty
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should accept a rescue block that returns false" do
|
129
|
+
content = <<-END
|
130
|
+
begin
|
131
|
+
call_method
|
132
|
+
rescue Exception => e
|
133
|
+
false
|
134
|
+
end
|
135
|
+
END
|
136
|
+
@roodi.check_content(content)
|
137
|
+
errors = @roodi.errors
|
138
|
+
errors.should be_empty
|
139
|
+
end
|
114
140
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
describe Roodi::Checks::MissingForeignKeyIndexCheck do
|
4
|
+
before(:each) do
|
5
|
+
@roodi = Roodi::Core::Runner.new(Roodi::Checks::MissingForeignKeyIndexCheck.new)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should warn about a missing foreign key" do
|
9
|
+
content = <<-END
|
10
|
+
ActiveRecord::Schema.define(:version => 20091215233604) do
|
11
|
+
create_table "projects", :force => true do |t|
|
12
|
+
t.string "name"
|
13
|
+
end
|
14
|
+
|
15
|
+
create_table "revisions", :force => true do |t|
|
16
|
+
t.integer "project_id"
|
17
|
+
t.string "key"
|
18
|
+
end
|
19
|
+
|
20
|
+
add_index "revisions", ["project_id"], :name => "index_revisions_on_project_id"
|
21
|
+
|
22
|
+
create_table "source_files", :force => true do |t|
|
23
|
+
t.integer "revision_id"
|
24
|
+
t.string "filename"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
END
|
28
|
+
@roodi.check_content(content, "schema.rb")
|
29
|
+
errors = @roodi.errors
|
30
|
+
errors.should_not be_empty
|
31
|
+
errors[0].to_s.should eql("schema.rb:1 - Table 'source_files' is missing an index on the foreign key 'revision_id'")
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
describe Roodi::Core::Runner do
|
4
|
+
|
5
|
+
describe "given a custom config file" do
|
6
|
+
before do
|
7
|
+
@runner = Roodi::Core::Runner.new
|
8
|
+
@runner.config= File.expand_path(File.dirname(__FILE__) + '/../roodi.yml')
|
9
|
+
end
|
10
|
+
|
11
|
+
it "uses check from it" do
|
12
|
+
# @runner.check_file(File.expand_path(File.dirname(__FILE__) + '/../fixtures/test_class.rb'))
|
13
|
+
content = <<-RUBY
|
14
|
+
class TestClass
|
15
|
+
|
16
|
+
def METHOD
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
RUBY
|
21
|
+
@runner.check_content(content)
|
22
|
+
@runner.errors.should be_empty
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
MethodNameCheck: { pattern: !ruby/regexp /^[A-Z]+$/ }
|
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: 2.0
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marty Andrews
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-12-28 00:00:00 +11:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -30,7 +30,7 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 2.3.3
|
34
34
|
version:
|
35
35
|
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.
|
36
36
|
email:
|
@@ -69,6 +69,7 @@ files:
|
|
69
69
|
- lib/roodi/checks/line_count_check.rb
|
70
70
|
- lib/roodi/checks/method_line_count_check.rb
|
71
71
|
- lib/roodi/checks/method_name_check.rb
|
72
|
+
- lib/roodi/checks/missing_foreign_key_index_check.rb
|
72
73
|
- lib/roodi/checks/module_line_count_check.rb
|
73
74
|
- lib/roodi/checks/module_name_check.rb
|
74
75
|
- lib/roodi/checks/name_check.rb
|
@@ -96,10 +97,13 @@ files:
|
|
96
97
|
- spec/roodi/checks/for_loop_check_spec.rb
|
97
98
|
- spec/roodi/checks/method_line_count_check_spec.rb
|
98
99
|
- spec/roodi/checks/method_name_check_spec.rb
|
100
|
+
- spec/roodi/checks/missing_foreign_key_index_check_spec.rb
|
99
101
|
- spec/roodi/checks/module_line_count_check_spec.rb
|
100
102
|
- spec/roodi/checks/module_name_check_spec.rb
|
101
103
|
- spec/roodi/checks/npath_complexity_method_check_spec.rb
|
102
104
|
- spec/roodi/checks/parameter_number_check_spec.rb
|
105
|
+
- spec/roodi/core/runner_spec.rb
|
106
|
+
- spec/roodi/roodi.yml
|
103
107
|
- spec/spec_helper.rb
|
104
108
|
has_rdoc: true
|
105
109
|
homepage: http://roodi.rubyforge.org
|