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
@@ -0,0 +1,114 @@
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 SmellRepository with all relevant smells (smells/smell_repository)
44
+ * initializes a WarningCollector (cli/warning_collector)
45
+ * runs all corresponding smell detectors via a Treewalker (core/tree_walker) for the SmellRepository above
46
+ / | \
47
+ / | \
48
+ / | \
49
+ UtilityFunction FeatureEnvy TooManyMethods
50
+ \ | /
51
+ \ | /
52
+ \ | /
53
+ WarningCollector
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 SmellRepository only with eligible smells
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 TreeWalker then traverses this now adorned tree again and
114
+ runs all SmellDetectors from the SmellRepository above
@@ -0,0 +1,39 @@
1
+ # Irresponsible Module
2
+
3
+ ## Introduction
4
+
5
+ Classes and modules are the units of reuse and release. It is therefore considered good practice to annotate every class and module with a brief comment outlining its responsibilities.
6
+
7
+ ## Example
8
+
9
+ Given
10
+
11
+ ```Ruby
12
+ class Dummy
13
+ # Do things...
14
+ end
15
+ ```
16
+
17
+ `reek` would emit the following warning:
18
+
19
+ ```
20
+ test.rb -- 1 warning:
21
+ [1]:Dummy has no descriptive comment (IrresponsibleModule)
22
+ ```
23
+
24
+ Fixing this is simple - just an explaining comment:
25
+
26
+ ```Ruby
27
+ # The Dummy class is responsible for ...
28
+ class Dummy
29
+ # Do things...
30
+ end
31
+ ```
32
+
33
+ ## Current Support in reek
34
+
35
+ `Irresponsible Module` currently checks classes, but not modules.
36
+
37
+ ## Configuration
38
+
39
+ `Irresponsible Module` supports only the [Basic Smell Options](Basic-Smell-Options.md).
@@ -0,0 +1,20 @@
1
+ # Large Class
2
+
3
+ ## Introduction
4
+
5
+ A `Large Class` is a class or module that has a large number of instance variables, methods or lines of code in any one piece of its specification. (That is, this smell relates to pieces of the class's specification, not to the size of the corresponding instance of `Class`.)
6
+
7
+ ## Current Support in Reek
8
+
9
+ `Large Class` reports classes having more than a configurable number of methods or instance variables. The method count includes public, protected and private methods, and excludes methods inherited from superclasses or included modules.
10
+
11
+ ## Configuration
12
+
13
+ `reek`'s Large Class detector supports the [Basic Smell Options](Basic-Smell-Options.md), plus:
14
+
15
+ | Option | Value | Effect |
16
+ | ---------------|-------------|---------|
17
+ | max_methods | integer | The maximum number of methods allowed in a class before a warning is issued. Defaults to 25. |
18
+ | max_instance_variables | integer | The maximum number of instance variables allowed in a class before a warning is issued. Defaults to 9. |
19
+
20
+ The `Large Class` detector is enabled whenever `reek` is asked to check an instance of `Class` or `Module`.
@@ -0,0 +1,38 @@
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 [Basic Smell Options](Basic-Smell-Options.md), plus:
35
+
36
+ | Option | Value | Effect |
37
+ | ---------------|-------------|---------|
38
+ | max_params | integer | The maximum number of parameters allowed in a method or block before a warning is issued. Defaults to 3. |
@@ -0,0 +1,36 @@
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 it gets passed.
6
+
7
+ ## Example
8
+
9
+ ```Ruby
10
+ class Dummy
11
+ def yields_a_lot(foo,bar,baz,fling,flung)
12
+ yield foo,bar,baz,fling,flung
13
+ end
14
+ end
15
+ ```
16
+
17
+ `reek` would report the following warning:
18
+
19
+ ```
20
+ test.rb -- 1 warning:
21
+ [4]:Dummy#yields_a_lot yields 5 parameters (LongYieldList)
22
+ ```
23
+
24
+ A common solution to this problem would be the introduction of parameter objects.
25
+
26
+ ## Current Support in Reek
27
+
28
+ Currently Long Parameter List reports any method or block with more than 3 parameters.
29
+
30
+ ## Configuration
31
+
32
+ Reek's Long Parameter List detector supports the [Basic Smell Options](Basic-Smell-Options.md), plus:
33
+
34
+ | Option | Value | Effect |
35
+ | ---------------|-------------|---------|
36
+ | max_params | integer | The maximum number of parameters allowed in a method or block before a warning is issued. Defaults to 3. |
@@ -0,0 +1,62 @@
1
+ # Module Initialize
2
+
3
+ ## Introduction
4
+
5
+ A module is usually a mixin, so when an `#initialize` method is present it is
6
+ hard to tell initialization order and parameters so having `#initialize`
7
+ in a module is usually a bad idea.
8
+
9
+ ## Example
10
+
11
+ The `Foo` module below contains a method `initialize`. Although class `B` inherits from `A`, the inclusion of `Foo` stops `A#initialize` from being called.
12
+
13
+ ```Ruby
14
+ class A
15
+ def initialize(a)
16
+ @a = a
17
+ end
18
+ end
19
+
20
+ module Foo
21
+ def initialize(foo)
22
+ @foo = foo
23
+ end
24
+ end
25
+
26
+ class B < A
27
+ include Foo
28
+
29
+ def initialize(b)
30
+ super('bar')
31
+ @b = b
32
+ end
33
+ end
34
+ ```
35
+
36
+ A simple solution is to rename `Foo#initialize` and call that method by name:
37
+
38
+ ```Ruby
39
+ module Foo
40
+ def setup_foo_module(foo)
41
+ @foo = foo
42
+ end
43
+ end
44
+
45
+ class B < A
46
+ include Foo
47
+
48
+ def initialize(b)
49
+ super 'bar'
50
+ setup_foo_module('foo')
51
+ @b = b
52
+ end
53
+ end
54
+ ```
55
+
56
+ ## Current Support in reek
57
+
58
+ `reek` warns about module initialize when an instance method named `initialize` is present in a module.
59
+
60
+ ## Configuration
61
+
62
+ Module Initialize supports the [Basic Smell Options](Basic-Smell-Options.md).
@@ -0,0 +1,38 @@
1
+ # Nested Iterators
2
+
3
+ ## Introduction
4
+
5
+ A `Nested Iterator` occurs when a block contains another block.
6
+
7
+ ## Example
8
+
9
+ Given
10
+
11
+ ```Ruby
12
+ class Duck
13
+ class << self
14
+ def duck_names
15
+ %i!tick trick track!.each do |surname|
16
+ %i!duck!.each do |last_name|
17
+ puts "full name is #{surname} #{last_name}"
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ ```
24
+
25
+ `reek` would report the following warning:
26
+
27
+ ```
28
+ test.rb -- 1 warning:
29
+ [5]:Duck#duck_names contains iterators nested 2 deep (NestedIterators)
30
+ ```
31
+
32
+ ## Current Support in Reek
33
+
34
+ Nested Iterators reports failing methods only once.
35
+
36
+ ## Configuration
37
+
38
+ `Nested Iterators` offers the [Basic Smell Options](Basic-Smell-Options.md).
data/docs/Nil-Check.md ADDED
@@ -0,0 +1,39 @@
1
+ # Nil Check
2
+
3
+ ## Introduction
4
+
5
+ A `NilCheck` is a type check. Failures of `NilCheck` violate the ["tell, don't ask"](http://robots.thoughtbot.com/tell-dont-ask) principle.
6
+ Additionally to that, type checks often mask bigger problems in your source code like not using OOP and / or polymorphism when you should.
7
+
8
+ ## Example
9
+
10
+ Given
11
+
12
+ ```Ruby
13
+ class Klass
14
+ def nil_checker(argument)
15
+ if argument.nil?
16
+ puts "argument isn't nil!"
17
+ end
18
+ end
19
+ end
20
+ ```
21
+
22
+ `reek` would emit the following warning:
23
+
24
+ ```
25
+ test.rb -- 1 warning:
26
+ [3]:Klass#nil_checker performs a nil-check. (NilCheck)
27
+ ```
28
+
29
+ ## Current Support in Reek
30
+
31
+ `NilCheck` reports use of
32
+
33
+ * <code>.nil?</code> method
34
+ * <code>==</code> and <code>===</code> operators when checking vs. <code>nil</code>
35
+ * case statements that use syntax like <code>when nil</code>
36
+
37
+ ## Configuration
38
+
39
+ `Nil Check` offers the [Basic Smell Options](Basic-Smell-Options.md).
@@ -0,0 +1,53 @@
1
+ # Prima Donna Method
2
+
3
+ ## Introduction
4
+
5
+ A candidate method for the `Prima Donna Method` smell are methods whose names end with an exclamation mark.
6
+
7
+ An exclamation mark in method names means (the explanation below is taken from [here](http://dablog.rubypal.com/2007/8/15/bang-methods-or-danger-will-rubyist) ):
8
+
9
+ >>
10
+ The ! in method names that end with ! means, “This method is dangerous”—or, more precisely, this method is the “dangerous” version of an otherwise equivalent method, with the same name minus the !. “Danger” is relative; the ! doesn’t mean anything at all unless the method name it’s in corresponds to a similar but bang-less method name.
11
+ So, for example, gsub! is the dangerous version of gsub. exit! is the dangerous version of exit. flatten! is the dangerous version of flatten. And so forth.
12
+
13
+ Such a method is called `Prima Donna Method` if and only if her non-bang version does not exist and this method is reported as a smell.
14
+
15
+ ## Example
16
+
17
+ Given
18
+
19
+ ```Ruby
20
+ class C
21
+ def foo; end
22
+ def foo!; end
23
+ def bar!; end
24
+ end
25
+ ```
26
+
27
+ `reek` would report `bar!` as `prima donna method` smell but not `foo!`.
28
+
29
+ `reek` reports this smell only in a class context, not in a module context in order to allow perfectly legit code like this:
30
+
31
+
32
+ ```Ruby
33
+ class Parent
34
+ def foo; end
35
+ end
36
+
37
+ module Dangerous
38
+ def foo!; end
39
+ end
40
+
41
+ class Son < Parent
42
+ include Dangerous
43
+ end
44
+
45
+ class Daughter < Parent
46
+ end
47
+ ```
48
+
49
+ In this example, `reek` would not report the `prima donna method` smell for the method `foo` of the `Dangerous` module.
50
+
51
+ ## Configuration
52
+
53
+ `Prima Donna Method` offers the [Basic Smell Options](Basic-Smell-Options.md).
@@ -0,0 +1,137 @@
1
+ # RSpec matchers
2
+
3
+ ## Introduction
4
+
5
+ `reek` offers matchers for RSpec you can easily include into your project.
6
+
7
+ There are 3 matchers available:
8
+
9
+ - `reek`
10
+ - `reek_of`
11
+ - `reek_only_of`
12
+
13
+ ## Quickstart
14
+
15
+ Let's install the dependencies:
16
+
17
+ ```
18
+ gem install reek
19
+ gem install rspec
20
+ ```
21
+
22
+ And then use it like that in your spec file:
23
+
24
+ ```Ruby
25
+ require 'reek'
26
+ require 'reek/spec'
27
+ require 'rspec'
28
+
29
+ RSpec.describe 'Reek Integration' do
30
+ it 'works with reek' do
31
+ smelly_class = 'class C; def m; end; end'
32
+ expect(smelly_class).not_to reek
33
+ end
34
+ end
35
+ ```
36
+
37
+ Running this via
38
+
39
+ ```
40
+ rspec reek-integration-spec.rb
41
+ ```
42
+
43
+ would give you:
44
+
45
+ ```
46
+ Failures:
47
+
48
+ 1) Reek Integration works with reek
49
+ Failure/Error: expect(smelly_class).not_to reek
50
+ Expected no smells, but got:
51
+ C has no descriptive comment (IrresponsibleModule)
52
+ C has the name 'C' (UncommunicativeModuleName)
53
+ C#m has the name 'm' (UncommunicativeMethodName)
54
+ # ./reek-integration-spec.rb:8:in `block (2 levels) in <top (required)>'
55
+
56
+ Finished in 0.00284 seconds (files took 0.28815 seconds to load)
57
+ 1 example, 1 failure
58
+
59
+ Failed examples:
60
+
61
+ rspec ./reek-integration-spec.rb:6 # Reek Integration works with reek
62
+ ```
63
+
64
+ ## The matchers explained
65
+
66
+ ### `reek`
67
+
68
+ A very generic matcher that basically just tells you if something reeks, but
69
+ not after what exactly.
70
+ See the "Quickstart" example from above.
71
+
72
+ ### `reek_of`
73
+
74
+ Checks the target source code for instances of "smell category"
75
+ and returns true only if it can find one of them that matches.
76
+
77
+ Remember that this includes our "smell types" as well. So it could be the
78
+ "smell type" UtilityFunction, which is represented as a concrete class
79
+ in reek but it could also be "Duplication" which is a "smell categgory".
80
+
81
+ In theory you could pass many different types of input here:
82
+ - `:UtilityFunction`
83
+ - `"UtilityFunction"`
84
+ - `UtilityFunction` (this works in our specs because we tend to do "include
85
+ Reek:Smells")
86
+ - `Reek::Smells::UtilityFunction` (the right way if you really want to pass a
87
+ class)
88
+ - `"Duplication"` or `:Duplication` which is an abstract "smell category"
89
+
90
+ It is recommended to pass this as a symbol like `:UtilityFunction`. However we
91
+ don't enforce this.
92
+
93
+ Additionally you can be more specific and pass in "smell_details" you want to
94
+ check for as well e.g. "name" or "count" (see the examples below). The
95
+ parameters you can check for are depending on the smell you are checking for.
96
+ For instance "count" doesn't make sense everywhere whereas "name" does in most
97
+ cases. If you pass in a parameter that doesn't exist (e.g. you make a typo like
98
+ "namme") reek will raise an ArgumentError to give you a hint that you passed
99
+ something that doesn't make much sense.
100
+
101
+ So in a nutshell `reek_of` takes the following two arguments:
102
+
103
+ - `smell_category` - The "smell category" or "smell_type" we check for.
104
+ - `smells_details` - A hash containing "smell warning" parameters
105
+
106
+ **Examples**
107
+
108
+ Without smell_details:
109
+
110
+ ```Ruby
111
+ reek_of(:FeatureEnvy)
112
+ reek_of(Reek::Smells::UtilityFunction)
113
+ ```
114
+
115
+ With smell_details:
116
+
117
+ ```Ruby
118
+ reek_of(UncommunicativeParameterName, name: 'x2')
119
+ reek_of(DataClump, count: 3)
120
+ ```
121
+
122
+ **Examples from a real spec**
123
+
124
+ ```Ruby
125
+ expect(src).to reek_of(Reek::Smells::DuplicateMethodCall, name: '@other.thing')
126
+ ```
127
+
128
+ ### reek_only_of
129
+
130
+ See the documentaton for `reek_of`.
131
+
132
+ **Notable differences to reek_of:**
133
+
134
+ 1. `reek_of` doesn't mind if there are other smells of a different category.
135
+ "reek_only_of" will fail in that case.
136
+
137
+ 2. `reek_only_of` doesn't support the additional smell_details hash.
data/docs/Rake-Task.md ADDED
@@ -0,0 +1,58 @@
1
+ # Rake Task
2
+
3
+ ## Introduction
4
+
5
+ `reek` provides a Rake task that runs `reek` on a set of source files. In its most simple form you just include something like that in your Rakefile:
6
+
7
+ ```Ruby
8
+ require 'reek/rake/task'
9
+
10
+ Reek::Rake::Task.new do |t|
11
+ t.fail_on_error = false
12
+ end
13
+ ```
14
+
15
+ In its most simple form, that's it.
16
+
17
+ When you now run:
18
+
19
+ ```Bash
20
+ rake -T
21
+ ```
22
+
23
+ you should see
24
+
25
+ ```Bash
26
+ rake reek # Check for code smells
27
+ ```
28
+
29
+ ## Configuration via task
30
+
31
+ An more sophisticated rake task that would make use of all available configuration options could look like this:
32
+
33
+ ```Ruby
34
+ Reek::Rake::Task.new do |t|
35
+ t.name = 'custom_rake' # Whatever name you want. Defaults to "reek".
36
+ t.config_file = 'config/config.reek' # Defaults to nothing.
37
+ t.source_files = 'vendor/**/*.rb' # Glob pattern to match source files. Defaults to lib/**/*.rb
38
+ t.reek_opts = '-U' # Defaults to ''. You can pass all the options here in that are shown by "reek -h"
39
+ t.fail_on_error = false # Defaults to true
40
+ t.verbose = true # Defaults to false
41
+ end
42
+ ```
43
+
44
+ ## Configuration via environment variables
45
+
46
+ You can overwrite the following attributes by environment variables:
47
+
48
+ - "reek_opts" by using REEK_OPTS
49
+ - "config_file" by using REEK_CFG
50
+ - "source_files" by using REEK_SRC
51
+
52
+ An example rake call using environment variables could look like this:
53
+
54
+ ```Bash
55
+ REEK_CFG="config/custom.reek" REEK_OPTS="-s" rake reek
56
+ ```
57
+
58
+ See also: [Reek-Driven-Development](Reek-Driven-Development.md)
@@ -0,0 +1,37 @@
1
+ # Reek Driven Development
2
+
3
+ ## rake
4
+
5
+ One way to drive quality into your code from the very beginning of a project is to run `reek` as a part of your testing process. For example, you could do that by adding a [Rake Task](Rake-Task.md) to your rakefile, which will make it easy to run `reek` on all your source files whenever you need to.
6
+
7
+ ```Ruby
8
+ require 'reek/rake/task'
9
+
10
+ Reek::Rake::Task.new do |t|
11
+ t.fail_on_error = true
12
+ t.verbose = false
13
+ t.source_files = 'lib/**/*.rb'
14
+ end
15
+ ```
16
+
17
+ Now the command `reek` will run `reek` on your source code (and in this case, it fails if it finds any smells). For more detailed information about `reek`'s integration with Rake, see [Rake Task](Rake-Task.md) in this wiki.
18
+
19
+ ## reek/spec
20
+
21
+ But there's another way; a much more effective "Reek-driven" approach: add `reek` expectations directly into your Rspec specs. Here's an example taken directly from `reek`'s own source code:
22
+
23
+ ```Ruby
24
+ it 'contains no code smells' do
25
+ Dir['lib/**/*.rb'].should_not reek
26
+ end
27
+ ```
28
+
29
+ By requiring "reek/spec":http://reek.rubyforge.org/rdoc/classes/Reek/Spec.html you gain access to the `reek` matcher, which returns true if and only if `reek` finds smells in your code. And if the test fails, the matcher produces an error message that includes details of all the smells it found.
30
+
31
+ ## assert
32
+
33
+ If you're not yet into BDD with Rspec, you can still gain the benefits of Reek-driven development using assertions:
34
+
35
+ ```Ruby
36
+ assert !Dir['lib/**/*.rb'].to_source.smelly?
37
+ ```