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.
- data/History.txt +69 -0
- data/README.rdoc +72 -0
- data/VERSION.yml +4 -0
- data/bin/excellent +34 -0
- data/lib/simplabs/excellent.rb +16 -0
- data/lib/simplabs/excellent/checks.rb +33 -0
- data/lib/simplabs/excellent/checks/abc_metric_method_check.rb +43 -0
- data/lib/simplabs/excellent/checks/assignment_in_conditional_check.rb +39 -0
- data/lib/simplabs/excellent/checks/base.rb +62 -0
- data/lib/simplabs/excellent/checks/case_missing_else_check.rb +34 -0
- data/lib/simplabs/excellent/checks/class_line_count_check.rb +36 -0
- data/lib/simplabs/excellent/checks/class_name_check.rb +38 -0
- data/lib/simplabs/excellent/checks/control_coupling_check.rb +35 -0
- data/lib/simplabs/excellent/checks/cyclomatic_complexity_block_check.rb +48 -0
- data/lib/simplabs/excellent/checks/cyclomatic_complexity_check.rb +23 -0
- data/lib/simplabs/excellent/checks/cyclomatic_complexity_method_check.rb +48 -0
- data/lib/simplabs/excellent/checks/empty_rescue_body_check.rb +31 -0
- data/lib/simplabs/excellent/checks/flog_block_check.rb +40 -0
- data/lib/simplabs/excellent/checks/flog_check.rb +27 -0
- data/lib/simplabs/excellent/checks/flog_class_check.rb +40 -0
- data/lib/simplabs/excellent/checks/flog_method_check.rb +40 -0
- data/lib/simplabs/excellent/checks/for_loop_check.rb +42 -0
- data/lib/simplabs/excellent/checks/global_variable_check.rb +33 -0
- data/lib/simplabs/excellent/checks/line_count_check.rb +27 -0
- data/lib/simplabs/excellent/checks/method_line_count_check.rb +36 -0
- data/lib/simplabs/excellent/checks/method_name_check.rb +38 -0
- data/lib/simplabs/excellent/checks/module_line_count_check.rb +36 -0
- data/lib/simplabs/excellent/checks/module_name_check.rb +38 -0
- data/lib/simplabs/excellent/checks/name_check.rb +27 -0
- data/lib/simplabs/excellent/checks/nested_iterators_check.rb +34 -0
- data/lib/simplabs/excellent/checks/parameter_number_check.rb +38 -0
- data/lib/simplabs/excellent/checks/rails.rb +22 -0
- data/lib/simplabs/excellent/checks/rails/attr_accessible_check.rb +38 -0
- data/lib/simplabs/excellent/checks/rails/attr_protected_check.rb +39 -0
- data/lib/simplabs/excellent/checks/rails/custom_initialize_method_check.rb +37 -0
- data/lib/simplabs/excellent/checks/rails/instance_var_in_partial_check.rb +37 -0
- data/lib/simplabs/excellent/checks/rails/params_hash_in_view_check.rb +38 -0
- data/lib/simplabs/excellent/checks/rails/session_hash_in_view_check.rb +38 -0
- data/lib/simplabs/excellent/checks/rails/validations_check.rb +36 -0
- data/lib/simplabs/excellent/checks/singleton_variable_check.rb +33 -0
- data/lib/simplabs/excellent/command_line_runner.rb +37 -0
- data/lib/simplabs/excellent/extensions/sexp.rb +21 -0
- data/lib/simplabs/excellent/extensions/string.rb +28 -0
- data/lib/simplabs/excellent/formatters.rb +13 -0
- data/lib/simplabs/excellent/formatters/base.rb +49 -0
- data/lib/simplabs/excellent/formatters/html.rb +153 -0
- data/lib/simplabs/excellent/formatters/text.rb +40 -0
- data/lib/simplabs/excellent/parsing.rb +10 -0
- data/lib/simplabs/excellent/parsing/abc_measure.rb +52 -0
- data/lib/simplabs/excellent/parsing/block_context.rb +43 -0
- data/lib/simplabs/excellent/parsing/call_context.rb +52 -0
- data/lib/simplabs/excellent/parsing/case_context.rb +31 -0
- data/lib/simplabs/excellent/parsing/class_context.rb +99 -0
- data/lib/simplabs/excellent/parsing/code_processor.rb +165 -0
- data/lib/simplabs/excellent/parsing/conditional_context.rb +25 -0
- data/lib/simplabs/excellent/parsing/cvar_context.rb +28 -0
- data/lib/simplabs/excellent/parsing/cyclomatic_complexity_measure.rb +73 -0
- data/lib/simplabs/excellent/parsing/flog_measure.rb +192 -0
- data/lib/simplabs/excellent/parsing/for_loop_context.rb +15 -0
- data/lib/simplabs/excellent/parsing/gvar_context.rb +21 -0
- data/lib/simplabs/excellent/parsing/if_context.rb +38 -0
- data/lib/simplabs/excellent/parsing/ivar_context.rb +32 -0
- data/lib/simplabs/excellent/parsing/method_context.rb +50 -0
- data/lib/simplabs/excellent/parsing/module_context.rb +29 -0
- data/lib/simplabs/excellent/parsing/parser.rb +35 -0
- data/lib/simplabs/excellent/parsing/resbody_context.rb +39 -0
- data/lib/simplabs/excellent/parsing/scopeable.rb +34 -0
- data/lib/simplabs/excellent/parsing/sexp_context.rb +125 -0
- data/lib/simplabs/excellent/parsing/singleton_method_context.rb +55 -0
- data/lib/simplabs/excellent/parsing/until_context.rb +24 -0
- data/lib/simplabs/excellent/parsing/while_context.rb +24 -0
- data/lib/simplabs/excellent/rake.rb +1 -0
- data/lib/simplabs/excellent/rake/excellent_task.rb +61 -0
- data/lib/simplabs/excellent/runner.rb +143 -0
- data/lib/simplabs/excellent/warning.rb +53 -0
- data/spec/checks/abc_metric_method_check_spec.rb +122 -0
- data/spec/checks/assignment_in_conditional_check_spec.rb +90 -0
- data/spec/checks/case_missing_else_check_spec.rb +42 -0
- data/spec/checks/class_line_count_check_spec.rb +62 -0
- data/spec/checks/class_name_check_spec.rb +48 -0
- data/spec/checks/control_coupling_check_spec.rb +103 -0
- data/spec/checks/cyclomatic_complexity_block_check_spec.rb +47 -0
- data/spec/checks/cyclomatic_complexity_method_check_spec.rb +210 -0
- data/spec/checks/empty_rescue_body_check_spec.rb +170 -0
- data/spec/checks/flog_block_check_spec.rb +28 -0
- data/spec/checks/flog_class_check_spec.rb +28 -0
- data/spec/checks/flog_method_check_spec.rb +46 -0
- data/spec/checks/for_loop_check_spec.rb +52 -0
- data/spec/checks/global_variable_check_spec.rb +66 -0
- data/spec/checks/method_line_count_check_spec.rb +49 -0
- data/spec/checks/method_name_check_spec.rb +112 -0
- data/spec/checks/module_line_count_check_spec.rb +48 -0
- data/spec/checks/module_name_check_spec.rb +61 -0
- data/spec/checks/nested_iterators_check_spec.rb +44 -0
- data/spec/checks/parameter_number_check_spec.rb +97 -0
- data/spec/checks/rails/attr_accessible_check_spec.rb +79 -0
- data/spec/checks/rails/attr_protected_check_spec.rb +77 -0
- data/spec/checks/rails/custom_initialize_method_check_spec.rb +58 -0
- data/spec/checks/rails/instance_var_in_partial_check_spec.rb +40 -0
- data/spec/checks/rails/params_hash_in_view_check_spec.rb +40 -0
- data/spec/checks/rails/session_hash_in_view_check_spec.rb +40 -0
- data/spec/checks/rails/validations_check_spec.rb +81 -0
- data/spec/checks/singleton_variable_check_spec.rb +66 -0
- data/spec/extensions/string_spec.rb +13 -0
- data/spec/spec_helper.rb +13 -0
- metadata +189 -0
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'simplabs/excellent/checks/base'
|
2
|
+
|
3
|
+
module Simplabs
|
4
|
+
|
5
|
+
module Excellent
|
6
|
+
|
7
|
+
module Checks
|
8
|
+
|
9
|
+
module Rails
|
10
|
+
|
11
|
+
# This check reports views (and partials) that access the +params+ hash. Accessing the +params+ hash directly in views can result in security
|
12
|
+
# problems if the value is printed to the HTML output and in general is a bad habit because the controller, which is actually the part of the
|
13
|
+
# application that is responsible for dealing with parameters, is circumvented.
|
14
|
+
#
|
15
|
+
# ==== Applies to
|
16
|
+
#
|
17
|
+
# * partials and regular views
|
18
|
+
class ParamsHashInViewCheck < Base
|
19
|
+
|
20
|
+
def initialize #:nodoc:
|
21
|
+
super
|
22
|
+
@interesting_nodes = [:call]
|
23
|
+
@interesting_files = [/^.*\.(erb|rhtml)$/]
|
24
|
+
end
|
25
|
+
|
26
|
+
def evaluate(context) #:nodoc:
|
27
|
+
add_warning(context, 'Params hash used in view.') if (context.full_name == 'params')
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'simplabs/excellent/checks/base'
|
2
|
+
|
3
|
+
module Simplabs
|
4
|
+
|
5
|
+
module Excellent
|
6
|
+
|
7
|
+
module Checks
|
8
|
+
|
9
|
+
module Rails
|
10
|
+
|
11
|
+
# This check reports views (and partials) that access the +session+ hash. Accessing the +session+ hash directly in views can result in security
|
12
|
+
# problems if the value is printed to the HTML output and in general is a bad habit because the controller, which is actually the part of the
|
13
|
+
# application that is responsible for dealing with session data, is circumvented.
|
14
|
+
#
|
15
|
+
# ==== Applies to
|
16
|
+
#
|
17
|
+
# * partials and regular views
|
18
|
+
class SessionHashInViewCheck < Base
|
19
|
+
|
20
|
+
def initialize #:nodoc:
|
21
|
+
super
|
22
|
+
@interesting_nodes = [:call]
|
23
|
+
@interesting_files = [/^.*\.(erb|rhtml)$/]
|
24
|
+
end
|
25
|
+
|
26
|
+
def evaluate(context) #:nodoc:
|
27
|
+
add_warning(context, 'Session hash used in view.') if (context.full_name == 'session')
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'simplabs/excellent/checks/base'
|
2
|
+
|
3
|
+
module Simplabs
|
4
|
+
|
5
|
+
module Excellent
|
6
|
+
|
7
|
+
module Checks
|
8
|
+
|
9
|
+
module Rails
|
10
|
+
|
11
|
+
# This check reports +ActiveRecord+ models that do not validate anything. Not validating anything in the model is usually not intended and has
|
12
|
+
# most likely just been forgotten. For more information on validations and why to use them, see http://guides.rubyonrails.org/activerecord_validations_callbacks.html#why-use-validations.
|
13
|
+
#
|
14
|
+
# ==== Applies to
|
15
|
+
#
|
16
|
+
# * +ActiveRecord+ models
|
17
|
+
class ValidationsCheck < Base
|
18
|
+
|
19
|
+
def initialize #:nodoc:
|
20
|
+
super
|
21
|
+
@interesting_nodes = [:class]
|
22
|
+
end
|
23
|
+
|
24
|
+
def evaluate(context) #:nodoc:
|
25
|
+
add_warning(context, '{{class}} does not validate any attributes.', { :class => context.full_name }) if context.active_record_model? && !context.validating?
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'simplabs/excellent/checks/base'
|
2
|
+
|
3
|
+
module Simplabs
|
4
|
+
|
5
|
+
module Excellent
|
6
|
+
|
7
|
+
module Checks
|
8
|
+
|
9
|
+
# This check reports class variables. Class variables in Ruby have a very complicated inheritance policy that often leads to errors. Usually class
|
10
|
+
# variables can be replaced with another construct which will also lead to better design.
|
11
|
+
#
|
12
|
+
# ==== Applies to
|
13
|
+
#
|
14
|
+
# * class variables
|
15
|
+
class SingletonVariableCheck < Base
|
16
|
+
|
17
|
+
def initialize #:nodoc:
|
18
|
+
super
|
19
|
+
@interesting_nodes = [:cvar]
|
20
|
+
@interesting_files = [/\.rb$/, /\.erb$/]
|
21
|
+
end
|
22
|
+
|
23
|
+
def evaluate(context) #:nodoc:
|
24
|
+
add_warning(context, 'Singleton variable {{variable}} used.', { :variable => context.full_name })
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'simplabs/excellent/runner'
|
2
|
+
require 'simplabs/excellent/formatters'
|
3
|
+
|
4
|
+
module Simplabs
|
5
|
+
|
6
|
+
module Excellent
|
7
|
+
|
8
|
+
class CommandLineRunner < Runner #:nodoc:
|
9
|
+
|
10
|
+
def initialize(*checks)
|
11
|
+
super
|
12
|
+
@formatter = Formatters::Text.new
|
13
|
+
@formatter.start
|
14
|
+
end
|
15
|
+
|
16
|
+
def check_paths(paths)
|
17
|
+
paths.each do |path|
|
18
|
+
if File.file?(path)
|
19
|
+
check_file(path)
|
20
|
+
elsif File.directory?(path)
|
21
|
+
Dir.glob(File.join(path, '**/*.rb')).each { |file| check_file(file) }
|
22
|
+
else
|
23
|
+
next
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def check_file(filename)
|
29
|
+
super
|
30
|
+
@formatter.file(filename, (@checks || []).collect { |check| check.warnings }.flatten)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Simplabs
|
2
|
+
|
3
|
+
module Excellent
|
4
|
+
|
5
|
+
module Extensions #:nodoc:
|
6
|
+
|
7
|
+
::String.class_eval do
|
8
|
+
|
9
|
+
def underscore
|
10
|
+
to_s.gsub(/::/, '/').
|
11
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
12
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
13
|
+
tr("-", "_").
|
14
|
+
downcase
|
15
|
+
end
|
16
|
+
|
17
|
+
def lpad(to, with = ' ')
|
18
|
+
return self if self.length >= to
|
19
|
+
"#{with * (to - self.length)}#{self}"
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Simplabs
|
2
|
+
|
3
|
+
module Excellent
|
4
|
+
|
5
|
+
module Formatters
|
6
|
+
|
7
|
+
# The base class for all formatters.
|
8
|
+
class Base
|
9
|
+
|
10
|
+
# Initializes the formatter.
|
11
|
+
#
|
12
|
+
# Always call +super+ in custom formatters!
|
13
|
+
def initialize(stream)
|
14
|
+
@stream = stream
|
15
|
+
end
|
16
|
+
|
17
|
+
# Called when the Simplabs::Excellent::Runner starts processing code.
|
18
|
+
#
|
19
|
+
# The text formatter renders the heading here ('Excellent result')
|
20
|
+
def start
|
21
|
+
end
|
22
|
+
|
23
|
+
# Called whenever the Simplabs::Excellent::Runner processes a file. Yields the formatter
|
24
|
+
#
|
25
|
+
# You have to <tt>yield self</tt> in custom formatters. +file+ is called like that by the runner:
|
26
|
+
#
|
27
|
+
# formatter.file(filename) do |formatter|
|
28
|
+
# warnings.each { |warning| formatter.warning(warning) }
|
29
|
+
# end
|
30
|
+
def file(filename)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Called when the Simplabs::Excellent::Runner found a warning. This warning will always refer to the last filename, +file+ was invoked with.
|
34
|
+
def warning(warning)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Called when the Simplabs::Excellent::Runner ends processing code.
|
38
|
+
#
|
39
|
+
# The text formatter renders the footer here ('Found <x> warnings').
|
40
|
+
def end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
require 'simplabs/excellent/formatters/base'
|
2
|
+
|
3
|
+
module Simplabs
|
4
|
+
|
5
|
+
module Excellent
|
6
|
+
|
7
|
+
module Formatters
|
8
|
+
|
9
|
+
class Html < Base #:nodoc:
|
10
|
+
|
11
|
+
def initialize(stream = $stdout)
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
def start
|
16
|
+
@stream.write(HEADER_TEMPLATE)
|
17
|
+
end
|
18
|
+
|
19
|
+
def file(filename)
|
20
|
+
@stream.write(START_FIlE_TAMPLATE.sub('{{filename}}', filename))
|
21
|
+
yield self
|
22
|
+
@stream.write(END_FIlE_TAMPLATE)
|
23
|
+
end
|
24
|
+
|
25
|
+
def warning(warning)
|
26
|
+
@stream.write(WARNING_TEMPLATE.sub('{{line_number}}', warning.line_number.to_s).sub('{{message}}', warning.message))
|
27
|
+
end
|
28
|
+
|
29
|
+
def end
|
30
|
+
@stream.write(FOOTER_TEMPLATE)
|
31
|
+
end
|
32
|
+
|
33
|
+
HEADER_TEMPLATE = <<-END
|
34
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
35
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
36
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
37
|
+
<head>
|
38
|
+
<title>Excellent result</title>
|
39
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
40
|
+
<style type="text/css">
|
41
|
+
body {
|
42
|
+
margin: 0;
|
43
|
+
padding: 0;
|
44
|
+
background: #fff;
|
45
|
+
font-size: 80%;
|
46
|
+
font-family: "Lucida Grande", Helvetica, sans-serif;
|
47
|
+
}
|
48
|
+
|
49
|
+
#header {
|
50
|
+
background: #ccc;
|
51
|
+
color: #000;
|
52
|
+
height: 4em;
|
53
|
+
}
|
54
|
+
|
55
|
+
#header h1 {
|
56
|
+
font-size: 1.8em;
|
57
|
+
margin: 0px 10px 0px 10px;
|
58
|
+
padding: 10px;
|
59
|
+
position: absolute;
|
60
|
+
}
|
61
|
+
|
62
|
+
#results {
|
63
|
+
margin: 0px 10px 5px;
|
64
|
+
}
|
65
|
+
|
66
|
+
dt {
|
67
|
+
background: #ccc;
|
68
|
+
padding: 5px;
|
69
|
+
font-weight: bold;
|
70
|
+
margin: 30px 0 0 0;
|
71
|
+
}
|
72
|
+
|
73
|
+
dd {
|
74
|
+
margin: 5px 0px 5px 20px;
|
75
|
+
}
|
76
|
+
|
77
|
+
dd.warning {
|
78
|
+
background: #faf834;
|
79
|
+
}
|
80
|
+
|
81
|
+
dd.warning span.lineNumber {
|
82
|
+
background: #ccc;
|
83
|
+
font-weight: bold;
|
84
|
+
padding: 3px;
|
85
|
+
display: inline-block;
|
86
|
+
margin: 0 10px 0 0;
|
87
|
+
}
|
88
|
+
|
89
|
+
dd.warning span.number {
|
90
|
+
width: 30px;
|
91
|
+
text-align: right;
|
92
|
+
display: inline-block;
|
93
|
+
}
|
94
|
+
|
95
|
+
#footer {
|
96
|
+
margin: 20px 0 20px 0;
|
97
|
+
padding: 5px;
|
98
|
+
text-align: center;
|
99
|
+
}
|
100
|
+
|
101
|
+
#footer a {
|
102
|
+
color: #000;
|
103
|
+
background-color: #ddd;
|
104
|
+
text-decoration: none;
|
105
|
+
padding: 0 2px 0 2px;
|
106
|
+
}
|
107
|
+
|
108
|
+
#footer a:active, #footer a:hover {
|
109
|
+
color: #fff;
|
110
|
+
background-color: #222;
|
111
|
+
}
|
112
|
+
</style>
|
113
|
+
</head>
|
114
|
+
<body>
|
115
|
+
<div id="header">
|
116
|
+
<div id="label">
|
117
|
+
<h1>Excellent result</h1>
|
118
|
+
</div>
|
119
|
+
</div>
|
120
|
+
<div id="results">
|
121
|
+
END
|
122
|
+
|
123
|
+
START_FIlE_TAMPLATE = <<-END
|
124
|
+
<div class="file">
|
125
|
+
<dl>
|
126
|
+
<dt>{{filename}}</dt>
|
127
|
+
END
|
128
|
+
|
129
|
+
END_FIlE_TAMPLATE = <<-END
|
130
|
+
</dl>
|
131
|
+
</div>
|
132
|
+
END
|
133
|
+
|
134
|
+
WARNING_TEMPLATE = <<-END
|
135
|
+
<dd class="warning"><span class="lineNumber">Line <span class="number">{{line_number}}</span></span>{{message}}</dd>
|
136
|
+
END
|
137
|
+
|
138
|
+
FOOTER_TEMPLATE = <<-END
|
139
|
+
</div>
|
140
|
+
<div id="footer">
|
141
|
+
<a href="http://github.com/simplabs/excellent" title="Excellent at github">Excellent</a> by <a href="http://simplabs.com" title="simplabs">simplabs</a>
|
142
|
+
</div>
|
143
|
+
</body>
|
144
|
+
</html>
|
145
|
+
END
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|