excellent 1.5.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. data/History.txt +69 -0
  2. data/README.rdoc +72 -0
  3. data/VERSION.yml +4 -0
  4. data/bin/excellent +34 -0
  5. data/lib/simplabs/excellent.rb +16 -0
  6. data/lib/simplabs/excellent/checks.rb +33 -0
  7. data/lib/simplabs/excellent/checks/abc_metric_method_check.rb +43 -0
  8. data/lib/simplabs/excellent/checks/assignment_in_conditional_check.rb +39 -0
  9. data/lib/simplabs/excellent/checks/base.rb +62 -0
  10. data/lib/simplabs/excellent/checks/case_missing_else_check.rb +34 -0
  11. data/lib/simplabs/excellent/checks/class_line_count_check.rb +36 -0
  12. data/lib/simplabs/excellent/checks/class_name_check.rb +38 -0
  13. data/lib/simplabs/excellent/checks/control_coupling_check.rb +35 -0
  14. data/lib/simplabs/excellent/checks/cyclomatic_complexity_block_check.rb +48 -0
  15. data/lib/simplabs/excellent/checks/cyclomatic_complexity_check.rb +23 -0
  16. data/lib/simplabs/excellent/checks/cyclomatic_complexity_method_check.rb +48 -0
  17. data/lib/simplabs/excellent/checks/empty_rescue_body_check.rb +31 -0
  18. data/lib/simplabs/excellent/checks/flog_block_check.rb +40 -0
  19. data/lib/simplabs/excellent/checks/flog_check.rb +27 -0
  20. data/lib/simplabs/excellent/checks/flog_class_check.rb +40 -0
  21. data/lib/simplabs/excellent/checks/flog_method_check.rb +40 -0
  22. data/lib/simplabs/excellent/checks/for_loop_check.rb +42 -0
  23. data/lib/simplabs/excellent/checks/global_variable_check.rb +33 -0
  24. data/lib/simplabs/excellent/checks/line_count_check.rb +27 -0
  25. data/lib/simplabs/excellent/checks/method_line_count_check.rb +36 -0
  26. data/lib/simplabs/excellent/checks/method_name_check.rb +38 -0
  27. data/lib/simplabs/excellent/checks/module_line_count_check.rb +36 -0
  28. data/lib/simplabs/excellent/checks/module_name_check.rb +38 -0
  29. data/lib/simplabs/excellent/checks/name_check.rb +27 -0
  30. data/lib/simplabs/excellent/checks/nested_iterators_check.rb +34 -0
  31. data/lib/simplabs/excellent/checks/parameter_number_check.rb +38 -0
  32. data/lib/simplabs/excellent/checks/rails.rb +22 -0
  33. data/lib/simplabs/excellent/checks/rails/attr_accessible_check.rb +38 -0
  34. data/lib/simplabs/excellent/checks/rails/attr_protected_check.rb +39 -0
  35. data/lib/simplabs/excellent/checks/rails/custom_initialize_method_check.rb +37 -0
  36. data/lib/simplabs/excellent/checks/rails/instance_var_in_partial_check.rb +37 -0
  37. data/lib/simplabs/excellent/checks/rails/params_hash_in_view_check.rb +38 -0
  38. data/lib/simplabs/excellent/checks/rails/session_hash_in_view_check.rb +38 -0
  39. data/lib/simplabs/excellent/checks/rails/validations_check.rb +36 -0
  40. data/lib/simplabs/excellent/checks/singleton_variable_check.rb +33 -0
  41. data/lib/simplabs/excellent/command_line_runner.rb +37 -0
  42. data/lib/simplabs/excellent/extensions/sexp.rb +21 -0
  43. data/lib/simplabs/excellent/extensions/string.rb +28 -0
  44. data/lib/simplabs/excellent/formatters.rb +13 -0
  45. data/lib/simplabs/excellent/formatters/base.rb +49 -0
  46. data/lib/simplabs/excellent/formatters/html.rb +153 -0
  47. data/lib/simplabs/excellent/formatters/text.rb +40 -0
  48. data/lib/simplabs/excellent/parsing.rb +10 -0
  49. data/lib/simplabs/excellent/parsing/abc_measure.rb +52 -0
  50. data/lib/simplabs/excellent/parsing/block_context.rb +43 -0
  51. data/lib/simplabs/excellent/parsing/call_context.rb +52 -0
  52. data/lib/simplabs/excellent/parsing/case_context.rb +31 -0
  53. data/lib/simplabs/excellent/parsing/class_context.rb +99 -0
  54. data/lib/simplabs/excellent/parsing/code_processor.rb +165 -0
  55. data/lib/simplabs/excellent/parsing/conditional_context.rb +25 -0
  56. data/lib/simplabs/excellent/parsing/cvar_context.rb +28 -0
  57. data/lib/simplabs/excellent/parsing/cyclomatic_complexity_measure.rb +73 -0
  58. data/lib/simplabs/excellent/parsing/flog_measure.rb +192 -0
  59. data/lib/simplabs/excellent/parsing/for_loop_context.rb +15 -0
  60. data/lib/simplabs/excellent/parsing/gvar_context.rb +21 -0
  61. data/lib/simplabs/excellent/parsing/if_context.rb +38 -0
  62. data/lib/simplabs/excellent/parsing/ivar_context.rb +32 -0
  63. data/lib/simplabs/excellent/parsing/method_context.rb +50 -0
  64. data/lib/simplabs/excellent/parsing/module_context.rb +29 -0
  65. data/lib/simplabs/excellent/parsing/parser.rb +35 -0
  66. data/lib/simplabs/excellent/parsing/resbody_context.rb +39 -0
  67. data/lib/simplabs/excellent/parsing/scopeable.rb +34 -0
  68. data/lib/simplabs/excellent/parsing/sexp_context.rb +125 -0
  69. data/lib/simplabs/excellent/parsing/singleton_method_context.rb +55 -0
  70. data/lib/simplabs/excellent/parsing/until_context.rb +24 -0
  71. data/lib/simplabs/excellent/parsing/while_context.rb +24 -0
  72. data/lib/simplabs/excellent/rake.rb +1 -0
  73. data/lib/simplabs/excellent/rake/excellent_task.rb +61 -0
  74. data/lib/simplabs/excellent/runner.rb +143 -0
  75. data/lib/simplabs/excellent/warning.rb +53 -0
  76. data/spec/checks/abc_metric_method_check_spec.rb +122 -0
  77. data/spec/checks/assignment_in_conditional_check_spec.rb +90 -0
  78. data/spec/checks/case_missing_else_check_spec.rb +42 -0
  79. data/spec/checks/class_line_count_check_spec.rb +62 -0
  80. data/spec/checks/class_name_check_spec.rb +48 -0
  81. data/spec/checks/control_coupling_check_spec.rb +103 -0
  82. data/spec/checks/cyclomatic_complexity_block_check_spec.rb +47 -0
  83. data/spec/checks/cyclomatic_complexity_method_check_spec.rb +210 -0
  84. data/spec/checks/empty_rescue_body_check_spec.rb +170 -0
  85. data/spec/checks/flog_block_check_spec.rb +28 -0
  86. data/spec/checks/flog_class_check_spec.rb +28 -0
  87. data/spec/checks/flog_method_check_spec.rb +46 -0
  88. data/spec/checks/for_loop_check_spec.rb +52 -0
  89. data/spec/checks/global_variable_check_spec.rb +66 -0
  90. data/spec/checks/method_line_count_check_spec.rb +49 -0
  91. data/spec/checks/method_name_check_spec.rb +112 -0
  92. data/spec/checks/module_line_count_check_spec.rb +48 -0
  93. data/spec/checks/module_name_check_spec.rb +61 -0
  94. data/spec/checks/nested_iterators_check_spec.rb +44 -0
  95. data/spec/checks/parameter_number_check_spec.rb +97 -0
  96. data/spec/checks/rails/attr_accessible_check_spec.rb +79 -0
  97. data/spec/checks/rails/attr_protected_check_spec.rb +77 -0
  98. data/spec/checks/rails/custom_initialize_method_check_spec.rb +58 -0
  99. data/spec/checks/rails/instance_var_in_partial_check_spec.rb +40 -0
  100. data/spec/checks/rails/params_hash_in_view_check_spec.rb +40 -0
  101. data/spec/checks/rails/session_hash_in_view_check_spec.rb +40 -0
  102. data/spec/checks/rails/validations_check_spec.rb +81 -0
  103. data/spec/checks/singleton_variable_check_spec.rb +66 -0
  104. data/spec/extensions/string_spec.rb +13 -0
  105. data/spec/spec_helper.rb +13 -0
  106. metadata +189 -0
@@ -0,0 +1,53 @@
1
+ require 'simplabs/excellent/extensions/string'
2
+
3
+ module Simplabs
4
+
5
+ module Excellent
6
+
7
+ # The warnings returned by Excellent provide information about the +file+ the *possibly* problematic code was found in as well as the +line+ of the
8
+ # occurence, a warning message and a Hash with specific information about the warning.
9
+ #
10
+ # Warnings also provide the message template that was used to generate the message. The message template contains tokens like <tt>{{token}}</tt> that
11
+ # correspond to the valeus in the +info+ hash, e.g.:
12
+ #
13
+ # '{{method}} has abc score of {{score}}.'
14
+ # { :method => 'User#full_name', :score => 10 }
15
+ class Warning
16
+
17
+ # The check that produced the warning
18
+ attr_reader :check
19
+
20
+ # The name of the file the check found the problematic code in
21
+ attr_reader :filename
22
+
23
+ # The file number where the check found the problematic code
24
+ attr_reader :line_number
25
+
26
+ # The warning message
27
+ attr_reader :message
28
+
29
+ # Additional info for the warning (see above)
30
+ attr_reader :info
31
+
32
+ # The template used to produce the warning (see above)
33
+ attr_reader :message_template
34
+
35
+ def initialize(check, message, filename, line_number, info) #:nodoc:
36
+ @check = check.to_s.underscore.to_sym
37
+ @info = info
38
+ @filename = filename
39
+ @line_number = line_number.to_i
40
+
41
+ @message = ''
42
+ if !message.nil?
43
+ @message_template = message
44
+ @info.each { |key, value| message.gsub!(/\{\{#{key}\}\}/, value.to_s) }
45
+ @message = message
46
+ end
47
+ end
48
+
49
+ end
50
+
51
+ end
52
+
53
+ end
@@ -0,0 +1,122 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Simplabs::Excellent::Checks::AbcMetricMethodCheck do
4
+
5
+ before do
6
+ @excellent = Simplabs::Excellent::Runner.new(Simplabs::Excellent::Checks::AbcMetricMethodCheck.new({ :threshold => 0 }))
7
+ end
8
+
9
+ describe '#evaluate' do
10
+
11
+ describe 'when processing assignments' do
12
+
13
+ it "should find =" do
14
+ code = <<-END
15
+ def method_name
16
+ foo = 1
17
+ end
18
+ END
19
+
20
+ verify_code_score(code, 1, 0, 0)
21
+ end
22
+
23
+ ['*=', '/=', '%=', '+=', '<<=', '>>=', '&=', '|=', '^=', '-=', '**='].each do |assignment|
24
+
25
+ it "should find #{assignment}" do
26
+ code = <<-END
27
+ def method_name
28
+ foo #{assignment} 1
29
+ end
30
+ END
31
+
32
+ # these special assignments have score 2 since before the value is assigned, a method is called on the old value
33
+ verify_code_score(code, 1, 0, 1)
34
+ end
35
+
36
+ end
37
+
38
+ end
39
+
40
+ describe 'when processing branches' do
41
+
42
+ it 'should find a virtual method call' do
43
+ code = <<-END
44
+ def method_name
45
+ call_foo
46
+ end
47
+ END
48
+
49
+ verify_code_score(code, 0, 1, 0)
50
+ end
51
+
52
+ it 'should find an explicit method call' do
53
+ code = <<-END
54
+ def method_name
55
+ @object.call_foo
56
+ end
57
+ END
58
+
59
+ verify_code_score(code, 0, 1, 0)
60
+ end
61
+
62
+ it 'should exclude a condition' do
63
+ code = <<-END
64
+ def method_name
65
+ @object.call_foo < 10
66
+ end
67
+ END
68
+
69
+ verify_code_score(code, 0, 1, 1)
70
+ end
71
+
72
+ end
73
+
74
+ describe 'when processing conditions' do
75
+
76
+ ['==', '!=', '<=', '>=', '<', '>', '<=>', '=~'].each do |conditional|
77
+
78
+ it "should find #{conditional}" do
79
+ code = <<-END
80
+ def method_name
81
+ @foo #{conditional} @bar
82
+ end
83
+ END
84
+
85
+ verify_code_score(code, 0, 0, 1)
86
+ end
87
+
88
+ end
89
+
90
+ end
91
+
92
+ it 'should also work on singleton methods' do
93
+ code = <<-END
94
+ class Class
95
+ def self.method_name
96
+ foo = 1
97
+ end
98
+ end
99
+ END
100
+ @excellent.check_code(code)
101
+ warnings = @excellent.warnings
102
+
103
+ warnings.should_not be_empty
104
+ warnings[0].info.should == { :method => 'Class.method_name', :score => 1.0 }
105
+ warnings[0].line_number.should == 2
106
+ warnings[0].message.should == 'Class.method_name has abc score of 1.0.'
107
+ end
108
+
109
+ end
110
+
111
+ def verify_code_score(code, a, b, c)
112
+ score = Math.sqrt(a*a + b*b + c*c)
113
+ @excellent.check_code(code)
114
+ warnings = @excellent.warnings
115
+
116
+ warnings.should_not be_empty
117
+ warnings[0].info.should == { :method => 'method_name', :score => score }
118
+ warnings[0].line_number.should == 1
119
+ warnings[0].message.should == "method_name has abc score of #{score}."
120
+ end
121
+
122
+ end
@@ -0,0 +1,90 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Simplabs::Excellent::Checks::AssignmentInConditionalCheck do
4
+
5
+ before do
6
+ @excellent = Simplabs::Excellent::Runner.new(Simplabs::Excellent::Checks::AssignmentInConditionalCheck.new)
7
+ end
8
+
9
+ describe '#evaluate' do
10
+
11
+ it 'should accept an assignment before an if clause' do
12
+ code = <<-END
13
+ count = count += 1 if @some_condition
14
+ END
15
+ @excellent.check_code(code)
16
+
17
+ @excellent.warnings.should be_empty
18
+ end
19
+
20
+ it 'should accept block parameters in an if clause' do
21
+ code = <<-END
22
+ return true if exp.children.any? { |child| contains_statements?(child) }
23
+ END
24
+ @excellent.check_code(code)
25
+
26
+ @excellent.warnings.should be_empty
27
+ end
28
+
29
+ it 'should reject assignments of results of blocks in an if clause' do
30
+ code = <<-END
31
+ return true if value = exp.children.find { |child| contains_statements?(child) }
32
+ END
33
+
34
+ verify_warning_found(code)
35
+ end
36
+
37
+ it 'should reject an assignment inside an if clause' do
38
+ code = <<-END
39
+ call_foo if bar = bam
40
+ END
41
+
42
+ verify_warning_found(code)
43
+ end
44
+
45
+ it 'should reject an assignment inside an unless clause' do
46
+ code = <<-END
47
+ call_foo unless bar = bam
48
+ END
49
+
50
+ verify_warning_found(code)
51
+ end
52
+
53
+ it 'should reject an assignment inside a while clause' do
54
+ code = <<-END
55
+ call_foo while bar = bam
56
+ END
57
+
58
+ verify_warning_found(code)
59
+ end
60
+
61
+ it 'should reject an assignment inside an until clause' do
62
+ code = <<-END
63
+ call_foo until bar = bam
64
+ END
65
+
66
+ verify_warning_found(code)
67
+ end
68
+
69
+ it 'should reject an assignment inside a ternary operator check clause' do
70
+ code = <<-END
71
+ call_foo (bar = bam) ? baz : bad
72
+ END
73
+
74
+ #RubyParser sets line number 2 here
75
+ verify_warning_found(code, 2)
76
+ end
77
+
78
+ end
79
+
80
+ def verify_warning_found(code, line_number = nil)
81
+ @excellent.check_code(code)
82
+ warnings = @excellent.warnings
83
+
84
+ warnings.should_not be_empty
85
+ warnings[0].info.should == {}
86
+ warnings[0].line_number.should == (line_number || 1)
87
+ warnings[0].message.should == 'Assignment in condition.'
88
+ end
89
+
90
+ end
@@ -0,0 +1,42 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Simplabs::Excellent::Checks::CaseMissingElseCheck do
4
+
5
+ before do
6
+ @excellent = Simplabs::Excellent::Runner.new(Simplabs::Excellent::Checks::CaseMissingElseCheck.new)
7
+ end
8
+
9
+ describe '#evaluate' do
10
+
11
+ it 'should accept case statements that do have an else clause' do
12
+ code = <<-END
13
+ case foo
14
+ when "bar": "ok"
15
+ else "good"
16
+ end
17
+ END
18
+ @excellent.check_code(code)
19
+ warnings = @excellent.warnings
20
+
21
+ warnings.should be_empty
22
+ end
23
+
24
+ it 'should reject case statements that do not have an else clause' do
25
+ code = <<-END
26
+ case foo
27
+ when "bar": "ok"
28
+ when "bar": "bad"
29
+ end
30
+ END
31
+ @excellent.check_code(code)
32
+ warnings = @excellent.warnings
33
+
34
+ warnings.should_not be_empty
35
+ warnings[0].info.should == {}
36
+ warnings[0].line_number.should == 2
37
+ warnings[0].message.should == 'Case statement is missing else clause.'
38
+ end
39
+
40
+ end
41
+
42
+ end
@@ -0,0 +1,62 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Simplabs::Excellent::Checks::ClassLineCountCheck do
4
+
5
+ before do
6
+ @excellent = Simplabs::Excellent::Runner.new(Simplabs::Excellent::Checks::ClassLineCountCheck.new({ :threshold => 3 }))
7
+ end
8
+
9
+ describe '#evaluate' do
10
+
11
+ it 'should accept classes with less lines than the threshold' do
12
+ code = <<-END
13
+ class OneLineClass; end
14
+ END
15
+ @excellent.check_code(code)
16
+
17
+ @excellent.warnings.should be_empty
18
+ end
19
+
20
+ it 'should accept classes with the same number of lines as the threshold' do
21
+ code = <<-END
22
+ class ThreeLineClass
23
+ @foo = 1
24
+ end
25
+ END
26
+ @excellent.check_code(code)
27
+
28
+ @excellent.warnings.should be_empty
29
+ end
30
+
31
+ it 'should not count blank lines' do
32
+ code = <<-END
33
+ class ThreeLineClass
34
+
35
+ @foo = 1
36
+
37
+ end
38
+ END
39
+ @excellent.check_code(code)
40
+
41
+ @excellent.warnings.should be_empty
42
+ end
43
+
44
+ it 'should reject classes with more lines than the threshold' do
45
+ code = <<-END
46
+ class FourLineClass
47
+ @foo = 1
48
+ @bar = 2
49
+ end
50
+ END
51
+ @excellent.check_code(code)
52
+ warnings = @excellent.warnings
53
+
54
+ warnings.should_not be_empty
55
+ warnings[0].info.should == { :class => 'FourLineClass', :count => 4 }
56
+ warnings[0].line_number.should == 1
57
+ warnings[0].message.should == 'FourLineClass has 4 lines.'
58
+ end
59
+
60
+ end
61
+
62
+ end
@@ -0,0 +1,48 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Simplabs::Excellent::Checks::ClassNameCheck do
4
+
5
+ before(:each) do
6
+ @excellent = Simplabs::Excellent::Runner.new(Simplabs::Excellent::Checks::ClassNameCheck.new)
7
+ end
8
+
9
+ describe '#evaluate' do
10
+
11
+ it 'should accept camel case class names starting in capitals' do
12
+ code = <<-END
13
+ class GoodClassName; end
14
+ END
15
+ @excellent.check_code(code)
16
+
17
+ @excellent.warnings.should be_empty
18
+ end
19
+
20
+ it 'should be able to parse scoped class names' do
21
+ code = <<-END
22
+ class Outer::Inner::GoodClassName
23
+ def method
24
+ end
25
+ end
26
+ END
27
+ @excellent.check_code(code)
28
+ s
29
+ @excellent.warnings.should be_empty
30
+ end
31
+
32
+ it 'should reject class names with underscores' do
33
+ code = <<-END
34
+ class Bad_ClassName
35
+ end
36
+ END
37
+ @excellent.check_code(code)
38
+ warnings = @excellent.warnings
39
+
40
+ warnings.should_not be_empty
41
+ warnings[0].info.should == { :class => 'Bad_ClassName' }
42
+ warnings[0].line_number.should == 1
43
+ warnings[0].message.should == 'Bad class name Bad_ClassName.'
44
+ end
45
+
46
+ end
47
+
48
+ end
@@ -0,0 +1,103 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Simplabs::Excellent::Checks::ControlCouplingCheck do
4
+
5
+ before(:each) do
6
+ @excellent = Simplabs::Excellent::Runner.new(Simplabs::Excellent::Checks::ControlCouplingCheck.new)
7
+ end
8
+
9
+ describe '#evaluate' do
10
+
11
+ it 'should accept methods that just print out the parameter' do
12
+ code = <<-END
13
+ def write(quoted)
14
+ pp quoted
15
+ end
16
+ END
17
+ @excellent.check_code(code)
18
+ warnings = @excellent.warnings
19
+
20
+ warnings.should be_empty
21
+ end
22
+
23
+ it 'should accept methods with ternary operators using an instance variable' do
24
+ code = <<-END
25
+ def write(quoted)
26
+ @quoted ? write_quoted('1') : write_quoted('2')
27
+ end
28
+ END
29
+
30
+ @excellent.check_code(code)
31
+ warnings = @excellent.warnings
32
+
33
+ warnings.should be_empty
34
+ end
35
+
36
+ it 'should accept methods with ternary operators using a local variable' do
37
+ code = <<-END
38
+ def write(quoted)
39
+ test = false
40
+ test ? write_quoted('1') : write_quoted('2')
41
+ end
42
+ END
43
+
44
+ @excellent.check_code(code)
45
+ warnings = @excellent.warnings
46
+
47
+ warnings.should be_empty
48
+ end
49
+
50
+ %w(if unless).each do |conditional|
51
+
52
+ it "should reject methods with #{conditional} checks using a parameter" do
53
+ code = <<-END
54
+ def write(quoted)
55
+ #{conditional} quoted
56
+ write_quoted('test')
57
+ end
58
+ end
59
+ END
60
+
61
+ verify_warning_found(code)
62
+ end
63
+
64
+ end
65
+
66
+ it 'should reject methods with ternary operators using a parameter' do
67
+ code = <<-END
68
+ def write(quoted)
69
+ quoted ? write_quoted('1') : write_quoted('2')
70
+ end
71
+ END
72
+
73
+ verify_warning_found(code)
74
+ end
75
+
76
+ it "should reject methods with case statements using a parameter" do
77
+ code = <<-END
78
+ def write(quoted)
79
+ case quoted
80
+ when 1
81
+ write_quoted('1')
82
+ when 2
83
+ write_quoted('2')
84
+ end
85
+ end
86
+ END
87
+
88
+ verify_warning_found(code)
89
+ end
90
+
91
+ end
92
+
93
+ def verify_warning_found(code)
94
+ @excellent.check_code(code)
95
+ warnings = @excellent.warnings
96
+
97
+ warnings.should_not be_empty
98
+ warnings[0].info.should == { :method => 'write', :argument => 'quoted' }
99
+ warnings[0].line_number.should == 1
100
+ warnings[0].message.should == 'write is coupled to quoted.'
101
+ end
102
+
103
+ end