Skip to content
Browse files

Added MissingForeignKeyIndexCheck.

  • Loading branch information...
1 parent 14a0ea5 commit 2892d4ca5b056dbb44a1fb5a67333c24fb0b7330 Marty Andrews committed Dec 28, 2009
View
1 lib/roodi/checks.rb
@@ -11,6 +11,7 @@
require 'roodi/checks/for_loop_check'
require 'roodi/checks/method_line_count_check'
require 'roodi/checks/method_name_check'
+require 'roodi/checks/missing_foreign_key_index_check'
require 'roodi/checks/module_line_count_check'
require 'roodi/checks/module_name_check'
require 'roodi/checks/npath_complexity_method_check'
View
98 lib/roodi/checks/missing_foreign_key_index_check.rb
@@ -0,0 +1,98 @@
+require 'roodi/checks/check'
+require 'pathname'
+
+module Roodi
+ module Checks
+ # Checks to make sure for loops are not being used..
+ #
+ # Using a for loop is not idiomatic use of Ruby, and is usually a sign that someone with
+ # more experience in a different programming language is trying out Ruby. Use
+ # Enumerable.each with a block instead.
+ class MissingForeignKeyIndexCheck < Check
+ def initialize
+ @foreign_keys = {}
+ @indexes = {}
+ end
+
+ def interesting_nodes
+ [:call]
+ end
+
+ def evaluate_start_call(node)
+ if analyzing_schema(node)
+ if creating_table(node)
+ @current_table = create_table_name(node)
+ end
+
+ if creating_foreign_key(node)
+ @foreign_keys[@current_table] ||= []
+ @foreign_keys[@current_table] << foreign_key_column_name(node)
+ end
+
+ if adding_index(node)
+ @indexes[index_table_name(node)] ||= []
+ @indexes[index_table_name(node)] << index_column_name(node)
+ end
+ end
+ end
+
+ def evaluate_end_call(node)
+ #ignored
+ end
+
+ def analyzing_schema(node)
+ pathname = Pathname.new(node.file)
+ @analyzing_schema ||= ("schema.rb" == pathname.basename.to_s)
+ end
+
+ def creating_table(node)
+ :create_table == node[2]
+ end
+
+ def create_table_name(node)
+ # Get table name out of this:
+ # s(:call, nil, :create_table, s(:arglist, s(:str, "duplicate_blocks"), s(:hash, s(:lit, :force), s(:true))))
+ node[3][1][1]
+ end
+
+ def creating_foreign_key(node)
+ #s(:call, s(:lvar, :t), :integer, s(:arglist, s(:str, "duplicate_set_id"), s(:hash, s(:lit, :null), s(:false))))
+ column_type = node[2]
+ column_name = node[3][1][1]
+ :integer == column_type && "_id" == column_name[-3,3]
+ end
+
+ def foreign_key_column_name(node)
+ #s(:call, s(:lvar, :t), :integer, s(:arglist, s(:str, "duplicate_set_id"), s(:hash, s(:lit, :null), s(:false))))
+ column_name = node[3][1][1]
+ end
+
+ def adding_index(node)
+ :add_index == node[2]
+ end
+
+ def index_table_name(node)
+ # Get table name out of this:
+ # 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"))))
+ node[3][1][1]
+ end
+
+ def index_column_name(node)
+ # Get index column name out of this:
+ # 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"))))
+ node[3][2][1][1]
+ end
+
+ def end_file(filename)
+ @foreign_keys.keys.each do |table|
+ foreign_keys = @foreign_keys[table] || []
+ indexes = @indexes[table] || []
+ missing_indexes = foreign_keys - indexes
+ missing_indexes.each do |fkey|
+ add_error("Table '#{table}' is missing an index on the foreign key '#{fkey}'", filename, 1)
+ end
+ end
+ end
+ end
+ end
+end
View
1 roodi.yml
@@ -9,6 +9,7 @@ EmptyRescueBodyCheck: { }
ForLoopCheck: { }
MethodLineCountCheck: { line_count: 20 }
MethodNameCheck: { pattern: !ruby/regexp /^[_a-z<>=\[|+-\/\*`]+[_a-z0-9_<>=~@\[\]]*[=!\?]?$/ }
+# MissingForeignKeyIndexCheck: { }
ModuleLineCountCheck: { line_count: 300 }
ModuleNameCheck: { pattern: !ruby/regexp /^[A-Z][a-zA-Z0-9]*$/ }
ParameterNumberCheck: { parameter_count: 5 }
View
33 spec/roodi/checks/missing_foreign_key_index_check_spec.rb
@@ -0,0 +1,33 @@
+require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
+
+describe Roodi::Checks::MissingForeignKeyIndexCheck do
+ before(:each) do
+ @roodi = Roodi::Core::Runner.new(Roodi::Checks::MissingForeignKeyIndexCheck.new)
+ end
+
+ it "should warn about a missing foreign key" do
+ content = <<-END
+ActiveRecord::Schema.define(:version => 20091215233604) do
+ create_table "projects", :force => true do |t|
+ t.string "name"
+ end
+
+ create_table "revisions", :force => true do |t|
+ t.integer "project_id"
+ t.string "key"
+ end
+
+ add_index "revisions", ["project_id"], :name => "index_revisions_on_project_id"
+
+ create_table "source_files", :force => true do |t|
+ t.integer "revision_id"
+ t.string "filename"
+ end
+end
+ END
+ @roodi.check_content(content, "schema.rb")
+ errors = @roodi.errors
+ errors.should_not be_empty
+ errors[0].to_s.should eql("schema.rb:1 - Table 'source_files' is missing an index on the foreign key 'revision_id'")
+ end
+end

0 comments on commit 2892d4c

Please sign in to comment.
Something went wrong with that request. Please try again.