excellent 1.5.4

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.
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