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,158 @@
1
+ Given(/^the smelly file 'demo.rb' from the example in the README$/) do
2
+ contents = <<-EOS.strip_heredoc
3
+ class Dirty
4
+ # This method smells of :reek:NestedIterators but ignores them
5
+ def awful(x, y, offset = 0, log = false)
6
+ puts @screen.title
7
+ @screen = widgets.map {|w| w.each {|key| key += 3 * x}}
8
+ puts @screen.contents
9
+ end
10
+ end
11
+ EOS
12
+ write_file('demo.rb', contents)
13
+ end
14
+
15
+ Given(/^a smelly file with inline masking called 'inline.rb'$/) do
16
+ write_file 'inline.rb', <<-EOS.strip_heredoc
17
+ # smells of :reek:NestedIterators but ignores them
18
+ class Dirty
19
+ def a
20
+ puts @s.title
21
+ @s = foo.map {|x| x.each {|key| key += 3}}
22
+ puts @s.title
23
+ end
24
+ end
25
+ EOS
26
+ end
27
+
28
+ Given(/^the "(.*?)" sample file exists$/) do |file_name|
29
+ full_path = File.expand_path file_name, 'spec/samples'
30
+ in_current_dir { FileUtils.cp full_path, file_name }
31
+ end
32
+
33
+ Given(/^a directory called 'clean_files' containing some clean files$/) do
34
+ contents = <<-EOS.strip_heredoc
35
+ # clean class for testing purposes
36
+ class Clean
37
+ def assign
38
+ puts @sub.title
39
+ @sub.map {|para| para.name }
40
+ end
41
+ end
42
+ EOS
43
+ write_file 'clean_files/clean_one.rb', contents
44
+ write_file 'clean_files/clean_two.rb', contents
45
+ write_file 'clean_files/clean_three.rb', contents
46
+ end
47
+
48
+ Given(/^a directory called 'smelly' containing two smelly files$/) do
49
+ write_file('smelly/dirty_one.rb', <<-EOS.strip_heredoc)
50
+ class Dirty
51
+ def a; end
52
+ end
53
+ EOS
54
+ write_file('smelly/dirty_two.rb', <<-EOS.strip_heredoc)
55
+ class Dirty
56
+ def a; end
57
+ def b; end
58
+ end
59
+ EOS
60
+ end
61
+
62
+ Given(/^a smelly file called 'smelly.rb'( in a subdirectory)?$/) do |in_subdir|
63
+ file_name = in_subdir ? 'subdir/smelly.rb' : 'smelly.rb'
64
+ write_file file_name, <<-EOS.strip_heredoc
65
+ # smelly class for testing purposes
66
+ class Smelly
67
+ def m
68
+ puts @foo.bar
69
+ puts @foo.bar
70
+ end
71
+ end
72
+ EOS
73
+ end
74
+
75
+ Given(/^a file with smelly variable names called 'camel_case.rb'$/) do
76
+ write_file('camel_case.rb', <<-EOS.strip_heredoc)
77
+ # Class containing camelCase variable which would normally smell
78
+ class CamelCase
79
+ def initialize
80
+ # These next two would normally smell if it weren't for overridden config values
81
+ camelCaseVariable = []
82
+ anotherOne = 1
83
+
84
+ # this next one should still smell
85
+ x1 = 0
86
+
87
+ # this next one should not smell
88
+ should_not_smell = true
89
+ end
90
+ end
91
+ EOS
92
+ end
93
+
94
+ Given(/^an empty configuration file called 'empty.reek'$/) do
95
+ write_file('empty.reek', '')
96
+ end
97
+
98
+ Given(/^a corrupt configuration file called 'corrupt.reek'$/) do
99
+ write_file('corrupt.reek', 'This is not a configuration file')
100
+ end
101
+
102
+ Given(/^a masking configuration file called 'config.reek'$/) do
103
+ write_file('config.reek', <<-EOS.strip_heredoc)
104
+ ---
105
+ DuplicateMethodCall:
106
+ enabled: false
107
+ UncommunicativeMethodName:
108
+ enabled: false
109
+ EOS
110
+ end
111
+
112
+ Given(/^a configuration file masking some duplication smells called 'config.reek'$/) do
113
+ write_file('config.reek', <<-EOS.strip_heredoc)
114
+ ---
115
+ DuplicateMethodCall:
116
+ allow_calls:
117
+ - puts\\(@foo.bar\\)
118
+ EOS
119
+ end
120
+
121
+ Given(/^a configuration file allowing camel case variables$/) do
122
+ write_file('config.reek', <<-EOS.strip_heredoc)
123
+ ---
124
+ UncommunicativeVariableName:
125
+ enabled: true
126
+ reject:
127
+ - !ruby/regexp /^.$/
128
+ - !ruby/regexp /[0-9]$/
129
+ EOS
130
+ end
131
+
132
+ When(/^I run "reek (.*?)" in the subdirectory$/) do |args|
133
+ cd 'subdir'
134
+ reek(args)
135
+ end
136
+
137
+ Given(/^a masking configuration file in the HOME directory$/) do
138
+ set_env('HOME', File.expand_path(File.join(current_dir, 'home')))
139
+ write_file('home/config.reek', <<-EOS.strip_heredoc)
140
+ ---
141
+ DuplicateMethodCall:
142
+ enabled: false
143
+ UncommunicativeMethodName:
144
+ enabled: false
145
+ EOS
146
+ end
147
+
148
+ Given(/^an enabling configuration file in the subdirectory$/) do
149
+ write_file('subdir/config.reek', <<-EOS.strip_heredoc)
150
+ ---
151
+ IrresponsibleModule:
152
+ enabled: true
153
+ UncommunicativeModuleName:
154
+ enabled: true
155
+ UncommunicativeMethodName:
156
+ enabled: true
157
+ EOS
158
+ end
@@ -1,8 +1,6 @@
1
- require 'English'
2
- require 'tempfile'
3
- require 'fileutils'
4
- require 'open3'
5
- require 'reek/cli/application'
1
+ require_relative '../../lib/reek/cli/application'
2
+ require 'aruba/cucumber'
3
+ require 'active_support/core_ext/string/strip'
6
4
 
7
5
  begin
8
6
  require 'pry-byebug'
@@ -13,39 +11,30 @@ end
13
11
  # Provides runner methods used in the cucumber steps.
14
12
  #
15
13
  class ReekWorld
16
- def run(cmd)
17
- stderr_file = Tempfile.new('reek-world')
18
- stderr_file.close
19
- @last_stdout = `#{cmd} 2> #{stderr_file.path}`
20
- @last_exit_status = $CHILD_STATUS.exitstatus
21
- @last_stderr = IO.read(stderr_file.path)
22
- end
23
-
24
14
  def reek(args)
25
- run("ruby -Ilib bin/reek --no-color #{args}")
15
+ run_simple("reek --no-color #{args}", false)
26
16
  end
27
17
 
28
18
  def reek_with_pipe(stdin, args)
29
- run("echo \"#{stdin}\" | ruby -Ilib bin/reek --no-color #{args}")
19
+ run_interactive("reek --no-color #{args}")
20
+ type(stdin)
21
+ close_input
30
22
  end
31
23
 
32
24
  def rake(name, task_def)
33
- header = <<EOS
34
- $:.unshift('lib')
35
- require 'reek/rake/task'
25
+ header = <<-EOS.strip_heredoc
26
+ require 'reek/rake/task'
36
27
 
37
- EOS
38
- rakefile = Tempfile.new('rake_task', '.')
39
- rakefile.puts(header + task_def)
40
- rakefile.close
41
- run("rake -f #{rakefile.path} #{name}")
42
- lines = @last_stdout.split("\n")
43
- if lines.length > 0 && lines[0] =~ /^\(/
44
- @last_stdout = lines[1..-1].join("\n")
45
- end
28
+ EOS
29
+ write_file 'Rakefile', header + task_def
30
+ run_simple("rake #{name}", false)
46
31
  end
47
32
  end
48
33
 
49
34
  World do
50
35
  ReekWorld.new
51
36
  end
37
+
38
+ Before do
39
+ @aruba_timeout_seconds = 30
40
+ end
@@ -1,23 +1,22 @@
1
- require 'reek/source/ast_node'
2
- require 'reek/source/sexp_node'
3
- require 'reek/source/sexp_extensions'
1
+ require_relative 'node'
2
+ require_relative 'sexp_extensions'
4
3
 
5
4
  module Reek
6
- module Source
7
- # Maps AST node types to sublasses of AstNode extended with the relevant
5
+ module AST
6
+ # Maps AST node types to sublasses of ASTNode extended with the relevant
8
7
  # utility modules.
9
- class AstNodeClassMap
8
+ #
9
+ # @api private
10
+ class ASTNodeClassMap
10
11
  def initialize
11
12
  @klass_map = {}
12
13
  end
13
14
 
14
15
  def klass_for(type)
15
- @klass_map[type] ||=
16
- begin
17
- klass = Class.new(AstNode)
18
- klass.send :include, extension_map[type] if extension_map[type]
19
- klass.send :include, SexpNode
20
- end
16
+ @klass_map[type] ||= Class.new(Node).tap do |klass|
17
+ extension = extension_map[type]
18
+ klass.send :include, extension if extension
19
+ end
21
20
  end
22
21
 
23
22
  def extension_map
@@ -0,0 +1,132 @@
1
+ require 'parser'
2
+
3
+ module Reek
4
+ # @api private
5
+ module AST
6
+ # Base class for AST nodes extended with utility methods. Contains some
7
+ # methods to ease the transition from Sexp to AST::Node.
8
+ #
9
+ # @api private
10
+ class Node < ::Parser::AST::Node
11
+ attr_reader :parent
12
+
13
+ def initialize(type, children = [], options = {})
14
+ @comments = options.fetch(:comments, [])
15
+ @parent = options.fetch(:parent, nil)
16
+ super
17
+ end
18
+
19
+ def full_comment
20
+ @comments.map(&:text).join("\n")
21
+ end
22
+
23
+ def leading_comment
24
+ line = location.line
25
+ comment_lines = @comments.select do |comment|
26
+ comment.location.line < line
27
+ end
28
+ comment_lines.map(&:text).join("\n")
29
+ end
30
+
31
+ # @deprecated
32
+ def [](index)
33
+ elements[index]
34
+ end
35
+
36
+ def line
37
+ loc.line
38
+ end
39
+
40
+ # @deprecated
41
+ def first
42
+ type
43
+ end
44
+
45
+ #
46
+ # Carries out a depth-first traversal of this syntax tree, yielding
47
+ # every Sexp of type `target_type`. The traversal ignores any node
48
+ # whose type is listed in the Array `ignoring`.
49
+ # Takes a block as well.
50
+ #
51
+ # target_type - the type to look for, e.g. :send, :block
52
+ # ignoring - types to ignore, e.g. [:casgn, :class, :module]
53
+ # blk - block to execute for every hit
54
+ #
55
+ # Examples:
56
+ # context.each_node(:send, [:mlhs]) do |call_node| .... end
57
+ # context.each_node(:lvar).any? { |it| it.var_name == 'something' }
58
+ #
59
+ # Returns an array with all matching nodes.
60
+ def each_node(target_type, ignoring = [], &blk)
61
+ if block_given?
62
+ look_for_type(target_type, ignoring, &blk)
63
+ else
64
+ result = []
65
+ look_for_type(target_type, ignoring) { |exp| result << exp }
66
+ result
67
+ end
68
+ end
69
+
70
+ #
71
+ # Carries out a depth-first traversal of this syntax tree, yielding
72
+ # every Sexp of type `target_type`. The traversal ignores any node
73
+ # whose type is listed in the Array `ignoring`, including the top node.
74
+ # Takes a block as well.
75
+ #
76
+ # target_types - the types to look for, e.g. [:send, :block]
77
+ # ignoring - types to ignore, e.g. [:casgn, :class, :module]
78
+ # blk - block to execute for every hit
79
+ #
80
+ # Examples:
81
+ # exp.find_nodes([:block]).flat_map do |elem| ... end
82
+ #
83
+ # Returns an array with all matching nodes.
84
+ def find_nodes(target_types, ignoring = [])
85
+ result = []
86
+ look_for_types(target_types, ignoring) { |exp| result << exp }
87
+ result
88
+ end
89
+
90
+ def contains_nested_node?(target_type)
91
+ look_for_type(target_type) { |_elem| return true }
92
+ false
93
+ end
94
+
95
+ def format_to_ruby
96
+ SexpFormatter.format(self)
97
+ end
98
+
99
+ protected
100
+
101
+ # See ".each_node" for documentation.
102
+ def look_for_type(target_type, ignoring = [], &blk)
103
+ each_sexp do |elem|
104
+ elem.look_for_type(target_type, ignoring, &blk) unless ignoring.include?(elem.type)
105
+ end
106
+ blk.call(self) if type == target_type
107
+ end
108
+
109
+ # See ".find_nodes" for documentation.
110
+ def look_for_types(target_types, ignoring = [], &blk)
111
+ return if ignoring.include?(type)
112
+ if target_types.include? type
113
+ blk.call(self)
114
+ else
115
+ each_sexp do |elem|
116
+ elem.look_for_types(target_types, ignoring, &blk)
117
+ end
118
+ end
119
+ end
120
+
121
+ private
122
+
123
+ def each_sexp
124
+ children.each { |elem| yield elem if elem.is_a? ::Parser::AST::Node }
125
+ end
126
+
127
+ def elements
128
+ [type, *children]
129
+ end
130
+ end
131
+ end
132
+ end
@@ -1,8 +1,9 @@
1
1
  module Reek
2
- module Core
2
+ module AST
3
3
  #
4
4
  # Manages and counts the references out of a method to other objects.
5
5
  #
6
+ # @api private
6
7
  class ObjectRefs # :nodoc:
7
8
  def initialize
8
9
  @refs = Hash.new(0)
@@ -0,0 +1,32 @@
1
+ module Reek
2
+ module AST
3
+ #
4
+ # Locates references to the current object within a portion
5
+ # of an abstract syntax tree.
6
+ #
7
+ # @api private
8
+ class ReferenceCollector
9
+ STOP_NODES = [:class, :module, :def, :defs]
10
+
11
+ def initialize(ast)
12
+ @ast = ast
13
+ end
14
+
15
+ def num_refs_to_self
16
+ (explicit_self_calls + implicit_self_calls).size
17
+ end
18
+
19
+ private
20
+
21
+ def explicit_self_calls
22
+ [:self, :zsuper, :ivar, :ivasgn].flat_map do |node_type|
23
+ @ast.each_node(node_type, STOP_NODES)
24
+ end
25
+ end
26
+
27
+ def implicit_self_calls
28
+ @ast.each_node(:send, STOP_NODES).reject(&:receiver)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -1,11 +1,12 @@
1
- require 'reek/source/sexp_node'
1
+ require_relative 'reference_collector'
2
2
 
3
3
  module Reek
4
- module Source
4
+ module AST
5
5
  #
6
- # Extension modules providing utility methods to AstNode objects, depending
6
+ # Extension modules providing utility methods to ASTNode objects, depending
7
7
  # on their type.
8
8
  #
9
+ # @api private
9
10
  module SexpExtensions
10
11
  # Base module for utility methods for argument nodes.
11
12
  module ArgNodeBase
@@ -37,6 +38,10 @@ module Reek
37
38
  def anonymous_splat?
38
39
  false
39
40
  end
41
+
42
+ def components
43
+ [self]
44
+ end
40
45
  end
41
46
 
42
47
  # Utility methods for :arg nodes.
@@ -96,6 +101,24 @@ module Reek
96
101
  include ArgNodeBase
97
102
  end
98
103
 
104
+ # Base module for utility methods for nodes that can contain argument
105
+ # nodes nested through :mlhs nodes.
106
+ module NestedAssignables
107
+ def components
108
+ children.flat_map(&:components)
109
+ end
110
+ end
111
+
112
+ # Utility methods for :args nodes.
113
+ module ArgsNode
114
+ include NestedAssignables
115
+ end
116
+
117
+ # Utility methods for :mlhs nodes.
118
+ module MlhsNode
119
+ include NestedAssignables
120
+ end
121
+
99
122
  # Base module for utility methods for :and and :or nodes.
100
123
  module LogicOperatorBase
101
124
  def condition() self[1] end
@@ -202,7 +225,7 @@ module Reek
202
225
  end
203
226
 
204
227
  def parameters
205
- argslist.children
228
+ argslist.components
206
229
  end
207
230
 
208
231
  def parameter_names
@@ -239,6 +262,15 @@ module Reek
239
262
  prefix = outer == '' ? '' : "#{outer}#"
240
263
  "#{prefix}#{name}"
241
264
  end
265
+
266
+ def depends_on_instance?
267
+ ReferenceCollector.new(self).num_refs_to_self > 0
268
+ end
269
+
270
+ def singleton_method?
271
+ # This catches the case where methods are defined within the "class << self" syntax.
272
+ parent.type == :sclass if parent
273
+ end
242
274
  end
243
275
 
244
276
  # Utility methods for :defs nodes.
@@ -254,7 +286,11 @@ module Reek
254
286
  include MethodNodeBase
255
287
  def full_name(outer)
256
288
  prefix = outer == '' ? '' : "#{outer}#"
257
- "#{prefix}#{SexpNode.format(receiver)}.#{name}"
289
+ "#{prefix}#{SexpFormatter.format(receiver)}.#{name}"
290
+ end
291
+
292
+ def depends_on_instance?
293
+ false
258
294
  end
259
295
  end
260
296
 
@@ -296,7 +332,7 @@ module Reek
296
332
  def name() self[1] end
297
333
 
298
334
  def simple_name
299
- name.is_a?(AST::Node) ? name.simple_name : name
335
+ name.is_a?(::Parser::AST::Node) ? name.simple_name : name
300
336
  end
301
337
 
302
338
  def full_name(outer)
@@ -305,7 +341,7 @@ module Reek
305
341
  end
306
342
 
307
343
  def text_name
308
- SexpNode.format(name)
344
+ SexpFormatter.format(name)
309
345
  end
310
346
  end
311
347
 
@@ -0,0 +1,31 @@
1
+ require_relative '../cli/silencer'
2
+ Reek::CLI::Silencer.silently do
3
+ require 'unparser'
4
+ end
5
+
6
+ module Reek
7
+ module AST
8
+ #
9
+ # Formats snippets of syntax tree back into Ruby source code.
10
+ #
11
+ # @api private
12
+ class SexpFormatter
13
+ # Formats the given sexp.
14
+ #
15
+ # @param [AST::Node, #to_s] sexp - The expression to format
16
+ #
17
+ # @return [String] a formatted string representation.
18
+ #
19
+ # :reek:DuplicateMethodCall { max_calls: 2 } is ok for lines.first
20
+ def self.format(sexp)
21
+ return sexp.to_s unless sexp.is_a? ::Parser::AST::Node
22
+ lines = Unparser.unparse(sexp).split "\n"
23
+ case lines.length
24
+ when 1 then lines.first
25
+ when 2 then lines.join('; ')
26
+ else [lines.first, lines.last].join(' ... ')
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,14 +1,15 @@
1
- require 'reek/cli/options'
2
- require 'reek/cli/reek_command'
3
- require 'reek/configuration/app_configuration'
1
+ require_relative 'options'
2
+ require_relative 'reek_command'
3
+ require_relative '../configuration/app_configuration'
4
4
 
5
5
  module Reek
6
- module Cli
6
+ module CLI
7
7
  #
8
8
  # Represents an instance of a Reek application.
9
9
  # This is the entry point for all invocations of Reek from the
10
10
  # command line.
11
11
  #
12
+ # @api private
12
13
  class Application
13
14
  STATUS_SUCCESS = 0
14
15
  STATUS_ERROR = 1
@@ -1,8 +1,9 @@
1
1
  module Reek
2
- module Cli
2
+ module CLI
3
3
  #
4
4
  # Base class for all commands
5
5
  #
6
+ # @api private
6
7
  class Command
7
8
  attr_reader :options
8
9
 
@@ -1,10 +1,11 @@
1
- require 'reek/source'
1
+ require_relative '../source/source_locator'
2
2
 
3
3
  module Reek
4
- module Cli
4
+ module CLI
5
5
  #
6
6
  # CLI Input utility
7
7
  #
8
+ # @api private
8
9
  module Input
9
10
  def sources
10
11
  if no_source_files_given?
@@ -31,15 +32,15 @@ module Reek
31
32
  end
32
33
 
33
34
  def working_directory_as_source
34
- Source::SourceLocator.new(['.']).all_sources
35
+ Source::SourceLocator.new(['.']).sources
35
36
  end
36
37
 
37
38
  def sources_from_argv
38
- Source::SourceLocator.new(@argv).all_sources
39
+ Source::SourceLocator.new(@argv).sources
39
40
  end
40
41
 
41
42
  def source_from_pipe
42
- [$stdin.to_reek_source('$stdin')]
43
+ [$stdin]
43
44
  end
44
45
  end
45
46
  end
@@ -1,16 +1,17 @@
1
1
  require 'forwardable'
2
- require 'reek/cli/input'
3
- require 'reek/cli/report/report'
4
- require 'reek/cli/report/formatter'
5
- require 'reek/cli/report/heading_formatter'
2
+ require_relative 'input'
3
+ require_relative '../report/report'
4
+ require_relative '../report/formatter'
5
+ require_relative '../report/heading_formatter'
6
6
 
7
7
  module Reek
8
- module Cli
8
+ module CLI
9
9
  #
10
10
  # Interprets the options set from the command line
11
11
  #
12
+ # @api private
12
13
  class OptionInterpreter
13
- include Cli::Input
14
+ include Input
14
15
 
15
16
  extend Forwardable
16
17
 
@@ -30,14 +31,17 @@ module Reek
30
31
  heading_formatter: heading_formatter)
31
32
  end
32
33
 
34
+ # TODO: Move report type mapping into Report
33
35
  def report_class
34
36
  case @options.report_format
35
37
  when :yaml
36
- Report::YamlReport
38
+ Report::YAMLReport
37
39
  when :json
38
- Report::JsonReport
40
+ Report::JSONReport
39
41
  when :html
40
- Report::HtmlReport
42
+ Report::HTMLReport
43
+ when :xml
44
+ Report::XMLReport
41
45
  else # :text
42
46
  Report::TextReport
43
47
  end