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.
- data/History.txt +19 -0
- data/README.rdoc +34 -0
- data/VERSION.yml +1 -1
- data/bin/excellent +21 -6
- data/lib/simplabs/excellent.rb +7 -5
- data/lib/simplabs/excellent/checks.rb +18 -1
- data/lib/simplabs/excellent/checks/abc_metric_method_check.rb +19 -56
- data/lib/simplabs/excellent/checks/assignment_in_conditional_check.rb +16 -16
- data/lib/simplabs/excellent/checks/base.rb +30 -21
- data/lib/simplabs/excellent/checks/case_missing_else_check.rb +13 -5
- data/lib/simplabs/excellent/checks/class_line_count_check.rb +10 -8
- data/lib/simplabs/excellent/checks/class_name_check.rb +11 -10
- data/lib/simplabs/excellent/checks/control_coupling_check.rb +13 -10
- data/lib/simplabs/excellent/checks/cyclomatic_complexity_block_check.rb +25 -9
- data/lib/simplabs/excellent/checks/cyclomatic_complexity_check.rb +4 -20
- data/lib/simplabs/excellent/checks/cyclomatic_complexity_method_check.rb +25 -10
- data/lib/simplabs/excellent/checks/duplication_check.rb +50 -0
- data/lib/simplabs/excellent/checks/empty_rescue_body_check.rb +10 -18
- 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 +20 -4
- data/lib/simplabs/excellent/checks/line_count_check.rb +3 -21
- data/lib/simplabs/excellent/checks/method_line_count_check.rb +9 -7
- data/lib/simplabs/excellent/checks/method_name_check.rb +13 -9
- data/lib/simplabs/excellent/checks/module_line_count_check.rb +9 -7
- data/lib/simplabs/excellent/checks/module_name_check.rb +11 -7
- data/lib/simplabs/excellent/checks/name_check.rb +3 -8
- data/lib/simplabs/excellent/checks/nested_iterators_check.rb +33 -0
- data/lib/simplabs/excellent/checks/parameter_number_check.rb +13 -12
- data/lib/simplabs/excellent/checks/rails.rb +17 -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/singleton_variable_check.rb +32 -0
- data/lib/simplabs/excellent/extensions/sexp.rb +21 -0
- data/lib/simplabs/excellent/extensions/string.rb +23 -0
- data/lib/simplabs/excellent/parsing.rb +12 -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 +36 -0
- data/lib/simplabs/excellent/parsing/case_context.rb +31 -0
- data/lib/simplabs/excellent/parsing/class_context.rb +68 -0
- data/lib/simplabs/excellent/parsing/code_processor.rb +154 -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/if_context.rb +38 -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/{core → parsing}/parser.rb +4 -2
- 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/runner.rb +105 -0
- data/lib/simplabs/excellent/warning.rb +53 -0
- data/spec/checks/abc_metric_method_check_spec.rb +36 -8
- data/spec/checks/assignment_in_conditional_check_spec.rb +31 -14
- data/spec/checks/case_missing_else_check_spec.rb +8 -8
- data/spec/checks/class_line_count_check_spec.rb +24 -11
- data/spec/checks/class_name_check_spec.rb +9 -9
- data/spec/checks/control_coupling_check_spec.rb +84 -13
- data/spec/checks/cyclomatic_complexity_block_check_spec.rb +13 -17
- data/spec/checks/cyclomatic_complexity_method_check_spec.rb +32 -6
- data/spec/checks/duplication_check_spec.rb +139 -0
- data/spec/checks/empty_rescue_body_check_spec.rb +54 -16
- 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 +11 -11
- data/spec/checks/method_line_count_check_spec.rb +11 -12
- data/spec/checks/method_name_check_spec.rb +34 -13
- data/spec/checks/module_line_count_check_spec.rb +11 -12
- data/spec/checks/module_name_check_spec.rb +31 -7
- data/spec/checks/nested_iterators_check_spec.rb +44 -0
- data/spec/checks/parameter_number_check_spec.rb +48 -12
- 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/singleton_variable_check_spec.rb +66 -0
- data/spec/{core/extensions/underscore_spec.rb → extensions/string_spec.rb} +1 -1
- metadata +58 -15
- data/README.markdown +0 -30
- data/lib/simplabs/excellent/checks/class_variable_check.rb +0 -25
- data/lib/simplabs/excellent/core.rb +0 -2
- data/lib/simplabs/excellent/core/checking_visitor.rb +0 -34
- data/lib/simplabs/excellent/core/error.rb +0 -31
- data/lib/simplabs/excellent/core/extensions/underscore.rb +0 -27
- data/lib/simplabs/excellent/core/iterator_visitor.rb +0 -29
- data/lib/simplabs/excellent/core/parse_tree_runner.rb +0 -88
- data/lib/simplabs/excellent/core/visitable_sexp.rb +0 -31
- data/spec/checks/class_variable_check_spec.rb +0 -26
@@ -0,0 +1,34 @@
|
|
1
|
+
module Simplabs
|
2
|
+
|
3
|
+
module Excellent
|
4
|
+
|
5
|
+
module Parsing
|
6
|
+
|
7
|
+
module Scopeable #:nodoc:
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def get_names
|
12
|
+
if @exp[1].is_a?(Sexp)
|
13
|
+
name = @exp[1].pop.to_s.strip
|
14
|
+
[name, "#{extract_prefixes}#{name}"]
|
15
|
+
else
|
16
|
+
[@exp[1].to_s, nil]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def extract_prefixes(exp = @exp[1].deep_clone, prefix = '')
|
21
|
+
prefix = "#{exp.pop}::#{prefix}" if exp.last.is_a?(Symbol)
|
22
|
+
if exp.last.is_a?(Sexp)
|
23
|
+
prefix = extract_prefixes(exp.last, prefix)
|
24
|
+
end
|
25
|
+
prefix
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
module Simplabs
|
2
|
+
|
3
|
+
module Excellent
|
4
|
+
|
5
|
+
module Parsing
|
6
|
+
|
7
|
+
# For most nodes the Excellent processor processes, it will create the corresponding context that contains meta information of the processed
|
8
|
+
# node. This is the base class for all these contexts.
|
9
|
+
#
|
10
|
+
# === Example
|
11
|
+
#
|
12
|
+
# For a method like the following:
|
13
|
+
#
|
14
|
+
# module Shop
|
15
|
+
# class Basket
|
16
|
+
# def buy_product(product)
|
17
|
+
# other_method
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# four context will be generated:
|
23
|
+
#
|
24
|
+
# ModuleContext
|
25
|
+
# name: 'Shop'
|
26
|
+
# full_name: 'Shop'
|
27
|
+
# parent: nil
|
28
|
+
# ClassContext
|
29
|
+
# name: 'Basket'
|
30
|
+
# full_name: 'Shop::Basket'
|
31
|
+
# parent: ModuleContext
|
32
|
+
# MethodContext
|
33
|
+
# name: 'buy_product'
|
34
|
+
# full_name: 'Shop::Basket#buy_product'
|
35
|
+
# parent: ClassContext
|
36
|
+
# parameters: [:product]
|
37
|
+
# CallContext (other_method)
|
38
|
+
# name: nil
|
39
|
+
# full_name: nil
|
40
|
+
# parent: MethodContext
|
41
|
+
# method: :other_method
|
42
|
+
#
|
43
|
+
# === Custom Processors
|
44
|
+
#
|
45
|
+
# The Excelent processor will also invoke custom processor methods on the contexts if they are defined. To process call nodes in the context for
|
46
|
+
# example, you could simply define a +process_call+ method in the context that will be invoked with each call Sexp (S-expression, see
|
47
|
+
# http://en.wikipedia.org/wiki/S_expression) that is processed by the Excellent processor.
|
48
|
+
#
|
49
|
+
# def process_call(exp)
|
50
|
+
# super
|
51
|
+
# do_something()
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# Custom <b>processor methods must always call +super+</b> since there might be several processor methods defined in several modules that are in the
|
55
|
+
# included in the context and all of these have to be invoked. Also <b>processor methods must not modify the passed Sexp</b> since other processor
|
56
|
+
# methods also need the complete Sexp. If you have to modify the Sexp in a processor method, deep clone it:
|
57
|
+
#
|
58
|
+
# exp = exp.deep_clone
|
59
|
+
#
|
60
|
+
class SexpContext
|
61
|
+
|
62
|
+
# The parent context
|
63
|
+
attr_reader :parent
|
64
|
+
|
65
|
+
# The name of the code fragment the context is bound to (e.g. 'User' for a +class+)
|
66
|
+
attr_reader :name
|
67
|
+
|
68
|
+
# The file the code fragment was read from
|
69
|
+
attr_reader :file
|
70
|
+
|
71
|
+
# The line the code fragment is located at
|
72
|
+
attr_reader :line
|
73
|
+
|
74
|
+
# Initializes a SexpContext.
|
75
|
+
#
|
76
|
+
# Always call +super+ in inherited custom contexts!
|
77
|
+
#
|
78
|
+
# === Parameters
|
79
|
+
#
|
80
|
+
# * <tt>exp</tt> - The Sexp (S-expression, see http://en.wikipedia.org/wiki/S_expression) the context is created for.
|
81
|
+
# * <tt>parent</tt> - The parent context.
|
82
|
+
def initialize(exp, parent = nil)
|
83
|
+
@exp = exp
|
84
|
+
@parent = parent
|
85
|
+
@file = exp.file
|
86
|
+
@line = exp.line
|
87
|
+
@full_name = nil
|
88
|
+
end
|
89
|
+
|
90
|
+
# Gets the full name of the code fragment the context is bound to. For a method +name+ might be '+add_product+' while +full_name+ might be
|
91
|
+
# 'Basket#add_product'.
|
92
|
+
def full_name
|
93
|
+
return @full_name if @full_name
|
94
|
+
return @name if @parent.blank?
|
95
|
+
"#{@parent.full_name}::#{@name}"
|
96
|
+
end
|
97
|
+
|
98
|
+
def method_missing(method, *args) #:nodoc:
|
99
|
+
return if method.to_s =~ /^process_[a-zA-Z0-9_]+$/
|
100
|
+
super
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def count_lines(node = @exp, line_numbers = [])
|
106
|
+
count = 0
|
107
|
+
line_numbers << node.line
|
108
|
+
node.children.each { |child| count += count_lines(child, line_numbers) }
|
109
|
+
line_numbers.uniq.length
|
110
|
+
end
|
111
|
+
|
112
|
+
def has_assignment?(exp = @exp[1])
|
113
|
+
found_assignment = false
|
114
|
+
found_assignment = found_assignment || exp.node_type == :lasgn
|
115
|
+
exp.children.each { |child| found_assignment = found_assignment || has_assignment?(child) }
|
116
|
+
found_assignment
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'simplabs/excellent/parsing/cyclomatic_complexity_measure'
|
2
|
+
require 'simplabs/excellent/parsing/abc_measure'
|
3
|
+
|
4
|
+
module Simplabs
|
5
|
+
|
6
|
+
module Excellent
|
7
|
+
|
8
|
+
module Parsing
|
9
|
+
|
10
|
+
class SingletonMethodContext < MethodContext #:nodoc:
|
11
|
+
|
12
|
+
include CyclomaticComplexityMeasure
|
13
|
+
include AbcMeasure
|
14
|
+
|
15
|
+
attr_reader :parameters
|
16
|
+
attr_reader :calls
|
17
|
+
|
18
|
+
def initialize(exp, parent)
|
19
|
+
super
|
20
|
+
@name = exp[2].to_s
|
21
|
+
@full_name = get_full_name
|
22
|
+
@calls = Hash.new(0)
|
23
|
+
end
|
24
|
+
|
25
|
+
def full_name
|
26
|
+
return @full_name if @full_name
|
27
|
+
parent = @parent.is_a?(BlockContext) ? @parent.parent : @parent
|
28
|
+
return @name if parent.blank?
|
29
|
+
"#{parent.full_name}.#{@name}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def record_call_to(exp)
|
33
|
+
@calls[exp] += 1
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def get_full_name
|
39
|
+
if @exp[1].is_a?(Sexp)
|
40
|
+
if @exp[1].node_type == :call
|
41
|
+
return "#{@exp[1][2]}.#{@name}"
|
42
|
+
elsif @exp[1].node_type == :const
|
43
|
+
return "#{@exp[1][1]}.#{@name}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Simplabs
|
2
|
+
|
3
|
+
module Excellent
|
4
|
+
|
5
|
+
module Parsing
|
6
|
+
|
7
|
+
class UntilContext < SexpContext #:nodoc:
|
8
|
+
|
9
|
+
def initialize(exp, parent)
|
10
|
+
super
|
11
|
+
@contains_assignment = has_assignment?
|
12
|
+
end
|
13
|
+
|
14
|
+
def tests_assignment?
|
15
|
+
@contains_assignment
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Simplabs
|
2
|
+
|
3
|
+
module Excellent
|
4
|
+
|
5
|
+
module Parsing
|
6
|
+
|
7
|
+
class WhileContext < SexpContext #:nodoc:
|
8
|
+
|
9
|
+
def initialize(exp, parent)
|
10
|
+
super
|
11
|
+
@contains_assignment = has_assignment?
|
12
|
+
end
|
13
|
+
|
14
|
+
def tests_assignment?
|
15
|
+
@contains_assignment
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'yaml'
|
3
|
+
require 'simplabs/excellent/parsing/parser'
|
4
|
+
require 'simplabs/excellent/parsing/code_processor'
|
5
|
+
|
6
|
+
module Simplabs
|
7
|
+
|
8
|
+
module Excellent
|
9
|
+
|
10
|
+
# The Runner is the interface to invoke parsing and processing of source code. You can pass either a String containing the code to process or the
|
11
|
+
# name of a file to read the code to process from.
|
12
|
+
class Runner
|
13
|
+
|
14
|
+
DEFAULT_CONFIG = {
|
15
|
+
:AssignmentInConditionalCheck => { },
|
16
|
+
:CaseMissingElseCheck => { },
|
17
|
+
:ClassLineCountCheck => { :threshold => 300 },
|
18
|
+
:ClassNameCheck => { :pattern => /^[A-Z][a-zA-Z0-9]*$/ },
|
19
|
+
:SingletonVariableCheck => { },
|
20
|
+
:CyclomaticComplexityBlockCheck => { :complexity => 4 },
|
21
|
+
:CyclomaticComplexityMethodCheck => { :complexity => 8 },
|
22
|
+
:EmptyRescueBodyCheck => { },
|
23
|
+
:ForLoopCheck => { },
|
24
|
+
:MethodLineCountCheck => { :line_count => 20 },
|
25
|
+
:MethodNameCheck => { :pattern => /^[_a-z<>=\[|+-\/\*`]+[_a-z0-9_<>=~@\[\]]*[=!\?]?$/ },
|
26
|
+
:ModuleLineCountCheck => { :line_count => 300 },
|
27
|
+
:ModuleNameCheck => { :pattern => /^[A-Z][a-zA-Z0-9]*$/ },
|
28
|
+
:ParameterNumberCheck => { :parameter_count => 3 },
|
29
|
+
:FlogMethodCheck => { },
|
30
|
+
:FlogBlockCheck => { },
|
31
|
+
:FlogClassCheck => { },
|
32
|
+
:'Rails::AttrProtectedCheck' => { },
|
33
|
+
:'Rails::AttrAccessibleCheck' => { }
|
34
|
+
}
|
35
|
+
|
36
|
+
attr_accessor :config #:nodoc:
|
37
|
+
|
38
|
+
# Initializes a Runner
|
39
|
+
#
|
40
|
+
# ==== Parameters
|
41
|
+
#
|
42
|
+
# * <tt>checks</tt> - The checks to apply - passed instances of the various check classes. If no checks are specified, all checks will be applied.
|
43
|
+
def initialize(*checks)
|
44
|
+
@config = DEFAULT_CONFIG
|
45
|
+
@checks = checks unless checks.empty?
|
46
|
+
@parser = Parsing::Parser.new
|
47
|
+
end
|
48
|
+
|
49
|
+
# Processes the +code+ and sets the file name of the warning to +filename+
|
50
|
+
#
|
51
|
+
# ==== Parameters
|
52
|
+
#
|
53
|
+
# * <tt>filename</tt> - The name of the file the code was read from.
|
54
|
+
# * <tt>code</tt> - The code to process (String).
|
55
|
+
def check(filename, code)
|
56
|
+
@checks ||= load_checks
|
57
|
+
@processor ||= Parsing::CodeProcessor.new(@checks)
|
58
|
+
node = parse(filename, code)
|
59
|
+
@processor.process(node)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Processes the +code+, setting the file name of the warnings to '+dummy-file.rb+'
|
63
|
+
#
|
64
|
+
# ==== Parameters
|
65
|
+
#
|
66
|
+
# * <tt>code</tt> - The code to process (String).
|
67
|
+
def check_code(code)
|
68
|
+
check('dummy-file.rb', code)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Processes the file +filename+. The code will be read from the file.
|
72
|
+
#
|
73
|
+
# ==== Parameters
|
74
|
+
#
|
75
|
+
# * <tt>filename</tt> - The name of the file to read the code from.
|
76
|
+
def check_file(filename)
|
77
|
+
check(filename, File.read(filename))
|
78
|
+
end
|
79
|
+
|
80
|
+
# Gets the warnings that were produced by the checks.
|
81
|
+
def warnings
|
82
|
+
@checks ||= []
|
83
|
+
@checks.collect { |check| check.warnings }.flatten
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def parse(filename, code)
|
89
|
+
@parser.parse(code, filename)
|
90
|
+
end
|
91
|
+
|
92
|
+
def load_checks
|
93
|
+
check_objects = []
|
94
|
+
DEFAULT_CONFIG.each_pair do |key, value|
|
95
|
+
klass = eval("Simplabs::Excellent::Checks::#{key.to_s}")
|
96
|
+
check_objects << (value.empty? ? klass.new : klass.new(value))
|
97
|
+
end
|
98
|
+
check_objects
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
@@ -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
|
@@ -3,14 +3,24 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
|
3
3
|
describe Simplabs::Excellent::Checks::AbcMetricMethodCheck do
|
4
4
|
|
5
5
|
before do
|
6
|
-
@excellent = Simplabs::Excellent::
|
6
|
+
@excellent = Simplabs::Excellent::Runner.new(Simplabs::Excellent::Checks::AbcMetricMethodCheck.new({ :threshold => 0 }))
|
7
7
|
end
|
8
8
|
|
9
9
|
describe '#evaluate' do
|
10
10
|
|
11
11
|
describe 'when processing assignments' do
|
12
12
|
|
13
|
-
|
13
|
+
it "should find =" do
|
14
|
+
content = <<-END
|
15
|
+
def method_name
|
16
|
+
foo = 1
|
17
|
+
end
|
18
|
+
END
|
19
|
+
|
20
|
+
verify_content_score(content, 1, 0, 0)
|
21
|
+
end
|
22
|
+
|
23
|
+
['*=', '/=', '%=', '+=', '<<=', '>>=', '&=', '|=', '^=', '-=', '**='].each do |assignment|
|
14
24
|
|
15
25
|
it "should find #{assignment}" do
|
16
26
|
content = <<-END
|
@@ -19,7 +29,8 @@ describe Simplabs::Excellent::Checks::AbcMetricMethodCheck do
|
|
19
29
|
end
|
20
30
|
END
|
21
31
|
|
22
|
-
|
32
|
+
# these special assignments have score 2 since before the value is assigned, a method is called on the old value
|
33
|
+
verify_content_score(content, 1, 0, 1)
|
23
34
|
end
|
24
35
|
|
25
36
|
end
|
@@ -78,17 +89,34 @@ describe Simplabs::Excellent::Checks::AbcMetricMethodCheck do
|
|
78
89
|
|
79
90
|
end
|
80
91
|
|
92
|
+
it 'should also work on singleton methods' do
|
93
|
+
content = <<-END
|
94
|
+
class Class
|
95
|
+
def self.method_name
|
96
|
+
foo = 1
|
97
|
+
end
|
98
|
+
end
|
99
|
+
END
|
100
|
+
@excellent.check_content(content)
|
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
|
+
|
81
109
|
end
|
82
110
|
|
83
111
|
def verify_content_score(content, a, b, c)
|
84
112
|
score = Math.sqrt(a*a + b*b + c*c)
|
85
113
|
@excellent.check_content(content)
|
86
|
-
|
114
|
+
warnings = @excellent.warnings
|
87
115
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
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}."
|
92
120
|
end
|
93
121
|
|
94
122
|
end
|