reek 6.0.1 → 6.0.5
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.yml +2 -20
- data/.rubocop_todo.yml +27 -20
- data/CHANGELOG.md +22 -0
- data/CONTRIBUTING.md +3 -0
- data/Dockerfile +1 -1
- data/Gemfile +6 -6
- data/README.md +1 -1
- data/bin/code_climate_reek +2 -3
- data/lib/reek.rb +1 -0
- data/lib/reek/ast/ast_node_class_map.rb +1 -1
- data/lib/reek/ast/node.rb +1 -1
- data/lib/reek/cli/options.rb +1 -1
- data/lib/reek/configuration/app_configuration.rb +4 -3
- data/lib/reek/configuration/directory_directives.rb +2 -2
- data/lib/reek/configuration/excluded_paths.rb +2 -1
- data/lib/reek/context/code_context.rb +1 -1
- data/lib/reek/context/module_context.rb +3 -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_configuration.yml +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_detectors/boolean_parameter.rb +3 -1
- data/lib/reek/smell_detectors/uncommunicative_variable_name.rb +1 -1
- data/lib/reek/smell_warning.rb +2 -3
- data/lib/reek/source/source_locator.rb +14 -13
- data/lib/reek/version.rb +1 -1
- data/reek.gemspec +14 -3
- metadata +29 -232
- data/.travis.yml +0 -36
- 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 -17
- data/spec/quality/documentation_spec.rb +0 -40
- 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 -19
- 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).
|