reek 6.0.2 → 6.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +9 -0
- data/.github/workflows/ruby.yml +52 -0
- data/.rubocop_todo.yml +6 -4
- data/CHANGELOG.md +20 -0
- data/CONTRIBUTING.md +3 -0
- data/Dockerfile +1 -1
- data/Gemfile +6 -6
- data/bin/code_climate_reek +2 -3
- data/lib/reek/ast/ast_node_class_map.rb +1 -1
- data/lib/reek/ast/node.rb +1 -1
- data/lib/reek/code_comment.rb +22 -17
- data/lib/reek/configuration/excluded_paths.rb +2 -1
- data/lib/reek/context/code_context.rb +1 -1
- data/lib/reek/context/refinement_context.rb +16 -0
- data/lib/reek/context_builder.rb +16 -2
- data/lib/reek/report/code_climate/code_climate_formatter.rb +1 -3
- data/lib/reek/smell_detectors/base_detector.rb +1 -1
- data/lib/reek/smell_warning.rb +1 -1
- data/lib/reek/source/source_locator.rb +1 -3
- data/lib/reek/version.rb +2 -2
- data/reek.gemspec +5 -3
- metadata +11 -239
- data/.travis.yml +0 -40
- data/docs/API.md +0 -174
- data/docs/Attribute.md +0 -39
- data/docs/Basic-Smell-Options.md +0 -85
- data/docs/Boolean-Parameter.md +0 -54
- data/docs/Class-Variable.md +0 -40
- data/docs/Code-Smells.md +0 -39
- data/docs/Command-Line-Options.md +0 -119
- data/docs/Control-Couple.md +0 -26
- data/docs/Control-Parameter.md +0 -32
- data/docs/Data-Clump.md +0 -46
- data/docs/Duplicate-Method-Call.md +0 -264
- data/docs/Feature-Envy.md +0 -93
- data/docs/How-To-Write-New-Detectors.md +0 -132
- data/docs/How-reek-works-internally.md +0 -114
- data/docs/Instance-Variable-Assumption.md +0 -163
- data/docs/Irresponsible-Module.md +0 -47
- data/docs/Large-Class.md +0 -16
- data/docs/Long-Parameter-List.md +0 -39
- data/docs/Long-Yield-List.md +0 -37
- data/docs/Manual-Dispatch.md +0 -30
- data/docs/Missing-Safe-Method.md +0 -92
- data/docs/Module-Initialize.md +0 -62
- data/docs/Nested-Iterators.md +0 -59
- data/docs/Nil-Check.md +0 -47
- data/docs/RSpec-matchers.md +0 -129
- data/docs/Rake-Task.md +0 -66
- data/docs/Reek-4-to-Reek-5-migration.md +0 -188
- data/docs/Reek-Driven-Development.md +0 -46
- data/docs/Repeated-Conditional.md +0 -47
- data/docs/Simulated-Polymorphism.md +0 -16
- data/docs/Smell-Suppression.md +0 -96
- data/docs/Style-Guide.md +0 -19
- data/docs/Subclassed-From-Core-Class.md +0 -79
- data/docs/Too-Many-Constants.md +0 -37
- data/docs/Too-Many-Instance-Variables.md +0 -43
- data/docs/Too-Many-Methods.md +0 -56
- data/docs/Too-Many-Statements.md +0 -54
- data/docs/Uncommunicative-Method-Name.md +0 -94
- data/docs/Uncommunicative-Module-Name.md +0 -92
- data/docs/Uncommunicative-Name.md +0 -18
- data/docs/Uncommunicative-Parameter-Name.md +0 -90
- data/docs/Uncommunicative-Variable-Name.md +0 -96
- data/docs/Unused-Parameters.md +0 -28
- data/docs/Unused-Private-Method.md +0 -101
- data/docs/Utility-Function.md +0 -56
- data/docs/Versioning-Policy.md +0 -7
- data/docs/YAML-Reports.md +0 -93
- data/docs/defaults.reek.yml +0 -129
- data/docs/templates/default/docstring/html/public_api_marker.erb +0 -3
- data/docs/templates/default/docstring/setup.rb +0 -37
- data/docs/templates/default/fulldoc/html/css/common.css +0 -1
- data/docs/yard_plugin.rb +0 -17
- data/features/command_line_interface/basic_usage.feature +0 -15
- data/features/command_line_interface/options.feature +0 -123
- data/features/command_line_interface/show_progress.feature +0 -33
- data/features/command_line_interface/smell_selection.feature +0 -15
- data/features/command_line_interface/smells_count.feature +0 -38
- data/features/command_line_interface/stdin.feature +0 -65
- data/features/configuration_files/accept_setting.feature +0 -87
- data/features/configuration_files/directory_specific_directives.feature +0 -274
- data/features/configuration_files/exclude_directives.feature +0 -35
- data/features/configuration_files/exclude_paths_directives.feature +0 -42
- data/features/configuration_files/masking_smells.feature +0 -94
- data/features/configuration_files/mix_accept_reject_setting.feature +0 -84
- data/features/configuration_files/reject_setting.feature +0 -89
- data/features/configuration_files/schema_validation.feature +0 -59
- data/features/configuration_files/show_configuration_file.feature +0 -44
- data/features/configuration_files/unused_private_method.feature +0 -68
- data/features/configuration_loading.feature +0 -91
- data/features/configuration_via_source_comments/erroneous_source_comments.feature +0 -68
- data/features/configuration_via_source_comments/well_formed_source_comments.feature +0 -116
- data/features/locales.feature +0 -32
- data/features/programmatic_access.feature +0 -41
- data/features/rake_task/rake_task.feature +0 -138
- data/features/reports/codeclimate.feature +0 -59
- data/features/reports/json.feature +0 -59
- data/features/reports/reports.feature +0 -219
- data/features/reports/yaml.feature +0 -52
- data/features/rspec_matcher.feature +0 -41
- data/features/samples.feature +0 -305
- data/features/step_definitions/.rubocop.yml +0 -5
- data/features/step_definitions/reek_steps.rb +0 -102
- data/features/step_definitions/sample_file_steps.rb +0 -63
- data/features/support/env.rb +0 -33
- data/features/todo_list.feature +0 -108
- data/samples/checkstyle.xml +0 -7
- data/samples/clean_source/clean.rb +0 -6
- data/samples/configuration/accepts_rejects_and_excludes_for_detectors.reek.yml +0 -29
- data/samples/configuration/accepts_rejects_and_excludes_for_directory_directives.reek.yml +0 -30
- data/samples/configuration/corrupt.reek +0 -1
- data/samples/configuration/empty.reek +0 -0
- data/samples/configuration/full_configuration.reek +0 -13
- data/samples/configuration/full_mask.reek +0 -6
- data/samples/configuration/home/home.reek.yml +0 -4
- data/samples/configuration/partial_mask.reek +0 -4
- data/samples/configuration/regular_configuration/.reek.yml +0 -4
- data/samples/configuration/regular_configuration/empty_sub_directory/.gitignore +0 -0
- data/samples/configuration/with_excluded_paths.reek +0 -5
- data/samples/no_config_file/.keep +0 -0
- data/samples/paths.rb +0 -5
- data/samples/smelly_source/inline.rb +0 -704
- data/samples/smelly_source/optparse.rb +0 -1788
- data/samples/smelly_source/redcloth.rb +0 -1130
- data/samples/smelly_source/ruby.rb +0 -368
- data/samples/smelly_source/smelly.rb +0 -7
- data/samples/source_with_exclude_paths/ignore_me/uncommunicative_method_name.rb +0 -5
- data/samples/source_with_exclude_paths/nested/ignore_me_as_well/irresponsible_module.rb +0 -2
- data/samples/source_with_exclude_paths/nested/uncommunicative_parameter_name.rb +0 -6
- data/samples/source_with_exclude_paths/nested/uncommunicative_variable_name.rb +0 -6
- data/samples/source_with_hidden_directories/.hidden/hidden.rb +0 -1
- data/samples/source_with_hidden_directories/not_hidden.rb +0 -1
- data/samples/source_with_non_ruby_files/gibberish +0 -1
- data/samples/source_with_non_ruby_files/python_source.py +0 -1
- data/samples/source_with_non_ruby_files/ruby.rb +0 -6
- data/spec/performance/reek/smell_detectors/runtime_speed_spec.rb +0 -15
- data/spec/quality/documentation_spec.rb +0 -41
- data/spec/quality/reek_source_spec.rb +0 -11
- data/spec/reek/ast/node_spec.rb +0 -211
- data/spec/reek/ast/object_refs_spec.rb +0 -83
- data/spec/reek/ast/reference_collector_spec.rb +0 -47
- data/spec/reek/ast/sexp_extensions_spec.rb +0 -498
- data/spec/reek/cli/application_spec.rb +0 -168
- data/spec/reek/cli/command/report_command_spec.rb +0 -44
- data/spec/reek/cli/command/todo_list_command_spec.rb +0 -86
- data/spec/reek/cli/options_spec.rb +0 -51
- data/spec/reek/cli/silencer_spec.rb +0 -28
- data/spec/reek/code_comment_spec.rb +0 -184
- data/spec/reek/configuration/app_configuration_spec.rb +0 -195
- data/spec/reek/configuration/configuration_file_finder_spec.rb +0 -230
- data/spec/reek/configuration/default_directive_spec.rb +0 -13
- data/spec/reek/configuration/directory_directives_spec.rb +0 -122
- data/spec/reek/configuration/excluded_paths_spec.rb +0 -16
- data/spec/reek/configuration/rake_task_converter_spec.rb +0 -33
- data/spec/reek/configuration/schema_validator_spec.rb +0 -165
- data/spec/reek/context/code_context_spec.rb +0 -192
- data/spec/reek/context/ghost_context_spec.rb +0 -60
- data/spec/reek/context/method_context_spec.rb +0 -72
- data/spec/reek/context/module_context_spec.rb +0 -55
- data/spec/reek/context/root_context_spec.rb +0 -12
- data/spec/reek/context/statement_counter_spec.rb +0 -24
- data/spec/reek/context_builder_spec.rb +0 -457
- data/spec/reek/detector_repository_spec.rb +0 -22
- data/spec/reek/documentation_link_spec.rb +0 -20
- data/spec/reek/errors/base_error_spec.rb +0 -13
- data/spec/reek/examiner_spec.rb +0 -309
- data/spec/reek/logging_error_handler_spec.rb +0 -24
- data/spec/reek/rake/task_spec.rb +0 -56
- data/spec/reek/report/code_climate/code_climate_configuration_spec.rb +0 -22
- data/spec/reek/report/code_climate/code_climate_fingerprint_spec.rb +0 -126
- data/spec/reek/report/code_climate/code_climate_formatter_spec.rb +0 -51
- data/spec/reek/report/code_climate/code_climate_report_spec.rb +0 -56
- data/spec/reek/report/html_report_spec.rb +0 -19
- data/spec/reek/report/json_report_spec.rb +0 -58
- data/spec/reek/report/location_formatter_spec.rb +0 -32
- data/spec/reek/report/progress_formatter_spec.rb +0 -68
- data/spec/reek/report/text_report_spec.rb +0 -89
- data/spec/reek/report/xml_report_spec.rb +0 -24
- data/spec/reek/report/yaml_report_spec.rb +0 -55
- data/spec/reek/report_spec.rb +0 -28
- data/spec/reek/smell_configuration_spec.rb +0 -56
- data/spec/reek/smell_detectors/attribute_spec.rb +0 -197
- data/spec/reek/smell_detectors/base_detector_spec.rb +0 -50
- data/spec/reek/smell_detectors/boolean_parameter_spec.rb +0 -93
- data/spec/reek/smell_detectors/class_variable_spec.rb +0 -106
- data/spec/reek/smell_detectors/control_parameter_spec.rb +0 -300
- data/spec/reek/smell_detectors/data_clump_spec.rb +0 -134
- data/spec/reek/smell_detectors/duplicate_method_call_spec.rb +0 -211
- data/spec/reek/smell_detectors/feature_envy_spec.rb +0 -295
- data/spec/reek/smell_detectors/instance_variable_assumption_spec.rb +0 -96
- data/spec/reek/smell_detectors/irresponsible_module_spec.rb +0 -226
- data/spec/reek/smell_detectors/long_parameter_list_spec.rb +0 -61
- data/spec/reek/smell_detectors/long_yield_list_spec.rb +0 -49
- data/spec/reek/smell_detectors/manual_dispatch_spec.rb +0 -75
- data/spec/reek/smell_detectors/missing_safe_method_spec.rb +0 -68
- data/spec/reek/smell_detectors/module_initialize_spec.rb +0 -77
- data/spec/reek/smell_detectors/nested_iterators_spec.rb +0 -333
- data/spec/reek/smell_detectors/nil_check_spec.rb +0 -100
- data/spec/reek/smell_detectors/repeated_conditional_spec.rb +0 -100
- data/spec/reek/smell_detectors/subclassed_from_core_class_spec.rb +0 -77
- data/spec/reek/smell_detectors/too_many_constants_spec.rb +0 -144
- data/spec/reek/smell_detectors/too_many_instance_variables_spec.rb +0 -132
- data/spec/reek/smell_detectors/too_many_methods_spec.rb +0 -54
- data/spec/reek/smell_detectors/too_many_statements_spec.rb +0 -90
- data/spec/reek/smell_detectors/uncommunicative_method_name_spec.rb +0 -78
- data/spec/reek/smell_detectors/uncommunicative_module_name_spec.rb +0 -78
- data/spec/reek/smell_detectors/uncommunicative_parameter_name_spec.rb +0 -147
- data/spec/reek/smell_detectors/uncommunicative_variable_name_spec.rb +0 -201
- data/spec/reek/smell_detectors/unused_parameters_spec.rb +0 -114
- data/spec/reek/smell_detectors/unused_private_method_spec.rb +0 -205
- data/spec/reek/smell_detectors/utility_function_spec.rb +0 -293
- data/spec/reek/smell_warning_spec.rb +0 -137
- data/spec/reek/source/source_code_spec.rb +0 -79
- data/spec/reek/source/source_locator_spec.rb +0 -166
- data/spec/reek/spec/should_reek_of_spec.rb +0 -153
- data/spec/reek/spec/should_reek_only_of_spec.rb +0 -91
- data/spec/reek/spec/should_reek_spec.rb +0 -52
- data/spec/reek/spec/smell_matcher_spec.rb +0 -87
- data/spec/reek/tree_dresser_spec.rb +0 -46
- data/spec/spec_helper.rb +0 -110
- data/tasks/configuration.rake +0 -18
- data/tasks/console.rake +0 -5
- data/tasks/reek.rake +0 -6
- data/tasks/rubocop.rake +0 -11
- data/tasks/test.rake +0 -32
@@ -1,264 +0,0 @@
|
|
1
|
-
# Duplicate Method Call
|
2
|
-
|
3
|
-
## Introduction
|
4
|
-
|
5
|
-
Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.
|
6
|
-
|
7
|
-
Let's look at an example that is quite common in the Rails world:
|
8
|
-
|
9
|
-
```Ruby
|
10
|
-
def not_production?
|
11
|
-
Rails.env.development? || Rails.env.test?
|
12
|
-
end
|
13
|
-
```
|
14
|
-
|
15
|
-
While this duplicate usage of `Rails.env` might seem innocuous there are 2 problems with it:
|
16
|
-
|
17
|
-
1.) Efficiency
|
18
|
-
|
19
|
-
```Ruby
|
20
|
-
Rails.env.development? || Rails.env.test?
|
21
|
-
```
|
22
|
-
|
23
|
-
is not as efficient as it could be. If the call to `env` is not memoized your basically paying twice in terms of computation for something that you should only pay once.
|
24
|
-
|
25
|
-
Here
|
26
|
-
|
27
|
-
```Ruby
|
28
|
-
Rails.env.development? || Rails.env.test?
|
29
|
-
```
|
30
|
-
|
31
|
-
you have 4 method calls while here:
|
32
|
-
|
33
|
-
```Ruby
|
34
|
-
env = Rails.env
|
35
|
-
env.development? || env.test?
|
36
|
-
```
|
37
|
-
|
38
|
-
you have one assignment (which is very cheap in terms of computation) and 3 method calls.
|
39
|
-
The difference might not be much here but just imagine you're writing a high performance app or you doing some expensive database calls in each method call.
|
40
|
-
|
41
|
-
It doesn't really matter though if the efficiency difference is significant. This is a matter of principle - we believe that being efficient is one of the vital traits of good software.
|
42
|
-
|
43
|
-
2.) Maintainability
|
44
|
-
|
45
|
-
The second point is a bit more subtle. This
|
46
|
-
|
47
|
-
```Ruby
|
48
|
-
env = Rails.env
|
49
|
-
env.development? || env.test?
|
50
|
-
```
|
51
|
-
|
52
|
-
is a lot more intention revealing than
|
53
|
-
|
54
|
-
```Ruby
|
55
|
-
Rails.env.development? || Rails.env.test?
|
56
|
-
```
|
57
|
-
|
58
|
-
Here
|
59
|
-
|
60
|
-
```Ruby
|
61
|
-
env = Rails.env
|
62
|
-
env.development? || env.test?
|
63
|
-
```
|
64
|
-
|
65
|
-
I'm very clear on what I do: I get the environment and then I run some checks on it.
|
66
|
-
|
67
|
-
Here
|
68
|
-
|
69
|
-
```Ruby
|
70
|
-
Rails.env.development? || Rails.env.test?
|
71
|
-
```
|
72
|
-
|
73
|
-
I'm not very clear on what I do and it requires quite more mental effort: Ok, so I'm talking to Rails, getting the environment and then running a check on it ...or .....oh, I get the same Rails constant again, get the same environment and run another check on it.
|
74
|
-
|
75
|
-
## Example
|
76
|
-
|
77
|
-
Here's a very much simplified and contrived example. The following method will report a warning:
|
78
|
-
|
79
|
-
```Ruby
|
80
|
-
def double_thing
|
81
|
-
@other.thing + @other.thing
|
82
|
-
end
|
83
|
-
```
|
84
|
-
|
85
|
-
One quick approach to silence Reek would be to refactor the code thus:
|
86
|
-
|
87
|
-
```Ruby
|
88
|
-
def double_thing
|
89
|
-
thing = @other.thing
|
90
|
-
thing + thing
|
91
|
-
end
|
92
|
-
```
|
93
|
-
|
94
|
-
A slightly different approach would be to replace all calls in `double_thing` by calls to `thing`:
|
95
|
-
|
96
|
-
```Ruby
|
97
|
-
class Other
|
98
|
-
def double_thing
|
99
|
-
thing + thing
|
100
|
-
end
|
101
|
-
|
102
|
-
def thing
|
103
|
-
@other.thing
|
104
|
-
end
|
105
|
-
end
|
106
|
-
```
|
107
|
-
|
108
|
-
The approach you take will depend on balancing other factors in your code.
|
109
|
-
|
110
|
-
## Current support in Reek
|
111
|
-
|
112
|
-
Reek's Duplicate Method Call detector checks for repeated identical method calls within
|
113
|
-
any one method definition. This is intended to complement the checks performed by tools
|
114
|
-
such as [Flay](http://ruby.sadi.st/Flay.html) and [Simian](http://www.redhillconsulting.com.au/products/simian/).
|
115
|
-
|
116
|
-
## Edge cases
|
117
|
-
|
118
|
-
Be aware that there are some edge cases like this code:
|
119
|
-
|
120
|
-
```Ruby
|
121
|
-
class Foo
|
122
|
-
def bar(switch)
|
123
|
-
case switch
|
124
|
-
when :a
|
125
|
-
->(arg) { arg.call_me(:maybe); do_something }
|
126
|
-
when :b
|
127
|
-
->(arg) { arg.call_me(:maybe); do_something_else }
|
128
|
-
when :c
|
129
|
-
->(arg) { arg.call_me(:maybe); do_something_different }
|
130
|
-
end
|
131
|
-
end
|
132
|
-
end
|
133
|
-
```
|
134
|
-
|
135
|
-
Reek cannot reliably detect that each call's receiver is a different arg and will report:
|
136
|
-
|
137
|
-
```
|
138
|
-
[5, 7, 9]:DuplicateMethodCall: Foo#bar calls 'arg.call_me(:maybe)' 3 times
|
139
|
-
```
|
140
|
-
|
141
|
-
If you're running into this problem you can disable this smell detector for this method either via
|
142
|
-
configuration:
|
143
|
-
|
144
|
-
```Yaml
|
145
|
-
---
|
146
|
-
DuplicateMethodCall:
|
147
|
-
exclude:
|
148
|
-
- 'Foo#bar'
|
149
|
-
```
|
150
|
-
|
151
|
-
or via source code comment:
|
152
|
-
|
153
|
-
```Ruby
|
154
|
-
class Foo
|
155
|
-
# :reek:DuplicateMethodCall
|
156
|
-
def bar(switch)
|
157
|
-
# ....
|
158
|
-
end
|
159
|
-
end
|
160
|
-
```
|
161
|
-
|
162
|
-
## Configuration
|
163
|
-
|
164
|
-
Reek's Duplicate Method Call detector currently offers the [Basic Smell Options](Basic-Smell-Options.md), plus:
|
165
|
-
|
166
|
-
Option | Value | Effect
|
167
|
-
-------|-------|-------
|
168
|
-
`max_calls` | integer | The maximum number of duplicate calls allowed within a method. Defaults to 1.
|
169
|
-
`allow_calls` | an array of strings or regular expressions | Ignores any context who matches it |
|
170
|
-
|
171
|
-
## Example configuration
|
172
|
-
|
173
|
-
### Adjusting `max_calls`
|
174
|
-
|
175
|
-
Imagine code like this:
|
176
|
-
|
177
|
-
```Ruby
|
178
|
-
class Alfa
|
179
|
-
def bravo
|
180
|
-
charlie.delta
|
181
|
-
charlie.delta
|
182
|
-
end
|
183
|
-
end
|
184
|
-
```
|
185
|
-
|
186
|
-
This would report:
|
187
|
-
|
188
|
-
>>
|
189
|
-
src.rb -- 1 warning:
|
190
|
-
[4, 5]:DuplicateMethodCall: Alfa#bravo calls 'charlie.delta' 2 times
|
191
|
-
|
192
|
-
If you want to allow those double calls here you can disable it in 2 different ways:
|
193
|
-
|
194
|
-
1.) Via source code comment:
|
195
|
-
|
196
|
-
```Ruby
|
197
|
-
class Alfa
|
198
|
-
# :reek:DuplicateMethodCall { max_calls: 2 }
|
199
|
-
def bravo
|
200
|
-
charlie.delta
|
201
|
-
charlie.delta
|
202
|
-
end
|
203
|
-
end
|
204
|
-
```
|
205
|
-
|
206
|
-
2.) Via configuration file:
|
207
|
-
|
208
|
-
```Yaml
|
209
|
-
DuplicateMethodCall:
|
210
|
-
max_calls: 2
|
211
|
-
```
|
212
|
-
|
213
|
-
Note though that the latter way will set `max_calls` to 2 for all instances
|
214
|
-
of the smell detector which might not be what you want - in this case
|
215
|
-
you'll have to use source code comments.
|
216
|
-
|
217
|
-
### Adjusting `allow_calls`
|
218
|
-
|
219
|
-
Imagine code like this:
|
220
|
-
|
221
|
-
```Ruby
|
222
|
-
class Alfa
|
223
|
-
def bravo
|
224
|
-
charlie.delta
|
225
|
-
charlie.delta
|
226
|
-
echo.foxtrot
|
227
|
-
echo.foxtrot
|
228
|
-
end
|
229
|
-
end
|
230
|
-
```
|
231
|
-
|
232
|
-
This would report:
|
233
|
-
|
234
|
-
>>
|
235
|
-
src.rb -- 2 warnings:
|
236
|
-
[4, 5]:DuplicateMethodCall: Alfa#bravo calls charlie.delta 2 times
|
237
|
-
[6, 7]:DuplicateMethodCall: Alfa#bravo calls echo.foxtrot 2 times
|
238
|
-
|
239
|
-
So let's say you're ok with the `echo.foxtrot` calls you can stop reporting them like this:
|
240
|
-
|
241
|
-
1.) Via source code comment:
|
242
|
-
|
243
|
-
```Ruby
|
244
|
-
class Alfa
|
245
|
-
# :reek:DuplicateMethodCall { allow_calls: ['echo.foxtrot'] }
|
246
|
-
def bravo
|
247
|
-
charlie.delta
|
248
|
-
charlie.delta
|
249
|
-
echo.foxtrot
|
250
|
-
echo.foxtrot
|
251
|
-
end
|
252
|
-
end
|
253
|
-
```
|
254
|
-
|
255
|
-
2.) Via configuration file:
|
256
|
-
|
257
|
-
```Yaml
|
258
|
-
DuplicateMethodCall:
|
259
|
-
allow_calls:
|
260
|
-
- 'echo.foxtrot'
|
261
|
-
```
|
262
|
-
|
263
|
-
Note though that the latter way will allow those calls across your source code which might not be what you want.
|
264
|
-
In this case you'll have to use source code comments.
|
data/docs/Feature-Envy.md
DELETED
@@ -1,93 +0,0 @@
|
|
1
|
-
# Feature Envy
|
2
|
-
|
3
|
-
## Introduction
|
4
|
-
|
5
|
-
_Feature Envy_ occurs when a code fragment references another object more often than it references itself, or when several clients do the same series of manipulations on a particular type of object.
|
6
|
-
|
7
|
-
_Feature Envy_ reduces the code's ability to communicate intent: code that "belongs" on one class but which is located in another can be hard to find, and may upset the "System of Names" in the host class.
|
8
|
-
|
9
|
-
_Feature Envy_ also affects the design's flexibility: A code fragment that is in the wrong class creates couplings that may not be natural within the application's domain, and creates a loss of cohesion in the unwilling host class.
|
10
|
-
|
11
|
-
_Feature Envy_ often arises because it must manipulate other objects (usually its arguments) to get them into a useful form, and one force preventing them (the arguments) doing this themselves is that the common knowledge lives outside the arguments, or the arguments are of too basic a type to justify extending that type. Therefore there must be something which 'knows' about the contents or purposes of the arguments. That thing would have to be more than just a basic type, because the basic types are either containers which don't know about their contents, or they are single objects which can't capture their relationship with their fellows of the same type. So, this thing with the extra knowledge should be reified into a class, and the utility method will most likely belong there.
|
12
|
-
|
13
|
-
## Example
|
14
|
-
|
15
|
-
Running Reek on:
|
16
|
-
|
17
|
-
```Ruby
|
18
|
-
class Warehouse
|
19
|
-
def sale_price(item)
|
20
|
-
(item.price - item.rebate) * @vat
|
21
|
-
end
|
22
|
-
end
|
23
|
-
```
|
24
|
-
|
25
|
-
would report:
|
26
|
-
|
27
|
-
```Bash
|
28
|
-
Warehouse#sale_price refers to item more than self (FeatureEnvy)
|
29
|
-
```
|
30
|
-
|
31
|
-
since this:
|
32
|
-
|
33
|
-
```Ruby
|
34
|
-
(item.price - item.rebate)
|
35
|
-
```
|
36
|
-
|
37
|
-
belongs to the Item class, not the Warehouse.
|
38
|
-
|
39
|
-
## Current Support in Reek
|
40
|
-
|
41
|
-
_Feature Envy_ reports any method that refers to self less often than it refers to (ie. send messages to) some other object.
|
42
|
-
|
43
|
-
## Edge cases
|
44
|
-
|
45
|
-
Be aware that there are some edge cases like this code:
|
46
|
-
|
47
|
-
```Ruby
|
48
|
-
class Foo
|
49
|
-
def initialize
|
50
|
-
@map = {
|
51
|
-
a: ->(arg) { arg.css('table') },
|
52
|
-
b: ->(arg) { arg.css('div') },
|
53
|
-
c: ->(arg) { arg.css('span') }
|
54
|
-
}
|
55
|
-
end
|
56
|
-
end
|
57
|
-
```
|
58
|
-
|
59
|
-
Reek cannot reliably detect that each call's receiver is a different arg and will report:
|
60
|
-
|
61
|
-
```
|
62
|
-
[4, 5, 6]:FeatureEnvy: Foo#initialize refers to 'arg' more than self (maybe move it to another class?)
|
63
|
-
```
|
64
|
-
|
65
|
-
If you're running into this problem you can disable this smell detector for this method either via
|
66
|
-
configuration:
|
67
|
-
|
68
|
-
```Yaml
|
69
|
-
---
|
70
|
-
FeatureEnvy:
|
71
|
-
exclude:
|
72
|
-
- 'Foo#bar'
|
73
|
-
```
|
74
|
-
|
75
|
-
or via source code comment:
|
76
|
-
|
77
|
-
```Ruby
|
78
|
-
class Foo
|
79
|
-
# :reek:FeatureEnvy
|
80
|
-
def initialize
|
81
|
-
@map = {
|
82
|
-
# ....
|
83
|
-
end
|
84
|
-
end
|
85
|
-
```
|
86
|
-
|
87
|
-
## Differences to _Utility Function_
|
88
|
-
|
89
|
-
_Feature Envy_ is only triggered if there are some references to self and _[Utility Function](Utility-Function.md)_ is triggered if there are no references to self.
|
90
|
-
|
91
|
-
## Configuration
|
92
|
-
|
93
|
-
_Feature Envy_ supports the [Basic Smell Options](Basic-Smell-Options.md).
|
@@ -1,132 +0,0 @@
|
|
1
|
-
## How to write new detectors
|
2
|
-
|
3
|
-
### Outline what you have in mind
|
4
|
-
|
5
|
-
Before starting to code you should discuss the overall idea for your new smell detector with
|
6
|
-
us in a corresponding github issue.
|
7
|
-
We all should have a solid understanding of what this detector actually reports, the edgecases
|
8
|
-
it covers and the overall rationale behind it.
|
9
|
-
|
10
|
-
### Structure
|
11
|
-
|
12
|
-
All smell detectors reside in `lib/reek/smell_detectors` and have the following base structure:
|
13
|
-
|
14
|
-
```Ruby
|
15
|
-
require_relative 'base_detector'
|
16
|
-
require_relative 'smell_warning'
|
17
|
-
|
18
|
-
module Reek
|
19
|
-
module SmellDetectors
|
20
|
-
#
|
21
|
-
# Here goes your introduction for this detector.
|
22
|
-
#
|
23
|
-
# See {file:docs/Your-Detector.md} for details.
|
24
|
-
class YourDetector < BaseDetector
|
25
|
-
def self.contexts
|
26
|
-
[:class] # In case you're operating on class contexts only - just an example.
|
27
|
-
end
|
28
|
-
|
29
|
-
#
|
30
|
-
# Here you should document what you expect the detector's context to look
|
31
|
-
# like.
|
32
|
-
#
|
33
|
-
# @return [Array<SmellWarning>]
|
34
|
-
#
|
35
|
-
def sniff
|
36
|
-
# "found_smells" below is just an abstraction for
|
37
|
-
# "find the smells in question" and iterate over them.
|
38
|
-
# This can just be a method but it can also be a more sophisticated set up.
|
39
|
-
# Check out other smell detectors to get a feeling for what to do here.
|
40
|
-
found_smells.map do |smell|
|
41
|
-
# "smell_warning" is defined in BaseDetector and should be used by you
|
42
|
-
# to construct smell warnings
|
43
|
-
smell_warning(
|
44
|
-
lines: [], # lines on which the smell was detected
|
45
|
-
message: "...", # the message that is printed on STDOUT
|
46
|
-
# whatever you interpolate into the "message" should go into
|
47
|
-
# parameters below - if you do not interpolate anything you
|
48
|
-
# can omit this
|
49
|
-
parameters: { })
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
private
|
55
|
-
|
56
|
-
# Here goes everything you need for finding smells.
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
```
|
61
|
-
|
62
|
-
For your detector to be properly loaded you need to require it in `lib/reek/smell_detectors.rb` as well.
|
63
|
-
|
64
|
-
### defaults.reek.yml
|
65
|
-
|
66
|
-
After you ran
|
67
|
-
|
68
|
-
```
|
69
|
-
bundle exec rake
|
70
|
-
```
|
71
|
-
|
72
|
-
for the first time with your shiny new detector in place the `docs/defaults.reek.yml`
|
73
|
-
file should have been updated automatically. Make sure you don't forget to check
|
74
|
-
in those changes as well.
|
75
|
-
|
76
|
-
### Documentation
|
77
|
-
|
78
|
-
* Above every `SmellDetector::sniff` method it should be documented what the expected AST is
|
79
|
-
* Every detector should have a separate documentation page in /docs. You can
|
80
|
-
take any arbitrary existing smell detector documentation page as template (since
|
81
|
-
they all have the same structure already)
|
82
|
-
* The detector should be listed under [Code Smells](docs/Code-Smells.md)
|
83
|
-
* Depending on what your detector does it might make sense to add it to other doc pages as
|
84
|
-
well e.g. [Simulated Polymorphism](docs/Simulated-Polymorphism.md)
|
85
|
-
|
86
|
-
### Rspec examples
|
87
|
-
|
88
|
-
All smell detector specs start out with 2 generic examples like below - the second one
|
89
|
-
only if it makes sense.
|
90
|
-
Here's what it looks like for `UncommunicativeVariableName`:
|
91
|
-
|
92
|
-
```Ruby
|
93
|
-
it 'reports the right values' do
|
94
|
-
src = <<-RUBY
|
95
|
-
def alfa
|
96
|
-
bravo = 5
|
97
|
-
end
|
98
|
-
RUBY
|
99
|
-
|
100
|
-
expect(src).to reek_of(:UncommunicativeVariableName,
|
101
|
-
lines: [2],
|
102
|
-
context: 'alfa',
|
103
|
-
message: "has the variable name 'bravo'",
|
104
|
-
source: 'string',
|
105
|
-
name: 'bravo')
|
106
|
-
end
|
107
|
-
|
108
|
-
it 'does count all occurences' do
|
109
|
-
src = <<-RUBY
|
110
|
-
def alfa
|
111
|
-
bravo = 3
|
112
|
-
charlie = 7
|
113
|
-
end
|
114
|
-
RUBY
|
115
|
-
|
116
|
-
expect(src).to reek_of(:UncommunicativeVariableName,
|
117
|
-
lines: [2],
|
118
|
-
name: 'bravo')
|
119
|
-
expect(src).to reek_of(:UncommunicativeVariableName,
|
120
|
-
lines: [3],
|
121
|
-
name: 'charlie')
|
122
|
-
end
|
123
|
-
```
|
124
|
-
|
125
|
-
The following examples should then cover the detector specific features.
|
126
|
-
|
127
|
-
### Cucumber features
|
128
|
-
|
129
|
-
We are trying to write as few Cucumber features as possible.
|
130
|
-
Normally, there should be no need to write a new feature for a new smell detector.
|
131
|
-
If you feel like this is necessary in this case, please discuss this with us via
|
132
|
-
github issue or in your work-in-progress pull request before doing anything.
|