reek 1.2.7.3 → 1.2.8
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +17 -0
- data/README.md +32 -48
- data/config/defaults.reek +7 -1
- data/features/api.feature +20 -0
- data/features/masking_smells.feature +41 -0
- data/features/options.feature +4 -0
- data/features/rake_task.feature +14 -0
- data/features/yaml.feature +8 -8
- data/lib/reek.rb +1 -1
- data/lib/reek/cli/command_line.rb +9 -2
- data/lib/reek/cli/reek_command.rb +5 -4
- data/lib/reek/cli/yaml_command.rb +2 -2
- data/lib/reek/core/code_context.rb +10 -1
- data/lib/reek/core/method_context.rb +2 -2
- data/lib/reek/core/sniffer.rb +3 -1
- data/lib/reek/core/stop_context.rb +4 -0
- data/lib/reek/examiner.rb +2 -2
- data/lib/reek/rake/task.rb +16 -0
- data/lib/reek/smell_warning.rb +3 -2
- data/lib/reek/smells/attribute.rb +13 -9
- data/lib/reek/smells/boolean_parameter.rb +14 -9
- data/lib/reek/smells/class_variable.rb +16 -5
- data/lib/reek/smells/control_couple.rb +11 -6
- data/lib/reek/smells/data_clump.rb +33 -30
- data/lib/reek/smells/duplication.rb +39 -8
- data/lib/reek/smells/feature_envy.rb +7 -8
- data/lib/reek/smells/irresponsible_module.rb +12 -3
- data/lib/reek/smells/large_class.rb +31 -15
- data/lib/reek/smells/long_method.rb +15 -5
- data/lib/reek/smells/long_parameter_list.rb +14 -7
- data/lib/reek/smells/long_yield_list.rb +12 -9
- data/lib/reek/smells/nested_iterators.rb +46 -11
- data/lib/reek/smells/simulated_polymorphism.rb +16 -8
- data/lib/reek/smells/smell_detector.rb +13 -13
- data/lib/reek/smells/uncommunicative_method_name.rb +12 -20
- data/lib/reek/smells/uncommunicative_module_name.rb +17 -19
- data/lib/reek/smells/uncommunicative_parameter_name.rb +22 -15
- data/lib/reek/smells/uncommunicative_variable_name.rb +24 -18
- data/lib/reek/smells/utility_function.rb +6 -6
- data/lib/reek/source/code_comment.rb +19 -1
- data/lib/reek/source/tree_dresser.rb +40 -22
- data/reek.gemspec +6 -4
- data/spec/matchers/smell_of_matcher.rb +58 -0
- data/spec/reek/core/code_context_spec.rb +4 -2
- data/spec/reek/core/code_parser_spec.rb +2 -1
- data/spec/reek/core/method_context_spec.rb +5 -5
- data/spec/reek/smells/attribute_spec.rb +2 -4
- data/spec/reek/smells/boolean_parameter_spec.rb +32 -42
- data/spec/reek/smells/class_variable_spec.rb +22 -6
- data/spec/reek/smells/control_couple_spec.rb +15 -14
- data/spec/reek/smells/data_clump_spec.rb +29 -111
- data/spec/reek/smells/duplication_spec.rb +79 -49
- data/spec/reek/smells/feature_envy_spec.rb +1 -2
- data/spec/reek/smells/irresponsible_module_spec.rb +43 -22
- data/spec/reek/smells/large_class_spec.rb +34 -59
- data/spec/reek/smells/long_method_spec.rb +15 -10
- data/spec/reek/smells/long_parameter_list_spec.rb +24 -24
- data/spec/reek/smells/long_yield_list_spec.rb +13 -14
- data/spec/reek/smells/nested_iterators_spec.rb +93 -76
- data/spec/reek/smells/smell_detector_shared.rb +4 -2
- data/spec/reek/smells/uncommunicative_method_name_spec.rb +10 -27
- data/spec/reek/smells/uncommunicative_module_name_spec.rb +22 -23
- data/spec/reek/smells/uncommunicative_parameter_name_spec.rb +36 -26
- data/spec/reek/smells/uncommunicative_variable_name_spec.rb +45 -48
- data/spec/reek/smells/utility_function_spec.rb +14 -13
- data/spec/reek/source/code_comment_spec.rb +61 -3
- data/spec/reek/source/tree_dresser_spec.rb +96 -1
- data/spec/samples/config/allow_duplication.reek +3 -0
- data/spec/samples/config/deeper_nested_iterators.reek +3 -0
- data/spec/samples/demo/demo.rb +8 -0
- data/spec/samples/inline_config/dirty.rb +16 -0
- data/spec/samples/inline_config/masked.reek +7 -0
- data/spec/samples/mask_some/dirty.rb +8 -0
- data/spec/samples/mask_some/some.reek +8 -0
- data/spec/spec_helper.rb +2 -0
- metadata +15 -5
data/History.txt
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
== 1.2.8 (2010-04-26)
|
2
|
+
|
3
|
+
=== Major Changes
|
4
|
+
* Smell detectors can be configured or disabled in code comments
|
5
|
+
** Comment with :reek:smell_name disables the named smell for a class, module or method
|
6
|
+
** Comment with :reek:smell_name:{...} for more detailed configuration
|
7
|
+
* Additional config file(s) can be specified:
|
8
|
+
** on the command-line using -c
|
9
|
+
** via Reek::Rake::Task in the rakefile
|
10
|
+
|
11
|
+
=== Minor Changes
|
12
|
+
* Duplication can be configured to ignore specific calls
|
13
|
+
* IrresponsibleModule now reports scoped module names correctly (#66)
|
14
|
+
* NestedIterators is now more configurable:
|
15
|
+
** Option to specify permitted nesting depth (#14)
|
16
|
+
** Option to ignore certain iterator methods
|
17
|
+
|
1
18
|
== 1.2.7.3 (2010-03-29)
|
2
19
|
|
3
20
|
=== Minor Changes
|
data/README.md
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
Reek -- code smell detection for Ruby
|
2
|
-
=====================================
|
1
|
+
# Reek -- code smell detection for Ruby
|
3
2
|
|
4
3
|
Reek is a tool that examines Ruby classes, modules and methods and
|
5
4
|
reports any code smells it finds. Install it like this:
|
@@ -16,63 +15,47 @@ or run
|
|
16
15
|
|
17
16
|
$ reek --help
|
18
17
|
|
19
|
-
Example
|
20
|
-
|
21
|
-
|
22
|
-
Imagine a source file <tt>csv_writer.rb</tt> containing:
|
23
|
-
|
24
|
-
class CsvWriter
|
25
|
-
def write_line(fields)
|
26
|
-
if (fields.length == 0)
|
27
|
-
puts
|
28
|
-
else
|
29
|
-
write_field(fields[0])
|
30
|
-
1.upto(fields.length-1) do |i|
|
31
|
-
print ","
|
32
|
-
write_field(fields[i])
|
33
|
-
end
|
34
|
-
puts
|
35
|
-
end
|
36
|
-
end
|
18
|
+
## Example
|
19
|
+
|
20
|
+
Imagine a source file <tt>demo.rb</tt> containing:
|
37
21
|
|
38
|
-
|
22
|
+
class Dirty
|
23
|
+
# This method smells of :reek:NestedIterators but ignores them
|
24
|
+
def awful(x, y, offset = 0, log = false)
|
25
|
+
puts @screen.title
|
26
|
+
@screen = widgets.map {|w| w.each {|key| key += 3}}
|
27
|
+
puts @screen.contents
|
28
|
+
end
|
39
29
|
end
|
40
30
|
|
41
31
|
Reek will report the following code smells in this file:
|
42
32
|
|
43
|
-
$ reek
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
* [Long Parameter List](http://wiki.github.com/kevinrutherford/reek/long-parameter-list)
|
59
|
-
* [Simulated Polymorphism](http://wiki.github.com/kevinrutherford/reek/simulated-polymorphism)
|
60
|
-
* [Uncommunicative Name](http://wiki.github.com/kevinrutherford/reek/uncommunicative-name)
|
61
|
-
|
62
|
-
...and more. See the [Reek wiki](http://wiki.github.com/kevinrutherford/reek/code-smells)
|
33
|
+
$ reek demo.rb
|
34
|
+
spec/samples/demo/demo.rb -- 6 warnings:
|
35
|
+
Dirty has no descriptive comment (IrresponsibleModule)
|
36
|
+
Dirty#awful has 4 parameters (LongParameterList)
|
37
|
+
Dirty#awful has boolean parameter 'log' (ControlCouple)
|
38
|
+
Dirty#awful has the parameter name 'x' (UncommunicativeName)
|
39
|
+
Dirty#awful has the parameter name 'y' (UncommunicativeName)
|
40
|
+
Dirty#awful has the variable name 'w' (UncommunicativeName)
|
41
|
+
|
42
|
+
## Features
|
43
|
+
|
44
|
+
Reek currently includes checks for some aspects of Control Couple,
|
45
|
+
Data Clump, Feature Envy, Large Class, Long Method, Long Parameter List,
|
46
|
+
Simulated Polymorphism, Uncommunicative Name and more.
|
47
|
+
See the [Reek wiki](http://wiki.github.com/kevinrutherford/reek/code-smells)
|
63
48
|
for up to date details of exactly what Reek will check in your code.
|
64
49
|
|
65
|
-
Tool Integration
|
66
|
-
----------------
|
50
|
+
### Tool Integration
|
67
51
|
|
68
52
|
Reek integrates with many of your favourite tools:
|
69
53
|
|
70
|
-
*
|
71
|
-
*
|
54
|
+
* `require 'reek/rake/task'` to easily add Reek to your Rakefile
|
55
|
+
* `require 'reek/spec'` to add the `should_not reek` custom matcher to your Rspec examples
|
72
56
|
* Reek is fully compliant with Ruby 1.8.6, 1.8.7 and 1.9.1
|
73
57
|
|
74
|
-
Dependencies
|
75
|
-
------------
|
58
|
+
### Dependencies
|
76
59
|
|
77
60
|
Reek makes use of the following other gems:
|
78
61
|
|
@@ -86,5 +69,6 @@ Learn More
|
|
86
69
|
Find out more about Reek from any of the following sources:
|
87
70
|
|
88
71
|
* Browse the Reek documentation at [http://wiki.github.com/kevinrutherford/reek](http://wiki.github.com/kevinrutherford/reek)
|
89
|
-
* Browse the code or install the latest
|
72
|
+
* Browse the code or install the latest development version from [http://github.com/kevinrutherford/reek/tree](http://github.com/kevinrutherford/reek/tree)
|
90
73
|
* Read the code API at [http://rdoc.info/projects/kevinrutherford/reek](http://rdoc.info/projects/kevinrutherford/reek)
|
74
|
+
* Follow [@rubyreek](http://twitter.com/rubyreek) on twitter!
|
data/config/defaults.reek
CHANGED
@@ -56,14 +56,20 @@ UncommunicativeModuleName:
|
|
56
56
|
- !ruby/regexp /^.$/
|
57
57
|
- !ruby/regexp /[0-9]$/
|
58
58
|
NestedIterators:
|
59
|
-
|
59
|
+
ignore_iterators: []
|
60
|
+
|
61
|
+
exclude: []
|
62
|
+
|
60
63
|
enabled: true
|
64
|
+
max_allowed_nesting: 1
|
61
65
|
LongMethod:
|
62
66
|
max_statements: 5
|
63
67
|
exclude:
|
64
68
|
- initialize
|
65
69
|
enabled: true
|
66
70
|
Duplication:
|
71
|
+
allow_calls: []
|
72
|
+
|
67
73
|
exclude: []
|
68
74
|
|
69
75
|
enabled: true
|
@@ -0,0 +1,20 @@
|
|
1
|
+
@masking
|
2
|
+
Feature: The Reek API maintains backwards compatibility
|
3
|
+
In order to use Reek witthout fuss
|
4
|
+
As a developer
|
5
|
+
I want to mave a stable API
|
6
|
+
|
7
|
+
Scenario: the demo example reports as expected
|
8
|
+
When I run reek spec/samples/demo
|
9
|
+
Then the exit status indicates smells
|
10
|
+
And it reports:
|
11
|
+
"""
|
12
|
+
spec/samples/demo/demo.rb -- 6 warnings:
|
13
|
+
Dirty has no descriptive comment (IrresponsibleModule)
|
14
|
+
Dirty#awful has 4 parameters (LongParameterList)
|
15
|
+
Dirty#awful has boolean parameter 'log' (ControlCouple)
|
16
|
+
Dirty#awful has the parameter name 'x' (UncommunicativeName)
|
17
|
+
Dirty#awful has the parameter name 'y' (UncommunicativeName)
|
18
|
+
Dirty#awful has the variable name 'w' (UncommunicativeName)
|
19
|
+
|
20
|
+
"""
|
@@ -86,3 +86,44 @@ Feature: Masking smells using config files
|
|
86
86
|
Dirty#a calls puts(@s.title) twice (Duplication)
|
87
87
|
|
88
88
|
"""
|
89
|
+
|
90
|
+
Scenario: allow masking some calls for duplication smell
|
91
|
+
When I run reek spec/samples/mask_some/dirty.rb
|
92
|
+
Then the exit status indicates smells
|
93
|
+
And it reports:
|
94
|
+
"""
|
95
|
+
spec/samples/mask_some/dirty.rb -- 2 warnings:
|
96
|
+
Dirty#a calls @s.title twice (Duplication)
|
97
|
+
Dirty#a contains iterators nested 2 deep (NestedIterators)
|
98
|
+
|
99
|
+
"""
|
100
|
+
|
101
|
+
@comments
|
102
|
+
Scenario: provide extra masking inline in comments
|
103
|
+
When I run reek spec/samples/inline_config
|
104
|
+
Then the exit status indicates smells
|
105
|
+
And it reports:
|
106
|
+
"""
|
107
|
+
spec/samples/inline_config/dirty.rb -- 1 warning:
|
108
|
+
Dirty#a calls @s.title twice (Duplication)
|
109
|
+
|
110
|
+
"""
|
111
|
+
|
112
|
+
Scenario: supports a config file
|
113
|
+
When I run reek -c spec/samples/config/allow_duplication.reek spec/samples/masked/dirty.rb
|
114
|
+
Then the exit status indicates smells
|
115
|
+
And it reports:
|
116
|
+
"""
|
117
|
+
spec/samples/masked/dirty.rb -- 1 warning:
|
118
|
+
Dirty#a contains iterators nested 2 deep (NestedIterators)
|
119
|
+
|
120
|
+
"""
|
121
|
+
|
122
|
+
Scenario: supports multiple config files
|
123
|
+
When I run reek -c spec/samples/config/allow_duplication.reek -c spec/samples/config/deeper_nested_iterators.reek spec/samples/masked/dirty.rb
|
124
|
+
Then it succeeds
|
125
|
+
And it reports:
|
126
|
+
"""
|
127
|
+
spec/samples/masked/dirty.rb -- 0 warnings
|
128
|
+
|
129
|
+
"""
|
data/features/options.feature
CHANGED
@@ -35,6 +35,10 @@ Feature: Reek can be controlled using command-line options
|
|
35
35
|
-v, --version Show version
|
36
36
|
|
37
37
|
|
38
|
+
Configuration:
|
39
|
+
-c, --config FILE Read configuration options from FILE
|
40
|
+
|
41
|
+
|
38
42
|
Report formatting:
|
39
43
|
-q, --[no-]quiet Suppress headings for smell-free source files
|
40
44
|
-y, --yaml Report smells in YAML format
|
data/features/rake_task.feature
CHANGED
@@ -65,3 +65,17 @@ Feature: Reek can be driven through its Task
|
|
65
65
|
Dirty#a has the name 'a' (UncommunicativeName)
|
66
66
|
Dirty#a has the variable name 'x' (UncommunicativeName)
|
67
67
|
"""
|
68
|
+
|
69
|
+
Scenario: can be configured with config_files
|
70
|
+
When I run rake reek with:
|
71
|
+
"""
|
72
|
+
Reek::Rake::Task.new do |t|
|
73
|
+
t.config_files = 'spec/samples/config/**/*.reek'
|
74
|
+
t.source_files = 'spec/samples/masked/dirty.rb'
|
75
|
+
end
|
76
|
+
"""
|
77
|
+
Then it succeeds
|
78
|
+
And it reports:
|
79
|
+
"""
|
80
|
+
spec/samples/masked/dirty.rb -- 0 warnings
|
81
|
+
"""
|
data/features/yaml.feature
CHANGED
@@ -54,7 +54,7 @@ Feature: Report smells using simple YAML layout
|
|
54
54
|
smell:
|
55
55
|
class: NestedIterators
|
56
56
|
depth: 2
|
57
|
-
subclass:
|
57
|
+
subclass: NestedIterators
|
58
58
|
message: contains iterators nested 2 deep
|
59
59
|
status:
|
60
60
|
is_active: true
|
@@ -63,7 +63,7 @@ Feature: Report smells using simple YAML layout
|
|
63
63
|
|
64
64
|
@stdin
|
65
65
|
Scenario: return non-zero status when there are smells
|
66
|
-
When I pass "
|
66
|
+
When I pass "class Turn; end" to reek --yaml
|
67
67
|
Then the exit status indicates smells
|
68
68
|
And it reports:
|
69
69
|
"""
|
@@ -71,14 +71,14 @@ Feature: Report smells using simple YAML layout
|
|
71
71
|
- !ruby/object:Reek::SmellWarning
|
72
72
|
location:
|
73
73
|
lines:
|
74
|
-
-
|
75
|
-
context: Turn
|
74
|
+
- 1
|
75
|
+
context: Turn
|
76
76
|
source: $stdin
|
77
77
|
smell:
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
message: has
|
78
|
+
class: IrresponsibleModule
|
79
|
+
subclass: IrresponsibleModule
|
80
|
+
module_name: Turn
|
81
|
+
message: has no descriptive comment
|
82
82
|
status:
|
83
83
|
is_active: true
|
84
84
|
|
data/lib/reek.rb
CHANGED
@@ -19,6 +19,7 @@ module Reek
|
|
19
19
|
@parser = OptionParser.new
|
20
20
|
@report_class = VerboseReport
|
21
21
|
@command_class = ReekCommand
|
22
|
+
@config_files = []
|
22
23
|
set_options
|
23
24
|
end
|
24
25
|
|
@@ -35,6 +36,7 @@ module Reek
|
|
35
36
|
# reek -v|--version Output the tool's version number
|
36
37
|
#
|
37
38
|
# reek [options] files List the smells in the given files
|
39
|
+
# -c|--config file Specify file(s) with config options
|
38
40
|
# -q|-[no-]quiet Only list files that have smells
|
39
41
|
# files Names of files or dirs to be checked
|
40
42
|
#
|
@@ -62,6 +64,11 @@ EOB
|
|
62
64
|
@command_class = VersionCommand
|
63
65
|
end
|
64
66
|
|
67
|
+
@parser.separator "\nConfiguration:"
|
68
|
+
@parser.on("-c", "--config FILE", "Read configuration options from FILE") do |file|
|
69
|
+
@config_files << file
|
70
|
+
end
|
71
|
+
|
65
72
|
@parser.separator "\nReport formatting:"
|
66
73
|
@parser.on("-q", "--[no-]quiet", "Suppress headings for smell-free source files") do |opt|
|
67
74
|
@report_class = opt ? QuietReport : VerboseReport
|
@@ -82,10 +89,10 @@ EOB
|
|
82
89
|
VersionCommand.new(@parser.program_name)
|
83
90
|
elsif @command_class == YamlCommand
|
84
91
|
sources = get_sources
|
85
|
-
YamlCommand.create(sources)
|
92
|
+
YamlCommand.create(sources, @config_files)
|
86
93
|
else
|
87
94
|
sources = get_sources
|
88
|
-
ReekCommand.create(sources, @report_class)
|
95
|
+
ReekCommand.create(sources, @report_class, @config_files)
|
89
96
|
end
|
90
97
|
end
|
91
98
|
|
@@ -8,19 +8,20 @@ module Reek
|
|
8
8
|
# text report format.
|
9
9
|
#
|
10
10
|
class ReekCommand
|
11
|
-
def self.create(sources, report_class)
|
12
|
-
new(report_class, sources)
|
11
|
+
def self.create(sources, report_class, config_files = [])
|
12
|
+
new(report_class, sources, config_files)
|
13
13
|
end
|
14
14
|
|
15
|
-
def initialize(report_class, sources)
|
15
|
+
def initialize(report_class, sources, config_files = [])
|
16
16
|
@sources = sources
|
17
17
|
@report_class = report_class
|
18
|
+
@config_files = config_files
|
18
19
|
end
|
19
20
|
|
20
21
|
def execute(view)
|
21
22
|
had_smells = false
|
22
23
|
@sources.each do |source|
|
23
|
-
examiner = Examiner.new(source)
|
24
|
+
examiner = Examiner.new(source, @config_files)
|
24
25
|
rpt = @report_class.new(examiner)
|
25
26
|
had_smells ||= examiner.smelly?
|
26
27
|
view.output(rpt.report)
|
@@ -8,8 +8,8 @@ module Reek
|
|
8
8
|
# YAML format.
|
9
9
|
#
|
10
10
|
class YamlCommand
|
11
|
-
def self.create(sources)
|
12
|
-
examiners = sources.map {|src| Examiner.new(src) }
|
11
|
+
def self.create(sources, config_files)
|
12
|
+
examiners = sources.map {|src| Examiner.new(src, config_files) }
|
13
13
|
new(examiners)
|
14
14
|
end
|
15
15
|
|
@@ -10,11 +10,12 @@ module Reek
|
|
10
10
|
#
|
11
11
|
class CodeContext
|
12
12
|
|
13
|
-
attr_reader :exp
|
13
|
+
attr_reader :exp, :config
|
14
14
|
|
15
15
|
def initialize(outer, exp)
|
16
16
|
@outer = outer
|
17
17
|
@exp = exp
|
18
|
+
@config = local_config
|
18
19
|
end
|
19
20
|
|
20
21
|
def name
|
@@ -50,6 +51,14 @@ module Reek
|
|
50
51
|
outer = @outer ? @outer.full_name : ''
|
51
52
|
exp.full_name(outer)
|
52
53
|
end
|
54
|
+
|
55
|
+
def local_config
|
56
|
+
return Hash.new if @exp.nil?
|
57
|
+
config = Source::CodeComment.new(@exp.comments || '').config
|
58
|
+
return config unless @outer
|
59
|
+
@outer.config.deep_copy.adopt!(config)
|
60
|
+
# no tests for this -----^
|
61
|
+
end
|
53
62
|
end
|
54
63
|
end
|
55
64
|
end
|
@@ -10,10 +10,10 @@ module Reek
|
|
10
10
|
module MethodParameters
|
11
11
|
def default_assignments
|
12
12
|
assignments = self[-1]
|
13
|
-
result =
|
13
|
+
result = []
|
14
14
|
return result unless is_assignment_block?(assignments)
|
15
15
|
assignments[1..-1].each do |exp|
|
16
|
-
result
|
16
|
+
result << exp[1..2] if exp[0] == :lasgn
|
17
17
|
end
|
18
18
|
result
|
19
19
|
end
|
data/lib/reek/core/sniffer.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require File.join(File.dirname(File.expand_path(__FILE__)), 'code_parser')
|
2
2
|
require File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), 'smells')
|
3
|
+
require File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), 'source', 'config_file')
|
3
4
|
require 'yaml'
|
4
5
|
|
5
6
|
#
|
@@ -64,12 +65,13 @@ module Reek
|
|
64
65
|
]
|
65
66
|
end
|
66
67
|
|
67
|
-
def initialize(src)
|
68
|
+
def initialize(src, config_files = [])
|
68
69
|
@typed_detectors = nil
|
69
70
|
@detectors = Hash.new
|
70
71
|
Sniffer.smell_classes.each do |klass|
|
71
72
|
@detectors[klass] = klass.new(src.desc)
|
72
73
|
end
|
74
|
+
config_files.each{ |cf| Reek::Source::ConfigFile.new(cf).configure(self) }
|
73
75
|
@source = src
|
74
76
|
src.configure(self)
|
75
77
|
end
|