roodi 2.0.1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,9 @@
1
+ = 2.1.0
2
+
3
+ * Ruby 1.9 compatible.
4
+ * New framework support to allow start and end file events.
5
+ * Added MissingForeignKeyIndexCheck. Still experimental for now.
6
+
1
7
  = 2.0.1
2
8
 
3
9
  * Fixed a bug where roodi.yml was not being loaded. Patch supplied by Rob Mitchell.
@@ -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
@@ -2,5 +2,5 @@ require 'roodi/checks'
2
2
  require 'roodi/core'
3
3
 
4
4
  module Roodi
5
- VERSION = '2.0.1'
5
+ VERSION = '2.1.0'
6
6
  end
@@ -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'
@@ -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 << Roodi::Core::Error.new("#{@node.file}", "#{@node.line}", error)
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
@@ -13,7 +13,7 @@ module Roodi
13
13
  end
14
14
 
15
15
  COMPLEXITY_NODE_TYPES.each do |type|
16
- define_method "evaluate_start_#{type}" do
16
+ define_method "evaluate_start_#{type}" do |node|
17
17
  @count = @count + 1 if counting?
18
18
  end
19
19
  end
@@ -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
- def visit(node)
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
- node.visitable_children.each {|sexp| sexp.accept(self)}
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
- end
23
+ end
24
24
  end
25
25
  end
26
26
  end
@@ -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("dummy-file.rb", content)
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.1
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-10-26 00:00:00 +11:00
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: 1.11.0
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