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,69 @@
1
+ = 1.5.4
2
+
3
+ * fixed handling of parser errors
4
+
5
+ = 1.5.3
6
+
7
+ * added missing dependency to facets gem
8
+
9
+ = 1.5.2
10
+
11
+ * added GlobalVariableCheck
12
+ * added Rails::CustomInitializeMethodCheck
13
+ * added Rails::ParamsHashInViewCheck
14
+ * added Rails::SessionHashInViewCheck
15
+
16
+ = 1.5.1
17
+
18
+ * removed duplication checks (they are just too coarse for dynamic languages like Ruby and especially Rails apps where you would call e.g. params all over the place)
19
+ * added Rails::ValidationsCheck to the default checks
20
+
21
+ = 1.5.0
22
+
23
+ * new check Rails::ValidationsCheck that reports ActiveRecord models that do not validate anything
24
+ * added DuplicationCheck to default checks
25
+ * split DuplicationCheck up into MethodDuplicationCheck and BlockDuplicationCheck
26
+
27
+ = 1.4.1
28
+
29
+ * added Rake Task (see Rakefile for example)
30
+
31
+ = 1.4.0
32
+
33
+ * Excellent not parses *.erb files
34
+ * added new check InstanceVarInPartialCheck
35
+
36
+ = 1.3.1
37
+
38
+ * FIX (forgot files in gemspec on 1.3.0)
39
+
40
+ = 1.3.0
41
+
42
+ * added formatters (currently text and HTML formatting is supported, see README)
43
+
44
+ = 1.2.2
45
+
46
+ * fixed specs
47
+
48
+ = 1.2.1
49
+
50
+ * renamed Error to Warning
51
+ * added documentation
52
+
53
+ = 1.2.0
54
+
55
+ * cleanup
56
+ * added 2 Rails specific checks, AttrAccessibleCheck and AttrProtectedCheck
57
+
58
+ = 1.1.0
59
+
60
+ * completely restructured, made everything running in the flow of the SexpProcessor
61
+ * added most tests from reek (except for UtilityFunction and FeatureEnvy)
62
+
63
+ = 1.0.1
64
+
65
+ * fixed some errors
66
+
67
+ = 1.0.0
68
+
69
+ * this is basically just a custom version of roodi, converted to ruby_parser to be 1.9 safe
@@ -0,0 +1,72 @@
1
+ = Excellent
2
+
3
+ Excellent *finds the nasty lines in your code*. It implements a comprehensive set of checks for possibly buggy parts of your app that would *otherwise make it into your repo and eventually to the production server*.
4
+
5
+ See the API documentation at http://docs.github.com/simplabs/excellent and the WIKI at http://wiki.github.com/simplabs/excellent.
6
+ Join the Google Group and discuss about the future and possibilities of Excellent: http://groups.google.com/group/excellent-gem.
7
+
8
+ Excellent also has a CI server set up at http://ci.simplabs.com/excellent.
9
+
10
+ == Installation
11
+
12
+ Simply install with Ruby Gems:
13
+
14
+ gem sources -a http://gems.github.com
15
+ sudo gem install simplabs-excellent
16
+
17
+ == Example
18
+
19
+ Assume you have the following class definition,
20
+
21
+ class ShoppingBasket < ActiveRecord::Base
22
+
23
+ def initialize(items = [])
24
+ self.items = items
25
+ end
26
+
27
+ end
28
+
29
+ then Excellent will report the problems in this piece of code:
30
+
31
+ $ excellent shopping_basket.rb
32
+
33
+ Excellent result:
34
+
35
+ test.rb
36
+ * Line 1: ShoppingBasket does not validate any attributes.
37
+ * Line 1: ShoppingBasket defines initialize method.
38
+ * Line 1: ShoppingBasket does not specify attr_accessible.
39
+
40
+ Found 3 warnings.
41
+
42
+ To analyse all the models in your Rails application, just do
43
+
44
+ excellent app/models
45
+
46
+ in your <tt>RAILS_ROOT</tt>. You can also invoke analysation through the Simplabs::Excellent::Runner class. Excellent can also produce HTML output. To
47
+ get a formatted HTML report, just specify <tt>html:<filename></tt>:
48
+
49
+ excellent html:out.html app/models
50
+
51
+ You can also use Excellent in a Rake task:
52
+
53
+ require 'simplabs/excellent/rake'
54
+
55
+ Simplabs::Excellent::Rake::ExcellentTask.new(:excellent) do |t|
56
+ t.html = 'doc/excellent.html' # optional, if you don't specify html, output will be written to $stdout
57
+ t.paths = %w(app lib)
58
+ end
59
+
60
+ == Static analysis
61
+
62
+ A few words regarding static code analysis: Static code analysis tools like Excellent can never really understand the code. They just search for patterns that *might* inidicate problematic code. The word *might* really has to be stressed here since static analysis will usually return a reasonable number of false positives. For example, there might be pretty good reasons for empty +rescue+ blocks that suppress all errors (Excellent itself does it). So, don't try and code with the aim of passing Excellent with zero warnings. That will most likely make your code a mess. Instead use Excellent as a helper to find *possibly* problematic code early.
63
+
64
+ == Contribute
65
+
66
+ If you want to contribute, just fork the repo. Also I would appretiate suggestions for more checks (especially Rails specific checks) - simply open a new issue: http://github.com/simplabs/excellent/issues.
67
+
68
+ == Author
69
+
70
+ Copyright (c) 2008-2009 Marco Otte-Witte (http://simplabs.com), released under the MIT license.
71
+
72
+ Excellent was inspired by roodi (http://github.com/martinjandrews/roodi), reek (http://github.com/kevinrutherford/reek) and flog (http://github.com/seattlerb/flog).
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 1
3
+ :minor: 5
4
+ :patch: 4
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
4
+
5
+ require 'simplabs/excellent'
6
+ require 'pathname'
7
+
8
+ excellent = Simplabs::Excellent::Runner.new
9
+
10
+ if ARGV.first =~ /html:[^\s]+/
11
+ begin
12
+ output = File.open(ARGV.first.sub(/html:/, ''), 'w+')
13
+ formatter = Simplabs::Excellent::Formatters::Html.new(output)
14
+ ARGV.shift
15
+ rescue
16
+ end
17
+ else
18
+ formatter = Simplabs::Excellent::Formatters::Text.new
19
+ end
20
+
21
+ if ARGV.empty?
22
+ puts "\n You must specify one or more directories to analyse, e.g.:\n"
23
+ puts "\n excellent app/\n\n"
24
+ exit 1
25
+ end
26
+
27
+ begin
28
+ excellent.check_paths(ARGV, formatter)
29
+ rescue ArgumentError
30
+ puts "\n** Excellent cannot find the paths specified!\n\n"
31
+ exit 1
32
+ end
33
+
34
+ exit 0
@@ -0,0 +1,16 @@
1
+ require 'simplabs/excellent/checks'
2
+ require 'simplabs/excellent/parsing'
3
+ require 'simplabs/excellent/formatters'
4
+ require 'simplabs/excellent/runner'
5
+ require 'rubygems'
6
+ require 'sexp'
7
+
8
+ module Simplabs #:nodoc:
9
+
10
+ module Excellent #:nodoc:
11
+
12
+ VERSION = '1.5.1'
13
+
14
+ end
15
+
16
+ end
@@ -0,0 +1,33 @@
1
+ module Simplabs
2
+
3
+ module Excellent
4
+
5
+ module Checks #:nodoc:
6
+ end
7
+
8
+ end
9
+
10
+ end
11
+
12
+ require 'simplabs/excellent/checks/abc_metric_method_check'
13
+ require 'simplabs/excellent/checks/assignment_in_conditional_check'
14
+ require 'simplabs/excellent/checks/case_missing_else_check'
15
+ require 'simplabs/excellent/checks/class_line_count_check'
16
+ require 'simplabs/excellent/checks/class_name_check'
17
+ require 'simplabs/excellent/checks/singleton_variable_check'
18
+ require 'simplabs/excellent/checks/global_variable_check'
19
+ require 'simplabs/excellent/checks/control_coupling_check'
20
+ require 'simplabs/excellent/checks/cyclomatic_complexity_block_check'
21
+ require 'simplabs/excellent/checks/cyclomatic_complexity_method_check'
22
+ require 'simplabs/excellent/checks/empty_rescue_body_check'
23
+ require 'simplabs/excellent/checks/for_loop_check'
24
+ require 'simplabs/excellent/checks/method_line_count_check'
25
+ require 'simplabs/excellent/checks/method_name_check'
26
+ require 'simplabs/excellent/checks/module_line_count_check'
27
+ require 'simplabs/excellent/checks/module_name_check'
28
+ require 'simplabs/excellent/checks/parameter_number_check'
29
+ require 'simplabs/excellent/checks/nested_iterators_check'
30
+ require 'simplabs/excellent/checks/flog_method_check'
31
+ require 'simplabs/excellent/checks/flog_block_check'
32
+ require 'simplabs/excellent/checks/flog_class_check'
33
+ require 'simplabs/excellent/checks/rails'
@@ -0,0 +1,43 @@
1
+ require 'simplabs/excellent/checks/base'
2
+
3
+ module Simplabs
4
+
5
+ module Excellent
6
+
7
+ module Checks
8
+
9
+ # This check reports methods with an ABC metric score that is higher than the threshold. The ABC metric is basically a measure for complexity
10
+ # and is calculated as:
11
+ #
12
+ # a = number of assignments
13
+ # b = number of branches
14
+ # c = number of conditions
15
+ #
16
+ # score = Math.sqrt(a*a + b*b + c*c)
17
+ #
18
+ # ==== Applies to
19
+ #
20
+ # * methods
21
+ class AbcMetricMethodCheck < Base
22
+
23
+ DEFAULT_THRESHOLD = 10
24
+
25
+ def initialize(options = {}) #:nodoc:
26
+ super()
27
+ @threshold = options[:threshold] || DEFAULT_THRESHOLD
28
+ @interesting_nodes = [:defn, :defs]
29
+ end
30
+
31
+ def evaluate(context) #:nodoc:
32
+ unless context.abc_score <= @threshold
33
+ add_warning(context, '{{method}} has abc score of {{score}}.', { :method => context.full_name, :score => context.abc_score })
34
+ end
35
+ end
36
+
37
+ end
38
+
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -0,0 +1,39 @@
1
+ require 'simplabs/excellent/checks/base'
2
+
3
+ module Simplabs
4
+
5
+ module Excellent
6
+
7
+ module Checks
8
+
9
+ # This check reports conditionals that test an assignment as in
10
+ #
11
+ # something(var) if var = method()
12
+ #
13
+ # Assignments in conditions are often typos.
14
+ #
15
+ # ==== Applies to
16
+ #
17
+ # * +if+
18
+ # * +else+
19
+ # * +while+
20
+ # * +until+
21
+ class AssignmentInConditionalCheck < Base
22
+
23
+ def initialize(options = {}) #:nodoc:
24
+ super()
25
+ @interesting_nodes = [:if, :while, :until]
26
+ @interesting_files = [/\.rb$/, /\.erb$/]
27
+ end
28
+
29
+ def evaluate(context) #:nodoc:
30
+ add_warning(context, 'Assignment in condition.') if context.tests_assignment?
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+
37
+ end
38
+
39
+ end
@@ -0,0 +1,62 @@
1
+ require 'simplabs/excellent/warning'
2
+
3
+ module Simplabs
4
+
5
+ module Excellent
6
+
7
+ module Checks
8
+
9
+ # This is the base class for all code checks. All checks must specify +interesting_nodes+. When one of these nodes is processed by Excellent, it
10
+ # will invoke the +evaluate_node+ method of all checks that specify the node as one if their +interesting_nodes+.
11
+ class Base
12
+
13
+ attr_reader :warnings
14
+
15
+ # An array of node types that are interesting for the check. These are symbols as returned by RubyParser (see http://parsetree.rubyforge.org/ruby_parser/),
16
+ # e.g. <tt>:if</tt> or <tt>:defn</tt>
17
+ attr_reader :interesting_nodes
18
+
19
+ # An array of regular expressions for file names that are interesting for the check. These will usually be path extensions rather than longer
20
+ # patterns (e.g. *.rb as well as *.erb files or *.rb files only).
21
+ #
22
+ # Defaults to /\.rb$/. If you do not specify anything else in custom checks, only *.rb files will be processed
23
+ attr_reader :interesting_files
24
+
25
+ def initialize #:nodoc:
26
+ @warnings = []
27
+ @interesting_files = [/\.rb$/]
28
+ end
29
+
30
+ # This method is called whenever Excellent processes a node that the check specified as one of the nodes it is interested in (see interesting_nodes).
31
+ #
32
+ # ==== Parameters
33
+ #
34
+ # * <tt>context</tt> - This is the last context the code processor has constructed. It contains all information required to execute the check (see Simplabs::Excellent::Parsing::SexpContext).
35
+ def evaluate_node(context)
36
+ evaluate(context)
37
+ end
38
+
39
+ # Adds a warning
40
+ #
41
+ # ==== Parameters
42
+ #
43
+ # * <tt>context</tt> - The context the check has been executed on.
44
+ # * <tt>message</tt> - The warning message.
45
+ # * <tt>info</tt> - The information hash that contains more info on the finding.
46
+ # * <tt>offset</tt> - The line offset that is added to the context's line property.
47
+ def add_warning(context, message, info = {}, offset = 0)
48
+ klass = self.class
49
+ @warnings << Simplabs::Excellent::Warning.new(klass, message, context.file, context.line + offset, info)
50
+ end
51
+
52
+ def warnings_for(filename) #:nodoc:
53
+ warnings.select { |warning| warning.filename == filename }
54
+ end
55
+
56
+ end
57
+
58
+ end
59
+
60
+ end
61
+
62
+ end
@@ -0,0 +1,34 @@
1
+ require 'simplabs/excellent/checks/base'
2
+
3
+ module Simplabs
4
+
5
+ module Excellent
6
+
7
+ module Checks
8
+
9
+ # This check reports +case+ statements that don't have an +else+ clause that would be executed when no case matches. If the tested value will never
10
+ # adopt any other values than the ones tested for in the cases, this should be expressed in the code by e.g. throwing an exception in the +else+
11
+ # clause.
12
+ #
13
+ # ==== Applies to
14
+ #
15
+ # * +case+ statements
16
+ class CaseMissingElseCheck < Base
17
+
18
+ def initialize #:nodoc:
19
+ super
20
+ @interesting_nodes = [:case]
21
+ @interesting_files = [/\.rb$/, /\.erb$/]
22
+ end
23
+
24
+ def evaluate(context) #:nodoc:
25
+ add_warning(context, 'Case statement is missing else clause.') unless context.has_else_clause?
26
+ end
27
+
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,36 @@
1
+ require 'simplabs/excellent/checks/line_count_check'
2
+
3
+ module Simplabs
4
+
5
+ module Excellent
6
+
7
+ module Checks
8
+
9
+ # This check reports classes which have more lines than the threshold. Classes with a large number of lines are hard to read and understand and
10
+ # often an indicator for badly designed code as well.
11
+ #
12
+ # ==== Applies to
13
+ #
14
+ # * classes
15
+ class ClassLineCountCheck < LineCountCheck
16
+
17
+ DEFAULT_THRESHOLD = 400
18
+
19
+ def initialize(options = {}) #:nodoc:
20
+ threshold = options[:threshold] || DEFAULT_THRESHOLD
21
+ super([:class], threshold)
22
+ end
23
+
24
+ protected
25
+
26
+ def warning_args(context) #:nodoc:
27
+ [context, '{{class}} has {{count}} lines.', { :class => context.full_name, :count => context.line_count }]
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+
36
+ end