reek 3.0.4 → 3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +6 -0
  3. data/README.md +41 -20
  4. data/features/configuration_files/directory_specific_directives.feature +280 -0
  5. data/features/configuration_files/masking_smells.feature +0 -14
  6. data/features/step_definitions/sample_file_steps.rb +2 -2
  7. data/lib/reek/ast/sexp_extensions.rb +72 -60
  8. data/lib/reek/cli/application.rb +2 -5
  9. data/lib/reek/cli/reek_command.rb +1 -1
  10. data/lib/reek/configuration/app_configuration.rb +115 -61
  11. data/lib/reek/configuration/configuration_file_finder.rb +19 -0
  12. data/lib/reek/context/code_context.rb +102 -29
  13. data/lib/reek/context/method_context.rb +11 -48
  14. data/lib/reek/context/module_context.rb +2 -6
  15. data/lib/reek/context/root_context.rb +7 -14
  16. data/lib/reek/examiner.rb +15 -10
  17. data/lib/reek/report/report.rb +5 -4
  18. data/lib/reek/smells/boolean_parameter.rb +1 -1
  19. data/lib/reek/smells/duplicate_method_call.rb +1 -5
  20. data/lib/reek/smells/irresponsible_module.rb +2 -2
  21. data/lib/reek/smells/smell_repository.rb +30 -16
  22. data/lib/reek/smells/utility_function.rb +1 -1
  23. data/lib/reek/source/source_code.rb +10 -6
  24. data/lib/reek/source/source_locator.rb +27 -26
  25. data/lib/reek/spec.rb +8 -6
  26. data/lib/reek/spec/should_reek.rb +6 -2
  27. data/lib/reek/spec/should_reek_of.rb +5 -2
  28. data/lib/reek/spec/should_reek_only_of.rb +1 -1
  29. data/lib/reek/tree_walker.rb +49 -21
  30. data/lib/reek/version.rb +1 -1
  31. data/spec/reek/ast/sexp_extensions_spec.rb +4 -12
  32. data/spec/reek/configuration/app_configuration_spec.rb +80 -52
  33. data/spec/reek/configuration/configuration_file_finder_spec.rb +27 -15
  34. data/spec/reek/context/code_context_spec.rb +66 -17
  35. data/spec/reek/context/method_context_spec.rb +66 -64
  36. data/spec/reek/context/root_context_spec.rb +3 -1
  37. data/spec/reek/context/singleton_method_context_spec.rb +1 -2
  38. data/spec/reek/examiner_spec.rb +5 -8
  39. data/spec/reek/report/xml_report_spec.rb +3 -2
  40. data/spec/reek/smells/attribute_spec.rb +12 -17
  41. data/spec/reek/smells/duplicate_method_call_spec.rb +17 -25
  42. data/spec/reek/smells/feature_envy_spec.rb +4 -5
  43. data/spec/reek/smells/irresponsible_module_spec.rb +43 -0
  44. data/spec/reek/smells/nested_iterators_spec.rb +12 -26
  45. data/spec/reek/smells/too_many_statements_spec.rb +2 -210
  46. data/spec/reek/smells/utility_function_spec.rb +49 -5
  47. data/spec/reek/source/source_locator_spec.rb +39 -43
  48. data/spec/reek/spec/should_reek_of_spec.rb +3 -2
  49. data/spec/reek/spec/should_reek_spec.rb +25 -17
  50. data/spec/reek/tree_walker_spec.rb +214 -23
  51. data/spec/samples/configuration/full_configuration.reek +9 -0
  52. data/spec/spec_helper.rb +10 -10
  53. metadata +4 -3
  54. data/features/configuration_files/overrides_defaults.feature +0 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 02b11e29be1e78d0ca3b5c5bb7e209d215a34142
4
- data.tar.gz: 2fbda696128728c307a65abe0c61479bccc197ea
3
+ metadata.gz: c490c79513c93451a148336881db8ffec7665e7e
4
+ data.tar.gz: 88059b5d7db2a76be01ca26339e3753f1ef4fa68
5
5
  SHA512:
6
- metadata.gz: 08a97e6cba42ad58c4f6691d89cf4267ce79777095766859421ef6853b98f51c44521988f977e2d78d6dd1ebcdb56bc9b88e53e1fdc0af204f1aba22fe38be8c
7
- data.tar.gz: 6074cc6366979c9e75722d8359fbdba75a2539eba9b3ffad21b5eaee59fa4ae7789d717ea6044b128577ccf13010a49caf69156eef34f239594cc592354eb2d1
6
+ metadata.gz: a93e6367ce746f4c1fba78e8ca91e035b9f3f9624316f329c1f5640415649816dfcc5608427ec113ac4c66aceb7f3eaed800af0c11a8931ebfc828e0a1931413
7
+ data.tar.gz: 24b764ba2d1a15fefe32702567f55cdf49270aaf00c78b6cab50420ebad3b96651d450c210934b8e76e50da2048b14e0b496c4a861ebeb98f4a6d6fedfdac21c
data/CHANGELOG CHANGED
@@ -1,3 +1,9 @@
1
+ == 3.1
2
+
3
+ * (troessner) Make smells configurable on a directory base
4
+ * (mvz) Handle modules defined by constant assignment
5
+ * (mvz) Handle modifier style use of module_function for UtilityFunction
6
+
1
7
  == 3.0.4
2
8
 
3
9
  * (troessner) Fix wiki-link cli option.
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
- The first thing you probably want to check out are the
148
- [Basic Smell Options](docs/Basic-Smell-Options.md)
149
- which are supported by every smell type. Certain smell
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
- - "ActiveModelErrorAdder#self.run" # should be refactored
169
- - "BookingRequests::Transfer#remote_validation"
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
- - app/views
179
- - app/controllers
191
+ - lib/legacy
192
+ - lib/rake/legacy_tasks
180
193
  ```
181
194
 
182
- Additionally to the smell configuration you can exclude whole directories from scans using `exclude_paths` as you can see at the end of the configuration above.
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 = File.expand_path file_name, 'spec/samples'
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('HOME', File.expand_path(File.join(current_directory, 'home')))
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() self[1] end
173
- def method_name() self[2] end
174
- def args() self[3..-1] end
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
- # Utility methods for :module nodes.
393
- module ModuleNode
394
- def name() self[1] end
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.is_a?(::Parser::AST::Node) ? name.simple_name : name
381
+ name.split('::').last
398
382
  end
383
+ end
399
384
 
400
- def full_name(outer)
401
- prefix = outer == '' ? '' : "#{outer}::"
402
- "#{prefix}#{text_name}"
403
- end
385
+ # Utility methods for :module nodes.
386
+ module ModuleNode
387
+ include ModuleNodeBase
404
388
 
405
- def text_name
406
- SexpFormatter.format(name)
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