reek 3.0.4 → 3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +6 -0
- data/README.md +41 -20
- data/features/configuration_files/directory_specific_directives.feature +280 -0
- data/features/configuration_files/masking_smells.feature +0 -14
- data/features/step_definitions/sample_file_steps.rb +2 -2
- data/lib/reek/ast/sexp_extensions.rb +72 -60
- data/lib/reek/cli/application.rb +2 -5
- data/lib/reek/cli/reek_command.rb +1 -1
- data/lib/reek/configuration/app_configuration.rb +115 -61
- data/lib/reek/configuration/configuration_file_finder.rb +19 -0
- data/lib/reek/context/code_context.rb +102 -29
- data/lib/reek/context/method_context.rb +11 -48
- data/lib/reek/context/module_context.rb +2 -6
- data/lib/reek/context/root_context.rb +7 -14
- data/lib/reek/examiner.rb +15 -10
- data/lib/reek/report/report.rb +5 -4
- data/lib/reek/smells/boolean_parameter.rb +1 -1
- data/lib/reek/smells/duplicate_method_call.rb +1 -5
- data/lib/reek/smells/irresponsible_module.rb +2 -2
- data/lib/reek/smells/smell_repository.rb +30 -16
- data/lib/reek/smells/utility_function.rb +1 -1
- data/lib/reek/source/source_code.rb +10 -6
- data/lib/reek/source/source_locator.rb +27 -26
- data/lib/reek/spec.rb +8 -6
- data/lib/reek/spec/should_reek.rb +6 -2
- data/lib/reek/spec/should_reek_of.rb +5 -2
- data/lib/reek/spec/should_reek_only_of.rb +1 -1
- data/lib/reek/tree_walker.rb +49 -21
- data/lib/reek/version.rb +1 -1
- data/spec/reek/ast/sexp_extensions_spec.rb +4 -12
- data/spec/reek/configuration/app_configuration_spec.rb +80 -52
- data/spec/reek/configuration/configuration_file_finder_spec.rb +27 -15
- data/spec/reek/context/code_context_spec.rb +66 -17
- data/spec/reek/context/method_context_spec.rb +66 -64
- data/spec/reek/context/root_context_spec.rb +3 -1
- data/spec/reek/context/singleton_method_context_spec.rb +1 -2
- data/spec/reek/examiner_spec.rb +5 -8
- data/spec/reek/report/xml_report_spec.rb +3 -2
- data/spec/reek/smells/attribute_spec.rb +12 -17
- data/spec/reek/smells/duplicate_method_call_spec.rb +17 -25
- data/spec/reek/smells/feature_envy_spec.rb +4 -5
- data/spec/reek/smells/irresponsible_module_spec.rb +43 -0
- data/spec/reek/smells/nested_iterators_spec.rb +12 -26
- data/spec/reek/smells/too_many_statements_spec.rb +2 -210
- data/spec/reek/smells/utility_function_spec.rb +49 -5
- data/spec/reek/source/source_locator_spec.rb +39 -43
- data/spec/reek/spec/should_reek_of_spec.rb +3 -2
- data/spec/reek/spec/should_reek_spec.rb +25 -17
- data/spec/reek/tree_walker_spec.rb +214 -23
- data/spec/samples/configuration/full_configuration.reek +9 -0
- data/spec/spec_helper.rb +10 -10
- metadata +4 -3
- data/features/configuration_files/overrides_defaults.feature +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c490c79513c93451a148336881db8ffec7665e7e
|
4
|
+
data.tar.gz: 88059b5d7db2a76be01ca26339e3753f1ef4fa68
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a93e6367ce746f4c1fba78e8ca91e035b9f3f9624316f329c1f5640415649816dfcc5608427ec113ac4c66aceb7f3eaed800af0c11a8931ebfc828e0a1931413
|
7
|
+
data.tar.gz: 24b764ba2d1a15fefe32702567f55cdf49270aaf00c78b6cab50420ebad3b96651d450c210934b8e76e50da2048b14e0b496c4a861ebeb98f4a6d6fedfdac21c
|
data/CHANGELOG
CHANGED
data/README.md
CHANGED
@@ -144,42 +144,63 @@ of how many `*.reek` files you might have on your filesystem.
|
|
144
144
|
|
145
145
|
#### Configuration options
|
146
146
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
types offer a configuration that goes beyond that
|
151
|
-
of the basic smell options, for instance
|
152
|
-
[Data Clump](docs/Data-Clump.md).
|
153
|
-
All options that go beyond the [Basic Smell Options](docs/Basic-Smell-Options.md)
|
154
|
-
should be documented in the corresponding smell type wiki page,
|
155
|
-
but if you want to get a quick and full overview over all possible
|
156
|
-
configurations you can always check out [the `config/default.reek`
|
157
|
-
file in this repository](config/defaults.reek).
|
158
|
-
|
159
|
-
Here's an excerpt of a `reek` configuration file from a commercial project:
|
147
|
+
We put a lot of effort into making `reek`'s configuration as self explanatory as possible so the
|
148
|
+
best way to understand it is by looking at a simple
|
149
|
+
example (e.g. `config.reek` in your project directory):
|
160
150
|
|
161
151
|
```yaml
|
162
152
|
---
|
153
|
+
|
154
|
+
### Generic smell configuration
|
155
|
+
|
156
|
+
# You can disable smells completely
|
163
157
|
IrresponsibleModule:
|
164
158
|
enabled: false
|
165
159
|
|
160
|
+
# You can use filters to silence reek warnings.
|
161
|
+
# Either because you simply disagree with reek (we are not the police) or
|
162
|
+
# because you want to fix this at a later point in time.
|
166
163
|
NestedIterators:
|
167
164
|
exclude:
|
168
|
-
- "
|
169
|
-
- "
|
170
|
-
- "BookingRequestsController#vehicle_options" # respond_to block
|
171
|
-
- "Content::Base#self.expose_fields" # unavoidable due to metaprogramming
|
165
|
+
- "MyWorker#self.class_method" # should be refactored
|
166
|
+
- "AnotherWorker#instance_method" # should be refactored as well
|
172
167
|
|
168
|
+
# A lot of smells allow fine tuning their configuration. You can look up all available options
|
169
|
+
# in the corresponding smell documentation in /docs. In most cases you probably can just go
|
170
|
+
# with the defaults we set in config/defaults.reek.
|
173
171
|
DataClump:
|
174
172
|
max_copies: 3
|
175
173
|
min_clump_size: 3
|
176
174
|
|
175
|
+
### Directory specific configuration
|
176
|
+
|
177
|
+
# You can configure smells on a per-directory base.
|
178
|
+
# E.g. the classic Rails case: controllers smell of NestedIterators (see /docs/Nested-Iterators.md) and
|
179
|
+
# helpers smell of UtilityFunction (see docs/Utility-Function.md)
|
180
|
+
"web_app/app/controllers":
|
181
|
+
NestedIterators:
|
182
|
+
enabled: false
|
183
|
+
"web_app/app/helpers":
|
184
|
+
UtilityFunction:
|
185
|
+
enabled: false
|
186
|
+
|
187
|
+
### Excluding directories
|
188
|
+
|
189
|
+
# Directories below will not be scanned at all
|
177
190
|
exclude_paths:
|
178
|
-
-
|
179
|
-
-
|
191
|
+
- lib/legacy
|
192
|
+
- lib/rake/legacy_tasks
|
180
193
|
```
|
181
194
|
|
182
|
-
|
195
|
+
Note you do not need a configuration file at all. If you're fine with all the [defaults](config/defaults.reek) we set you can skip this completely.
|
196
|
+
|
197
|
+
For more details please check out the [Basic Smell Options](docs/Basic-Smell-Options.md)
|
198
|
+
which are supported by every smell type. As you can see above, certain smell
|
199
|
+
types offer a configuration that goes beyond that of the basic smell options, for instance
|
200
|
+
[Data Clump](docs/Data-Clump.md).
|
201
|
+
All options that go beyond the [Basic Smell Options](docs/Basic-Smell-Options.md)
|
202
|
+
are documented in the corresponding smell type /docs page (if you want to get a quick overview over all possible
|
203
|
+
configurations you can also check out [the `config/default.reek` file in this repository](config/defaults.reek).
|
183
204
|
|
184
205
|
### Source code comments
|
185
206
|
|
@@ -0,0 +1,280 @@
|
|
1
|
+
Feature: Directory directives
|
2
|
+
In order to have a more fine-grained control over what reek reports
|
3
|
+
And to enable domain specific modes (like a Ruby on Rails mode)
|
4
|
+
As a user
|
5
|
+
I want to be able to configure reek using directory directives
|
6
|
+
|
7
|
+
Scenario: Configure multiple directories
|
8
|
+
Given a file named "web_app/config.reek" with:
|
9
|
+
"""
|
10
|
+
---
|
11
|
+
"web_app/app/controllers":
|
12
|
+
IrresponsibleModule:
|
13
|
+
enabled: false
|
14
|
+
NestedIterators:
|
15
|
+
enabled: false
|
16
|
+
"web_app/app/helpers":
|
17
|
+
IrresponsibleModule:
|
18
|
+
enabled: false
|
19
|
+
UtilityFunction:
|
20
|
+
enabled: false
|
21
|
+
"""
|
22
|
+
And a file named "web_app/app/controllers/users_controller.rb" with:
|
23
|
+
"""
|
24
|
+
class UsersController < ApplicationController
|
25
|
+
def show
|
26
|
+
respond_with do |format|
|
27
|
+
format.json { @user.to_custom_json }
|
28
|
+
format.xlm { @user.to_fancy_xml }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
"""
|
33
|
+
And a file named "web_app/app/helpers/application_helper.rb" with:
|
34
|
+
"""
|
35
|
+
module ApplicationHelper
|
36
|
+
def current_year
|
37
|
+
Time.zone.now.year
|
38
|
+
end
|
39
|
+
end
|
40
|
+
"""
|
41
|
+
And a file named "web_app/app/models/user.rb" with:
|
42
|
+
"""
|
43
|
+
class User < ActiveRecord::Base
|
44
|
+
def logged_in_with_role(r)
|
45
|
+
true
|
46
|
+
end
|
47
|
+
end
|
48
|
+
"""
|
49
|
+
When I run `reek -c web_app/config.reek web_app/`
|
50
|
+
Then it reports:
|
51
|
+
"""
|
52
|
+
web_app/app/models/user.rb -- 2 warnings:
|
53
|
+
[1]:User has no descriptive comment (IrresponsibleModule)
|
54
|
+
[2]:User#logged_in_with_role has unused parameter 'r' (UnusedParameters)
|
55
|
+
2 total warnings
|
56
|
+
"""
|
57
|
+
|
58
|
+
Scenario: Ignore trailing slashes
|
59
|
+
Given a file named "web_app/config.reek" with:
|
60
|
+
"""
|
61
|
+
---
|
62
|
+
"controllers/":
|
63
|
+
IrresponsibleModule:
|
64
|
+
enabled: false
|
65
|
+
"""
|
66
|
+
And a file named "controllers/users_controller.rb" with:
|
67
|
+
"""
|
68
|
+
class UsersController
|
69
|
+
def show
|
70
|
+
end
|
71
|
+
end
|
72
|
+
"""
|
73
|
+
When I run `reek -c web_app/config.reek controllers`
|
74
|
+
Then it reports nothing
|
75
|
+
|
76
|
+
Scenario: Partially mask smells in different directories
|
77
|
+
Given a file named "web_app/config.reek" with:
|
78
|
+
"""
|
79
|
+
---
|
80
|
+
"web_app/app/controllers":
|
81
|
+
IrresponsibleModule:
|
82
|
+
enabled: true
|
83
|
+
"web_app/app/helpers":
|
84
|
+
IrresponsibleModule:
|
85
|
+
enabled: false
|
86
|
+
UtilityFunction:
|
87
|
+
enabled: false
|
88
|
+
"web_app/app/models":
|
89
|
+
IrresponsibleModule:
|
90
|
+
enabled: false
|
91
|
+
"""
|
92
|
+
And a file named "web_app/app/controllers/users_controller.rb" with:
|
93
|
+
"""
|
94
|
+
class UsersController < ApplicationController
|
95
|
+
def show
|
96
|
+
respond_with do |format|
|
97
|
+
format.json { @user.to_custom_json }
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
"""
|
102
|
+
And a file named "web_app/app/helpers/application_helper.rb" with:
|
103
|
+
"""
|
104
|
+
module ApplicationHelper
|
105
|
+
def current_year
|
106
|
+
Time.zone.now.year
|
107
|
+
end
|
108
|
+
end
|
109
|
+
"""
|
110
|
+
And a file named "web_app/app/models/user.rb" with:
|
111
|
+
"""
|
112
|
+
class User < ActiveRecord::Base
|
113
|
+
def logged_in_with_role(r)
|
114
|
+
true
|
115
|
+
end
|
116
|
+
end
|
117
|
+
"""
|
118
|
+
When I run `reek -c web_app/config.reek web_app/`
|
119
|
+
Then it reports:
|
120
|
+
"""
|
121
|
+
web_app/app/controllers/users_controller.rb -- 2 warnings:
|
122
|
+
[1]:UsersController has no descriptive comment (IrresponsibleModule)
|
123
|
+
[4]:UsersController#show contains iterators nested 2 deep (NestedIterators)
|
124
|
+
web_app/app/models/user.rb -- 1 warning:
|
125
|
+
[2]:User#logged_in_with_role has unused parameter 'r' (UnusedParameters)
|
126
|
+
3 total warnings
|
127
|
+
"""
|
128
|
+
|
129
|
+
Scenario: Use the default directive if there is no directory directive
|
130
|
+
Given a file named "config.reek" with:
|
131
|
+
"""
|
132
|
+
---
|
133
|
+
"web_app/app/controllers":
|
134
|
+
IrresponsibleModule:
|
135
|
+
enabled: true
|
136
|
+
NestedIterators:
|
137
|
+
enabled: false
|
138
|
+
IrresponsibleModule:
|
139
|
+
enabled: false
|
140
|
+
NestedIterators:
|
141
|
+
enabled: true
|
142
|
+
"""
|
143
|
+
And a file named "web_app/app/controllers/users_controller.rb" with:
|
144
|
+
"""
|
145
|
+
class UsersController < ApplicationController
|
146
|
+
def show
|
147
|
+
respond_with do |format|
|
148
|
+
format.json { @user.to_custom_json }
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
"""
|
153
|
+
And a file named "other/projects_controller.rb" with:
|
154
|
+
"""
|
155
|
+
class ProjectController < ApplicationController
|
156
|
+
def show
|
157
|
+
respond_with do |format|
|
158
|
+
format.json { @project.to_custom_json }
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
"""
|
163
|
+
When I run `reek -c config.reek other/ web_app/`
|
164
|
+
Then it reports:
|
165
|
+
"""
|
166
|
+
other/projects_controller.rb -- 1 warning:
|
167
|
+
[4]:ProjectController#show contains iterators nested 2 deep (NestedIterators)
|
168
|
+
web_app/app/controllers/users_controller.rb -- 1 warning:
|
169
|
+
[1]:UsersController has no descriptive comment (IrresponsibleModule)
|
170
|
+
2 total warnings
|
171
|
+
"""
|
172
|
+
|
173
|
+
Scenario: Abort on non-existent directory
|
174
|
+
Given a file named "config.reek" with:
|
175
|
+
"""
|
176
|
+
---
|
177
|
+
"does/not/exist":
|
178
|
+
NestedIterators:
|
179
|
+
enabled: false
|
180
|
+
"""
|
181
|
+
When I run `reek -c config.reek lib/`
|
182
|
+
Then the exit status indicates an error
|
183
|
+
And stderr reports:
|
184
|
+
"""
|
185
|
+
Configuration error: Directory `does/not/exist` does not exist
|
186
|
+
|
187
|
+
"""
|
188
|
+
|
189
|
+
Scenario: Abort on non-existent smell type in directory directive
|
190
|
+
Given a file named "config.reek" with:
|
191
|
+
"""
|
192
|
+
---
|
193
|
+
"dummy_directory":
|
194
|
+
# Typo: Should be NestedIterators
|
195
|
+
IteratorsNested:
|
196
|
+
enabled: false
|
197
|
+
"""
|
198
|
+
And a file named "dummy_directory/dummy.rb" with:
|
199
|
+
"""
|
200
|
+
class Dummy
|
201
|
+
end
|
202
|
+
"""
|
203
|
+
|
204
|
+
When I run `reek -c config.reek dummy_directory/`
|
205
|
+
Then the exit status indicates an error
|
206
|
+
And stderr reports:
|
207
|
+
"""
|
208
|
+
You are trying to configure smell type IteratorsNested but we can't find one with that name.
|
209
|
+
Please make sure you spelled it right (see 'config/defaults.reek' in the reek
|
210
|
+
repository for a list of all available smell types.
|
211
|
+
|
212
|
+
"""
|
213
|
+
|
214
|
+
Scenario: Abort on file as directory directive
|
215
|
+
Given a file named "config.reek" with:
|
216
|
+
"""
|
217
|
+
---
|
218
|
+
"dummy_directory/dummy.rb":
|
219
|
+
NestedIterators:
|
220
|
+
enabled: false
|
221
|
+
"""
|
222
|
+
And a file named "dummy_directory/dummy.rb" with:
|
223
|
+
"""
|
224
|
+
class Dummy
|
225
|
+
end
|
226
|
+
"""
|
227
|
+
When I run `reek -c config.reek dummy_directory/`
|
228
|
+
Then the exit status indicates an error
|
229
|
+
And stderr reports:
|
230
|
+
"""
|
231
|
+
Configuration error: `dummy_directory/dummy.rb` is supposed to be a directory but is a file
|
232
|
+
|
233
|
+
"""
|
234
|
+
|
235
|
+
Scenario: In case of overlapping configurations, pick the most appropriate one
|
236
|
+
Given a file named "config.reek" with:
|
237
|
+
"""
|
238
|
+
---
|
239
|
+
"foo/bar/baz":
|
240
|
+
IrresponsibleModule:
|
241
|
+
enabled: false
|
242
|
+
NestedIterators:
|
243
|
+
enabled: true
|
244
|
+
"foo/bar":
|
245
|
+
IrresponsibleModule:
|
246
|
+
enabled: true
|
247
|
+
NestedIterators:
|
248
|
+
enabled: false
|
249
|
+
"""
|
250
|
+
And a file named "foo/bar/baz/klass.rb" with:
|
251
|
+
"""
|
252
|
+
class Klass
|
253
|
+
def meth
|
254
|
+
respond_to do
|
255
|
+
answer_to do
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
"""
|
261
|
+
And a file named "foo/bar/klazz.rb" with:
|
262
|
+
"""
|
263
|
+
class Klazz
|
264
|
+
def meth
|
265
|
+
respond_to do
|
266
|
+
answer_to do
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
"""
|
272
|
+
When I run `reek -c config.reek foo/`
|
273
|
+
Then it reports:
|
274
|
+
"""
|
275
|
+
foo/bar/baz/klass.rb -- 1 warning:
|
276
|
+
[4]:Klass#meth contains iterators nested 2 deep (NestedIterators)
|
277
|
+
foo/bar/klazz.rb -- 1 warning:
|
278
|
+
[1]:Klazz has no descriptive comment (IrresponsibleModule)
|
279
|
+
2 total warnings
|
280
|
+
"""
|
@@ -3,20 +3,6 @@ Feature: Masking smells using config files
|
|
3
3
|
As a developer
|
4
4
|
I want to mask some smells using config files
|
5
5
|
|
6
|
-
Scenario: empty config file is ignored
|
7
|
-
Given a smelly file called 'smelly.rb'
|
8
|
-
And an empty configuration file called 'empty.reek'
|
9
|
-
When I run reek -c empty.reek smelly.rb
|
10
|
-
Then it reports the error 'Warning: Invalid configuration file "empty.reek" -- Empty file'
|
11
|
-
And the exit status indicates smells
|
12
|
-
And it reports:
|
13
|
-
"""
|
14
|
-
smelly.rb -- 3 warnings:
|
15
|
-
[4, 5]:Smelly#m calls @foo.bar 2 times (DuplicateMethodCall)
|
16
|
-
[4, 5]:Smelly#m calls puts(@foo.bar) 2 times (DuplicateMethodCall)
|
17
|
-
[3]:Smelly#m has the name 'm' (UncommunicativeMethodName)
|
18
|
-
"""
|
19
|
-
|
20
6
|
Scenario: corrupt config file prevents normal output
|
21
7
|
Given a smelly file called 'smelly.rb'
|
22
8
|
And a corrupt configuration file called 'corrupt.reek'
|
@@ -26,7 +26,7 @@ Given(/^a smelly file with inline masking called 'inline.rb'$/) do
|
|
26
26
|
end
|
27
27
|
|
28
28
|
Given(/^the "(.*?)" sample file exists$/) do |file_name|
|
29
|
-
full_path =
|
29
|
+
full_path = Pathname.new("#{__dir__}/../../spec/samples/#{file_name}")
|
30
30
|
in_current_directory { FileUtils.cp full_path, file_name }
|
31
31
|
end
|
32
32
|
|
@@ -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
|
138
|
+
set_env 'HOME', Pathname.new("#{current_directory}/home").expand_path.to_s
|
139
139
|
write_file('home/config.reek', <<-EOS.strip_heredoc)
|
140
140
|
---
|
141
141
|
DuplicateMethodCall:
|
@@ -169,9 +169,9 @@ module Reek
|
|
169
169
|
|
170
170
|
# Utility methods for :send nodes.
|
171
171
|
module SendNode
|
172
|
-
def receiver
|
173
|
-
def method_name()
|
174
|
-
def args()
|
172
|
+
def receiver; children.first; end
|
173
|
+
def method_name() children[1]; end
|
174
|
+
def args() children[2..-1] end
|
175
175
|
|
176
176
|
def participants
|
177
177
|
([receiver] + args).compact
|
@@ -180,8 +180,14 @@ module Reek
|
|
180
180
|
def arg_names
|
181
181
|
args.map { |arg| arg[1] }
|
182
182
|
end
|
183
|
+
|
184
|
+
def object_creation_call?
|
185
|
+
method_name == :new
|
186
|
+
end
|
183
187
|
end
|
184
188
|
|
189
|
+
Op_AsgnNode = SendNode
|
190
|
+
|
185
191
|
# Base module for utility methods for nodes representing variables.
|
186
192
|
module VariableBase
|
187
193
|
def name() self[1] end
|
@@ -252,43 +258,7 @@ module Reek
|
|
252
258
|
# Checking if a method is a singleton method.
|
253
259
|
module SingletonMethod
|
254
260
|
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
|
261
|
+
singleton_method_via_class_self_notation?
|
292
262
|
end
|
293
263
|
|
294
264
|
# Ruby allows us to make a method a singleton_method using the
|
@@ -301,16 +271,6 @@ module Reek
|
|
301
271
|
return unless parent
|
302
272
|
parent.type == :sclass
|
303
273
|
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
274
|
end
|
315
275
|
|
316
276
|
# Utility methods for :def nodes.
|
@@ -346,6 +306,7 @@ module Reek
|
|
346
306
|
end
|
347
307
|
|
348
308
|
include MethodNodeBase
|
309
|
+
|
349
310
|
def full_name(outer)
|
350
311
|
prefix = outer == '' ? '' : "#{outer}#"
|
351
312
|
"#{prefix}#{SexpFormatter.format(receiver)}.#{name}"
|
@@ -389,21 +350,45 @@ module Reek
|
|
389
350
|
end
|
390
351
|
end
|
391
352
|
|
392
|
-
#
|
393
|
-
module
|
394
|
-
|
353
|
+
# Base module for utility methods for module nodes.
|
354
|
+
module ModuleNodeBase
|
355
|
+
# The full name of the module or class, including the name of any
|
356
|
+
# module or class it is nested inside of.
|
357
|
+
#
|
358
|
+
# For example, given code like this:
|
359
|
+
#
|
360
|
+
# module Foo
|
361
|
+
# class Bar::Baz
|
362
|
+
# end
|
363
|
+
# end
|
364
|
+
#
|
365
|
+
# The full name for the inner class will be 'Foo::Bar::Baz'. To return
|
366
|
+
# the correct name, the name of the outer context has to be passed into this method.
|
367
|
+
#
|
368
|
+
# @param outer [String] full name of the wrapping module or class
|
369
|
+
# @return the module's full name
|
370
|
+
def full_name(outer)
|
371
|
+
prefix = outer == '' ? '' : "#{outer}::"
|
372
|
+
"#{prefix}#{name}"
|
373
|
+
end
|
395
374
|
|
375
|
+
# The final section of the module or class name. For example, for a
|
376
|
+
# module with name 'Foo::Bar' this will return 'Bar'; for a module with
|
377
|
+
# name 'Foo' this will return 'Foo'.
|
378
|
+
#
|
379
|
+
# @return [String] the final section of the name
|
396
380
|
def simple_name
|
397
|
-
name.
|
381
|
+
name.split('::').last
|
398
382
|
end
|
383
|
+
end
|
399
384
|
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
end
|
385
|
+
# Utility methods for :module nodes.
|
386
|
+
module ModuleNode
|
387
|
+
include ModuleNodeBase
|
404
388
|
|
405
|
-
|
406
|
-
|
389
|
+
# @return [String] name as given in the module statement
|
390
|
+
def name
|
391
|
+
SexpFormatter.format(children.first)
|
407
392
|
end
|
408
393
|
end
|
409
394
|
|
@@ -413,6 +398,33 @@ module Reek
|
|
413
398
|
def superclass() self[2] end
|
414
399
|
end
|
415
400
|
|
401
|
+
# Utility methods for :casgn nodes.
|
402
|
+
module CasgnNode
|
403
|
+
include ModuleNodeBase
|
404
|
+
|
405
|
+
MODULE_DEFINERS = [:Class, :Struct]
|
406
|
+
|
407
|
+
def defines_module?
|
408
|
+
call = case value.type
|
409
|
+
when :block
|
410
|
+
value.call
|
411
|
+
when :send
|
412
|
+
value
|
413
|
+
end
|
414
|
+
call &&
|
415
|
+
call.object_creation_call? &&
|
416
|
+
MODULE_DEFINERS.include?(call.receiver.simple_name)
|
417
|
+
end
|
418
|
+
|
419
|
+
def name
|
420
|
+
SexpFormatter.format(children[1])
|
421
|
+
end
|
422
|
+
|
423
|
+
def value
|
424
|
+
children.last
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
416
428
|
# Utility methods for :yield nodes.
|
417
429
|
module YieldNode
|
418
430
|
def args() self[1..-1] end
|