forked from troessner/reek
/
smells.rb
162 lines (126 loc) · 3.18 KB
/
smells.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
$:.unshift File.dirname(__FILE__)
require 'reek/printer'
require 'reek/options'
module Reek
class Smell
include Comparable
def self.convert_camel_case(class_name)
class_name.gsub(/([a-z])([A-Z])/) { |s| "#{$1} #{$2}"}
end
def initialize(context, arg=nil)
@context = context
end
def self.check(exp, context, arg=nil)
smell = new(context, arg)
return false unless smell.recognise?(exp)
context.report(smell)
true
end
def recognise?(stuff)
@context != nil
end
def hash # :nodoc:
report.hash
end
def <=>(other) # :nodoc:
Options[:sort_order].compare(self, other)
end
alias eql? <=>
def name
self.class.convert_camel_case(self.class.name.split(/::/)[1])
end
def report
"[#{name}] #{detailed_report}"
end
alias inspect report
def to_s
report
end
end
class LongParameterList < Smell
MAX_ALLOWED = 3
def self.count_parameters(exp)
result = exp.length - 1
result -= 1 if Array === exp[-1] and exp[-1][0] == :block
result
end
def recognise?(args)
@num_params = LongParameterList.count_parameters(args)
@num_params > MAX_ALLOWED
end
def detailed_report
"#{@context.to_s} has #{@num_params} parameters"
end
end
class LongYieldList < LongParameterList
def recognise?(args)
@num_params = args.length
Array === args and @num_params > MAX_ALLOWED
end
def detailed_report
"#{@context} yields #{@num_params} parameters"
end
end
class LongMethod < Smell
MAX_ALLOWED = 5
def recognise?(num_stmts)
@num_stmts = num_stmts
num_stmts > MAX_ALLOWED
end
def detailed_report
"#{@context} has approx #{@num_stmts} statements"
end
end
class FeatureEnvy < Smell
def recognise?(refs)
@refs = refs
!refs.self_is_max?
end
def detailed_report
receiver = @refs.max_keys.map {|r| Printer.print(r)}.sort.join(' and ')
"#{@context} uses #{receiver} more than self"
end
end
class UtilityFunction < Smell
def recognise?(refs)
refs.refs_to_self == 0
end
def detailed_report
"#{@context} doesn't depend on instance state"
end
end
class LargeClass < Smell
MAX_ALLOWED = 25
def recognise?(name)
klass = Object.const_get(name) rescue return
@num_methods = klass.instance_methods.length - klass.superclass.instance_methods.length
@num_methods > MAX_ALLOWED
end
def detailed_report
"#{@context} has #{@num_methods} methods"
end
end
class UncommunicativeName < Smell
def initialize(context, symbol_type)
super
@symbol_type = symbol_type
end
def recognise?(symbol)
@symbol = symbol.to_s
return false if @symbol == '*'
min_len = (/^@/ === @symbol) ? 3 : 2;
@symbol.length < min_len
end
def detailed_report
"#{@context} uses the #{@symbol_type} name '#{@symbol}'"
end
end
class NestedIterators < Smell
def recognise?(already_in_iter)
already_in_iter
end
def detailed_report
"#{@context} has nested iterators"
end
end
end