reek 1.2.5 → 1.2.6
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 +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:
|