reek 1.2.5 → 1.2.6
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +6 -1
- data/features/samples.feature +68 -68
- data/features/stdin.feature +16 -0
- data/features/step_definitions/reek_steps.rb +4 -0
- data/lib/reek.rb +1 -1
- data/lib/reek/adapters/source.rb +11 -1
- data/lib/reek/block_context.rb +1 -4
- data/lib/reek/class_context.rb +0 -4
- data/lib/reek/code_context.rb +6 -7
- data/lib/reek/if_context.rb +2 -8
- data/lib/reek/method_context.rb +1 -4
- data/lib/reek/module_context.rb +1 -4
- data/lib/reek/singleton_method_context.rb +5 -6
- data/lib/reek/smell_warning.rb +10 -14
- data/lib/reek/smells/smell_detector.rb +2 -13
- data/lib/reek/smells/uncommunicative_name.rb +5 -1
- data/lib/reek/stop_context.rb +5 -4
- data/reek.gemspec +5 -3
- data/spec/reek/adapters/source_spec.rb +29 -4
- data/spec/reek/block_context_spec.rb +19 -0
- data/spec/reek/code_context_spec.rb +13 -6
- data/spec/reek/if_context_spec.rb +2 -2
- data/spec/reek/module_context_spec.rb +8 -0
- data/spec/reek/singleton_method_context_spec.rb +3 -4
- data/spec/reek/smell_warning_spec.rb +40 -16
- data/spec/reek/smells/uncommunicative_name_spec.rb +20 -10
- data/spec/reek/stop_context_spec.rb +6 -0
- metadata +6 -3
data/History.txt
CHANGED
@@ -1,7 +1,12 @@
|
|
1
|
+
== 1.2.6 (2009-11-28)
|
2
|
+
|
3
|
+
=== Minor Changes
|
4
|
+
* Corrected display of module methods to use # (#56)
|
5
|
+
|
1
6
|
== 1.2.5 (2009-11-19)
|
2
7
|
|
3
8
|
=== Minor Changes
|
4
|
-
* Ignores ruby_parser errors and
|
9
|
+
* Ignores ruby_parser errors and pretends the offending file was empty
|
5
10
|
|
6
11
|
== 1.2.4 (2009-11-17)
|
7
12
|
|
data/features/samples.feature
CHANGED
@@ -13,6 +13,8 @@ Feature: Basic smell detection
|
|
13
13
|
spec/samples/inline.rb -- 39 warnings (+1 masked):
|
14
14
|
Inline declares the class variable @@directory (Class Variable)
|
15
15
|
Inline declares the class variable @@rootdir (Class Variable)
|
16
|
+
Inline#self.rootdir calls env.nil? twice (Duplication)
|
17
|
+
Inline#self.rootdir has approx 8 statements (Long Method)
|
16
18
|
Inline::C declares the class variable @@type_map (Class Variable)
|
17
19
|
Inline::C has at least 13 instance variables (Large Class)
|
18
20
|
Inline::C takes parameters [options, src] to 5 methods (Data Clump)
|
@@ -45,8 +47,6 @@ Feature: Basic smell detection
|
|
45
47
|
Inline::C#parse_signature/block has the variable name 'x' (Uncommunicative Name)
|
46
48
|
Inline::C#strip_comments doesn't depend on instance state (Utility Function)
|
47
49
|
Inline::C#strip_comments refers to src more than self (Feature Envy)
|
48
|
-
Inline::self.rootdir calls env.nil? twice (Duplication)
|
49
|
-
Inline::self.rootdir has approx 8 statements (Long Method)
|
50
50
|
Module#inline calls Inline.const_get(lang) twice (Duplication)
|
51
51
|
Module#inline has approx 11 statements (Long Method)
|
52
52
|
Module#inline is controlled by argument options (Control Couple)
|
@@ -65,70 +65,6 @@ Feature: Basic smell detection
|
|
65
65
|
OptionParser tests default_pattern at least 7 times (Simulated Polymorphism)
|
66
66
|
OptionParser tests not_style at least 3 times (Simulated Polymorphism)
|
67
67
|
OptionParser tests s at least 7 times (Simulated Polymorphism)
|
68
|
-
OptionParser#CompletingHash#match/block/block is nested (Nested Iterators)
|
69
|
-
OptionParser#Completion::complete calls candidates.size twice (Duplication)
|
70
|
-
OptionParser#Completion::complete calls k.id2name twice (Duplication)
|
71
|
-
OptionParser#Completion::complete has approx 22 statements (Long Method)
|
72
|
-
OptionParser#Completion::complete has the variable name 'k' (Uncommunicative Name)
|
73
|
-
OptionParser#Completion::complete has the variable name 'v' (Uncommunicative Name)
|
74
|
-
OptionParser#Completion::complete is controlled by argument icase (Control Couple)
|
75
|
-
OptionParser#Completion::complete refers to candidates more than self (Feature Envy)
|
76
|
-
OptionParser#Completion::complete/block has the variable name 'k' (Uncommunicative Name)
|
77
|
-
OptionParser#Completion::complete/block has the variable name 'v' (Uncommunicative Name)
|
78
|
-
OptionParser#List#accept has the variable name 't' (Uncommunicative Name)
|
79
|
-
OptionParser#List#accept is controlled by argument pat (Control Couple)
|
80
|
-
OptionParser#List#accept refers to pat more than self (Feature Envy)
|
81
|
-
OptionParser#List#add_banner refers to opt more than self (Feature Envy)
|
82
|
-
OptionParser#List#complete has 4 parameters (Long Parameter List)
|
83
|
-
OptionParser#List#complete is controlled by argument icase (Control Couple)
|
84
|
-
OptionParser#List#reject has the variable name 't' (Uncommunicative Name)
|
85
|
-
OptionParser#List#summarize refers to opt more than self (Feature Envy)
|
86
|
-
OptionParser#List#update has 5 parameters (Long Parameter List)
|
87
|
-
OptionParser#List#update has approx 6 statements (Long Method)
|
88
|
-
OptionParser#List#update has the variable name 'o' (Uncommunicative Name)
|
89
|
-
OptionParser#List#update is controlled by argument lopts (Control Couple)
|
90
|
-
OptionParser#List#update is controlled by argument sopts (Control Couple)
|
91
|
-
OptionParser#List#update/block has the variable name 'o' (Uncommunicative Name)
|
92
|
-
OptionParser#ParseError#set_option is controlled by argument eq (Control Couple)
|
93
|
-
OptionParser#Switch#NoArgument#parse is controlled by argument arg (Control Couple)
|
94
|
-
OptionParser#Switch#OptionalArgument#parse is controlled by argument arg (Control Couple)
|
95
|
-
OptionParser#Switch#PlacedArgument#parse has approx 6 statements (Long Method)
|
96
|
-
OptionParser#Switch#RequiredArgument#parse is controlled by argument arg (Control Couple)
|
97
|
-
OptionParser#Switch#add_banner has the variable name 's' (Uncommunicative Name)
|
98
|
-
OptionParser#Switch#conv_arg calls conv twice (Duplication)
|
99
|
-
OptionParser#Switch#initialize has 7 parameters (Long Parameter List)
|
100
|
-
OptionParser#Switch#parse_arg calls pattern twice (Duplication)
|
101
|
-
OptionParser#Switch#parse_arg calls s.length twice (Duplication)
|
102
|
-
OptionParser#Switch#parse_arg has approx 11 statements (Long Method)
|
103
|
-
OptionParser#Switch#parse_arg has the variable name 'm' (Uncommunicative Name)
|
104
|
-
OptionParser#Switch#parse_arg has the variable name 's' (Uncommunicative Name)
|
105
|
-
OptionParser#Switch#self.guess has the variable name 't' (Uncommunicative Name)
|
106
|
-
OptionParser#Switch#self.incompatible_argument_styles has the variable name 't' (Uncommunicative Name)
|
107
|
-
OptionParser#Switch#summarize calls (indent + l) twice (Duplication)
|
108
|
-
OptionParser#Switch#summarize calls arg 4 times (Duplication)
|
109
|
-
OptionParser#Switch#summarize calls left.collect twice (Duplication)
|
110
|
-
OptionParser#Switch#summarize calls left.collect { |s| s.length }.max twice (Duplication)
|
111
|
-
OptionParser#Switch#summarize calls left.collect { |s| s.length }.max.to_i twice (Duplication)
|
112
|
-
OptionParser#Switch#summarize calls left.shift twice (Duplication)
|
113
|
-
OptionParser#Switch#summarize calls left[-1] 3 times (Duplication)
|
114
|
-
OptionParser#Switch#summarize calls s.length 3 times (Duplication)
|
115
|
-
OptionParser#Switch#summarize has 5 parameters (Long Parameter List)
|
116
|
-
OptionParser#Switch#summarize has approx 25 statements (Long Method)
|
117
|
-
OptionParser#Switch#summarize has the variable name 'l' (Uncommunicative Name)
|
118
|
-
OptionParser#Switch#summarize has the variable name 'r' (Uncommunicative Name)
|
119
|
-
OptionParser#Switch#summarize has the variable name 's' (Uncommunicative Name)
|
120
|
-
OptionParser#Switch#summarize/block has the variable name 's' (Uncommunicative Name)
|
121
|
-
OptionParser#Switch#summarize/block/block is nested (Nested Iterators)
|
122
|
-
OptionParser#block has the variable name 'f' (Uncommunicative Name)
|
123
|
-
OptionParser#block has the variable name 'k' (Uncommunicative Name)
|
124
|
-
OptionParser#block has the variable name 'o' (Uncommunicative Name)
|
125
|
-
OptionParser#block has the variable name 's' (Uncommunicative Name)
|
126
|
-
OptionParser#block is controlled by argument o (Control Couple)
|
127
|
-
OptionParser#block is controlled by argument s (Control Couple)
|
128
|
-
OptionParser#block/block has the variable name 's' (Uncommunicative Name)
|
129
|
-
OptionParser#block/block has the variable name 'v' (Uncommunicative Name)
|
130
|
-
OptionParser#block/block is controlled by argument pkg (Control Couple)
|
131
|
-
OptionParser#block/block is nested (Nested Iterators)
|
132
68
|
OptionParser#complete has 4 parameters (Long Parameter List)
|
133
69
|
OptionParser#complete is controlled by argument icase (Control Couple)
|
134
70
|
OptionParser#complete/block/block is nested (Nested Iterators)
|
@@ -182,6 +118,70 @@ Feature: Basic smell detection
|
|
182
118
|
OptionParser#summarize has 4 parameters (Long Parameter List)
|
183
119
|
OptionParser#summarize/block has the variable name 'l' (Uncommunicative Name)
|
184
120
|
OptionParser#ver has the variable name 'v' (Uncommunicative Name)
|
121
|
+
OptionParser/block has the variable name 'f' (Uncommunicative Name)
|
122
|
+
OptionParser/block has the variable name 'k' (Uncommunicative Name)
|
123
|
+
OptionParser/block has the variable name 'o' (Uncommunicative Name)
|
124
|
+
OptionParser/block has the variable name 's' (Uncommunicative Name)
|
125
|
+
OptionParser/block is controlled by argument o (Control Couple)
|
126
|
+
OptionParser/block is controlled by argument s (Control Couple)
|
127
|
+
OptionParser/block/block has the variable name 's' (Uncommunicative Name)
|
128
|
+
OptionParser/block/block has the variable name 'v' (Uncommunicative Name)
|
129
|
+
OptionParser/block/block is controlled by argument pkg (Control Couple)
|
130
|
+
OptionParser/block/block is nested (Nested Iterators)
|
131
|
+
OptionParser::CompletingHash#match/block/block is nested (Nested Iterators)
|
132
|
+
OptionParser::Completion#complete calls candidates.size twice (Duplication)
|
133
|
+
OptionParser::Completion#complete calls k.id2name twice (Duplication)
|
134
|
+
OptionParser::Completion#complete has approx 22 statements (Long Method)
|
135
|
+
OptionParser::Completion#complete has the variable name 'k' (Uncommunicative Name)
|
136
|
+
OptionParser::Completion#complete has the variable name 'v' (Uncommunicative Name)
|
137
|
+
OptionParser::Completion#complete is controlled by argument icase (Control Couple)
|
138
|
+
OptionParser::Completion#complete refers to candidates more than self (Feature Envy)
|
139
|
+
OptionParser::Completion#complete/block has the variable name 'k' (Uncommunicative Name)
|
140
|
+
OptionParser::Completion#complete/block has the variable name 'v' (Uncommunicative Name)
|
141
|
+
OptionParser::List#accept has the variable name 't' (Uncommunicative Name)
|
142
|
+
OptionParser::List#accept is controlled by argument pat (Control Couple)
|
143
|
+
OptionParser::List#accept refers to pat more than self (Feature Envy)
|
144
|
+
OptionParser::List#add_banner refers to opt more than self (Feature Envy)
|
145
|
+
OptionParser::List#complete has 4 parameters (Long Parameter List)
|
146
|
+
OptionParser::List#complete is controlled by argument icase (Control Couple)
|
147
|
+
OptionParser::List#reject has the variable name 't' (Uncommunicative Name)
|
148
|
+
OptionParser::List#summarize refers to opt more than self (Feature Envy)
|
149
|
+
OptionParser::List#update has 5 parameters (Long Parameter List)
|
150
|
+
OptionParser::List#update has approx 6 statements (Long Method)
|
151
|
+
OptionParser::List#update has the variable name 'o' (Uncommunicative Name)
|
152
|
+
OptionParser::List#update is controlled by argument lopts (Control Couple)
|
153
|
+
OptionParser::List#update is controlled by argument sopts (Control Couple)
|
154
|
+
OptionParser::List#update/block has the variable name 'o' (Uncommunicative Name)
|
155
|
+
OptionParser::ParseError#set_option is controlled by argument eq (Control Couple)
|
156
|
+
OptionParser::Switch#add_banner has the variable name 's' (Uncommunicative Name)
|
157
|
+
OptionParser::Switch#conv_arg calls conv twice (Duplication)
|
158
|
+
OptionParser::Switch#initialize has 7 parameters (Long Parameter List)
|
159
|
+
OptionParser::Switch#parse_arg calls pattern twice (Duplication)
|
160
|
+
OptionParser::Switch#parse_arg calls s.length twice (Duplication)
|
161
|
+
OptionParser::Switch#parse_arg has approx 11 statements (Long Method)
|
162
|
+
OptionParser::Switch#parse_arg has the variable name 'm' (Uncommunicative Name)
|
163
|
+
OptionParser::Switch#parse_arg has the variable name 's' (Uncommunicative Name)
|
164
|
+
OptionParser::Switch#self.guess has the variable name 't' (Uncommunicative Name)
|
165
|
+
OptionParser::Switch#self.incompatible_argument_styles has the variable name 't' (Uncommunicative Name)
|
166
|
+
OptionParser::Switch#summarize calls (indent + l) twice (Duplication)
|
167
|
+
OptionParser::Switch#summarize calls arg 4 times (Duplication)
|
168
|
+
OptionParser::Switch#summarize calls left.collect twice (Duplication)
|
169
|
+
OptionParser::Switch#summarize calls left.collect { |s| s.length }.max twice (Duplication)
|
170
|
+
OptionParser::Switch#summarize calls left.collect { |s| s.length }.max.to_i twice (Duplication)
|
171
|
+
OptionParser::Switch#summarize calls left.shift twice (Duplication)
|
172
|
+
OptionParser::Switch#summarize calls left[-1] 3 times (Duplication)
|
173
|
+
OptionParser::Switch#summarize calls s.length 3 times (Duplication)
|
174
|
+
OptionParser::Switch#summarize has 5 parameters (Long Parameter List)
|
175
|
+
OptionParser::Switch#summarize has approx 25 statements (Long Method)
|
176
|
+
OptionParser::Switch#summarize has the variable name 'l' (Uncommunicative Name)
|
177
|
+
OptionParser::Switch#summarize has the variable name 'r' (Uncommunicative Name)
|
178
|
+
OptionParser::Switch#summarize has the variable name 's' (Uncommunicative Name)
|
179
|
+
OptionParser::Switch#summarize/block has the variable name 's' (Uncommunicative Name)
|
180
|
+
OptionParser::Switch#summarize/block/block is nested (Nested Iterators)
|
181
|
+
OptionParser::Switch::NoArgument#parse is controlled by argument arg (Control Couple)
|
182
|
+
OptionParser::Switch::OptionalArgument#parse is controlled by argument arg (Control Couple)
|
183
|
+
OptionParser::Switch::PlacedArgument#parse has approx 6 statements (Long Method)
|
184
|
+
OptionParser::Switch::RequiredArgument#parse is controlled by argument arg (Control Couple)
|
185
185
|
block has the variable name 'q' (Uncommunicative Name)
|
186
186
|
|
187
187
|
"""
|
@@ -198,8 +198,6 @@ Feature: Basic smell detection
|
|
198
198
|
RedCloth tests codepre.zero? at least 3 times (Simulated Polymorphism)
|
199
199
|
RedCloth tests href at least 3 times (Simulated Polymorphism)
|
200
200
|
RedCloth tests title at least 4 times (Simulated Polymorphism)
|
201
|
-
RedCloth#block has the variable name 'a' (Uncommunicative Name)
|
202
|
-
RedCloth#block has the variable name 'b' (Uncommunicative Name)
|
203
201
|
RedCloth#block_markdown_atx refers to text more than self (Feature Envy)
|
204
202
|
RedCloth#block_markdown_bq has approx 6 statements (Long Method)
|
205
203
|
RedCloth#block_markdown_rule refers to text more than self (Feature Envy)
|
@@ -288,5 +286,7 @@ Feature: Basic smell detection
|
|
288
286
|
RedCloth#textile_p has 4 parameters (Long Parameter List)
|
289
287
|
RedCloth#textile_p is controlled by argument atts (Control Couple)
|
290
288
|
RedCloth#to_html has approx 24 statements (Long Method)
|
289
|
+
RedCloth/block has the variable name 'a' (Uncommunicative Name)
|
290
|
+
RedCloth/block has the variable name 'b' (Uncommunicative Name)
|
291
291
|
|
292
292
|
"""
|
data/features/stdin.feature
CHANGED
@@ -41,3 +41,19 @@ Feature: Reek reads from $stdin when no files are given
|
|
41
41
|
Turn#y has the name 'y' (Uncommunicative Name)
|
42
42
|
|
43
43
|
"""
|
44
|
+
|
45
|
+
@stderr
|
46
|
+
Scenario: syntax error causes the source to be ignored
|
47
|
+
When I pass "def incomplete" to reek
|
48
|
+
Then it succeeds
|
49
|
+
And it reports:
|
50
|
+
"""
|
51
|
+
$stdin -- 0 warnings
|
52
|
+
|
53
|
+
"""
|
54
|
+
And stderr reports:
|
55
|
+
"""
|
56
|
+
$stdin: Racc::ParseError:
|
57
|
+
parse error on value "$end" ($end)
|
58
|
+
|
59
|
+
"""
|
@@ -30,6 +30,10 @@ Then /^it reports:$/ do |report|
|
|
30
30
|
@last_stdout.should == report
|
31
31
|
end
|
32
32
|
|
33
|
+
Then /^stderr reports:$/ do |report|
|
34
|
+
@last_stderr.should == report
|
35
|
+
end
|
36
|
+
|
33
37
|
Then /^it reports the error ['"](.*)['"]$/ do |string|
|
34
38
|
@last_stderr.chomp.should == string
|
35
39
|
end
|
data/lib/reek.rb
CHANGED
data/lib/reek/adapters/source.rb
CHANGED
@@ -9,6 +9,16 @@ module Reek
|
|
9
9
|
#
|
10
10
|
class Source
|
11
11
|
|
12
|
+
@@err_io = $stderr
|
13
|
+
|
14
|
+
class << self
|
15
|
+
def err_io=(io)
|
16
|
+
original = @@err_io
|
17
|
+
@@err_io = io
|
18
|
+
original
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
12
22
|
attr_reader :desc
|
13
23
|
|
14
24
|
def initialize(code, desc, parser = RubyParser.new)
|
@@ -23,7 +33,7 @@ module Reek
|
|
23
33
|
begin
|
24
34
|
ast = @parser.parse(@source, @desc)
|
25
35
|
rescue Exception => error
|
26
|
-
|
36
|
+
@@err_io.puts "#{desc}: #{error.class.name}: #{error}"
|
27
37
|
end
|
28
38
|
ast ||= s()
|
29
39
|
TreeDresser.new.dress(ast)
|
data/lib/reek/block_context.rb
CHANGED
@@ -38,6 +38,7 @@ module Reek
|
|
38
38
|
def initialize(outer, exp)
|
39
39
|
super
|
40
40
|
@name = Name.new('block')
|
41
|
+
@scope_connector = '/'
|
41
42
|
@parameters = exp[2] || []
|
42
43
|
@parameters.extend(ParameterSet)
|
43
44
|
end
|
@@ -53,10 +54,6 @@ module Reek
|
|
53
54
|
def nested_block?
|
54
55
|
@outer.inside_a_block?
|
55
56
|
end
|
56
|
-
|
57
|
-
def outer_name
|
58
|
-
"#{@outer.outer_name}#{@name}/"
|
59
|
-
end
|
60
57
|
|
61
58
|
def variable_names
|
62
59
|
@parameters.names + @local_variables.to_a
|
data/lib/reek/class_context.rb
CHANGED
data/lib/reek/code_context.rb
CHANGED
@@ -20,6 +20,7 @@ module Reek
|
|
20
20
|
def initialize(outer, exp)
|
21
21
|
@outer = outer
|
22
22
|
@exp = exp
|
23
|
+
@scope_connector = ''
|
23
24
|
@myself = nil
|
24
25
|
end
|
25
26
|
|
@@ -42,7 +43,7 @@ module Reek
|
|
42
43
|
me = @name.to_s
|
43
44
|
strings.any? do |str|
|
44
45
|
re = /#{str}/
|
45
|
-
re === me or re === self.
|
46
|
+
re === me or re === self.full_name
|
46
47
|
end
|
47
48
|
end
|
48
49
|
|
@@ -58,12 +59,10 @@ module Reek
|
|
58
59
|
0
|
59
60
|
end
|
60
61
|
|
61
|
-
def
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
def to_s
|
66
|
-
"#{@outer.outer_name}#{@name}"
|
62
|
+
def full_name
|
63
|
+
outer = @outer ? @outer.full_name : ''
|
64
|
+
prefix = outer == '' ? '' : "#{outer}#{@scope_connector}"
|
65
|
+
"#{prefix}#{@name}"
|
67
66
|
end
|
68
67
|
end
|
69
68
|
end
|
data/lib/reek/if_context.rb
CHANGED
@@ -6,19 +6,13 @@ module Reek
|
|
6
6
|
|
7
7
|
def initialize(outer, exp)
|
8
8
|
super
|
9
|
+
@name = ''
|
10
|
+
@scope_connector = ''
|
9
11
|
@if_expr = exp[1]
|
10
12
|
end
|
11
13
|
|
12
14
|
def tests_a_parameter?
|
13
15
|
@if_expr[0] == :lvar and has_parameter(@if_expr[1])
|
14
16
|
end
|
15
|
-
|
16
|
-
def outer_name
|
17
|
-
@outer.outer_name
|
18
|
-
end
|
19
|
-
|
20
|
-
def to_s # SMELL: should be unnecessary :(
|
21
|
-
@outer.to_s
|
22
|
-
end
|
23
17
|
end
|
24
18
|
end
|
data/lib/reek/method_context.rb
CHANGED
@@ -76,6 +76,7 @@ module Reek
|
|
76
76
|
@parameters ||= []
|
77
77
|
@parameters.extend(MethodParameters)
|
78
78
|
@name = Name.new(exp[1])
|
79
|
+
@scope_connector = '#'
|
79
80
|
@num_statements = 0
|
80
81
|
@calls = Hash.new(0)
|
81
82
|
@depends_on_self = false
|
@@ -125,10 +126,6 @@ module Reek
|
|
125
126
|
@depends_on_self = true
|
126
127
|
end
|
127
128
|
|
128
|
-
def outer_name
|
129
|
-
"#{@outer.outer_name}#{@name}/"
|
130
|
-
end
|
131
|
-
|
132
129
|
def envious_receivers
|
133
130
|
return [] if @refs.self_is_max?
|
134
131
|
@refs.max_keys
|
data/lib/reek/module_context.rb
CHANGED
@@ -21,6 +21,7 @@ module Reek
|
|
21
21
|
def initialize(outer, name, exp)
|
22
22
|
super(outer, exp)
|
23
23
|
@name = name
|
24
|
+
@scope_connector = '::'
|
24
25
|
@parsed_methods = []
|
25
26
|
end
|
26
27
|
|
@@ -41,10 +42,6 @@ module Reek
|
|
41
42
|
@parsed_methods << meth
|
42
43
|
end
|
43
44
|
|
44
|
-
def outer_name
|
45
|
-
"#{@outer.outer_name}#{@name}::"
|
46
|
-
end
|
47
|
-
|
48
45
|
def variable_names
|
49
46
|
[]
|
50
47
|
end
|
@@ -9,19 +9,18 @@ module Reek
|
|
9
9
|
super(outer, exp)
|
10
10
|
@name = Name.new(exp[2])
|
11
11
|
@receiver = SexpFormatter.format(exp[1])
|
12
|
+
@scope_connector = ""
|
12
13
|
record_depends_on_self
|
13
14
|
end
|
14
15
|
|
15
16
|
def envious_receivers
|
16
17
|
[]
|
17
18
|
end
|
18
|
-
|
19
|
-
def outer_name
|
20
|
-
"#{@outer.outer_name}#{@receiver}.#{@name}/"
|
21
|
-
end
|
22
19
|
|
23
|
-
def
|
24
|
-
|
20
|
+
def full_name
|
21
|
+
outer = @outer ? @outer.full_name : ''
|
22
|
+
prefix = outer == '' ? '' : "#{outer}#"
|
23
|
+
"#{prefix}#{@receiver}.#{@name}"
|
25
24
|
end
|
26
25
|
end
|
27
26
|
end
|
data/lib/reek/smell_warning.rb
CHANGED
@@ -6,10 +6,11 @@ module Reek
|
|
6
6
|
class SmellWarning
|
7
7
|
include Comparable
|
8
8
|
|
9
|
-
def initialize(
|
10
|
-
@
|
9
|
+
def initialize(detector_class, context, line, message, masked)
|
10
|
+
@smell = detector_class.class.name.split(/::/)[-1]
|
11
11
|
@context = context
|
12
|
-
@
|
12
|
+
@line = line
|
13
|
+
@message = message
|
13
14
|
@is_masked = masked
|
14
15
|
end
|
15
16
|
|
@@ -25,28 +26,19 @@ module Reek
|
|
25
26
|
(self <=> other) == 0
|
26
27
|
end
|
27
28
|
|
28
|
-
#
|
29
|
-
# Returns +true+ only if this is a warning about an instance of
|
30
|
-
# +smell_class+ and its report string matches all of the +patterns+.
|
31
|
-
#
|
32
|
-
def matches?(smell_class, patterns)
|
33
|
-
return false unless smell_class.to_s == @detector.class.class_name
|
34
|
-
contains_all?(patterns)
|
35
|
-
end
|
36
|
-
|
37
29
|
def contains_all?(patterns)
|
38
30
|
rpt = sort_key.to_s
|
39
31
|
return patterns.all? {|exp| exp === rpt}
|
40
32
|
end
|
41
33
|
|
42
34
|
def sort_key
|
43
|
-
[@context
|
35
|
+
[@context, @message, smell_name]
|
44
36
|
end
|
45
37
|
|
46
38
|
protected :sort_key
|
47
39
|
|
48
40
|
def report(format)
|
49
|
-
format.gsub(/\%s/,
|
41
|
+
format.gsub(/\%s/, smell_name).gsub(/\%c/, @context).gsub(/\%w/, @message).gsub(/\%m/, @is_masked ? '(masked) ' : '')
|
50
42
|
end
|
51
43
|
|
52
44
|
def report_on(report)
|
@@ -56,5 +48,9 @@ module Reek
|
|
56
48
|
report.found_smell(self)
|
57
49
|
end
|
58
50
|
end
|
51
|
+
|
52
|
+
def smell_name
|
53
|
+
@smell.gsub(/([a-z])([A-Z])/) { |sub| "#{$1} #{$2}"}.split.join(' ')
|
54
|
+
end
|
59
55
|
end
|
60
56
|
end
|
@@ -1,12 +1,5 @@
|
|
1
1
|
require 'reek/configuration'
|
2
2
|
|
3
|
-
class Class
|
4
|
-
def name_words
|
5
|
-
class_name = name.split(/::/)[-1]
|
6
|
-
class_name.gsub(/([a-z])([A-Z])/) { |sub| "#{$1} #{$2}"}.split
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
3
|
module Reek
|
11
4
|
module Smells
|
12
5
|
|
@@ -92,8 +85,8 @@ module Reek
|
|
92
85
|
context.matches?(value(EXCLUDE_KEY, context, DEFAULT_EXCLUDE_SET))
|
93
86
|
end
|
94
87
|
|
95
|
-
def found(
|
96
|
-
smell = SmellWarning.new(self,
|
88
|
+
def found(context, message)
|
89
|
+
smell = SmellWarning.new(self, context.full_name, context.exp.line, message, @masked)
|
97
90
|
@smells_found << smell
|
98
91
|
smell
|
99
92
|
end
|
@@ -116,10 +109,6 @@ module Reek
|
|
116
109
|
(not @masked) and (@smells_found.length > 0)
|
117
110
|
end
|
118
111
|
|
119
|
-
def smell_name
|
120
|
-
self.class.name_words.join(' ')
|
121
|
-
end
|
122
|
-
|
123
112
|
def value(key, ctx, fall_back)
|
124
113
|
@config.value(key, ctx, fall_back)
|
125
114
|
end
|
@@ -65,11 +65,15 @@ module Reek
|
|
65
65
|
|
66
66
|
def consider_name(context) # :nodoc:
|
67
67
|
name = context.name
|
68
|
-
return false if
|
68
|
+
return false if accept?(context)
|
69
69
|
return false unless is_bad_name?(name, context)
|
70
70
|
found(context, "has the name '#{name}'")
|
71
71
|
end
|
72
72
|
|
73
|
+
def accept?(context)
|
74
|
+
value(ACCEPT_KEY, context, DEFAULT_ACCEPT_SET).include?(context.full_name)
|
75
|
+
end
|
76
|
+
|
73
77
|
def is_bad_name?(name, context) # :nodoc:
|
74
78
|
var = name.effective_name
|
75
79
|
return false if var == '*' or value(ACCEPT_KEY, context, DEFAULT_ACCEPT_SET).include?(var)
|
data/lib/reek/stop_context.rb
CHANGED
@@ -3,6 +3,7 @@ module Reek
|
|
3
3
|
|
4
4
|
def initialize
|
5
5
|
@refs = ObjectRefs.new
|
6
|
+
@name = ''
|
6
7
|
@myself = Object
|
7
8
|
end
|
8
9
|
|
@@ -18,6 +19,10 @@ module Reek
|
|
18
19
|
sym = name.to_s
|
19
20
|
@myself.const_defined?(sym) ? @myself.const_get(sym) : nil
|
20
21
|
end
|
22
|
+
|
23
|
+
def full_name
|
24
|
+
''
|
25
|
+
end
|
21
26
|
|
22
27
|
def inside_a_block?
|
23
28
|
false
|
@@ -26,9 +31,5 @@ module Reek
|
|
26
31
|
def is_overriding_method?(name)
|
27
32
|
false
|
28
33
|
end
|
29
|
-
|
30
|
-
def outer_name
|
31
|
-
''
|
32
|
-
end
|
33
34
|
end
|
34
35
|
end
|
data/reek.gemspec
CHANGED
@@ -2,13 +2,15 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{reek}
|
5
|
-
s.version = "1.2.
|
5
|
+
s.version = "1.2.6"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Kevin Rutherford"]
|
9
|
-
s.date = %q{2009-11-
|
9
|
+
s.date = %q{2009-11-28}
|
10
10
|
s.default_executable = %q{reek}
|
11
|
-
s.description = %q{
|
11
|
+
s.description = %q{Reek is a tool that examines Ruby classes, modules and methods
|
12
|
+
and reports any code smells it finds.
|
13
|
+
}
|
12
14
|
s.email = ["kevin@rutherford-software.com"]
|
13
15
|
s.executables = ["reek"]
|
14
16
|
s.extra_rdoc_files = ["History.txt", "License.txt", "README.rdoc"]
|
@@ -1,16 +1,41 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../../spec_helper.rb'
|
2
2
|
|
3
3
|
require 'reek/adapters/source'
|
4
|
+
require 'stringio'
|
4
5
|
|
5
6
|
include Reek
|
6
7
|
|
7
8
|
describe Source do
|
8
9
|
context 'when the parser fails' do
|
9
|
-
|
10
|
+
before :each do
|
11
|
+
@catcher = StringIO.new
|
12
|
+
@old_err_io = (Source.err_io = @catcher)
|
10
13
|
parser = mock('parser')
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
+
@error_message = 'Error message'
|
15
|
+
parser.should_receive(:parse).and_raise(SyntaxError.new(@error_message))
|
16
|
+
@source_name = 'Test source'
|
17
|
+
@src = Source.new('', @source_name, parser)
|
18
|
+
end
|
19
|
+
it 'raises a SyntaxError' do
|
20
|
+
@src.syntax_tree
|
21
|
+
end
|
22
|
+
it 'returns an empty syntax tree' do
|
23
|
+
@src.syntax_tree.should == s()
|
24
|
+
end
|
25
|
+
it 'records the syntax error' do
|
26
|
+
@src.syntax_tree
|
27
|
+
@catcher.string.should match(SyntaxError.name)
|
28
|
+
end
|
29
|
+
it 'records the source name' do
|
30
|
+
@src.syntax_tree
|
31
|
+
@catcher.string.should match(@source_name)
|
32
|
+
end
|
33
|
+
it 'records the error message' do
|
34
|
+
@src.syntax_tree
|
35
|
+
@catcher.string.should match(@error_message)
|
36
|
+
end
|
37
|
+
after :each do
|
38
|
+
Source.err_io = @old_err_io
|
14
39
|
end
|
15
40
|
end
|
16
41
|
end
|
@@ -43,4 +43,23 @@ describe BlockContext do
|
|
43
43
|
scope.record_instance_variable(:@list)
|
44
44
|
scope.variable_names.should == [:@list]
|
45
45
|
end
|
46
|
+
|
47
|
+
context 'full_name' do
|
48
|
+
it "reports full context" do
|
49
|
+
bctx = BlockContext.new(StopContext.new, s(nil, nil))
|
50
|
+
bctx.full_name.should == 'block'
|
51
|
+
end
|
52
|
+
it 'uses / to connect to the class name' do
|
53
|
+
element = StopContext.new
|
54
|
+
element = ClassContext.new(element, :Fred, s(:class, :Fred))
|
55
|
+
element = BlockContext.new(element, s(:iter, nil, s(:lasgn, :x), nil))
|
56
|
+
element.full_name.should == 'Fred/block'
|
57
|
+
end
|
58
|
+
it 'uses / to connect to the module name' do
|
59
|
+
element = StopContext.new
|
60
|
+
element = ModuleContext.new(element, :Fred, s(:module, :Fred))
|
61
|
+
element = BlockContext.new(element, s(:iter, nil, s(:lasgn, :x), nil))
|
62
|
+
element.full_name.should == 'Fred/block'
|
63
|
+
end
|
64
|
+
end
|
46
65
|
end
|
@@ -10,30 +10,37 @@ require 'reek/stop_context'
|
|
10
10
|
include Reek
|
11
11
|
|
12
12
|
describe CodeContext do
|
13
|
-
context '
|
13
|
+
context 'full_name' do
|
14
14
|
it "reports the full context" do
|
15
15
|
element = StopContext.new
|
16
16
|
element = ModuleContext.new(element, Name.new(:mod), s(:module, :mod, nil))
|
17
17
|
element = ClassContext.new(element, [0, :klass], s())
|
18
18
|
element = MethodContext.new(element, [0, :bad])
|
19
19
|
element = BlockContext.new(element, s(nil, nil))
|
20
|
-
element.
|
21
|
-
element.
|
22
|
-
element.
|
20
|
+
element.full_name.should match(/bad/)
|
21
|
+
element.full_name.should match(/klass/)
|
22
|
+
element.full_name.should match(/mod/)
|
23
23
|
end
|
24
24
|
|
25
25
|
it 'reports the method name via if context' do
|
26
26
|
element1 = StopContext.new
|
27
27
|
element2 = MethodContext.new(element1, [0, :bad])
|
28
28
|
element3 = IfContext.new(element2, [0,1])
|
29
|
-
BlockContext.new(element3, s(nil, nil)).
|
29
|
+
BlockContext.new(element3, s(nil, nil)).full_name.should match(/bad/)
|
30
30
|
end
|
31
31
|
|
32
32
|
it 'reports the method name via nested blocks' do
|
33
33
|
element1 = StopContext.new
|
34
34
|
element2 = MethodContext.new(element1, [0, :bad])
|
35
35
|
element3 = BlockContext.new(element2, s(nil, nil))
|
36
|
-
BlockContext.new(element3, s(nil, nil)).
|
36
|
+
BlockContext.new(element3, s(nil, nil)).full_name.should match(/bad/)
|
37
|
+
end
|
38
|
+
it 'includes the enclosing context name' do
|
39
|
+
outer_name = 'randomstring'
|
40
|
+
outer = mock('outer')
|
41
|
+
outer.should_receive(:full_name).and_return(outer_name)
|
42
|
+
ifc = IfContext.new(outer, s(:if, s()))
|
43
|
+
ifc.full_name.should == outer_name
|
37
44
|
end
|
38
45
|
end
|
39
46
|
|
@@ -5,11 +5,11 @@ require 'reek/if_context'
|
|
5
5
|
include Reek
|
6
6
|
|
7
7
|
describe IfContext do
|
8
|
-
it '
|
8
|
+
it 'finds a class within top-level code' do
|
9
9
|
'unless jim; class Array; end; end'.should_not reek
|
10
10
|
end
|
11
11
|
|
12
|
-
it '
|
12
|
+
it 'finds class within top-level code' do
|
13
13
|
stopctx = StopContext.new
|
14
14
|
ifctx = IfContext.new(stopctx, [:if, [:vcall, :jim]])
|
15
15
|
ifctx.find_module(Name.new(:Array)).should_not be_nil
|
@@ -35,4 +35,12 @@ describe ModuleContext do
|
|
35
35
|
element = ModuleContext.create(StopContext.new, [:module, [:colon3, :GlobalTestModule], nil])
|
36
36
|
element.myself.name.should == 'GlobalTestModule'
|
37
37
|
end
|
38
|
+
|
39
|
+
context 'full_name' do
|
40
|
+
it "reports full context" do
|
41
|
+
element = StopContext.new
|
42
|
+
element = ModuleContext.new(element, Name.new(:mod), s(:module, :mod, nil))
|
43
|
+
element.full_name.should == 'mod'
|
44
|
+
end
|
45
|
+
end
|
38
46
|
end
|
@@ -6,12 +6,11 @@ require 'reek/stop_context'
|
|
6
6
|
|
7
7
|
include Reek
|
8
8
|
|
9
|
-
describe SingletonMethodContext
|
10
|
-
|
11
|
-
it "should report full context" do
|
9
|
+
describe SingletonMethodContext do
|
10
|
+
it "reports full context" do
|
12
11
|
element = StopContext.new
|
13
12
|
element = ModuleContext.new(element, Name.new(:mod), s(:module, :mod, nil))
|
14
13
|
element = SingletonMethodContext.new(element, s(:defs, s(:call, nil, :a, s(:arglist)), :b, s(:args)))
|
15
|
-
element.
|
14
|
+
element.full_name.should match(/mod#a\.b/)
|
16
15
|
end
|
17
16
|
end
|
@@ -5,12 +5,12 @@ require 'reek/smells/feature_envy'
|
|
5
5
|
|
6
6
|
include Reek
|
7
7
|
|
8
|
-
describe SmellWarning
|
8
|
+
describe SmellWarning do
|
9
9
|
context 'sort order' do
|
10
10
|
context 'smells differing only by masking' do
|
11
11
|
before :each do
|
12
|
-
@first = SmellWarning.new(Smells::FeatureEnvy.new, "self", "self", true)
|
13
|
-
@second = SmellWarning.new(Smells::FeatureEnvy.new, "self", "self", false)
|
12
|
+
@first = SmellWarning.new(Smells::FeatureEnvy.new, "self", 27, "self", true)
|
13
|
+
@second = SmellWarning.new(Smells::FeatureEnvy.new, "self", 27, "self", false)
|
14
14
|
end
|
15
15
|
|
16
16
|
it 'should hash equal when the smell is the same' do
|
@@ -46,8 +46,8 @@ describe SmellWarning, 'equality' do
|
|
46
46
|
|
47
47
|
context 'smells differing only by detector' do
|
48
48
|
before :each do
|
49
|
-
@first = SmellWarning.new(Smells::Duplication.new, "self", "self", false)
|
50
|
-
@second = SmellWarning.new(Smells::FeatureEnvy.new, "self", "self", true)
|
49
|
+
@first = SmellWarning.new(Smells::Duplication.new, "self", 27, "self", false)
|
50
|
+
@second = SmellWarning.new(Smells::FeatureEnvy.new, "self", 27, "self", true)
|
51
51
|
end
|
52
52
|
|
53
53
|
it_should_behave_like 'first sorts ahead of second'
|
@@ -55,8 +55,8 @@ describe SmellWarning, 'equality' do
|
|
55
55
|
|
56
56
|
context 'smells differing only by context' do
|
57
57
|
before :each do
|
58
|
-
@first = SmellWarning.new(Smells::FeatureEnvy.new, "first", "self", true)
|
59
|
-
@second = SmellWarning.new(Smells::FeatureEnvy.new, "second", "self", false)
|
58
|
+
@first = SmellWarning.new(Smells::FeatureEnvy.new, "first", 27, "self", true)
|
59
|
+
@second = SmellWarning.new(Smells::FeatureEnvy.new, "second", 27, "self", false)
|
60
60
|
end
|
61
61
|
|
62
62
|
it_should_behave_like 'first sorts ahead of second'
|
@@ -64,8 +64,8 @@ describe SmellWarning, 'equality' do
|
|
64
64
|
|
65
65
|
context 'smells differing only by message' do
|
66
66
|
before :each do
|
67
|
-
@first = SmellWarning.new(Smells::FeatureEnvy.new, "context", "first", true)
|
68
|
-
@second = SmellWarning.new(Smells::FeatureEnvy.new, "context", "second", false)
|
67
|
+
@first = SmellWarning.new(Smells::FeatureEnvy.new, "context", 27, "first", true)
|
68
|
+
@second = SmellWarning.new(Smells::FeatureEnvy.new, "context", 27, "second", false)
|
69
69
|
end
|
70
70
|
|
71
71
|
it_should_behave_like 'first sorts ahead of second'
|
@@ -73,8 +73,8 @@ describe SmellWarning, 'equality' do
|
|
73
73
|
|
74
74
|
context 'message takes precedence over smell name' do
|
75
75
|
before :each do
|
76
|
-
@first = SmellWarning.new(Smells::UtilityFunction.new, "context", "first", true)
|
77
|
-
@second = SmellWarning.new(Smells::FeatureEnvy.new, "context", "second", false)
|
76
|
+
@first = SmellWarning.new(Smells::UtilityFunction.new, "context", 27, "first", true)
|
77
|
+
@second = SmellWarning.new(Smells::FeatureEnvy.new, "context", 27, "second", false)
|
78
78
|
end
|
79
79
|
|
80
80
|
it_should_behave_like 'first sorts ahead of second'
|
@@ -82,14 +82,14 @@ describe SmellWarning, 'equality' do
|
|
82
82
|
|
83
83
|
context 'smells differing everywhere' do
|
84
84
|
before :each do
|
85
|
-
@first = SmellWarning.new(Smells::UncommunicativeName.new, "Dirty", "has the variable name '@s'", true)
|
86
|
-
@second = SmellWarning.new(Smells::Duplication.new, 'Dirty#a', "calls @s.title twice", false)
|
85
|
+
@first = SmellWarning.new(Smells::UncommunicativeName.new, "Dirty", 27, "has the variable name '@s'", true)
|
86
|
+
@second = SmellWarning.new(Smells::Duplication.new, 'Dirty#a', 27, "calls @s.title twice", false)
|
87
87
|
end
|
88
88
|
|
89
89
|
it_should_behave_like 'first sorts ahead of second'
|
90
90
|
end
|
91
91
|
end
|
92
|
-
|
92
|
+
|
93
93
|
context 'masked reporting' do
|
94
94
|
class CountingReport
|
95
95
|
attr_reader :masked, :non_masked
|
@@ -106,8 +106,8 @@ describe SmellWarning, 'equality' do
|
|
106
106
|
end
|
107
107
|
|
108
108
|
before :each do
|
109
|
-
@masked = SmellWarning.new(Smells::FeatureEnvy.new,
|
110
|
-
@visible = SmellWarning.new(Smells::FeatureEnvy.new,
|
109
|
+
@masked = SmellWarning.new(Smells::FeatureEnvy.new, 'Fred', 27, "self", true)
|
110
|
+
@visible = SmellWarning.new(Smells::FeatureEnvy.new, 'Fred', 27, "self", false)
|
111
111
|
end
|
112
112
|
|
113
113
|
it 'reports as masked when masked' do
|
@@ -124,4 +124,28 @@ describe SmellWarning, 'equality' do
|
|
124
124
|
rpt.non_masked.should == 1
|
125
125
|
end
|
126
126
|
end
|
127
|
+
|
128
|
+
context 'YAML representation' do
|
129
|
+
before :each do
|
130
|
+
@message = 'message'
|
131
|
+
# Use a random string and a random bool
|
132
|
+
warning = SmellWarning.new(Smells::FeatureEnvy.new, 'Fred', 27, @message, true)
|
133
|
+
@yaml = warning.to_yaml
|
134
|
+
end
|
135
|
+
it 'includes the smell class' do
|
136
|
+
@yaml.should match(/smell:\s*FeatureEnvy/)
|
137
|
+
end
|
138
|
+
it 'includes the context' do
|
139
|
+
@yaml.should match(/context:\s*Fred/)
|
140
|
+
end
|
141
|
+
it 'includes the message' do
|
142
|
+
@yaml.should match(/message:\s*#{@message}/)
|
143
|
+
end
|
144
|
+
it 'indicates the masking' do
|
145
|
+
@yaml.should match(/is_masked:\s*true/)
|
146
|
+
end
|
147
|
+
it 'includes the line number' do
|
148
|
+
@yaml.should match(/line:\s*27/)
|
149
|
+
end
|
150
|
+
end
|
127
151
|
end
|
@@ -119,18 +119,28 @@ EOS
|
|
119
119
|
end
|
120
120
|
end
|
121
121
|
|
122
|
-
describe UncommunicativeName
|
122
|
+
describe UncommunicativeName do
|
123
123
|
before :each do
|
124
|
-
@
|
124
|
+
@detector = UncommunicativeName.new
|
125
125
|
end
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
126
|
+
|
127
|
+
context '#examine' do
|
128
|
+
it 'should return true when reporting a smell' do
|
129
|
+
mc = MethodContext.new(StopContext.new, s(:defn, :x, s(:args)))
|
130
|
+
@detector.examine(mc).should == true
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'should return false when not reporting a smell' do
|
134
|
+
mc = MethodContext.new(StopContext.new, s(:defn, :not_bad, s(:args)))
|
135
|
+
@detector.examine(mc).should == false
|
136
|
+
end
|
130
137
|
end
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
138
|
+
|
139
|
+
context 'accepting names' do
|
140
|
+
it 'accepts Inline::C' do
|
141
|
+
ctx = mock('context')
|
142
|
+
ctx.should_receive(:full_name).and_return('Inline::C')
|
143
|
+
@detector.accept?(ctx).should == true
|
144
|
+
end
|
135
145
|
end
|
136
146
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: reek
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Rutherford
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-11-
|
12
|
+
date: 2009-11-28 00:00:00 +00:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -42,7 +42,10 @@ dependencies:
|
|
42
42
|
- !ruby/object:Gem::Version
|
43
43
|
version: "3.0"
|
44
44
|
version:
|
45
|
-
description:
|
45
|
+
description: |
|
46
|
+
Reek is a tool that examines Ruby classes, modules and methods
|
47
|
+
and reports any code smells it finds.
|
48
|
+
|
46
49
|
email:
|
47
50
|
- kevin@rutherford-software.com
|
48
51
|
executables:
|