simplabs-excellent 1.0.1 → 1.2.1

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 (96) hide show
  1. data/History.txt +19 -0
  2. data/README.rdoc +34 -0
  3. data/VERSION.yml +1 -1
  4. data/bin/excellent +21 -6
  5. data/lib/simplabs/excellent.rb +7 -5
  6. data/lib/simplabs/excellent/checks.rb +18 -1
  7. data/lib/simplabs/excellent/checks/abc_metric_method_check.rb +19 -56
  8. data/lib/simplabs/excellent/checks/assignment_in_conditional_check.rb +16 -16
  9. data/lib/simplabs/excellent/checks/base.rb +30 -21
  10. data/lib/simplabs/excellent/checks/case_missing_else_check.rb +13 -5
  11. data/lib/simplabs/excellent/checks/class_line_count_check.rb +10 -8
  12. data/lib/simplabs/excellent/checks/class_name_check.rb +11 -10
  13. data/lib/simplabs/excellent/checks/control_coupling_check.rb +13 -10
  14. data/lib/simplabs/excellent/checks/cyclomatic_complexity_block_check.rb +25 -9
  15. data/lib/simplabs/excellent/checks/cyclomatic_complexity_check.rb +4 -20
  16. data/lib/simplabs/excellent/checks/cyclomatic_complexity_method_check.rb +25 -10
  17. data/lib/simplabs/excellent/checks/duplication_check.rb +50 -0
  18. data/lib/simplabs/excellent/checks/empty_rescue_body_check.rb +10 -18
  19. data/lib/simplabs/excellent/checks/flog_block_check.rb +40 -0
  20. data/lib/simplabs/excellent/checks/flog_check.rb +27 -0
  21. data/lib/simplabs/excellent/checks/flog_class_check.rb +40 -0
  22. data/lib/simplabs/excellent/checks/flog_method_check.rb +40 -0
  23. data/lib/simplabs/excellent/checks/for_loop_check.rb +20 -4
  24. data/lib/simplabs/excellent/checks/line_count_check.rb +3 -21
  25. data/lib/simplabs/excellent/checks/method_line_count_check.rb +9 -7
  26. data/lib/simplabs/excellent/checks/method_name_check.rb +13 -9
  27. data/lib/simplabs/excellent/checks/module_line_count_check.rb +9 -7
  28. data/lib/simplabs/excellent/checks/module_name_check.rb +11 -7
  29. data/lib/simplabs/excellent/checks/name_check.rb +3 -8
  30. data/lib/simplabs/excellent/checks/nested_iterators_check.rb +33 -0
  31. data/lib/simplabs/excellent/checks/parameter_number_check.rb +13 -12
  32. data/lib/simplabs/excellent/checks/rails.rb +17 -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/singleton_variable_check.rb +32 -0
  36. data/lib/simplabs/excellent/extensions/sexp.rb +21 -0
  37. data/lib/simplabs/excellent/extensions/string.rb +23 -0
  38. data/lib/simplabs/excellent/parsing.rb +12 -0
  39. data/lib/simplabs/excellent/parsing/abc_measure.rb +52 -0
  40. data/lib/simplabs/excellent/parsing/block_context.rb +43 -0
  41. data/lib/simplabs/excellent/parsing/call_context.rb +36 -0
  42. data/lib/simplabs/excellent/parsing/case_context.rb +31 -0
  43. data/lib/simplabs/excellent/parsing/class_context.rb +68 -0
  44. data/lib/simplabs/excellent/parsing/code_processor.rb +154 -0
  45. data/lib/simplabs/excellent/parsing/conditional_context.rb +25 -0
  46. data/lib/simplabs/excellent/parsing/cvar_context.rb +28 -0
  47. data/lib/simplabs/excellent/parsing/cyclomatic_complexity_measure.rb +73 -0
  48. data/lib/simplabs/excellent/parsing/flog_measure.rb +192 -0
  49. data/lib/simplabs/excellent/parsing/for_loop_context.rb +15 -0
  50. data/lib/simplabs/excellent/parsing/if_context.rb +38 -0
  51. data/lib/simplabs/excellent/parsing/method_context.rb +50 -0
  52. data/lib/simplabs/excellent/parsing/module_context.rb +29 -0
  53. data/lib/simplabs/excellent/{core → parsing}/parser.rb +4 -2
  54. data/lib/simplabs/excellent/parsing/resbody_context.rb +39 -0
  55. data/lib/simplabs/excellent/parsing/scopeable.rb +34 -0
  56. data/lib/simplabs/excellent/parsing/sexp_context.rb +125 -0
  57. data/lib/simplabs/excellent/parsing/singleton_method_context.rb +55 -0
  58. data/lib/simplabs/excellent/parsing/until_context.rb +24 -0
  59. data/lib/simplabs/excellent/parsing/while_context.rb +24 -0
  60. data/lib/simplabs/excellent/runner.rb +105 -0
  61. data/lib/simplabs/excellent/warning.rb +53 -0
  62. data/spec/checks/abc_metric_method_check_spec.rb +36 -8
  63. data/spec/checks/assignment_in_conditional_check_spec.rb +31 -14
  64. data/spec/checks/case_missing_else_check_spec.rb +8 -8
  65. data/spec/checks/class_line_count_check_spec.rb +24 -11
  66. data/spec/checks/class_name_check_spec.rb +9 -9
  67. data/spec/checks/control_coupling_check_spec.rb +84 -13
  68. data/spec/checks/cyclomatic_complexity_block_check_spec.rb +13 -17
  69. data/spec/checks/cyclomatic_complexity_method_check_spec.rb +32 -6
  70. data/spec/checks/duplication_check_spec.rb +139 -0
  71. data/spec/checks/empty_rescue_body_check_spec.rb +54 -16
  72. data/spec/checks/flog_block_check_spec.rb +28 -0
  73. data/spec/checks/flog_class_check_spec.rb +28 -0
  74. data/spec/checks/flog_method_check_spec.rb +46 -0
  75. data/spec/checks/for_loop_check_spec.rb +11 -11
  76. data/spec/checks/method_line_count_check_spec.rb +11 -12
  77. data/spec/checks/method_name_check_spec.rb +34 -13
  78. data/spec/checks/module_line_count_check_spec.rb +11 -12
  79. data/spec/checks/module_name_check_spec.rb +31 -7
  80. data/spec/checks/nested_iterators_check_spec.rb +44 -0
  81. data/spec/checks/parameter_number_check_spec.rb +48 -12
  82. data/spec/checks/rails/attr_accessible_check_spec.rb +79 -0
  83. data/spec/checks/rails/attr_protected_check_spec.rb +77 -0
  84. data/spec/checks/singleton_variable_check_spec.rb +66 -0
  85. data/spec/{core/extensions/underscore_spec.rb → extensions/string_spec.rb} +1 -1
  86. metadata +58 -15
  87. data/README.markdown +0 -30
  88. data/lib/simplabs/excellent/checks/class_variable_check.rb +0 -25
  89. data/lib/simplabs/excellent/core.rb +0 -2
  90. data/lib/simplabs/excellent/core/checking_visitor.rb +0 -34
  91. data/lib/simplabs/excellent/core/error.rb +0 -31
  92. data/lib/simplabs/excellent/core/extensions/underscore.rb +0 -27
  93. data/lib/simplabs/excellent/core/iterator_visitor.rb +0 -29
  94. data/lib/simplabs/excellent/core/parse_tree_runner.rb +0 -88
  95. data/lib/simplabs/excellent/core/visitable_sexp.rb +0 -31
  96. data/spec/checks/class_variable_check_spec.rb +0 -26
@@ -6,7 +6,7 @@ module Simplabs
6
6
 
7
7
  module Checks
8
8
 
9
- class LineCountCheck < Base
9
+ class LineCountCheck < Base #:nodoc:
10
10
 
11
11
  def initialize(interesting_nodes, threshold)
12
12
  super()
@@ -14,28 +14,10 @@ module Simplabs
14
14
  @threshold = threshold
15
15
  end
16
16
 
17
- def interesting_nodes
18
- @interesting_nodes
17
+ def evaluate(context)
18
+ add_warning(*warning_args(context)) unless context.line_count <= @threshold
19
19
  end
20
20
 
21
- def evaluate(node)
22
- line_count = count_lines(node_to_count(node)) - 1
23
- add_error(*error_args(node, line_count)) unless line_count <= @threshold
24
- end
25
-
26
- protected
27
-
28
- def node_to_count(node)
29
- node
30
- end
31
-
32
- def count_lines(node, line_numbers = [])
33
- count = 0
34
- line_numbers << node.line
35
- node.children.each { |child| count += count_lines(child, line_numbers) }
36
- line_numbers.uniq.length
37
- end
38
-
39
21
  end
40
22
 
41
23
  end
@@ -6,23 +6,25 @@ module Simplabs
6
6
 
7
7
  module Checks
8
8
 
9
+ # This check reports methods which have more lines than the threshold. Methods with a large number of lines are hard to read and understand
10
+ # and often an indicator for badly designed code as well.
11
+ #
12
+ # ==== Applies to
13
+ #
14
+ # * methods
9
15
  class MethodLineCountCheck < LineCountCheck
10
16
 
11
17
  DEFAULT_THRESHOLD = 20
12
18
 
13
- def initialize(options = {})
19
+ def initialize(options = {}) #:nodoc:
14
20
  threshold = options[:threshold] || DEFAULT_THRESHOLD
15
21
  super([:defn], threshold)
16
22
  end
17
23
 
18
24
  protected
19
25
 
20
- def node_to_count(node)
21
- node[3][1]
22
- end
23
-
24
- def error_args(node, line_count)
25
- ['Method {{method}} has {{count}} lines.', { :method => node[1], :count => line_count }]
26
+ def warning_args(context) #:nodoc:
27
+ [context, '{{method}} has {{count}} lines.', { :method => context.full_name, :count => context.line_count }]
26
28
  end
27
29
 
28
30
  end
@@ -6,23 +6,27 @@ module Simplabs
6
6
 
7
7
  module Checks
8
8
 
9
+ # This check reports methods with bad names. Badly named methods make reading and understanding the code much harder. Method names regarded as bad
10
+ # are for example:
11
+ #
12
+ # * names that are camel cased
13
+ #
14
+ # ==== Applies to
15
+ #
16
+ # * methods
9
17
  class MethodNameCheck < NameCheck
10
18
 
11
- DEFAULT_PATTERN = /^[_a-z<>=\[|+-\/\*\~\%\&`]+[_a-z0-9_<>=~@\[\]]*[=!\?]?$/
19
+ DEFAULT_PATTERN = /^[_a-z<>=\[|+-\/\*\~\%\&`\|\^]+[_a-z0-9_<>=~@\[\]]*[=!\?]?$/
12
20
 
13
- def initialize(options = {})
21
+ def initialize(options = {}) #:nodoc:
14
22
  pattern = options['pattern'] || DEFAULT_PATTERN
15
- super([:defn], pattern)
16
- end
17
-
18
- def find_name(node)
19
- node[1]
23
+ super([:defn, :defs], pattern)
20
24
  end
21
25
 
22
26
  protected
23
27
 
24
- def error_args(node)
25
- ['Bad method name {{method}}.', { :method => node[1] }]
28
+ def warning_args(context) #:nodoc:
29
+ [context, 'Bad method name {{method}}.', { :method => context.full_name }]
26
30
  end
27
31
 
28
32
  end
@@ -6,23 +6,25 @@ module Simplabs
6
6
 
7
7
  module Checks
8
8
 
9
+ # This check reports modules which have more lines than the threshold. Modules with a large number of lines are hard to read and understand
10
+ # and often an indicator for badly designed code as well.
11
+ #
12
+ # ==== Applies to
13
+ #
14
+ # * modules
9
15
  class ModuleLineCountCheck < LineCountCheck
10
16
 
11
17
  DEFAULT_THRESHOLD = 300
12
18
 
13
- def initialize(options = {})
19
+ def initialize(options = {}) #:nodoc:
14
20
  threshold = options[:threshold] || DEFAULT_THRESHOLD
15
21
  super([:module], threshold)
16
22
  end
17
23
 
18
24
  protected
19
25
 
20
- def node_to_count(node)
21
- node[2]
22
- end
23
-
24
- def error_args(node, line_count)
25
- ['Module {{module}} has {{count}} lines.', { :module => node[1], :count => line_count }]
26
+ def warning_args(context) #:nodoc:
27
+ [context, '{{module}} has {{count}} lines.', { :module => context.full_name, :count => context.line_count }]
26
28
  end
27
29
 
28
30
  end
@@ -6,23 +6,27 @@ module Simplabs
6
6
 
7
7
  module Checks
8
8
 
9
+ # This check reports modules with bad names. Badly named modules make reading and understanding the code much harder. Module names regarded as bad
10
+ # are for example:
11
+ #
12
+ # * names that are not Pascal cased (camel cased, starting with an upper case letter)
13
+ #
14
+ # ==== Applies to
15
+ #
16
+ # * modules
9
17
  class ModuleNameCheck < NameCheck
10
18
 
11
19
  DEFAULT_PATTERN = /^[A-Z][a-zA-Z0-9]*$/
12
20
 
13
- def initialize(options = {})
21
+ def initialize(options = {}) #:nodoc:
14
22
  pattern = options['pattern'] || DEFAULT_PATTERN
15
23
  super([:module], pattern)
16
24
  end
17
25
 
18
- def find_name(node)
19
- node[1].class == Symbol ? node[1] : node[1].last
20
- end
21
-
22
26
  protected
23
27
 
24
- def error_args(node)
25
- ['Bad module name {{module}}.', { :module => node[1] }]
28
+ def warning_args(context) #:nodoc:
29
+ [context, 'Bad module name {{module}}.', { :module => context.full_name }]
26
30
  end
27
31
 
28
32
  end
@@ -6,7 +6,7 @@ module Simplabs
6
6
 
7
7
  module Checks
8
8
 
9
- class NameCheck < Base
9
+ class NameCheck < Base #:nodoc:
10
10
 
11
11
  def initialize(interesting_nodes, pattern)
12
12
  super()
@@ -14,13 +14,8 @@ module Simplabs
14
14
  @pattern = pattern
15
15
  end
16
16
 
17
- def interesting_nodes
18
- @interesting_nodes
19
- end
20
-
21
- def evaluate(node)
22
- name = find_name(node)
23
- add_error(*error_args(node)) unless name.to_s =~ @pattern
17
+ def evaluate(context)
18
+ add_warning(*warning_args(context)) unless context.name.to_s =~ @pattern
24
19
  end
25
20
 
26
21
  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 nested iterators. Nested iterators lead to introduce performance issues.
10
+ #
11
+ # ==== Applies to
12
+ #
13
+ # * blocks
14
+ class NestedIteratorsCheck < Base
15
+
16
+ def initialize #:nodoc:
17
+ super
18
+ @interesting_nodes = [:iter]
19
+ end
20
+
21
+ def evaluate(context) #:nodoc:
22
+ if context.inside_block?
23
+ add_warning(context, '{{block}} inside of {{parent}}.', { :block => context.full_name, :parent => context.parent.full_name })
24
+ end
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+
33
+ end
@@ -6,25 +6,26 @@ module Simplabs
6
6
 
7
7
  module Checks
8
8
 
9
+ # This check reports method and blocks that have more parameters than the threshold. Methods with long parameter lists are harder to understand
10
+ # and often an indicator for bad design as well.
11
+ #
12
+ # ==== Applies to
13
+ #
14
+ # * methods
15
+ # * blocks
9
16
  class ParameterNumberCheck < Base
10
17
 
11
18
  DEFAULT_THRESHOLD = 3
12
19
 
13
- def initialize(options = {})
20
+ def initialize(options = {}) #:nodoc:
14
21
  super()
15
- @threshold = options[:threshold] || DEFAULT_THRESHOLD
22
+ @threshold = options[:threshold] || DEFAULT_THRESHOLD
23
+ @interesting_nodes = [:defn, :iter, :defs]
16
24
  end
17
25
 
18
- def interesting_nodes
19
- [:defn]
20
- end
21
-
22
- def evaluate(node)
23
- method_name = node[1]
24
- parameters = node[2][1..-1]
25
- parameter_count = parameters.inject(0) { |count, each| count = count + (each.class == Symbol ? 1 : 0) }
26
- unless parameter_count <= @threshold
27
- add_error('Method {{method}} has {{parameter_count}} parameters.', { :method => method_name, :parameter_count => parameter_count })
26
+ def evaluate(context) #:nodoc:
27
+ unless context.parameters.length <= @threshold
28
+ add_warning(context, '{{method}} has {{parameters}} parameters.', { :method => context.full_name, :parameters => context.parameters.length })
28
29
  end
29
30
  end
30
31
 
@@ -0,0 +1,17 @@
1
+ module Simplabs
2
+
3
+ module Excellent
4
+
5
+ module Checks
6
+
7
+ module Rails #:nodoc:
8
+ end
9
+
10
+ end
11
+
12
+ end
13
+
14
+ end
15
+
16
+ require 'simplabs/excellent/checks/rails/attr_accessible_check'
17
+ require 'simplabs/excellent/checks/rails/attr_protected_check'
@@ -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 +ActiveRecord+ models that do not specify +attr_accessible+. Specifying +attr_accessible+ is viable to protect models from
12
+ # mass assignment attacks (see http://guides.rubyonrails.org/security.html#mass-assignment). +attr_accessible+ specifies a list of properties
13
+ # that are writeable by mass assignments. For a +User+ model for example, that list would possibly include properties like +first_name+ and
14
+ # +last_name+ while it should not include properties like +is_admin+.
15
+ #
16
+ # ==== Applies to
17
+ #
18
+ # * +ActiveRecord+ models
19
+ class AttrAccessibleCheck < Base
20
+
21
+ def initialize #:nodoc:
22
+ super
23
+ @interesting_nodes = [:class]
24
+ end
25
+
26
+ def evaluate(context) #:nodoc:
27
+ add_warning(context, '{{class}} does not specify attr_accessible.', { :class => context.full_name }) if context.activerecord_model? && !context.specifies_attr_accessible?
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+
36
+ end
37
+
38
+ 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
+ module Rails
10
+
11
+ # This check reports +ActiveRecord+ models that specify +attr_protected+. Like +attr_accessible+, +attr_protected+ is a helper to secure
12
+ # +ActiveRecord+ models against mass assignment attacks (see http://guides.rubyonrails.org/security.html#mass-assignment), but instead of
13
+ # specifying a white list of properties that are writeable by mass assignments as +attr_accessible+ does, +attr_protected+ specifies a black
14
+ # list. Such a black list approach is usually less secure since the list has to be updated for every new property that is introduced, which
15
+ # is easy to forget.
16
+ #
17
+ # ==== Applies to
18
+ #
19
+ # * +ActiveRecord+ models
20
+ class AttrProtectedCheck < Base
21
+
22
+ def initialize #:nodoc:
23
+ super
24
+ @interesting_nodes = [:class]
25
+ end
26
+
27
+ def evaluate(context) #:nodoc:
28
+ add_warning(context, '{{class}} specifies attr_protected.', { :class => context.full_name }) if context.activerecord_model? && context.specifies_attr_protected?
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+
37
+ end
38
+
39
+ end
@@ -0,0 +1,32 @@
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
+ end
21
+
22
+ def evaluate(context) #:nodoc:
23
+ add_warning(context, 'Singleton variable {{variable}} used.', { :variable => context.full_name })
24
+ end
25
+
26
+ end
27
+
28
+ end
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,21 @@
1
+ module Simplabs
2
+
3
+ module Excellent
4
+
5
+ module Extensions #:nodoc:
6
+
7
+ ::Sexp.class_eval do
8
+
9
+ def children
10
+ sexp_body.select { |child| child.is_a?(Sexp) }
11
+ rescue
12
+ []
13
+ end
14
+
15
+ end
16
+
17
+ end
18
+
19
+ end
20
+
21
+ end
@@ -0,0 +1,23 @@
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
+ end
18
+
19
+ end
20
+
21
+ end
22
+
23
+ end