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