reek 3.0.3 → 3.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +2 -2
- data/CHANGELOG +4 -0
- data/CONTRIBUTING.md +10 -10
- data/features/reports/reports.feature +6 -6
- data/features/step_definitions/sample_file_steps.rb +2 -2
- data/features/support/env.rb +1 -1
- data/lib/reek/ast/node.rb +1 -1
- data/lib/reek/ast/object_refs.rb +12 -15
- data/lib/reek/ast/sexp_extensions.rb +67 -5
- data/lib/reek/cli/option_interpreter.rb +9 -35
- data/lib/reek/cli/options.rb +3 -1
- data/lib/reek/context/method_context.rb +5 -5
- data/lib/reek/context/module_context.rb +11 -0
- data/lib/reek/context/singleton_method_context.rb +1 -1
- data/lib/reek/report.rb +71 -0
- data/lib/reek/report/formatter.rb +2 -2
- data/lib/reek/report/heading_formatter.rb +4 -0
- data/{assets/html_output.html.erb → lib/reek/report/html_report.html.erb} +0 -0
- data/lib/reek/report/report.rb +4 -7
- data/lib/reek/smells/feature_envy.rb +4 -5
- data/lib/reek/smells/irresponsible_module.rb +10 -8
- data/lib/reek/version.rb +1 -1
- data/reek.gemspec +2 -2
- data/spec/reek/ast/object_refs_spec.rb +27 -27
- data/spec/reek/cli/option_interpreter_spec.rb +10 -5
- data/spec/reek/cli/options_spec.rb +19 -10
- data/spec/reek/context/method_context_spec.rb +2 -2
- data/spec/reek/report/html_report_spec.rb +5 -7
- data/spec/reek/smells/feature_envy_spec.rb +0 -1
- data/spec/reek/smells/irresponsible_module_spec.rb +84 -5
- data/spec/reek/smells/utility_function_spec.rb +18 -0
- metadata +8 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 02b11e29be1e78d0ca3b5c5bb7e209d215a34142
|
4
|
+
data.tar.gz: 2fbda696128728c307a65abe0c61479bccc197ea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 08a97e6cba42ad58c4f6691d89cf4267ce79777095766859421ef6853b98f51c44521988f977e2d78d6dd1ebcdb56bc9b88e53e1fdc0af204f1aba22fe38be8c
|
7
|
+
data.tar.gz: 6074cc6366979c9e75722d8359fbdba75a2539eba9b3ffad21b5eaee59fa4ae7789d717ea6044b128577ccf13010a49caf69156eef34f239594cc592354eb2d1
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -13,8 +13,8 @@ rvm:
|
|
13
13
|
- ruby-head
|
14
14
|
matrix:
|
15
15
|
include:
|
16
|
-
- rvm: jruby
|
17
|
-
env: JRUBY_OPTS='--server -Xcompile.invokedynamic=false'
|
16
|
+
- rvm: jruby
|
17
|
+
env: JRUBY_OPTS='--2.0 --server -Xcompile.invokedynamic=false'
|
18
18
|
- rvm: jruby-head
|
19
19
|
env: JRUBY_OPTS='--server -Xcompile.invokedynamic=false'
|
20
20
|
allow_failures:
|
data/CHANGELOG
CHANGED
data/CONTRIBUTING.md
CHANGED
@@ -45,13 +45,7 @@ bundle
|
|
45
45
|
bundle exec rake
|
46
46
|
```
|
47
47
|
|
48
|
-
Once you’re sure your copy of reek works
|
49
|
-
failing tests on the "develop" branch) create your own branch from our "develop" branch.
|
50
|
-
|
51
|
-
We are using the popular [gitflow branch model](http://nvie.com/posts/a-successful-git-branching-model/) and
|
52
|
-
require contributions to follow to this model as well.
|
53
|
-
This probably sounds more complicated than it is, for you this just means that you should branch off
|
54
|
-
of our "develop" branch for your pull request, not master (which is the default already):
|
48
|
+
Once you’re sure your copy of reek works create your own feature branch from our "master" branch:
|
55
49
|
|
56
50
|
```
|
57
51
|
git checkout -b your_feature_or_fix_name
|
@@ -81,8 +75,6 @@ Then go to your GitHub fork and [make a pull
|
|
81
75
|
request](https://help.github.com/articles/creating-a-pull-request/)
|
82
76
|
to the original repository.
|
83
77
|
|
84
|
-
|
85
|
-
|
86
78
|
## Review and Fixes
|
87
79
|
|
88
80
|
Try to gauge and let us know in the pull request whether what
|
@@ -105,7 +97,15 @@ If there were any fixes to your pull request we’ll ask you to
|
|
105
97
|
all of the commits into one:
|
106
98
|
|
107
99
|
```
|
108
|
-
git rebase -i
|
100
|
+
git rebase -i master
|
109
101
|
# squash squash squash
|
110
102
|
git push -f origin
|
111
103
|
```
|
104
|
+
|
105
|
+
## Versioning policy
|
106
|
+
|
107
|
+
We are following [semantic versioning](http://semver.org/).
|
108
|
+
|
109
|
+
## Breaking changes
|
110
|
+
|
111
|
+
If you're working on a change that is breaking backwards-compatibility according to our versioning policy from above just go ahead with your pull request like normal. We'll discuss this then in the pull request and help you to point your pull request to the right branch.
|
@@ -190,9 +190,9 @@ Feature: Correctly formatted reports
|
|
190
190
|
And it reports:
|
191
191
|
"""
|
192
192
|
smelly.rb -- 3 warnings:
|
193
|
-
[4, 5]:Smelly#m calls @foo.bar 2 times (DuplicateMethodCall) [https://github.com/troessner/reek/
|
194
|
-
[4, 5]:Smelly#m calls puts(@foo.bar) 2 times (DuplicateMethodCall) [https://github.com/troessner/reek/
|
195
|
-
[3]:Smelly#m has the name 'm' (UncommunicativeMethodName) [https://github.com/troessner/reek/
|
193
|
+
[4, 5]:Smelly#m calls @foo.bar 2 times (DuplicateMethodCall) [https://github.com/troessner/reek/blob/master/docs/Duplicate-Method-Call.md]
|
194
|
+
[4, 5]:Smelly#m calls puts(@foo.bar) 2 times (DuplicateMethodCall) [https://github.com/troessner/reek/blob/master/docs/Duplicate-Method-Call.md]
|
195
|
+
[3]:Smelly#m has the name 'm' (UncommunicativeMethodName) [https://github.com/troessner/reek/blob/master/docs/Uncommunicative-Method-Name.md]
|
196
196
|
"""
|
197
197
|
|
198
198
|
Examples:
|
@@ -207,9 +207,9 @@ Feature: Correctly formatted reports
|
|
207
207
|
And it reports:
|
208
208
|
"""
|
209
209
|
smelly.rb -- 3 warnings:
|
210
|
-
Smelly#m calls @foo.bar 2 times (DuplicateMethodCall) [https://github.com/troessner/reek/
|
211
|
-
Smelly#m calls puts(@foo.bar) 2 times (DuplicateMethodCall) [https://github.com/troessner/reek/
|
212
|
-
Smelly#m has the name 'm' (UncommunicativeMethodName) [https://github.com/troessner/reek/
|
210
|
+
Smelly#m calls @foo.bar 2 times (DuplicateMethodCall) [https://github.com/troessner/reek/blob/master/docs/Duplicate-Method-Call.md]
|
211
|
+
Smelly#m calls puts(@foo.bar) 2 times (DuplicateMethodCall) [https://github.com/troessner/reek/blob/master/docs/Duplicate-Method-Call.md]
|
212
|
+
Smelly#m has the name 'm' (UncommunicativeMethodName) [https://github.com/troessner/reek/blob/master/docs/Uncommunicative-Method-Name.md]
|
213
213
|
"""
|
214
214
|
|
215
215
|
Examples:
|
@@ -27,7 +27,7 @@ end
|
|
27
27
|
|
28
28
|
Given(/^the "(.*?)" sample file exists$/) do |file_name|
|
29
29
|
full_path = File.expand_path file_name, 'spec/samples'
|
30
|
-
|
30
|
+
in_current_directory { FileUtils.cp full_path, file_name }
|
31
31
|
end
|
32
32
|
|
33
33
|
Given(/^a directory called 'clean_files' containing some clean files$/) do
|
@@ -135,7 +135,7 @@ When(/^I run "reek (.*?)" in the subdirectory$/) do |args|
|
|
135
135
|
end
|
136
136
|
|
137
137
|
Given(/^a masking configuration file in the HOME directory$/) do
|
138
|
-
set_env('HOME', File.expand_path(File.join(
|
138
|
+
set_env('HOME', File.expand_path(File.join(current_directory, 'home')))
|
139
139
|
write_file('home/config.reek', <<-EOS.strip_heredoc)
|
140
140
|
---
|
141
141
|
DuplicateMethodCall:
|
data/features/support/env.rb
CHANGED
data/lib/reek/ast/node.rb
CHANGED
data/lib/reek/ast/object_refs.rb
CHANGED
@@ -1,33 +1,30 @@
|
|
1
1
|
module Reek
|
2
|
+
# @api private
|
2
3
|
module AST
|
4
|
+
ObjectRef = Struct.new(:name, :line)
|
3
5
|
#
|
4
6
|
# Manages and counts the references out of a method to other objects.
|
5
7
|
#
|
6
|
-
|
7
|
-
class ObjectRefs # :nodoc:
|
8
|
+
class ObjectRefs
|
8
9
|
def initialize
|
9
|
-
@refs = Hash.new
|
10
|
+
@refs = Hash.new { |refs, name| refs[name] = [] }
|
10
11
|
end
|
11
12
|
|
12
|
-
def
|
13
|
-
@refs
|
13
|
+
def most_popular
|
14
|
+
max = @refs.values.map(&:size).max
|
15
|
+
@refs.select { |_name, refs| refs.size == max }
|
14
16
|
end
|
15
17
|
|
16
|
-
def
|
17
|
-
@refs[
|
18
|
+
def record_reference_to(name, line: nil)
|
19
|
+
@refs[name] << ObjectRef.new(name, line)
|
18
20
|
end
|
19
21
|
|
20
|
-
def
|
21
|
-
@refs
|
22
|
-
end
|
23
|
-
|
24
|
-
def max_keys
|
25
|
-
max = max_refs
|
26
|
-
@refs.select { |_key, val| val == max }
|
22
|
+
def references_to(name)
|
23
|
+
@refs[name]
|
27
24
|
end
|
28
25
|
|
29
26
|
def self_is_max?
|
30
|
-
|
27
|
+
@refs.empty? || most_popular.keys.include?(:self)
|
31
28
|
end
|
32
29
|
end
|
33
30
|
end
|
@@ -249,6 +249,70 @@ module Reek
|
|
249
249
|
end
|
250
250
|
end
|
251
251
|
|
252
|
+
# Checking if a method is a singleton method.
|
253
|
+
module SingletonMethod
|
254
|
+
def singleton_method?
|
255
|
+
singleton_method_via_class_self_notation? ||
|
256
|
+
singleton_method_via_module_function?
|
257
|
+
end
|
258
|
+
|
259
|
+
# Ruby allows us to make a method a singleton_method after having defined the
|
260
|
+
# method via `module_function`.
|
261
|
+
#
|
262
|
+
# To check if we used "module_function" for a method we need to check on the parent
|
263
|
+
# level of the method in question if there is a call to "module_function".
|
264
|
+
# Given someting like
|
265
|
+
# class C
|
266
|
+
# def m; 3 + 7; end
|
267
|
+
# module_function :m
|
268
|
+
# end
|
269
|
+
# the AST (truncated, without the class definition) would look like this:
|
270
|
+
# (def :m
|
271
|
+
# (args)
|
272
|
+
# (send
|
273
|
+
# (int 3) :+
|
274
|
+
# (int 7)))
|
275
|
+
# (send nil :module_function
|
276
|
+
# (sym :m))))
|
277
|
+
# With multiple arguments to module_function this gets more complicated:
|
278
|
+
# (send nil :module_function
|
279
|
+
# (sym :m1)
|
280
|
+
# (send nil :m2))
|
281
|
+
#
|
282
|
+
# @return [Boolean]
|
283
|
+
def singleton_method_via_module_function?
|
284
|
+
return unless parent
|
285
|
+
module_function_calls(parent).any? do |module_function_call|
|
286
|
+
method_name_nodes = module_function_call.children[2..-1]
|
287
|
+
method_names = method_name_nodes.map do |node|
|
288
|
+
node.children.last
|
289
|
+
end
|
290
|
+
method_names.include?(name)
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
# Ruby allows us to make a method a singleton_method using the
|
295
|
+
# class << self syntax.
|
296
|
+
#
|
297
|
+
# To check for this we check if the parent node is of type :sclass.
|
298
|
+
#
|
299
|
+
# @return [Boolean]
|
300
|
+
def singleton_method_via_class_self_notation?
|
301
|
+
return unless parent
|
302
|
+
parent.type == :sclass
|
303
|
+
end
|
304
|
+
|
305
|
+
private
|
306
|
+
|
307
|
+
def module_function_calls(parent)
|
308
|
+
parent.children.select do |elem|
|
309
|
+
elem.is_a?(::Parser::AST::Node) &&
|
310
|
+
elem.type == :send &&
|
311
|
+
elem.children[1] == :module_function
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
252
316
|
# Utility methods for :def nodes.
|
253
317
|
module DefNode
|
254
318
|
def name() self[1] end
|
@@ -257,7 +321,7 @@ module Reek
|
|
257
321
|
def body
|
258
322
|
self[3]
|
259
323
|
end
|
260
|
-
|
324
|
+
|
261
325
|
def full_name(outer)
|
262
326
|
prefix = outer == '' ? '' : "#{outer}#"
|
263
327
|
"#{prefix}#{name}"
|
@@ -267,10 +331,8 @@ module Reek
|
|
267
331
|
ReferenceCollector.new(self).num_refs_to_self > 0
|
268
332
|
end
|
269
333
|
|
270
|
-
|
271
|
-
|
272
|
-
parent.type == :sclass if parent
|
273
|
-
end
|
334
|
+
include MethodNodeBase
|
335
|
+
include SingletonMethod
|
274
336
|
end
|
275
337
|
|
276
338
|
# Utility methods for :defs nodes.
|
@@ -1,8 +1,6 @@
|
|
1
1
|
require 'forwardable'
|
2
2
|
require_relative 'input'
|
3
|
-
require_relative '../report
|
4
|
-
require_relative '../report/formatter'
|
5
|
-
require_relative '../report/heading_formatter'
|
3
|
+
require_relative '../report'
|
6
4
|
|
7
5
|
module Reek
|
8
6
|
module CLI
|
@@ -31,48 +29,24 @@ module Reek
|
|
31
29
|
heading_formatter: heading_formatter)
|
32
30
|
end
|
33
31
|
|
34
|
-
# TODO: Move report type mapping into Report
|
35
32
|
def report_class
|
36
|
-
|
37
|
-
when :yaml
|
38
|
-
Report::YAMLReport
|
39
|
-
when :json
|
40
|
-
Report::JSONReport
|
41
|
-
when :html
|
42
|
-
Report::HTMLReport
|
43
|
-
when :xml
|
44
|
-
Report::XMLReport
|
45
|
-
else # :text
|
46
|
-
Report::TextReport
|
47
|
-
end
|
33
|
+
Report.report_class(@options.report_format)
|
48
34
|
end
|
49
35
|
|
50
36
|
def warning_formatter
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
klass.new(location_formatter)
|
37
|
+
warning_formatter_class.new(location_formatter)
|
38
|
+
end
|
39
|
+
|
40
|
+
def warning_formatter_class
|
41
|
+
Report.warning_formatter_class(@options.show_links ? :wiki_links : :simple)
|
57
42
|
end
|
58
43
|
|
59
44
|
def location_formatter
|
60
|
-
|
61
|
-
when :single_line
|
62
|
-
Report::SingleLineLocationFormatter
|
63
|
-
when :plain
|
64
|
-
Report::BlankLocationFormatter
|
65
|
-
else # :numbers
|
66
|
-
Report::DefaultLocationFormatter
|
67
|
-
end
|
45
|
+
Report.location_formatter(@options.location_format)
|
68
46
|
end
|
69
47
|
|
70
48
|
def heading_formatter
|
71
|
-
|
72
|
-
Report::HeadingFormatter::Verbose
|
73
|
-
else
|
74
|
-
Report::HeadingFormatter::Quiet
|
75
|
-
end
|
49
|
+
Report.heading_formatter(@options.show_empty ? :verbose : :quiet)
|
76
50
|
end
|
77
51
|
|
78
52
|
def sort_by_issue_count
|
data/lib/reek/cli/options.rb
CHANGED
@@ -15,7 +15,9 @@ module Reek
|
|
15
15
|
def initialize(argv = [])
|
16
16
|
@argv = argv
|
17
17
|
@parser = OptionParser.new
|
18
|
-
@options = OpenStruct.new(
|
18
|
+
@options = OpenStruct.new(report_format: :text,
|
19
|
+
location_format: :numbers,
|
20
|
+
colored: color_support?,
|
19
21
|
smells_to_detect: [])
|
20
22
|
set_up_parser
|
21
23
|
end
|
@@ -43,11 +43,11 @@ module Reek
|
|
43
43
|
receiver ||= [:self]
|
44
44
|
case receiver[0]
|
45
45
|
when :lvasgn
|
46
|
-
@refs.record_reference_to(receiver.name)
|
46
|
+
@refs.record_reference_to(receiver.name, line: exp.line)
|
47
47
|
when :lvar
|
48
|
-
@refs.record_reference_to(receiver.name) unless meth == :new
|
48
|
+
@refs.record_reference_to(receiver.name, line: exp.line) unless meth == :new
|
49
49
|
when :self
|
50
|
-
@refs.record_reference_to(:self)
|
50
|
+
@refs.record_reference_to(:self, line: exp.line)
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
@@ -56,8 +56,8 @@ module Reek
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def envious_receivers
|
59
|
-
return
|
60
|
-
@refs.
|
59
|
+
return {} if @refs.self_is_max?
|
60
|
+
@refs.most_popular
|
61
61
|
end
|
62
62
|
|
63
63
|
def references_self?
|
@@ -20,6 +20,17 @@ module Reek
|
|
20
20
|
def descriptively_commented?
|
21
21
|
CodeComment.new(exp.leading_comment).descriptive?
|
22
22
|
end
|
23
|
+
|
24
|
+
# A namespace module is a module (or class) that is only there for namespacing
|
25
|
+
# purposes, and thus contains only nested constants, modules or classes.
|
26
|
+
#
|
27
|
+
# However, if the module is empty, it is not considered a namespace module.
|
28
|
+
#
|
29
|
+
# @return true if the module is a namespace module
|
30
|
+
def namespace_module?
|
31
|
+
contents = exp.children.last
|
32
|
+
contents && contents.find_nodes([:def, :defs], [:class, :module]).empty?
|
33
|
+
end
|
23
34
|
end
|
24
35
|
end
|
25
36
|
end
|
data/lib/reek/report.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require_relative 'report/report'
|
2
|
+
require_relative 'report/formatter'
|
3
|
+
require_relative 'report/heading_formatter'
|
4
|
+
|
5
|
+
module Reek
|
6
|
+
# Reek reporting functionality.
|
7
|
+
module Report
|
8
|
+
module_function
|
9
|
+
|
10
|
+
# @api private
|
11
|
+
REPORT_CLASSES = {
|
12
|
+
yaml: YAMLReport,
|
13
|
+
json: JSONReport,
|
14
|
+
html: HTMLReport,
|
15
|
+
xml: XMLReport,
|
16
|
+
text: TextReport
|
17
|
+
}
|
18
|
+
|
19
|
+
# @api private
|
20
|
+
LOCATION_FORMATTERS = {
|
21
|
+
single_line: SingleLineLocationFormatter,
|
22
|
+
plain: BlankLocationFormatter,
|
23
|
+
numbers: DefaultLocationFormatter
|
24
|
+
}
|
25
|
+
|
26
|
+
# @api private
|
27
|
+
HEADING_FORMATTERS = {
|
28
|
+
verbose: HeadingFormatter::Verbose,
|
29
|
+
quiet: HeadingFormatter::Quiet }
|
30
|
+
|
31
|
+
# @api private
|
32
|
+
WARNING_FORMATTER_CLASSES = {
|
33
|
+
wiki_links: WikiLinkWarningFormatter,
|
34
|
+
simple: SimpleWarningFormatter
|
35
|
+
}
|
36
|
+
|
37
|
+
# Map report format symbol to a report class.
|
38
|
+
#
|
39
|
+
# @param [Symbol] report_format The format to map
|
40
|
+
#
|
41
|
+
# @return The mapped report class
|
42
|
+
#
|
43
|
+
# @api private
|
44
|
+
def report_class(report_format)
|
45
|
+
REPORT_CLASSES.fetch(report_format) do
|
46
|
+
raise "Unknown report format: #{report_format}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# @api private
|
51
|
+
def location_formatter(location_format)
|
52
|
+
LOCATION_FORMATTERS.fetch(location_format) do
|
53
|
+
raise "Unknown location format: #{location_format}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# @api private
|
58
|
+
def heading_formatter(heading_format)
|
59
|
+
HEADING_FORMATTERS.fetch(heading_format) do
|
60
|
+
raise "Unknown heading format: #{heading_format}"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# @api private
|
65
|
+
def warning_formatter_class(warning_format)
|
66
|
+
WARNING_FORMATTER_CLASSES.fetch(warning_format) do
|
67
|
+
raise "Unknown warning format: #{warning_format}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -51,11 +51,11 @@ module Reek
|
|
51
51
|
#
|
52
52
|
# @api private
|
53
53
|
class WikiLinkWarningFormatter < SimpleWarningFormatter
|
54
|
-
BASE_URL_FOR_HELP_LINK = 'https://github.com/troessner/reek/
|
54
|
+
BASE_URL_FOR_HELP_LINK = 'https://github.com/troessner/reek/blob/master/docs/'
|
55
55
|
|
56
56
|
def format(warning)
|
57
57
|
"#{super} " \
|
58
|
-
"[#{explanatory_link(warning)}]"
|
58
|
+
"[#{explanatory_link(warning)}.md]"
|
59
59
|
end
|
60
60
|
|
61
61
|
def explanatory_link(warning)
|
File without changes
|
data/lib/reek/report/report.rb
CHANGED
@@ -129,13 +129,10 @@ module Reek
|
|
129
129
|
class HTMLReport < Base
|
130
130
|
require 'erb'
|
131
131
|
|
132
|
-
def show
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
file.puts ERB.new(File.read(path)).result(binding)
|
137
|
-
end
|
138
|
-
print("Html file saved\n")
|
132
|
+
def show(target_path = 'reek.html')
|
133
|
+
template_path = File.expand_path('../html_report.html.erb', __FILE__)
|
134
|
+
File.write target_path, ERB.new(File.read(template_path)).result(binding)
|
135
|
+
puts 'HTML file saved'
|
139
136
|
end
|
140
137
|
end
|
141
138
|
|
@@ -48,13 +48,12 @@ module Reek
|
|
48
48
|
#
|
49
49
|
def examine_context(method_ctx)
|
50
50
|
return [] unless method_ctx.references_self?
|
51
|
-
method_ctx.envious_receivers.map do |
|
52
|
-
target = ref.to_s
|
51
|
+
method_ctx.envious_receivers.map do |name, refs|
|
53
52
|
SmellWarning.new self,
|
54
53
|
context: method_ctx.full_name,
|
55
|
-
lines:
|
56
|
-
message: "refers to #{
|
57
|
-
parameters: { name:
|
54
|
+
lines: refs.map(&:line),
|
55
|
+
message: "refers to #{name} more than self",
|
56
|
+
parameters: { name: name.to_s, count: refs.size }
|
58
57
|
end
|
59
58
|
end
|
60
59
|
end
|
@@ -10,12 +10,8 @@ module Reek
|
|
10
10
|
# See {file:docs/Irresponsible-Module.md} for details.
|
11
11
|
# @api private
|
12
12
|
class IrresponsibleModule < SmellDetector
|
13
|
-
def self.contexts
|
14
|
-
[:class]
|
15
|
-
end
|
16
|
-
|
17
|
-
def descriptive # :nodoc:
|
18
|
-
@descriptive ||= {}
|
13
|
+
def self.contexts
|
14
|
+
[:class, :module]
|
19
15
|
end
|
20
16
|
|
21
17
|
#
|
@@ -24,13 +20,19 @@ module Reek
|
|
24
20
|
# @return [Array<SmellWarning>]
|
25
21
|
#
|
26
22
|
def examine_context(ctx)
|
27
|
-
return [] if descriptive?(ctx)
|
23
|
+
return [] if descriptive?(ctx) || ctx.namespace_module?
|
28
24
|
expression = ctx.exp
|
29
25
|
[SmellWarning.new(self,
|
30
26
|
context: ctx.full_name,
|
31
27
|
lines: [expression.line],
|
32
28
|
message: 'has no descriptive comment',
|
33
|
-
parameters: {
|
29
|
+
parameters: { name: expression.text_name })]
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def descriptive
|
35
|
+
@descriptive ||= {}
|
34
36
|
end
|
35
37
|
|
36
38
|
def descriptive?(ctx)
|
data/lib/reek/version.rb
CHANGED
data/reek.gemspec
CHANGED
@@ -27,12 +27,12 @@ Gem::Specification.new do |s|
|
|
27
27
|
s.add_runtime_dependency 'unparser', '~> 0.2.2'
|
28
28
|
|
29
29
|
s.add_development_dependency 'activesupport', '~> 4.2'
|
30
|
-
s.add_development_dependency 'aruba', '~> 0.
|
30
|
+
s.add_development_dependency 'aruba', '~> 0.7.2'
|
31
31
|
s.add_development_dependency 'ataru', '~> 0.2.0'
|
32
32
|
s.add_development_dependency 'bundler', '~> 1.1'
|
33
33
|
s.add_development_dependency 'cucumber', '~> 2.0'
|
34
34
|
s.add_development_dependency 'factory_girl', '~> 4.0'
|
35
35
|
s.add_development_dependency 'rake', '~> 10.0'
|
36
36
|
s.add_development_dependency 'rspec', '~> 3.0'
|
37
|
-
s.add_development_dependency 'rubocop', '~> 0.
|
37
|
+
s.add_development_dependency 'rubocop', '~> 0.32.1'
|
38
38
|
end
|
@@ -8,24 +8,24 @@ RSpec.describe Reek::AST::ObjectRefs do
|
|
8
8
|
|
9
9
|
context 'when empty' do
|
10
10
|
it 'should report no refs to self' do
|
11
|
-
expect(@refs.references_to(:self)).to
|
11
|
+
expect(@refs.references_to(:self)).to be_empty
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
15
|
context 'with references to a, b, and a' do
|
16
16
|
context 'with no refs to self' do
|
17
17
|
before(:each) do
|
18
|
-
@refs.record_reference_to(
|
19
|
-
@refs.record_reference_to(
|
20
|
-
@refs.record_reference_to(
|
18
|
+
@refs.record_reference_to(:a)
|
19
|
+
@refs.record_reference_to(:b)
|
20
|
+
@refs.record_reference_to(:a)
|
21
21
|
end
|
22
22
|
|
23
23
|
it 'should report no refs to self' do
|
24
|
-
expect(@refs.references_to(:self)).to
|
24
|
+
expect(@refs.references_to(:self)).to be_empty
|
25
25
|
end
|
26
26
|
|
27
27
|
it 'should report :a as the max' do
|
28
|
-
expect(@refs.
|
28
|
+
expect(@refs.most_popular).to include(:a)
|
29
29
|
end
|
30
30
|
|
31
31
|
it 'should not report self as the max' do
|
@@ -38,12 +38,12 @@ RSpec.describe Reek::AST::ObjectRefs do
|
|
38
38
|
end
|
39
39
|
|
40
40
|
it 'should report 1 ref to self' do
|
41
|
-
expect(@refs.references_to(:self)).to eq(1)
|
41
|
+
expect(@refs.references_to(:self).size).to eq(1)
|
42
42
|
end
|
43
43
|
|
44
44
|
it 'should not report self among the max' do
|
45
|
-
expect(@refs.
|
46
|
-
expect(@refs.
|
45
|
+
expect(@refs.most_popular).to include(:a)
|
46
|
+
expect(@refs.most_popular).not_to include(:self)
|
47
47
|
end
|
48
48
|
|
49
49
|
it 'should not report self as the max' do
|
@@ -57,19 +57,19 @@ RSpec.describe Reek::AST::ObjectRefs do
|
|
57
57
|
before(:each) do
|
58
58
|
@refs.record_reference_to(:self)
|
59
59
|
@refs.record_reference_to(:self)
|
60
|
-
@refs.record_reference_to(
|
60
|
+
@refs.record_reference_to(:a)
|
61
61
|
@refs.record_reference_to(:self)
|
62
|
-
@refs.record_reference_to(
|
63
|
-
@refs.record_reference_to(
|
62
|
+
@refs.record_reference_to(:b)
|
63
|
+
@refs.record_reference_to(:a)
|
64
64
|
@refs.record_reference_to(:self)
|
65
65
|
end
|
66
66
|
|
67
67
|
it 'should report all refs to self' do
|
68
|
-
expect(@refs.references_to(:self)).to eq(4)
|
68
|
+
expect(@refs.references_to(:self).size).to eq(4)
|
69
69
|
end
|
70
70
|
|
71
71
|
it 'should report self among the max' do
|
72
|
-
expect(@refs.
|
72
|
+
expect(@refs.most_popular).to include(:self)
|
73
73
|
end
|
74
74
|
|
75
75
|
it 'should report self as the max' do
|
@@ -79,20 +79,20 @@ RSpec.describe Reek::AST::ObjectRefs do
|
|
79
79
|
|
80
80
|
context 'when self is not the only max' do
|
81
81
|
before(:each) do
|
82
|
-
@refs.record_reference_to(
|
82
|
+
@refs.record_reference_to(:a)
|
83
83
|
@refs.record_reference_to(:self)
|
84
84
|
@refs.record_reference_to(:self)
|
85
|
-
@refs.record_reference_to(
|
86
|
-
@refs.record_reference_to(
|
85
|
+
@refs.record_reference_to(:b)
|
86
|
+
@refs.record_reference_to(:a)
|
87
87
|
end
|
88
88
|
|
89
89
|
it 'should report all refs to self' do
|
90
|
-
expect(@refs.references_to(:self)).to eq(2)
|
90
|
+
expect(@refs.references_to(:self).size).to eq(2)
|
91
91
|
end
|
92
92
|
|
93
93
|
it 'should report self among the max' do
|
94
|
-
expect(@refs.
|
95
|
-
expect(@refs.
|
94
|
+
expect(@refs.most_popular).to include(:a)
|
95
|
+
expect(@refs.most_popular).to include(:self)
|
96
96
|
end
|
97
97
|
|
98
98
|
it 'should report self as the max' do
|
@@ -102,19 +102,19 @@ RSpec.describe Reek::AST::ObjectRefs do
|
|
102
102
|
|
103
103
|
context 'when self is not among the max' do
|
104
104
|
before(:each) do
|
105
|
-
@refs.record_reference_to(
|
106
|
-
@refs.record_reference_to(
|
107
|
-
@refs.record_reference_to(
|
108
|
-
@refs.record_reference_to(
|
105
|
+
@refs.record_reference_to(:a)
|
106
|
+
@refs.record_reference_to(:b)
|
107
|
+
@refs.record_reference_to(:a)
|
108
|
+
@refs.record_reference_to(:b)
|
109
109
|
end
|
110
110
|
|
111
111
|
it 'should report all refs to self' do
|
112
|
-
expect(@refs.references_to(:self)).to eq(0)
|
112
|
+
expect(@refs.references_to(:self).size).to eq(0)
|
113
113
|
end
|
114
114
|
|
115
115
|
it 'should not report self among the max' do
|
116
|
-
expect(@refs.
|
117
|
-
expect(@refs.
|
116
|
+
expect(@refs.most_popular).to include(:a)
|
117
|
+
expect(@refs.most_popular).to include(:b)
|
118
118
|
end
|
119
119
|
|
120
120
|
it 'should not report self as the max' do
|
@@ -4,12 +4,17 @@ require_relative '../../../lib/reek/cli/options'
|
|
4
4
|
require_relative '../../../lib/reek/report/report'
|
5
5
|
|
6
6
|
RSpec.describe Reek::CLI::OptionInterpreter do
|
7
|
-
let(:options) { OpenStruct.new }
|
8
|
-
let(:instance) { Reek::CLI::OptionInterpreter.new(options) }
|
9
|
-
|
10
7
|
describe '#reporter' do
|
11
|
-
|
12
|
-
|
8
|
+
let(:instance) { Reek::CLI::OptionInterpreter.new(options) }
|
9
|
+
|
10
|
+
context 'with a valid set of options' do
|
11
|
+
let(:options) do
|
12
|
+
OpenStruct.new(report_format: :text,
|
13
|
+
location_format: :plain)
|
14
|
+
end
|
15
|
+
it 'returns an object of the correct subclass of Report::Base' do
|
16
|
+
expect(instance.reporter).to be_instance_of Reek::Report::TextReport
|
17
|
+
end
|
13
18
|
end
|
14
19
|
end
|
15
20
|
end
|
@@ -3,17 +3,26 @@ require_relative '../../spec_helper'
|
|
3
3
|
require_relative '../../../lib/reek/cli/options'
|
4
4
|
|
5
5
|
RSpec.describe Reek::CLI::Options do
|
6
|
-
describe '#
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
describe '#parse' do
|
7
|
+
context 'with no arguments passed' do
|
8
|
+
let(:options) { Reek::CLI::Options.new.parse }
|
9
|
+
it 'enables colors when stdout is a TTY' do
|
10
|
+
allow($stdout).to receive_messages(tty?: false)
|
11
|
+
expect(options.colored).to be false
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'does not enable colors when stdout is not a TTY' do
|
15
|
+
allow($stdout).to receive_messages(tty?: true)
|
16
|
+
expect(options.colored).to be true
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'sets a valid default value for report_format' do
|
20
|
+
expect(options.report_format).to eq :text
|
21
|
+
end
|
12
22
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
expect(options.colored).to be true
|
23
|
+
it 'sets a valid default value for location_format' do
|
24
|
+
expect(options.location_format).to eq :numbers
|
25
|
+
end
|
17
26
|
end
|
18
27
|
end
|
19
28
|
end
|
@@ -33,7 +33,7 @@ RSpec.describe Reek::Context::MethodContext do
|
|
33
33
|
end
|
34
34
|
|
35
35
|
it 'should ignore explicit calls to self' do
|
36
|
-
mc.refs.record_reference_to
|
36
|
+
mc.refs.record_reference_to :other
|
37
37
|
mc.record_call_to s(:send, s(:self), :thing)
|
38
38
|
expect(mc.envious_receivers).to be_empty
|
39
39
|
end
|
@@ -46,7 +46,7 @@ RSpec.describe Reek::Context::MethodContext do
|
|
46
46
|
|
47
47
|
it 'should record envious calls' do
|
48
48
|
mc.record_call_to s(:send, s(:lvar, :bar), :baz)
|
49
|
-
expect(mc.envious_receivers).to
|
49
|
+
expect(mc.envious_receivers).to include(:bar)
|
50
50
|
end
|
51
51
|
end
|
52
52
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'tempfile'
|
1
2
|
require_relative '../../spec_helper'
|
2
3
|
require_relative '../../../lib/reek/examiner'
|
3
4
|
require_relative '../../../lib/reek/report/report'
|
@@ -13,13 +14,10 @@ RSpec.describe Reek::Report::HTMLReport do
|
|
13
14
|
end
|
14
15
|
|
15
16
|
it 'has the text 0 total warnings' do
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
File.delete(file)
|
21
|
-
|
22
|
-
expect(text).to include('0 total warnings')
|
17
|
+
tempfile = Tempfile.new(['Reek::Report::HTMLReport.', '.html'])
|
18
|
+
response = "HTML file saved\n"
|
19
|
+
expect { instance.show tempfile.path }.to output(response).to_stdout
|
20
|
+
expect(tempfile.read).to include('0 total warnings')
|
23
21
|
end
|
24
22
|
end
|
25
23
|
end
|
@@ -4,6 +4,16 @@ require_relative '../../../lib/reek/smells/irresponsible_module'
|
|
4
4
|
require_relative 'smell_detector_shared'
|
5
5
|
|
6
6
|
RSpec.describe Reek::Smells::IrresponsibleModule do
|
7
|
+
it 'reports a class without a comment' do
|
8
|
+
src = 'class BadClass; end'
|
9
|
+
expect(src).to reek_of :IrresponsibleModule, name: 'BadClass'
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'reports a module without a comment' do
|
13
|
+
src = 'module BadClass; end'
|
14
|
+
expect(src).to reek_of :IrresponsibleModule, name: 'BadClass'
|
15
|
+
end
|
16
|
+
|
7
17
|
it 'does not report re-opened modules' do
|
8
18
|
src = <<-EOS
|
9
19
|
# Abstract base class
|
@@ -22,11 +32,6 @@ RSpec.describe Reek::Smells::IrresponsibleModule do
|
|
22
32
|
expect(src).not_to reek_of(:IrresponsibleModule)
|
23
33
|
end
|
24
34
|
|
25
|
-
it 'reports a class without a comment' do
|
26
|
-
src = 'class BadClass; end'
|
27
|
-
expect(src).to reek_of :IrresponsibleModule, name: 'BadClass'
|
28
|
-
end
|
29
|
-
|
30
35
|
it 'reports a class with an empty comment' do
|
31
36
|
src = <<-EOS
|
32
37
|
#
|
@@ -61,6 +66,80 @@ RSpec.describe Reek::Smells::IrresponsibleModule do
|
|
61
66
|
expect(src).to reek_of :IrresponsibleModule, name: 'Foo::Bar'
|
62
67
|
end
|
63
68
|
|
69
|
+
it 'does not report modules used only as namespaces' do
|
70
|
+
src = <<-EOS
|
71
|
+
module Foo
|
72
|
+
# Describes Bar
|
73
|
+
class Bar
|
74
|
+
def baz
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
EOS
|
79
|
+
expect(src).not_to reek_of(:IrresponsibleModule)
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'does not report classes used only as namespaces' do
|
83
|
+
src = <<-EOS
|
84
|
+
class Foo
|
85
|
+
# Describes Bar
|
86
|
+
module Bar
|
87
|
+
def qux
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
EOS
|
92
|
+
expect(src).not_to reek_of(:IrresponsibleModule)
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'reports modules that have both nested modules and methods' do
|
96
|
+
src = <<-EOS
|
97
|
+
module Foo
|
98
|
+
def foofoo
|
99
|
+
end
|
100
|
+
# Describes Bar
|
101
|
+
module Bar
|
102
|
+
end
|
103
|
+
end
|
104
|
+
EOS
|
105
|
+
expect(src).to reek_of(:IrresponsibleModule)
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'reports modules that have both nested modules and singleton methods' do
|
109
|
+
src = <<-EOS
|
110
|
+
module Foo
|
111
|
+
def self.foofoo
|
112
|
+
end
|
113
|
+
# Describes Bar
|
114
|
+
module Bar
|
115
|
+
end
|
116
|
+
end
|
117
|
+
EOS
|
118
|
+
expect(src).to reek_of(:IrresponsibleModule)
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'reports modules that have both nested modules and methods on the singleton class' do
|
122
|
+
src = <<-EOS
|
123
|
+
module Foo
|
124
|
+
class << self
|
125
|
+
def foofoo
|
126
|
+
end
|
127
|
+
end
|
128
|
+
# Describes Bar
|
129
|
+
module Bar
|
130
|
+
end
|
131
|
+
end
|
132
|
+
EOS
|
133
|
+
expect(src).to reek_of(:IrresponsibleModule)
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'reports classes that have a defined superclass' do
|
137
|
+
src = <<-EOS
|
138
|
+
class Foo < Bar; end
|
139
|
+
EOS
|
140
|
+
expect(src).to reek_of(:IrresponsibleModule)
|
141
|
+
end
|
142
|
+
|
64
143
|
context 'when a smell is reported' do
|
65
144
|
before do
|
66
145
|
@source_name = 'dummy_source'
|
@@ -62,6 +62,24 @@ RSpec.describe Reek::Smells::UtilityFunction do
|
|
62
62
|
src = 'module M; def self.simple(a) a.to_s; end; end'
|
63
63
|
expect(src).not_to reek_of(:UtilityFunction)
|
64
64
|
end
|
65
|
+
|
66
|
+
context 'by using `module_function`' do
|
67
|
+
it 'should not report UtilityFunction' do
|
68
|
+
src = 'class C; def m(a) a.to_s; end; module_function :m; end'
|
69
|
+
expect(src).not_to reek_of(:UtilityFunction)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should not report UtilityFunction also when using multiple arguments' do
|
73
|
+
src = <<-EOS
|
74
|
+
class C
|
75
|
+
def m1(a) a.to_s; end
|
76
|
+
def m2(a) a.to_s; end
|
77
|
+
module_function :m1, m2
|
78
|
+
end
|
79
|
+
EOS
|
80
|
+
expect(src).not_to reek_of(:UtilityFunction)
|
81
|
+
end
|
82
|
+
end
|
65
83
|
end
|
66
84
|
|
67
85
|
context 'with no calls' do
|
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: 3.0.
|
4
|
+
version: 3.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Rutherford
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2015-07-
|
13
|
+
date: 2015-07-10 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: parser
|
@@ -74,14 +74,14 @@ dependencies:
|
|
74
74
|
requirements:
|
75
75
|
- - "~>"
|
76
76
|
- !ruby/object:Gem::Version
|
77
|
-
version: 0.
|
77
|
+
version: 0.7.2
|
78
78
|
type: :development
|
79
79
|
prerelease: false
|
80
80
|
version_requirements: !ruby/object:Gem::Requirement
|
81
81
|
requirements:
|
82
82
|
- - "~>"
|
83
83
|
- !ruby/object:Gem::Version
|
84
|
-
version: 0.
|
84
|
+
version: 0.7.2
|
85
85
|
- !ruby/object:Gem::Dependency
|
86
86
|
name: ataru
|
87
87
|
requirement: !ruby/object:Gem::Requirement
|
@@ -172,14 +172,14 @@ dependencies:
|
|
172
172
|
requirements:
|
173
173
|
- - "~>"
|
174
174
|
- !ruby/object:Gem::Version
|
175
|
-
version: 0.
|
175
|
+
version: 0.32.1
|
176
176
|
type: :development
|
177
177
|
prerelease: false
|
178
178
|
version_requirements: !ruby/object:Gem::Requirement
|
179
179
|
requirements:
|
180
180
|
- - "~>"
|
181
181
|
- !ruby/object:Gem::Version
|
182
|
-
version: 0.
|
182
|
+
version: 0.32.1
|
183
183
|
description: |2
|
184
184
|
Reek is a tool that examines Ruby classes, modules and methods and reports
|
185
185
|
any code smells it finds.
|
@@ -202,7 +202,6 @@ files:
|
|
202
202
|
- License.txt
|
203
203
|
- README.md
|
204
204
|
- Rakefile
|
205
|
-
- assets/html_output.html.erb
|
206
205
|
- bin/reek
|
207
206
|
- config/cucumber.yml
|
208
207
|
- config/defaults.reek
|
@@ -290,8 +289,10 @@ files:
|
|
290
289
|
- lib/reek/context/singleton_method_context.rb
|
291
290
|
- lib/reek/examiner.rb
|
292
291
|
- lib/reek/rake/task.rb
|
292
|
+
- lib/reek/report.rb
|
293
293
|
- lib/reek/report/formatter.rb
|
294
294
|
- lib/reek/report/heading_formatter.rb
|
295
|
+
- lib/reek/report/html_report.html.erb
|
295
296
|
- lib/reek/report/location_formatter.rb
|
296
297
|
- lib/reek/report/report.rb
|
297
298
|
- lib/reek/smells.rb
|