reek 2.0.4 → 3.0.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.
Files changed (275) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +13 -0
  3. data/.rubocop.yml +47 -0
  4. data/.travis.yml +31 -0
  5. data/.yardopts +3 -6
  6. data/CHANGELOG +24 -0
  7. data/CONTRIBUTING.md +111 -0
  8. data/Gemfile +16 -0
  9. data/README.md +112 -59
  10. data/bin/reek +2 -2
  11. data/config/cucumber.yml +3 -0
  12. data/config/defaults.reek +0 -1
  13. data/docs/API.md +79 -0
  14. data/docs/Attribute.md +43 -0
  15. data/docs/Basic-Smell-Options.md +44 -0
  16. data/docs/Boolean-Parameter.md +52 -0
  17. data/docs/Class-Variable.md +40 -0
  18. data/docs/Code-Smells.md +34 -0
  19. data/docs/Command-Line-Options.md +84 -0
  20. data/docs/Configuration-Files.md +49 -0
  21. data/docs/Control-Couple.md +22 -0
  22. data/docs/Control-Parameter.md +29 -0
  23. data/docs/Data-Clump.md +44 -0
  24. data/docs/Duplicate-Method-Call.md +49 -0
  25. data/docs/Feature-Envy.md +49 -0
  26. data/docs/How-reek-works-internally.md +114 -0
  27. data/docs/Irresponsible-Module.md +39 -0
  28. data/docs/Large-Class.md +20 -0
  29. data/docs/Long-Parameter-List.md +38 -0
  30. data/docs/Long-Yield-List.md +36 -0
  31. data/docs/Module-Initialize.md +62 -0
  32. data/docs/Nested-Iterators.md +38 -0
  33. data/docs/Nil-Check.md +39 -0
  34. data/docs/Prima-Donna-Method.md +53 -0
  35. data/docs/RSpec-matchers.md +137 -0
  36. data/docs/Rake-Task.md +58 -0
  37. data/docs/Reek-Driven-Development.md +37 -0
  38. data/docs/Repeated-Conditional.md +44 -0
  39. data/docs/Simulated-Polymorphism.md +16 -0
  40. data/docs/Smell-Suppression.md +32 -0
  41. data/docs/Too-Many-Instance-Variables.md +43 -0
  42. data/docs/Too-Many-Methods.md +55 -0
  43. data/docs/Too-Many-Statements.md +50 -0
  44. data/docs/Uncommunicative-Method-Name.md +24 -0
  45. data/docs/Uncommunicative-Module-Name.md +23 -0
  46. data/docs/Uncommunicative-Name.md +16 -0
  47. data/docs/Uncommunicative-Parameter-Name.md +24 -0
  48. data/docs/Uncommunicative-Variable-Name.md +24 -0
  49. data/docs/Unused-Parameters.md +27 -0
  50. data/docs/Utility-Function.md +44 -0
  51. data/docs/Versioning-Policy.md +7 -0
  52. data/docs/YAML-Reports.md +111 -0
  53. data/docs/yard_plugin.rb +14 -0
  54. data/features/{ruby_api/api.feature → command_line_interface/basic_usage.feature} +6 -5
  55. data/features/command_line_interface/options.feature +1 -0
  56. data/features/command_line_interface/smell_selection.feature +4 -8
  57. data/features/command_line_interface/smells_count.feature +25 -32
  58. data/features/command_line_interface/stdin.feature +2 -2
  59. data/features/configuration_files/masking_smells.feature +30 -41
  60. data/features/configuration_files/overrides_defaults.feature +5 -3
  61. data/features/configuration_loading.feature +26 -23
  62. data/features/programmatic_access.feature +43 -0
  63. data/features/rake_task/rake_task.feature +25 -22
  64. data/features/reports/json.feature +24 -26
  65. data/features/reports/reports.feature +77 -103
  66. data/features/reports/yaml.feature +26 -20
  67. data/features/samples.feature +26 -31
  68. data/features/step_definitions/.rubocop.yml +5 -0
  69. data/features/step_definitions/reek_steps.rb +16 -28
  70. data/features/step_definitions/sample_file_steps.rb +158 -0
  71. data/features/support/env.rb +16 -27
  72. data/lib/reek/{source → ast}/ast_node_class_map.rb +11 -12
  73. data/lib/reek/ast/node.rb +132 -0
  74. data/lib/reek/{core → ast}/object_refs.rb +2 -1
  75. data/lib/reek/ast/reference_collector.rb +32 -0
  76. data/lib/reek/{source → ast}/sexp_extensions.rb +43 -7
  77. data/lib/reek/ast/sexp_formatter.rb +31 -0
  78. data/lib/reek/cli/application.rb +5 -4
  79. data/lib/reek/cli/command.rb +2 -1
  80. data/lib/reek/cli/input.rb +6 -5
  81. data/lib/reek/cli/option_interpreter.rb +13 -9
  82. data/lib/reek/cli/options.rb +16 -8
  83. data/lib/reek/cli/reek_command.rb +4 -3
  84. data/lib/reek/cli/silencer.rb +14 -0
  85. data/lib/reek/{core → cli}/warning_collector.rb +2 -1
  86. data/lib/reek/code_comment.rb +36 -0
  87. data/lib/reek/configuration/app_configuration.rb +18 -3
  88. data/lib/reek/configuration/configuration_file_finder.rb +18 -32
  89. data/lib/reek/{core → context}/code_context.rb +8 -6
  90. data/lib/reek/{core → context}/method_context.rb +11 -5
  91. data/lib/reek/{core → context}/module_context.rb +9 -4
  92. data/lib/reek/{core/stop_context.rb → context/root_context.rb} +4 -2
  93. data/lib/reek/{core → context}/singleton_method_context.rb +3 -6
  94. data/lib/reek/examiner.rb +32 -29
  95. data/lib/reek/report/formatter.rb +70 -0
  96. data/lib/reek/report/heading_formatter.rb +45 -0
  97. data/lib/reek/report/location_formatter.rb +35 -0
  98. data/lib/reek/report/report.rb +198 -0
  99. data/lib/reek/smells/attribute.rb +41 -11
  100. data/lib/reek/smells/boolean_parameter.rb +4 -2
  101. data/lib/reek/smells/class_variable.rb +4 -2
  102. data/lib/reek/smells/control_parameter.rb +4 -2
  103. data/lib/reek/smells/data_clump.rb +4 -3
  104. data/lib/reek/smells/duplicate_method_call.rb +15 -5
  105. data/lib/reek/smells/feature_envy.rb +8 -2
  106. data/lib/reek/smells/irresponsible_module.rb +13 -8
  107. data/lib/reek/smells/long_parameter_list.rb +6 -4
  108. data/lib/reek/smells/long_yield_list.rb +4 -2
  109. data/lib/reek/smells/module_initialize.rb +4 -2
  110. data/lib/reek/smells/nested_iterators.rb +4 -2
  111. data/lib/reek/smells/nil_check.rb +6 -3
  112. data/lib/reek/smells/prima_donna_method.rb +21 -12
  113. data/lib/reek/smells/repeated_conditional.rb +7 -4
  114. data/lib/reek/{core → smells}/smell_configuration.rb +3 -1
  115. data/lib/reek/smells/smell_detector.rb +11 -5
  116. data/lib/reek/{core → smells}/smell_repository.rb +9 -5
  117. data/lib/reek/smells/smell_warning.rb +101 -0
  118. data/lib/reek/smells/too_many_instance_variables.rb +4 -2
  119. data/lib/reek/smells/too_many_methods.rb +4 -2
  120. data/lib/reek/smells/too_many_statements.rb +4 -2
  121. data/lib/reek/smells/uncommunicative_method_name.rb +4 -2
  122. data/lib/reek/smells/uncommunicative_module_name.rb +4 -2
  123. data/lib/reek/smells/uncommunicative_parameter_name.rb +4 -2
  124. data/lib/reek/smells/uncommunicative_variable_name.rb +4 -2
  125. data/lib/reek/smells/unused_parameters.rb +4 -2
  126. data/lib/reek/smells/utility_function.rb +11 -22
  127. data/lib/reek/smells.rb +24 -11
  128. data/lib/reek/source/source_code.rb +69 -12
  129. data/lib/reek/source/source_locator.rb +34 -19
  130. data/lib/reek/spec/should_reek.rb +4 -3
  131. data/lib/reek/spec/should_reek_of.rb +2 -1
  132. data/lib/reek/spec/should_reek_only_of.rb +4 -3
  133. data/lib/reek/spec.rb +3 -3
  134. data/lib/reek/tree_dresser.rb +32 -0
  135. data/lib/reek/tree_walker.rb +182 -0
  136. data/lib/reek/version.rb +1 -1
  137. data/lib/reek.rb +3 -3
  138. data/reek.gemspec +18 -25
  139. data/spec/factories/factories.rb +4 -2
  140. data/spec/gem/updates_spec.rb +1 -1
  141. data/spec/gem/yard_spec.rb +2 -2
  142. data/spec/quality/reek_source_spec.rb +3 -3
  143. data/spec/reek/{source/sexp_node_spec.rb → ast/node_spec.rb} +4 -6
  144. data/spec/reek/{core → ast}/object_refs_spec.rb +4 -6
  145. data/spec/reek/{source → ast}/reference_collector_spec.rb +27 -19
  146. data/spec/reek/{source → ast}/sexp_extensions_spec.rb +7 -19
  147. data/spec/reek/{source → ast}/sexp_formatter_spec.rb +13 -8
  148. data/spec/reek/cli/option_interpreter_spec.rb +15 -0
  149. data/spec/reek/cli/options_spec.rb +19 -0
  150. data/spec/reek/cli/warning_collector_spec.rb +28 -0
  151. data/spec/reek/{source/code_comment_spec.rb → code_comment_spec.rb} +19 -17
  152. data/spec/reek/configuration/app_configuration_spec.rb +37 -26
  153. data/spec/reek/configuration/configuration_file_finder_spec.rb +67 -21
  154. data/spec/reek/{core → context}/code_context_spec.rb +18 -21
  155. data/spec/reek/{core → context}/method_context_spec.rb +33 -27
  156. data/spec/reek/{core → context}/module_context_spec.rb +5 -7
  157. data/spec/reek/context/root_context_spec.rb +14 -0
  158. data/spec/reek/context/singleton_method_context_spec.rb +17 -0
  159. data/spec/reek/examiner_spec.rb +9 -50
  160. data/spec/reek/{cli → report}/html_report_spec.rb +6 -9
  161. data/spec/reek/report/json_report_spec.rb +20 -0
  162. data/spec/reek/{cli → report}/text_report_spec.rb +16 -19
  163. data/spec/reek/report/xml_report_spec.rb +34 -0
  164. data/spec/reek/report/yaml_report_spec.rb +20 -0
  165. data/spec/reek/smells/attribute_spec.rb +93 -88
  166. data/spec/reek/smells/behaves_like_variable_detector.rb +1 -1
  167. data/spec/reek/smells/boolean_parameter_spec.rb +31 -5
  168. data/spec/reek/smells/class_variable_spec.rb +10 -10
  169. data/spec/reek/smells/control_parameter_spec.rb +5 -5
  170. data/spec/reek/smells/data_clump_spec.rb +6 -6
  171. data/spec/reek/smells/duplicate_method_call_spec.rb +7 -7
  172. data/spec/reek/smells/feature_envy_spec.rb +12 -13
  173. data/spec/reek/smells/irresponsible_module_spec.rb +28 -32
  174. data/spec/reek/smells/long_parameter_list_spec.rb +7 -7
  175. data/spec/reek/smells/long_yield_list_spec.rb +6 -6
  176. data/spec/reek/smells/module_initialize_spec.rb +4 -4
  177. data/spec/reek/smells/nested_iterators_spec.rb +6 -6
  178. data/spec/reek/smells/nil_check_spec.rb +6 -6
  179. data/spec/reek/smells/prima_donna_method_spec.rb +8 -5
  180. data/spec/reek/smells/repeated_conditional_spec.rb +18 -17
  181. data/spec/reek/{core → smells}/smell_configuration_spec.rb +5 -7
  182. data/spec/reek/smells/smell_detector_shared.rb +6 -7
  183. data/spec/reek/smells/smell_repository_spec.rb +29 -0
  184. data/spec/reek/{smell_warning_spec.rb → smells/smell_warning_spec.rb} +7 -9
  185. data/spec/reek/smells/too_many_instance_variables_spec.rb +5 -5
  186. data/spec/reek/smells/too_many_methods_spec.rb +12 -8
  187. data/spec/reek/smells/too_many_statements_spec.rb +10 -9
  188. data/spec/reek/smells/uncommunicative_method_name_spec.rb +5 -5
  189. data/spec/reek/smells/uncommunicative_module_name_spec.rb +8 -8
  190. data/spec/reek/smells/uncommunicative_parameter_name_spec.rb +18 -6
  191. data/spec/reek/smells/uncommunicative_variable_name_spec.rb +10 -7
  192. data/spec/reek/smells/unused_parameters_spec.rb +4 -4
  193. data/spec/reek/smells/utility_function_spec.rb +65 -39
  194. data/spec/reek/source/source_code_spec.rb +10 -10
  195. data/spec/reek/source/source_locator_spec.rb +30 -0
  196. data/spec/reek/spec/should_reek_of_spec.rb +9 -25
  197. data/spec/reek/spec/should_reek_only_of_spec.rb +4 -7
  198. data/spec/reek/spec/should_reek_spec.rb +4 -32
  199. data/spec/reek/tree_dresser_spec.rb +16 -0
  200. data/spec/reek/{core/tree_walker_spec.rb → tree_walker_spec.rb} +5 -7
  201. data/spec/samples/checkstyle.xml +2 -0
  202. data/spec/samples/configuration/with_excluded_paths.reek +4 -0
  203. data/spec/samples/{overrides/masked/lower.reek → masked_by_dotfile/.reek} +3 -1
  204. data/spec/samples/source_with_exclude_paths/ignore_me/uncommunicative_method_name.rb +5 -0
  205. data/spec/samples/source_with_exclude_paths/nested/ignore_me_as_well/irresponsible_module.rb +2 -0
  206. data/spec/samples/source_with_exclude_paths/nested/uncommunicative_parameter_name.rb +6 -0
  207. data/spec/samples/{ruby20_syntax.rb → unusual_syntax.rb} +8 -0
  208. data/spec/spec_helper.rb +21 -10
  209. data/tasks/develop.rake +2 -2
  210. data/tasks/reek.rake +1 -1
  211. metadata +158 -141
  212. data/features/support/hooks.rb +0 -15
  213. data/lib/reek/cli/report/formatter.rb +0 -69
  214. data/lib/reek/cli/report/heading_formatter.rb +0 -45
  215. data/lib/reek/cli/report/location_formatter.rb +0 -34
  216. data/lib/reek/cli/report/report.rb +0 -131
  217. data/lib/reek/core/tree_walker.rb +0 -180
  218. data/lib/reek/smell_warning.rb +0 -87
  219. data/lib/reek/source/ast_node.rb +0 -38
  220. data/lib/reek/source/code_comment.rb +0 -37
  221. data/lib/reek/source/core_extras.rb +0 -46
  222. data/lib/reek/source/reference_collector.rb +0 -27
  223. data/lib/reek/source/sexp_formatter.rb +0 -22
  224. data/lib/reek/source/sexp_node.rb +0 -79
  225. data/lib/reek/source/source_file.rb +0 -16
  226. data/lib/reek/source/source_repository.rb +0 -45
  227. data/lib/reek/source/tree_dresser.rb +0 -24
  228. data/lib/reek/source.rb +0 -16
  229. data/spec/reek/cli/json_report_spec.rb +0 -20
  230. data/spec/reek/cli/option_interperter_spec.rb +0 -14
  231. data/spec/reek/cli/yaml_report_spec.rb +0 -23
  232. data/spec/reek/core/singleton_method_context_spec.rb +0 -9
  233. data/spec/reek/core/smell_repository_spec.rb +0 -17
  234. data/spec/reek/core/stop_context_spec.rb +0 -17
  235. data/spec/reek/core/warning_collector_spec.rb +0 -30
  236. data/spec/reek/source/object_source_spec.rb +0 -20
  237. data/spec/reek/source/tree_dresser_spec.rb +0 -18
  238. data/spec/samples/config/allow_duplication.reek +0 -3
  239. data/spec/samples/config/deeper_nested_iterators.reek +0 -3
  240. data/spec/samples/configuration_loading/minimal_dirty.rb +0 -4
  241. data/spec/samples/configuration_loading/reek-test-run-disable_smells.reek +0 -7
  242. data/spec/samples/configuration_loading/reek-test-run-enable_smells.reek +0 -7
  243. data/spec/samples/corrupt_config_file/corrupt.reek +0 -1
  244. data/spec/samples/corrupt_config_file/dirty.rb +0 -8
  245. data/spec/samples/demo/demo.rb +0 -8
  246. data/spec/samples/empty_config_file/dirty.rb +0 -8
  247. data/spec/samples/empty_config_file/empty.reek +0 -0
  248. data/spec/samples/inline_config/dirty.rb +0 -16
  249. data/spec/samples/inline_config/masked.reek +0 -7
  250. data/spec/samples/mask_some/dirty.rb +0 -8
  251. data/spec/samples/mask_some/some.reek +0 -8
  252. data/spec/samples/masked/dirty.rb +0 -8
  253. data/spec/samples/masked/masked.reek +0 -5
  254. data/spec/samples/minimal_smelly_and_masked/config.reek +0 -7
  255. data/spec/samples/minimal_smelly_and_masked/minimal_dirty.rb +0 -4
  256. data/spec/samples/mixed_results/clean_one.rb +0 -7
  257. data/spec/samples/mixed_results/clean_three.rb +0 -7
  258. data/spec/samples/mixed_results/clean_two.rb +0 -7
  259. data/spec/samples/mixed_results/dirty_one.rb +0 -8
  260. data/spec/samples/mixed_results/dirty_two.rb +0 -8
  261. data/spec/samples/not_quite_masked/dirty.rb +0 -8
  262. data/spec/samples/not_quite_masked/masked.reek +0 -5
  263. data/spec/samples/not_quite_masked/smelly.rb +0 -3
  264. data/spec/samples/one_smelly_file/dirty.rb +0 -3
  265. data/spec/samples/overrides/masked/dirty.rb +0 -8
  266. data/spec/samples/overrides/upper.reek +0 -5
  267. data/spec/samples/overrides_defaults/camel_case.rb +0 -14
  268. data/spec/samples/overrides_defaults/config.reek +0 -6
  269. data/spec/samples/ruby21_syntax.rb +0 -5
  270. data/spec/samples/standard_smelly/dirty.rb +0 -8
  271. data/spec/samples/standard_smelly/minimal_dirty.rb +0 -4
  272. data/spec/samples/three_smelly_files/dirty_one.rb +0 -3
  273. data/spec/samples/three_smelly_files/dirty_three.rb +0 -5
  274. data/spec/samples/three_smelly_files/dirty_two.rb +0 -4
  275. /data/spec/samples/{simple_configuration.reek → configuration/simple_configuration.reek} +0 -0
data/docs/API.md ADDED
@@ -0,0 +1,79 @@
1
+ # Using `reek` inside your Ruby application
2
+
3
+ `reek` can be used inside another Ruby project.
4
+
5
+ ```bash
6
+ gem install reek
7
+ ```
8
+
9
+ ## Using a reporter
10
+
11
+ You can use reek inside your Ruby file `check_dirty.rb`
12
+
13
+ ```ruby
14
+ require 'reek'
15
+
16
+ source = <<-END
17
+ class Dirty
18
+ # This method smells of :reek:NestedIterators but ignores them
19
+ def awful(x, y, offset = 0, log = false)
20
+ puts @screen.title
21
+ @screen = widgets.map { |w| w.each { |key| key += 3 * x } }
22
+ puts @screen.contents
23
+ fail
24
+ end
25
+ end
26
+ END
27
+
28
+ reporter = Reek::Report::TextReport.new
29
+ examiner = Reek::Examiner.new(source)
30
+ reporter.add_examiner examiner
31
+ reporter.show
32
+ ```
33
+
34
+ This will show the list of errors in variable `source`.
35
+
36
+ `Reek::Examiner.new` can take `source` as `String`, `File` or `IO`.
37
+
38
+ ```
39
+ # Examine a file object
40
+ reporter.add_examiner Reek::Examiner.new(File.new('dirty.rb'))
41
+ ```
42
+
43
+ Also, besides normal text output, `reek` can generate output in YAML,
44
+ JSON, HTML and XML by using the following Report types:
45
+
46
+ ```
47
+ TextReport
48
+ YAMLReport
49
+ JSONReport
50
+ HTMLReport
51
+ XMLReport
52
+ ```
53
+
54
+ ## Accessing the smell warnings directly
55
+
56
+ You can also access the smells detected by an examiner directly:
57
+
58
+ ```ruby
59
+ require 'reek'
60
+
61
+ source = <<-END
62
+ class Dirty
63
+ # This method smells of :reek:NestedIterators but ignores them
64
+ def awful(x, y, offset = 0, log = false)
65
+ puts @screen.title
66
+ @screen = widgets.map { |w| w.each { |key| key += 3 * x } }
67
+ puts @screen.contents
68
+ fail
69
+ end
70
+ end
71
+ END
72
+
73
+ examiner = Reek::Examiner.new(source)
74
+ examiner.smells.each do |smell|
75
+ puts smell.message
76
+ end
77
+ ```
78
+
79
+ `Examiner#smells` returns a list of `SmellWarning` objects.
data/docs/Attribute.md ADDED
@@ -0,0 +1,43 @@
1
+ # Attribute
2
+
3
+ ## Introduction
4
+
5
+ A class that publishes a getter or setter for an instance variable invites client classes to become too intimate with its inner workings, and in particular with its representation of state.
6
+
7
+ ## Example
8
+
9
+ Given:
10
+
11
+ ```Ruby
12
+ class Klass
13
+ attr_accessor :dummy
14
+ end
15
+ ```
16
+
17
+ `reek` would emit the following warning:
18
+
19
+ ```
20
+ reek test.rb
21
+
22
+ test.rb -- 1 warning:
23
+ [2]:Klass declares the attribute dummy (Attribute)
24
+ ```
25
+
26
+ ## Support in Reek
27
+
28
+ Right now this smell is disabled by default since it is highly subjective.
29
+
30
+ When this detector is enabled it raises a warning for every `attr`, `attr_reader`, `attr_writer` and `attr_accessor` -- including those that are private.
31
+
32
+ ## Configuration
33
+
34
+ If you want to enable it you can do so by placing
35
+
36
+ ```yaml
37
+ Attribute:
38
+ enabled: true
39
+ ```
40
+
41
+ in your reek configuration file.
42
+
43
+ `Attribute` supports only the [Basic Smell Options](Basic-Smell-Options.md).
@@ -0,0 +1,44 @@
1
+ # Basic Smell Options
2
+
3
+ ## Introduction
4
+
5
+ Every smell detector in Reek offers at least the following configuration options:
6
+
7
+ | Option | Value | Effect |
8
+ | ---------------|-------------|---------|
9
+ | `enabled` | Boolean | Determines whether the smell detector is active. Defaults to `true` |
10
+ | `exclude` | an array of strings or regular expressions | Ignores any context whose full description (see <strong>%c</strong> in [Command-Line Options](Command-Line-Options.md)) matches any element of this array. |
11
+
12
+ The file `config/defaults.reek` (shipped with the Reek gem) lists any default exclusions for each smell.
13
+
14
+ ## Examples
15
+
16
+ <u>An easy one:</u>
17
+
18
+ To stop Reek reporting smells in any method called `write` you might create a configuration file containing this:
19
+
20
+ ```yaml
21
+ ControlCouple:
22
+ exclude:
23
+ - write
24
+ ```
25
+
26
+ Or a little more sophisticated using a ruby regex like this:
27
+
28
+ ```yaml
29
+ ControlCouple:
30
+ exclude:
31
+ - !ruby/regexp /write/
32
+ ```
33
+
34
+ <u>A more sophisticated one:</u>
35
+
36
+ ```yaml
37
+ FeatureEnvy:
38
+ exclude:
39
+ - "MyModel#do_things"
40
+ - "MyHelper"
41
+ - "ApplicationController#respond"
42
+ ```
43
+
44
+ This would not report FeatureEnvy for the instance method `MyModel#do_things`, the whole module `MyHelper` and the `respond` instance method of `ApplicationController`
@@ -0,0 +1,52 @@
1
+ # Boolean Parameter
2
+
3
+ ## Introduction
4
+
5
+ `Boolean Parameter` is a special case of [Control Couple](Control-Couple.md), where a method parameter is defaulted
6
+ to true or false. A _Boolean Parameter_ effectively permits a method's caller
7
+ to decide which execution path to take. This is a case of bad cohesion. You're creating a dependency between methods that is not really necessary, thus increasing coupling.
8
+
9
+ ## Example
10
+
11
+ Given
12
+
13
+ ```Ruby
14
+ class Dummy
15
+ def hit_the_switch(switch = true)
16
+ if switch
17
+ puts 'Hitting the switch'
18
+ # do other things...
19
+ else
20
+ puts 'Not hitting the switch'
21
+ # do other things...
22
+ end
23
+ end
24
+ end
25
+ ```
26
+
27
+ `reek` would emit the following warning:
28
+
29
+ ```
30
+ test.rb -- 3 warnings:
31
+ [1]:Dummy#hit_the_switch has boolean parameter 'switch' (BooleanParameter)
32
+ [2]:Dummy#hit_the_switch is controlled by argument switch (ControlParameter)
33
+ ```
34
+
35
+ Note that both smells are reported, `Boolean Parameter` and `Control Parameter`.
36
+
37
+ ## Getting rid of the smell
38
+
39
+ This is highly dependant on your exact architecture, but looking at the example above what you could do is:
40
+
41
+ * Move everything in the `if` branch into a separate method
42
+ * Move everything in the `else` branch into a separate method
43
+ * Get rid of the `hit_the_switch` method alltogether
44
+ * Make the decision what method to call in the initial caller of `hit_the_switch`
45
+
46
+ ## Current support in Reek
47
+
48
+ Reek can only detect a Boolean parameter when it has a default initializer like in the example above.
49
+
50
+ ## Configuration
51
+
52
+ `Boolean Parameter` supports the [Basic Smell Options](Basic-Smell-Options.md).
@@ -0,0 +1,40 @@
1
+ # Class Variable
2
+
3
+ ## Introduction
4
+
5
+ Class variables form part of the global runtime state, and as such make it easy for one part of the system to accidentally or inadvertently depend on another part of the system. So the system becomes more prone to problems where changing something over here breaks something over there. In particular, class variables can make it hard to set up tests (because the context of the test includes all global state).
6
+
7
+ For a detailed explanation, check out [this article](http://4thmouse.com/index.php/2011/03/20/why-class-variables-in-ruby-are-a-bad-idea/)
8
+
9
+ ## Example
10
+
11
+ Given
12
+
13
+ ```Ruby
14
+ class Dummy
15
+ @@class_variable = :whatever
16
+ end
17
+ ```
18
+
19
+ `reek` would emit the following warning:
20
+
21
+ ```
22
+ reek test.rb
23
+
24
+ test.rb -- 1 warning:
25
+ [2]:Dummy declares the class variable @@class_variable (ClassVariable)
26
+ ```
27
+
28
+ ## Getting rid of the smell
29
+
30
+ You can use class-instance variable to mitigate the problem (as also suggested in the linked article above):
31
+
32
+ ```Ruby
33
+ class Dummy
34
+ @class_variable = :whatever
35
+ end
36
+ ```
37
+
38
+ ## Configuration
39
+
40
+ `Class Variable` supports the [Basic Smell Options](Basic-Smell-Options.md).
@@ -0,0 +1,34 @@
1
+ # Code Smells
2
+
3
+ Smells are indicators of where your code might be hard to read, maintain or evolve, rather than things that are specifically _wrong_. Naturally this means that Reek is looking towards your code's future (and that can make its reports seem somewhat subjective, of course).
4
+
5
+ Reek currently includes checks for the following smells:
6
+
7
+ * [Attribute](Attribute.md) (disabled by default)
8
+ * [Class Variable](Class-Variable.md)
9
+ * [Control Couple](Control-Couple.md), including
10
+ * [Boolean Parameter](Boolean-Parameter.md)
11
+ * [Control Parameter](Control-Parameter.md)
12
+ * [Data Clump](Data-Clump.md)
13
+ * [Duplicate Method Call](Duplicate-Method-Call.md)
14
+ * [Irresponsible Module](Irresponsible-Module.md)
15
+ * [Large Class](Large-Class.md), including
16
+ * [Too Many Instance Variables](Too-Many-Instance-Variables.md)
17
+ * [Too Many Methods](Too-Many-Methods.md)
18
+ * [Long Parameter List](Long-Parameter-List.md), and its special case [Long Yield List](Long-Yield-List.md)
19
+ * Low Cohesion, including
20
+ * [Feature Envy](Feature-Envy.md)
21
+ * [Utility Function](Utility-Function.md)
22
+ * [Module Initialize](Module-Initialize.md)
23
+ * [Nested Iterators](Nested-Iterators.md)
24
+ * [Prima-Donna-Method](Prima-Donna-Method.md)
25
+ * [Simulated Polymorphism](Simulated-Polymorphism.md), including
26
+ * [Nil Check](Nil-Check.md)
27
+ * [Repeated Conditional](Repeated-Conditional.md)
28
+ * [Too Many Statements](Too-Many-Statements.md)
29
+ * [Uncommunicative Name](Uncommunicative-Name.md), including
30
+ * [Uncommunicative Method Name](Uncommunicative-Method-Name.md)
31
+ * [Uncommunicative Module Name](Uncommunicative-Module-Name.md)
32
+ * [Uncommunicative Parameter Name](Uncommunicative-Parameter-Name.md)
33
+ * [Uncommunicative Variable Name](Uncommunicative-Variable-Name.md)
34
+ * [Unused Parameters](Unused-Parameters.md)
@@ -0,0 +1,84 @@
1
+ # Command Line Options
2
+
3
+ ## Introduction
4
+
5
+ reek follows standard Unix convention for passing arguments.
6
+
7
+ Check out
8
+
9
+ ```Bash
10
+ reek -h
11
+ ```
12
+
13
+ for details.
14
+
15
+ ## Telling Reek Which Code to Check
16
+
17
+ Probably the most standard use case would be to check all ruby files in the lib directory:
18
+
19
+ ```Bash
20
+ reek lib/*.rb
21
+ ```
22
+
23
+ In general, if any command-line argument is a directory, Reek searches that directory and all sub-directories for Ruby source files. Thus
24
+
25
+ ```Bash
26
+ reek lib
27
+ ```
28
+
29
+ would be equivalent to
30
+
31
+ ```Bash
32
+ reek lib/**/*.rb
33
+ ```
34
+
35
+ Occasionally you may want to quickly check a code snippet without going to the trouble of creating a file to hold it. You can pass the snippet directly to Reek's standard input:
36
+
37
+ ```Bash
38
+ echo "def x() true end" | reek
39
+ ```
40
+
41
+ ## Output options
42
+
43
+ ### Output smell's line number
44
+
45
+ By passing in a "-n" flag to the _reek_ command, the output will suppress the line numbers:
46
+
47
+ ```Bash
48
+ $ reek -n mess.rb
49
+ ```
50
+
51
+ ```
52
+ mess.rb -- 2 warnings:
53
+ x doesn't depend on instance state (UtilityFunction)
54
+ x has the name 'x' (UncommunicativeMethodName)
55
+ ```
56
+
57
+ Otherwise line numbers will be shown as default at the beginning of each warning in square brackets:
58
+
59
+ ```Bash
60
+ $ reek mess.rb
61
+ ```
62
+
63
+ ```
64
+ mess.rb -- 2 warnings:
65
+ [2]:x doesn't depend on instance state (UtilityFunction)
66
+ [2]:x has the name 'x' (UncommunicativeMethodName)
67
+ ```
68
+
69
+ ### Enable the ultra-verbose mode
70
+
71
+ _reek_ has a ultra-verbose mode which you might find helpful as a beginner. "ultra-verbose" just means that behind each warning a helpful link will be displayed which leads directly to the corresponding _reek_ wiki page.
72
+ This mode can be enabled via the "-U" or "--ultra-verbose" flag.
73
+
74
+ So for instance, if your test file would smell of _ClassVariable_, this is what the _reek_ output would look like:
75
+
76
+ ```Bash
77
+ reek -U test.rb
78
+ ```
79
+ ```
80
+ test.rb -- 1 warning:
81
+ [2]:Dummy declares the class variable @@class_variable (ClassVariable) [https://github.com/troessner/reek/wiki/Class-Variable]
82
+ ```
83
+
84
+ Note the link at the end.
@@ -0,0 +1,49 @@
1
+ # Configuration Files
2
+
3
+ ## Configuration loading
4
+
5
+ Configuring `reek` via configuration file is by far the most powerful way.
6
+
7
+ There are 3 ways of passing `reek` a configuration file:
8
+
9
+ 1. Using the cli "-c" switch (see [Command Line Options](Command-Line-Options.md))
10
+ 2. Having a file ending with .reek either in your current working directory or in a parent directory (more on that later)
11
+ 3. Having a file ending with .reek in your HOME directory
12
+
13
+ The order in which `reek` tries to find such a configuration file is exactly like above: First `reek` checks if we have given it a configuration file explicitly via CLI. Then it checks the current working directory for a file and if it can't find one, it traverses up the directories until it hits the root directory. And lastly, it checks your HOME directory.
14
+
15
+ As soon as `reek` detects a configuration file it stops searching immediately, meaning that from `reek`'s point of view there exists one configuration file and one configuration only regardless of how many ".reek" files you might have on your filesystem.
16
+
17
+ ## Configuration options for smells
18
+
19
+ The first thing you probably want to check out are the [Basic Smell Options](Basic-Smell-Options.md) which are supported by every smell type.
20
+ Certain smell types offer a configuration that goes beyond that of the basic smell options - for instance [Data Clump](Data-Clump.md).
21
+ All options that go beyond the [Basic Smell Options](Basic-Smell-Options.md) should be documented in the corresponding smell type wiki page but if you want to get a quick and full overview over all possible configurations you can always check out [the default.reek file in this repository](https://github.com/troessner/reek/blob/master/config/defaults.reek).
22
+
23
+ Here's an excerpt of a `reek` configuration file from a commercial project:
24
+
25
+ ```yaml
26
+ ---
27
+ IrresponsibleModule:
28
+ enabled: false
29
+ NestedIterators:
30
+ exclude:
31
+ - "ActiveModelErrorAdder#self.run" # should be refactored
32
+ - "BookingRequests::Transfer#remote_validation"
33
+ - "BookingRequestsController#vehicle_options" # respond_to block
34
+ - "Content::Base#self.expose_fields" # unavoidable due to metaprogramming
35
+ DataClump:
36
+ max_copies: 3
37
+ min_clump_size: 3
38
+ ```
39
+
40
+ ## Excluding directories from scans
41
+
42
+ You can exclude whole directories from scans using `exclude_paths` in your configuration file:
43
+
44
+ ```yaml
45
+ ---
46
+ exclude_paths:
47
+ - app/views
48
+ - app/controllers
49
+ ```
@@ -0,0 +1,22 @@
1
+ # Control Couple
2
+
3
+ ## Introduction
4
+
5
+ Control coupling occurs when a method or block checks the value of a parameter in order to decide which execution path to take. The offending parameter is often called a `Control Couple`.
6
+
7
+ Control Coupling is a kind of duplication, because the calling method already knows which path should be taken.
8
+
9
+ Control Coupling reduces the code's flexibility by creating a dependency between the caller and callee: any change to the possible values of the controlling parameter must be reflected on both sides of the call. A `Control Couple` also reveals a loss of simplicity: the called method probably has more than one responsibility, because it includes at least two different code paths.
10
+
11
+ You can find a good write-up regarding this problem [here](http://solnic.eu/2012/04/11/get-rid-of-that-code-smell-control-couple.html).
12
+
13
+ ## Current Support in reek
14
+
15
+ `reek` warns about control coupling when:
16
+
17
+ * [Control-Parameter](Control-Parameter.md) - a method parameter or block parameter is the tested value in a conditional statement (as in the example below); or
18
+ * [Boolean-Parameter](Boolean-Parameter.md) - a method parameter is defaulted to `true` or `false`.
19
+
20
+ ## Configuration
21
+
22
+ Control Couple supports the [Basic Smell Options](Basic-Smell-Options.md).
@@ -0,0 +1,29 @@
1
+ # Control Parameter
2
+
3
+ ## Introduction
4
+
5
+ `Control Parameter` is a special case of [Control Couple](Control-Couple.md)
6
+
7
+ ## Example
8
+
9
+ A simple example would be the "quoted" parameter in the following method:
10
+
11
+ ```Ruby
12
+ def write(quoted)
13
+ if quoted
14
+ write_quoted @value
15
+ else
16
+ write_unquoted @value
17
+ end
18
+ end
19
+ ```
20
+
21
+ Fixing those problems is out of the scope of this document but an easy solution could be to remove the "write" method alltogether and to move the calls to "write_quoted" / "write_unquoted" in the initial caller of "write".
22
+
23
+ ## Current Support in reek
24
+
25
+ `reek` warns about control coupling when a method parameter or block parameter is the tested value in a conditional statement.
26
+
27
+ ## Configuration
28
+
29
+ Control Couple supports the [Basic Smell Options](Basic-Smell-Options.md).
@@ -0,0 +1,44 @@
1
+ # Data Clump
2
+
3
+ ## Introduction
4
+
5
+ In general, a `Data Clump` occurs when the same two or three items frequently appear together in classes and parameter lists, or when a group of instance variable names start or end with similar substrings.
6
+
7
+ The recurrence of the items often means there is duplicate code spread around to handle them. There may be an abstraction missing from the code, making the system harder to understand.
8
+
9
+ ## Example
10
+
11
+ Given
12
+
13
+ ```Ruby
14
+ class Dummy
15
+ def x(y1,y2); end
16
+ def y(y1,y2); end
17
+ def z(y1,y2); end
18
+ end
19
+ ```
20
+
21
+ `reek` would emit the following warning:
22
+
23
+ ```
24
+ test.rb -- 1 warning:
25
+ [2, 3, 4]:Dummy takes parameters [y1, y2] to 3 methods (DataClump)
26
+ ```
27
+
28
+ A possible way to fix this problem (quoting from [Martin Fowler](http://martinfowler.com/bliki/DataClump.html)):
29
+
30
+ >> The first step is to replace data clumps with objects and use the objects whenever you see them. An immediate benefit is that you'll shrink some parameter lists. The interesting stuff happens as you begin to look for behavior to move into the new objects.
31
+
32
+ ## Current Support in Reek
33
+
34
+ `reek` looks for a group of two or more parameters with the same names that are expected by three or more methods of a class.
35
+
36
+ ## Configuration
37
+
38
+ Reek's Data Clump detector offers the [Basic Smell Options](Basic-Smell-Options.md), plus:
39
+
40
+ | Option | Value | Effect |
41
+ | ---------------|-------------|---------|
42
+ | max_copies | integer | The maximum number of methods that are permitted to take the same group of parameters. Defaults to 2 |
43
+ | min_clump_size | integer | The smallest number of parameters that can be reported as a clump. Defaults to 2 |
44
+
@@ -0,0 +1,49 @@
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
+ `reek` implements a check for _Duplicate Method Call_.
7
+
8
+ ## Example
9
+
10
+ Here's a very much simplified and contrived example. The following method will report a warning:
11
+
12
+ ```Ruby
13
+ def double_thing()
14
+ @other.thing + @other.thing
15
+ end
16
+ ```
17
+
18
+ One quick approach to silence Reek would be to refactor the code thus:
19
+
20
+ ```Ruby
21
+ def double_thing()
22
+ thing = @other.thing
23
+ thing + thing
24
+ end
25
+ ```
26
+
27
+ A slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:
28
+
29
+ ```Ruby
30
+ class Other
31
+ def double_thing()
32
+ thing + thing
33
+ end
34
+ end
35
+ ```
36
+
37
+ The approach you take will depend on balancing other factors in your code.
38
+
39
+ ## Current support in reek
40
+
41
+ `reek`'s Duplicate Method Call detector checks for repeated identical method calls within any one method definition. This is intended to complement the checks performed by tools such as [Flay](http://ruby.sadi.st/Flay.html) and [Simian](http://www.redhillconsulting.com.au/products/simian/).
42
+
43
+ ## Configuration
44
+
45
+ Reek's Duplication detector currently offers the [Basic Smell Options](Basic-Smell-Options.md), plus:
46
+
47
+ Option | Value | Effect
48
+ -------|-------|-------
49
+ `max_calls` | integer | The maximum number of duplicate calls allowed within a method. Defaults to 1.
@@ -0,0 +1,49 @@
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#total_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
+ ## Differences to _Utility Function_
44
+
45
+ _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.
46
+
47
+ ## Configuration
48
+
49
+ _Feature Envy_ supports the [Basic Smell Options](Basic-Smell-Options.md).