reek 6.0.2 → 6.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +9 -0
- data/.github/workflows/ruby.yml +57 -0
- data/.rubocop.yml +5 -3
- data/.rubocop_todo.yml +6 -4
- data/CHANGELOG.md +26 -0
- data/CONTRIBUTING.md +3 -0
- data/Dockerfile +1 -1
- data/Gemfile +7 -7
- data/README.md +1 -1
- 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/ast/sexp_extensions/arguments.rb +11 -0
- data/lib/reek/ast/sexp_extensions/case.rb +1 -1
- data/lib/reek/ast/sexp_extensions/if.rb +1 -1
- data/lib/reek/ast/sexp_extensions/send.rb +1 -1
- data/lib/reek/cli/command/todo_list_command.rb +1 -1
- data/lib/reek/cli/options.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 +17 -3
- data/lib/reek/rake/task.rb +1 -1
- 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/spec/should_reek_of.rb +6 -4
- data/lib/reek/version.rb +2 -2
- data/reek.gemspec +28 -25
- metadata +13 -240
- 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
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.
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
# How Reek works internally
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
## The big picture
|
|
5
|
-
|
|
6
|
-
```
|
|
7
|
-
["class C; end" | reek] [reek lib/*.rb] [expect(files).not_to reek_of(:LargeClass)]
|
|
8
|
-
\ | |
|
|
9
|
-
\ | |
|
|
10
|
-
\ | |
|
|
11
|
-
\ creates a | |
|
|
12
|
-
\ | |
|
|
13
|
-
\ | |
|
|
14
|
-
\ | |
|
|
15
|
-
\ | |
|
|
16
|
-
\---------- Application (cli/application.rb) + |
|
|
17
|
-
Options (cli/options) |
|
|
18
|
-
| |
|
|
19
|
-
| |
|
|
20
|
-
| |
|
|
21
|
-
| |
|
|
22
|
-
creates a | |
|
|
23
|
-
| |
|
|
24
|
-
| |
|
|
25
|
-
| |
|
|
26
|
-
| |
|
|
27
|
-
ReekCommand (cli/reek_command) |
|
|
28
|
-
* uses a reporter (report/report) |
|
|
29
|
-
* uses a SourceLocator (source/source_locator) |
|
|
30
|
-
/ | \ |
|
|
31
|
-
/ | \ |
|
|
32
|
-
/ | \ |
|
|
33
|
-
Source Source Source (source/source_code) |
|
|
34
|
-
| | | |
|
|
35
|
-
| | | |
|
|
36
|
-
| | | |
|
|
37
|
-
Examiner | Examiner |
|
|
38
|
-
| |
|
|
39
|
-
| |
|
|
40
|
-
Examiner (core/examiner) --------------------------------------
|
|
41
|
-
* generates the AST out of the given source
|
|
42
|
-
* adorns the generated AST via a TreeDresser (core/tree_dresser)
|
|
43
|
-
* initializes a DetectorRepository with all relevant smells (smells/detector_repository)
|
|
44
|
-
* builds a tree of Contexts using ContextBuilder
|
|
45
|
-
* tells the DetectorRepository above to run each of its smell detectors above on each of the contexts
|
|
46
|
-
/ | \
|
|
47
|
-
/ | \
|
|
48
|
-
/ | \
|
|
49
|
-
UtilityFunction FeatureEnvy TooManyMethods
|
|
50
|
-
\ | /
|
|
51
|
-
\ | /
|
|
52
|
-
\ | /
|
|
53
|
-
DetectorRepository
|
|
54
|
-
|
|
|
55
|
-
|
|
|
56
|
-
|
|
|
57
|
-
Application output
|
|
58
|
-
|
|
59
|
-
## A closer look at how an Examiner works
|
|
60
|
-
|
|
61
|
-
The core foundation of Reek and its API is the Examiner.
|
|
62
|
-
As you can see above, the Examiner is run for every source it gets passed and then runs the configured SmellDetectors.
|
|
63
|
-
The overall workflow is like this:
|
|
64
|
-
|
|
65
|
-
Examiner
|
|
66
|
-
|
|
|
67
|
-
|
|
|
68
|
-
|
|
|
69
|
-
Initialize DetectorRepository only with eligible SmellDetectors
|
|
70
|
-
|
|
|
71
|
-
|
|
|
72
|
-
|
|
|
73
|
-
Generate the AST out of the given source using SourceCode#syntax_tree, which works like this:
|
|
74
|
-
|
|
75
|
-
- We generate a "rough" AST using the "parser" gem
|
|
76
|
-
- We then obtain the comments from the source code separately
|
|
77
|
-
- We pass this unprocessed AST and the comment_map to TreeDresser#dress which
|
|
78
|
-
returns an instance of Reek::AST::SexpNode with type-dependent SexpExtensions mixed in.
|
|
79
|
-
|
|
80
|
-
An example should make this more palpable.
|
|
81
|
-
Given:
|
|
82
|
-
|
|
83
|
-
class C
|
|
84
|
-
def m
|
|
85
|
-
puts 'nada'
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
The AST generated by the parser gem (consisting of Parser::AST::Node) looks like this:
|
|
90
|
-
|
|
91
|
-
(class
|
|
92
|
-
(const nil :C)
|
|
93
|
-
nil
|
|
94
|
-
(def :m
|
|
95
|
-
(args)
|
|
96
|
-
(send nil :puts
|
|
97
|
-
(str "nada"))))
|
|
98
|
-
|
|
99
|
-
TreeDresser#dress would transform this into a very similar tree, but this time not consisting
|
|
100
|
-
of Parser::AST::Node but of Reek::AST::SexpNode and with node-dependent SexpExtensions
|
|
101
|
-
mixed in (noted in []):
|
|
102
|
-
|
|
103
|
-
(class [AST::SexpExtensions::ClassNode, AST::SexpExtensions::ModuleNode]
|
|
104
|
-
(const nil :C) [AST::SexpExtensions::ConstNode]
|
|
105
|
-
nil
|
|
106
|
-
(def :m [AST::SexpExtensions::DefNode, AST::SexpExtensions::MethodNodeBase]
|
|
107
|
-
(args) [AST::SexpExtensions::ArgsNode]
|
|
108
|
-
(send nil :puts [AST::SexpExtensions::SendNode]
|
|
109
|
-
(str "nada"))))
|
|
110
|
-
|
|
|
111
|
-
|
|
|
112
|
-
|
|
|
113
|
-
A ContextBuilder then traverses this now adorned tree again and
|
|
114
|
-
runs all SmellDetectors from the DetectorRepository above
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
# Instance Variable Assumption
|
|
2
|
-
|
|
3
|
-
## Introduction
|
|
4
|
-
|
|
5
|
-
Classes should not assume that instance variables are set or present outside of the current class definition.
|
|
6
|
-
|
|
7
|
-
Good:
|
|
8
|
-
|
|
9
|
-
```Ruby
|
|
10
|
-
class Foo
|
|
11
|
-
def initialize
|
|
12
|
-
@bar = :foo
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def foo?
|
|
16
|
-
@bar == :foo
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
Good as well:
|
|
22
|
-
|
|
23
|
-
```Ruby
|
|
24
|
-
class Foo
|
|
25
|
-
def foo?
|
|
26
|
-
bar == :foo
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def bar
|
|
30
|
-
@bar ||= :foo
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
Bad:
|
|
36
|
-
|
|
37
|
-
```Ruby
|
|
38
|
-
class Foo
|
|
39
|
-
def go_foo!
|
|
40
|
-
@bar = :foo
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def foo?
|
|
44
|
-
@bar == :foo
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## Example
|
|
50
|
-
|
|
51
|
-
Running Reek on:
|
|
52
|
-
|
|
53
|
-
```Ruby
|
|
54
|
-
class Dummy
|
|
55
|
-
def test
|
|
56
|
-
@ivar
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
would report:
|
|
62
|
-
|
|
63
|
-
```Bash
|
|
64
|
-
[1]:InstanceVariableAssumption: Dummy assumes too much for instance variable @ivar
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
Note that this example would trigger this smell warning as well:
|
|
68
|
-
|
|
69
|
-
```Ruby
|
|
70
|
-
class Parent
|
|
71
|
-
def initialize(omg)
|
|
72
|
-
@omg = omg
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
class Child < Parent
|
|
77
|
-
def foo
|
|
78
|
-
@omg
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
The way to address the smell warning is that you should create an `attr_reader` to use `@omg` in the subclass and not access `@omg` directly like this:
|
|
84
|
-
|
|
85
|
-
```Ruby
|
|
86
|
-
class Parent
|
|
87
|
-
attr_reader :omg
|
|
88
|
-
|
|
89
|
-
def initialize(omg)
|
|
90
|
-
@omg = omg
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
class Child < Parent
|
|
95
|
-
def foo
|
|
96
|
-
omg
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
Directly accessing instance variables is considered a smell because it [breaks encapsulation](http://designisrefactoring.com/2015/03/29/organizing-data-self-encapsulation/) and makes it harder to reason about code.
|
|
102
|
-
|
|
103
|
-
If you don't want to expose those methods as public API just make them private like this:
|
|
104
|
-
|
|
105
|
-
```Ruby
|
|
106
|
-
class Parent
|
|
107
|
-
def initialize(omg)
|
|
108
|
-
@omg = omg
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
private
|
|
112
|
-
attr_reader :omg
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
class Child < Parent
|
|
116
|
-
def foo
|
|
117
|
-
omg
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
## Current Support in Reek
|
|
124
|
-
|
|
125
|
-
An instance variable must:
|
|
126
|
-
|
|
127
|
-
* be set in the constructor
|
|
128
|
-
* or be accessed through a method with lazy initialization / memoization.
|
|
129
|
-
|
|
130
|
-
If not, _Instance Variable Assumption_ will be reported.
|
|
131
|
-
|
|
132
|
-
## Using Instance Variable Assumption in a Rails context
|
|
133
|
-
|
|
134
|
-
In ActiveRecord it seems common to use callbacks like `after_initialize` to initialize instance variables as
|
|
135
|
-
outlined [here](https://stackoverflow.com/questions/41165520/overriding-applicationrecord-initialize-bad-idea)
|
|
136
|
-
or [here](http://blog.dalethatcher.com/2008/03/rails-dont-override-initialize-on.html)
|
|
137
|
-
instead of overriding the `initialize` method.
|
|
138
|
-
If an instance variable is initialized in such a callback Reek will report it correspondingly.
|
|
139
|
-
|
|
140
|
-
This would smell for instance:
|
|
141
|
-
|
|
142
|
-
```Ruby
|
|
143
|
-
class Sample < ApplicationRecord
|
|
144
|
-
after_initialize do
|
|
145
|
-
@my_var = false
|
|
146
|
-
end
|
|
147
|
-
end
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
Since Reek cannot reliably detect that is used in a Rails context we recommend to disable this detector
|
|
151
|
-
for "app/models" like this:
|
|
152
|
-
|
|
153
|
-
```Yaml
|
|
154
|
-
directories:
|
|
155
|
-
# Your other configuration....
|
|
156
|
-
"app/models":
|
|
157
|
-
InstanceVariableAssumption:
|
|
158
|
-
enabled: false
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
## Configuration
|
|
162
|
-
|
|
163
|
-
_Instance Variable Assumption_ supports the [Basic Smell Options](Basic-Smell-Options.md).
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
# Irresponsible Module
|
|
2
|
-
|
|
3
|
-
## Introduction
|
|
4
|
-
|
|
5
|
-
Classes and modules are the units of reuse and release. It is therefore
|
|
6
|
-
considered good practice to annotate every class and module with a brief
|
|
7
|
-
comment outlining its responsibilities.
|
|
8
|
-
|
|
9
|
-
For further guideline on how to write good documentation in Ruby, see these
|
|
10
|
-
links:
|
|
11
|
-
- [Rails API documentation guidelines](http://edgeguides.rubyonrails.org/api_documentation_guidelines.html)
|
|
12
|
-
- [Comments tell you why](https://blog.codinghorror.com/code-tells-you-how-comments-tell-you-why/)
|
|
13
|
-
|
|
14
|
-
## Example
|
|
15
|
-
|
|
16
|
-
Given
|
|
17
|
-
|
|
18
|
-
```Ruby
|
|
19
|
-
class Dummy
|
|
20
|
-
# Do things...
|
|
21
|
-
end
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
Reek would emit the following warning:
|
|
25
|
-
|
|
26
|
-
```
|
|
27
|
-
test.rb -- 1 warning:
|
|
28
|
-
[1]:IrresponsibleModule: Dummy has no descriptive comment
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
Fixing this is simple - just an explaining comment:
|
|
32
|
-
|
|
33
|
-
```Ruby
|
|
34
|
-
# The Dummy class is responsible for ...
|
|
35
|
-
class Dummy
|
|
36
|
-
# Do things...
|
|
37
|
-
end
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
## Current Support in Reek
|
|
41
|
-
|
|
42
|
-
_Irresponsible Module_ checks classes and modules, including those
|
|
43
|
-
created through `Struct.new` and `Class.new` and directly assigned to a constant.
|
|
44
|
-
|
|
45
|
-
## Configuration
|
|
46
|
-
|
|
47
|
-
_Irresponsible Module_ supports only the [Basic Smell Options](Basic-Smell-Options.md).
|
data/docs/Large-Class.md
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# Large Class
|
|
2
|
-
|
|
3
|
-
## Introduction
|
|
4
|
-
|
|
5
|
-
A _Large Class_ is a class or module that has a large number of instance
|
|
6
|
-
variables, methods or lines of code in any one piece of its specification.
|
|
7
|
-
(That is, this smell relates to pieces of the class's specification, not to the
|
|
8
|
-
size of the corresponding instance of `Class`.)
|
|
9
|
-
|
|
10
|
-
## Current Support in Reek
|
|
11
|
-
|
|
12
|
-
Reek offers three checks in this category.
|
|
13
|
-
|
|
14
|
-
* [Too Many Constants](Too-Many-Constants.md)
|
|
15
|
-
* [Too Many Instance Variables](Too-Many-Instance-Variables.md)
|
|
16
|
-
* [Too Many Methods](Too-Many-Methods.md)
|
data/docs/Long-Parameter-List.md
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
# Long Parameter List
|
|
2
|
-
|
|
3
|
-
## Introduction
|
|
4
|
-
|
|
5
|
-
A _Long Parameter List_ occurs when a method has a lot of parameters.
|
|
6
|
-
|
|
7
|
-
## Example
|
|
8
|
-
|
|
9
|
-
Given
|
|
10
|
-
|
|
11
|
-
```Ruby
|
|
12
|
-
class Dummy
|
|
13
|
-
def long_list(foo,bar,baz,fling,flung)
|
|
14
|
-
puts foo,bar,baz,fling,flung
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
Reek would report the following warning:
|
|
20
|
-
|
|
21
|
-
```
|
|
22
|
-
test.rb -- 1 warning:
|
|
23
|
-
[2]:Dummy#long_list has 5 parameters (LongParameterList)
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
A common solution to this problem would be the introduction of parameter objects.
|
|
27
|
-
|
|
28
|
-
## Current Support in Reek
|
|
29
|
-
|
|
30
|
-
_Long Parameter List_ reports any method or block with more than 3 parameters.
|
|
31
|
-
|
|
32
|
-
## Configuration
|
|
33
|
-
|
|
34
|
-
Reek's _Long Parameter List_ detector supports the
|
|
35
|
-
[Basic Smell Options](Basic-Smell-Options.md), plus:
|
|
36
|
-
|
|
37
|
-
| Option | Value | Effect |
|
|
38
|
-
| -------------|---------|---------|
|
|
39
|
-
| `max_params` | integer | The maximum number of parameters allowed in a method or block before a warning is issued. Defaults to 3. |
|
data/docs/Long-Yield-List.md
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# Long Yield List
|
|
2
|
-
|
|
3
|
-
## Introduction
|
|
4
|
-
|
|
5
|
-
A _Long Yield List_ occurs when a method yields a lot of arguments to the block
|
|
6
|
-
it gets passed. It is a special case of [Long Parameter List](Long-Parameter-List.md).
|
|
7
|
-
|
|
8
|
-
## Example
|
|
9
|
-
|
|
10
|
-
```Ruby
|
|
11
|
-
class Dummy
|
|
12
|
-
def yields_a_lot(foo,bar,baz,fling,flung)
|
|
13
|
-
yield foo,bar,baz,fling,flung
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
Reek would report the following warning:
|
|
19
|
-
|
|
20
|
-
```
|
|
21
|
-
test.rb -- 1 warning:
|
|
22
|
-
[4]:Dummy#yields_a_lot yields 5 parameters (LongYieldList)
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
A common solution to this problem would be the introduction of parameter objects.
|
|
26
|
-
|
|
27
|
-
## Current Support in Reek
|
|
28
|
-
|
|
29
|
-
Currently _Long Yield List_ reports any method or block with more than 3 parameters.
|
|
30
|
-
|
|
31
|
-
## Configuration
|
|
32
|
-
|
|
33
|
-
Reek's _Long Yield List_ detector supports the [Basic Smell Options](Basic-Smell-Options.md), plus:
|
|
34
|
-
|
|
35
|
-
| Option | Value | Effect |
|
|
36
|
-
| -------------|---------|---------|
|
|
37
|
-
| `max_params` | integer | The maximum number of parameters allowed in a method or block before a warning is issued. Defaults to 3. |
|
data/docs/Manual-Dispatch.md
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
## Introduction
|
|
2
|
-
|
|
3
|
-
Reek reports a _Manual Dispatch_ smell if it finds source code that manually checks whether an object responds to a method before that method is called. Manual dispatch is a type of [Simulated Polymorphism](Simulated-Polymorphism.md) which leads to code that is harder to reason about, debug, and refactor.
|
|
4
|
-
|
|
5
|
-
## Example
|
|
6
|
-
|
|
7
|
-
```Ruby
|
|
8
|
-
class MyManualDispatcher
|
|
9
|
-
attr_reader :foo
|
|
10
|
-
|
|
11
|
-
def initialize(foo)
|
|
12
|
-
@foo = foo
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def call
|
|
16
|
-
foo.bar if foo.respond_to?(:bar)
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
Reek would emit the following warning:
|
|
22
|
-
|
|
23
|
-
```
|
|
24
|
-
test.rb -- 1 warning:
|
|
25
|
-
[9]: MyManualDispatcher manually dispatches method call (ManualDispatch)
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## Configuration
|
|
29
|
-
|
|
30
|
-
_Manual Dispatch_ offers the [Basic Smell Options](Basic-Smell-Options.md).
|