reek 4.6.0 → 5.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 (294) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +17 -12
  3. data/.rubocop.yml +79 -26
  4. data/.simplecov +1 -0
  5. data/.travis.yml +3 -9
  6. data/.yardopts +1 -1
  7. data/CHANGELOG.md +76 -0
  8. data/CONTRIBUTING.md +1 -1
  9. data/Gemfile +5 -5
  10. data/README.md +138 -107
  11. data/Rakefile +16 -3
  12. data/bin/code_climate_reek +1 -0
  13. data/bin/reek +2 -3
  14. data/docs/API.md +2 -9
  15. data/docs/Basic-Smell-Options.md +51 -11
  16. data/docs/Code-Smells.md +1 -1
  17. data/docs/Command-Line-Options.md +14 -4
  18. data/docs/Duplicate-Method-Call.md +49 -1
  19. data/docs/Feature-Envy.md +44 -0
  20. data/docs/How-To-Write-New-Detectors.md +7 -7
  21. data/docs/Instance-Variable-Assumption.md +1 -1
  22. data/docs/{Prima-Donna-Method.md → Missing-Safe-Method.md} +11 -9
  23. data/docs/Rake-Task.md +1 -1
  24. data/docs/Reek-4-to-Reek-5-migration.md +193 -0
  25. data/docs/Reek-Driven-Development.md +1 -1
  26. data/docs/Uncommunicative-Method-Name.md +45 -6
  27. data/docs/Uncommunicative-Module-Name.md +49 -7
  28. data/docs/Uncommunicative-Parameter-Name.md +43 -5
  29. data/docs/Uncommunicative-Variable-Name.md +73 -2
  30. data/docs/Unused-Private-Method.md +3 -3
  31. data/docs/defaults.reek.yml +129 -0
  32. data/docs/yard_plugin.rb +1 -0
  33. data/features/command_line_interface/basic_usage.feature +2 -2
  34. data/features/command_line_interface/options.feature +46 -4
  35. data/features/command_line_interface/show_progress.feature +4 -4
  36. data/features/command_line_interface/smell_selection.feature +1 -1
  37. data/features/command_line_interface/smells_count.feature +6 -6
  38. data/features/command_line_interface/stdin.feature +31 -5
  39. data/features/configuration_files/accept_setting.feature +45 -28
  40. data/features/configuration_files/directory_specific_directives.feature +80 -75
  41. data/features/configuration_files/exclude_directives.feature +11 -10
  42. data/features/configuration_files/exclude_paths_directives.feature +4 -4
  43. data/features/configuration_files/masking_smells.feature +38 -9
  44. data/features/configuration_files/mix_accept_reject_setting.feature +31 -28
  45. data/features/configuration_files/reject_setting.feature +52 -41
  46. data/features/configuration_files/schema_validation.feature +59 -0
  47. data/features/configuration_files/unused_private_method.feature +18 -16
  48. data/features/configuration_loading.feature +53 -10
  49. data/features/configuration_via_source_comments/erroneous_source_comments.feature +3 -3
  50. data/features/configuration_via_source_comments/well_formed_source_comments.feature +2 -2
  51. data/features/locales.feature +32 -0
  52. data/features/rake_task/rake_task.feature +58 -18
  53. data/features/reports/codeclimate.feature +59 -0
  54. data/features/reports/json.feature +3 -3
  55. data/features/reports/reports.feature +34 -34
  56. data/features/reports/yaml.feature +3 -3
  57. data/features/rspec_matcher.feature +40 -0
  58. data/features/samples.feature +287 -287
  59. data/features/step_definitions/reek_steps.rb +14 -2
  60. data/features/step_definitions/sample_file_steps.rb +9 -4
  61. data/features/support/env.rb +2 -11
  62. data/features/todo_list.feature +26 -23
  63. data/lib/reek/ast/ast_node_class_map.rb +1 -0
  64. data/lib/reek/ast/builder.rb +16 -0
  65. data/lib/reek/ast/node.rb +50 -58
  66. data/lib/reek/ast/object_refs.rb +2 -1
  67. data/lib/reek/ast/reference_collector.rb +3 -4
  68. data/lib/reek/ast/sexp_extensions/arguments.rb +1 -0
  69. data/lib/reek/ast/sexp_extensions/attribute_assignments.rb +1 -0
  70. data/lib/reek/ast/sexp_extensions/begin.rb +17 -0
  71. data/lib/reek/ast/sexp_extensions/block.rb +1 -0
  72. data/lib/reek/ast/sexp_extensions/case.rb +2 -1
  73. data/lib/reek/ast/sexp_extensions/constant.rb +1 -0
  74. data/lib/reek/ast/sexp_extensions/if.rb +9 -1
  75. data/lib/reek/ast/sexp_extensions/lambda.rb +1 -0
  76. data/lib/reek/ast/sexp_extensions/literal.rb +1 -0
  77. data/lib/reek/ast/sexp_extensions/logical_operators.rb +2 -1
  78. data/lib/reek/ast/sexp_extensions/methods.rb +5 -6
  79. data/lib/reek/ast/sexp_extensions/module.rb +55 -8
  80. data/lib/reek/ast/sexp_extensions/nested_assignables.rb +1 -0
  81. data/lib/reek/ast/sexp_extensions/self.rb +1 -0
  82. data/lib/reek/ast/sexp_extensions/send.rb +1 -4
  83. data/lib/reek/ast/sexp_extensions/super.rb +1 -0
  84. data/lib/reek/ast/sexp_extensions/symbols.rb +1 -0
  85. data/lib/reek/ast/sexp_extensions/variables.rb +1 -0
  86. data/lib/reek/ast/sexp_extensions/when.rb +1 -0
  87. data/lib/reek/ast/sexp_extensions/yield.rb +1 -0
  88. data/lib/reek/ast/sexp_extensions.rb +2 -0
  89. data/lib/reek/cli/application.rb +5 -3
  90. data/lib/reek/cli/command/base_command.rb +1 -0
  91. data/lib/reek/cli/command/report_command.rb +2 -2
  92. data/lib/reek/cli/command/todo_list_command.rb +9 -8
  93. data/lib/reek/cli/options.rb +32 -16
  94. data/lib/reek/cli/silencer.rb +15 -3
  95. data/lib/reek/cli/status.rb +1 -0
  96. data/lib/reek/code_comment.rb +14 -16
  97. data/lib/reek/configuration/app_configuration.rb +34 -28
  98. data/lib/reek/configuration/configuration_converter.rb +110 -0
  99. data/lib/reek/configuration/configuration_file_finder.rb +17 -41
  100. data/lib/reek/configuration/configuration_validator.rb +13 -23
  101. data/lib/reek/configuration/default_directive.rb +18 -3
  102. data/lib/reek/configuration/directory_directives.rb +18 -11
  103. data/lib/reek/configuration/excluded_paths.rb +2 -1
  104. data/lib/reek/configuration/rake_task_converter.rb +29 -0
  105. data/lib/reek/configuration/schema.yml +210 -0
  106. data/lib/reek/configuration/schema_validator.rb +38 -0
  107. data/lib/reek/context/attribute_context.rb +4 -3
  108. data/lib/reek/context/class_context.rb +1 -0
  109. data/lib/reek/context/code_context.rb +49 -44
  110. data/lib/reek/context/ghost_context.rb +1 -2
  111. data/lib/reek/context/method_context.rb +26 -18
  112. data/lib/reek/context/module_context.rb +11 -11
  113. data/lib/reek/context/root_context.rb +1 -4
  114. data/lib/reek/context/send_context.rb +3 -2
  115. data/lib/reek/context/singleton_attribute_context.rb +1 -0
  116. data/lib/reek/context/singleton_method_context.rb +1 -0
  117. data/lib/reek/context/statement_counter.rb +1 -0
  118. data/lib/reek/context/visibility_tracker.rb +1 -0
  119. data/lib/reek/context_builder.rb +44 -44
  120. data/lib/reek/detector_repository.rb +12 -11
  121. data/lib/reek/documentation_link.rb +28 -0
  122. data/lib/reek/errors/bad_detector_configuration_key_in_comment_error.rb +15 -13
  123. data/lib/reek/errors/bad_detector_in_comment_error.rb +13 -11
  124. data/lib/reek/errors/base_error.rb +3 -0
  125. data/lib/reek/errors/config_file_error.rb +11 -0
  126. data/lib/reek/errors/encoding_error.rb +43 -0
  127. data/lib/reek/errors/garbage_detector_configuration_in_comment_error.rb +14 -12
  128. data/lib/reek/errors/incomprehensible_source_error.rb +23 -24
  129. data/lib/reek/errors/syntax_error.rb +41 -0
  130. data/lib/reek/examiner.rb +24 -22
  131. data/lib/reek/logging_error_handler.rb +8 -5
  132. data/lib/reek/rake/task.rb +8 -5
  133. data/lib/reek/report/base_report.rb +9 -12
  134. data/lib/reek/report/code_climate/code_climate_configuration.rb +2 -1
  135. data/lib/reek/report/code_climate/code_climate_configuration.yml +6 -6
  136. data/lib/reek/report/code_climate/code_climate_fingerprint.rb +1 -0
  137. data/lib/reek/report/code_climate/code_climate_formatter.rb +1 -0
  138. data/lib/reek/report/code_climate/code_climate_report.rb +1 -0
  139. data/lib/reek/report/code_climate.rb +1 -0
  140. data/lib/reek/report/documentation_link_warning_formatter.rb +17 -0
  141. data/lib/reek/report/heading_formatter.rb +54 -0
  142. data/lib/reek/report/html_report.rb +1 -0
  143. data/lib/reek/report/json_report.rb +2 -1
  144. data/lib/reek/report/location_formatter.rb +40 -0
  145. data/lib/reek/report/progress_formatter.rb +79 -0
  146. data/lib/reek/report/simple_warning_formatter.rb +34 -0
  147. data/lib/reek/report/text_report.rb +2 -2
  148. data/lib/reek/report/xml_report.rb +4 -3
  149. data/lib/reek/report/yaml_report.rb +2 -1
  150. data/lib/reek/report.rb +16 -10
  151. data/lib/reek/smell_configuration.rb +3 -2
  152. data/lib/reek/smell_detectors/attribute.rb +6 -11
  153. data/lib/reek/smell_detectors/base_detector.rb +31 -26
  154. data/lib/reek/smell_detectors/boolean_parameter.rb +4 -5
  155. data/lib/reek/smell_detectors/class_variable.rb +6 -14
  156. data/lib/reek/smell_detectors/control_parameter.rb +19 -33
  157. data/lib/reek/smell_detectors/data_clump.rb +16 -9
  158. data/lib/reek/smell_detectors/duplicate_method_call.rb +24 -17
  159. data/lib/reek/smell_detectors/feature_envy.rb +10 -7
  160. data/lib/reek/smell_detectors/instance_variable_assumption.rb +15 -23
  161. data/lib/reek/smell_detectors/irresponsible_module.rb +6 -12
  162. data/lib/reek/smell_detectors/long_parameter_list.rb +11 -7
  163. data/lib/reek/smell_detectors/long_yield_list.rb +11 -7
  164. data/lib/reek/smell_detectors/manual_dispatch.rb +5 -5
  165. data/lib/reek/smell_detectors/{prima_donna_method.rb → missing_safe_method.rb} +21 -20
  166. data/lib/reek/smell_detectors/module_initialize.rb +4 -5
  167. data/lib/reek/smell_detectors/nested_iterators.rb +17 -24
  168. data/lib/reek/smell_detectors/nil_check.rb +9 -15
  169. data/lib/reek/smell_detectors/repeated_conditional.rb +14 -11
  170. data/lib/reek/smell_detectors/subclassed_from_core_class.rb +8 -8
  171. data/lib/reek/smell_detectors/too_many_constants.rb +11 -9
  172. data/lib/reek/smell_detectors/too_many_instance_variables.rb +11 -6
  173. data/lib/reek/smell_detectors/too_many_methods.rb +12 -7
  174. data/lib/reek/smell_detectors/too_many_statements.rb +11 -6
  175. data/lib/reek/smell_detectors/uncommunicative_method_name.rb +11 -11
  176. data/lib/reek/smell_detectors/uncommunicative_module_name.rb +15 -18
  177. data/lib/reek/smell_detectors/uncommunicative_parameter_name.rb +18 -22
  178. data/lib/reek/smell_detectors/uncommunicative_variable_name.rb +27 -27
  179. data/lib/reek/smell_detectors/unused_parameters.rb +5 -6
  180. data/lib/reek/smell_detectors/unused_private_method.rb +14 -20
  181. data/lib/reek/smell_detectors/utility_function.rb +13 -16
  182. data/lib/reek/smell_detectors.rb +2 -1
  183. data/lib/reek/smell_warning.rb +16 -8
  184. data/lib/reek/source/source_code.rb +65 -46
  185. data/lib/reek/source/source_locator.rb +9 -8
  186. data/lib/reek/spec/should_reek.rb +3 -2
  187. data/lib/reek/spec/should_reek_of.rb +13 -26
  188. data/lib/reek/spec/should_reek_only_of.rb +5 -4
  189. data/lib/reek/spec/smell_matcher.rb +2 -1
  190. data/lib/reek/spec.rb +7 -6
  191. data/lib/reek/tree_dresser.rb +9 -8
  192. data/lib/reek/version.rb +2 -1
  193. data/lib/reek.rb +1 -0
  194. data/reek.gemspec +5 -6
  195. data/samples/checkstyle.xml +1 -1
  196. data/samples/configuration/accepts_rejects_and_excludes_for_detectors.reek.yml +29 -0
  197. data/samples/configuration/accepts_rejects_and_excludes_for_directory_directives.reek.yml +30 -0
  198. data/samples/configuration/full_configuration.reek +8 -4
  199. data/samples/configuration/full_mask.reek +5 -4
  200. data/samples/configuration/partial_mask.reek +3 -2
  201. data/samples/configuration/regular_configuration/.reek.yml +4 -0
  202. data/samples/paths.rb +5 -4
  203. data/samples/source_with_hidden_directories/.hidden/hidden.rb +1 -0
  204. data/samples/source_with_hidden_directories/not_hidden.rb +1 -0
  205. data/spec/factories/factories.rb +2 -13
  206. data/spec/reek/ast/node_spec.rb +103 -10
  207. data/spec/reek/ast/reference_collector_spec.rb +1 -1
  208. data/spec/reek/ast/sexp_extensions_spec.rb +15 -34
  209. data/spec/reek/cli/application_spec.rb +52 -42
  210. data/spec/reek/cli/command/todo_list_command_spec.rb +6 -4
  211. data/spec/reek/cli/silencer_spec.rb +28 -0
  212. data/spec/reek/code_comment_spec.rb +31 -38
  213. data/spec/reek/configuration/app_configuration_spec.rb +46 -33
  214. data/spec/reek/configuration/configuration_file_finder_spec.rb +133 -51
  215. data/spec/reek/configuration/default_directive_spec.rb +1 -1
  216. data/spec/reek/configuration/directory_directives_spec.rb +6 -7
  217. data/spec/reek/configuration/excluded_paths_spec.rb +6 -6
  218. data/spec/reek/configuration/rake_task_converter_spec.rb +33 -0
  219. data/spec/reek/configuration/schema_validator_spec.rb +165 -0
  220. data/spec/reek/context/code_context_spec.rb +70 -106
  221. data/spec/reek/context/ghost_context_spec.rb +9 -9
  222. data/spec/reek/context/method_context_spec.rb +2 -2
  223. data/spec/reek/context/module_context_spec.rb +3 -3
  224. data/spec/reek/context/root_context_spec.rb +1 -1
  225. data/spec/reek/context/statement_counter_spec.rb +1 -0
  226. data/spec/reek/context_builder_spec.rb +20 -0
  227. data/spec/reek/documentation_link_spec.rb +20 -0
  228. data/spec/reek/errors/base_error_spec.rb +13 -0
  229. data/spec/reek/examiner_spec.rb +137 -29
  230. data/spec/reek/rake/task_spec.rb +25 -2
  231. data/spec/reek/report/code_climate/code_climate_fingerprint_spec.rb +82 -78
  232. data/spec/reek/report/code_climate/code_climate_formatter_spec.rb +6 -6
  233. data/spec/reek/report/code_climate/code_climate_report_spec.rb +22 -22
  234. data/spec/reek/report/json_report_spec.rb +13 -46
  235. data/spec/reek/report/{formatter/location_formatter_spec.rb → location_formatter_spec.rb} +5 -5
  236. data/spec/reek/report/{formatter/progress_formatter_spec.rb → progress_formatter_spec.rb} +4 -4
  237. data/spec/reek/report/text_report_spec.rb +4 -4
  238. data/spec/reek/report/xml_report_spec.rb +3 -3
  239. data/spec/reek/report/yaml_report_spec.rb +17 -46
  240. data/spec/reek/report_spec.rb +3 -3
  241. data/spec/reek/smell_detectors/base_detector_spec.rb +4 -5
  242. data/spec/reek/smell_detectors/boolean_parameter_spec.rb +2 -2
  243. data/spec/reek/smell_detectors/class_variable_spec.rb +26 -32
  244. data/spec/reek/smell_detectors/control_parameter_spec.rb +34 -4
  245. data/spec/reek/smell_detectors/duplicate_method_call_spec.rb +3 -3
  246. data/spec/reek/smell_detectors/feature_envy_spec.rb +47 -2
  247. data/spec/reek/smell_detectors/irresponsible_module_spec.rb +59 -21
  248. data/spec/reek/smell_detectors/{prima_donna_method_spec.rb → missing_safe_method_spec.rb} +10 -10
  249. data/spec/reek/smell_detectors/module_initialize_spec.rb +14 -0
  250. data/spec/reek/smell_detectors/nested_iterators_spec.rb +1 -1
  251. data/spec/reek/smell_detectors/too_many_constants_spec.rb +3 -3
  252. data/spec/reek/smell_detectors/too_many_instance_variables_spec.rb +1 -1
  253. data/spec/reek/smell_detectors/uncommunicative_method_name_spec.rb +6 -6
  254. data/spec/reek/smell_detectors/uncommunicative_module_name_spec.rb +6 -4
  255. data/spec/reek/smell_detectors/uncommunicative_parameter_name_spec.rb +36 -15
  256. data/spec/reek/smell_detectors/uncommunicative_variable_name_spec.rb +9 -9
  257. data/spec/reek/smell_detectors/unused_parameters_spec.rb +3 -3
  258. data/spec/reek/smell_detectors/unused_private_method_spec.rb +21 -10
  259. data/spec/reek/smell_detectors/utility_function_spec.rb +57 -5
  260. data/spec/reek/smell_warning_spec.rb +12 -8
  261. data/spec/reek/source/source_code_spec.rb +27 -38
  262. data/spec/reek/source/source_locator_spec.rb +42 -12
  263. data/spec/reek/spec/should_reek_of_spec.rb +25 -30
  264. data/spec/reek/spec/should_reek_only_of_spec.rb +2 -2
  265. data/spec/reek/spec/should_reek_spec.rb +8 -8
  266. data/spec/reek/spec/smell_matcher_spec.rb +23 -23
  267. data/spec/reek/tree_dresser_spec.rb +12 -17
  268. data/spec/spec_helper.rb +7 -17
  269. data/tasks/configuration.rake +8 -5
  270. metadata +77 -40
  271. data/defaults.reek +0 -128
  272. data/features/configuration_files/warn_about_multiple_configuration_files.feature +0 -44
  273. data/lib/reek/errors/parse_error.rb +0 -19
  274. data/lib/reek/report/formatter/heading_formatter.rb +0 -51
  275. data/lib/reek/report/formatter/location_formatter.rb +0 -41
  276. data/lib/reek/report/formatter/progress_formatter.rb +0 -80
  277. data/lib/reek/report/formatter/simple_warning_formatter.rb +0 -35
  278. data/lib/reek/report/formatter/wiki_link_warning_formatter.rb +0 -35
  279. data/lib/reek/report/formatter.rb +0 -32
  280. data/samples/configuration/non_public_modifiers_mask.reek +0 -3
  281. data/samples/smelly_with_inline_mask.rb +0 -8
  282. data/samples/smelly_with_modifiers.rb +0 -12
  283. data/samples/source_with_hidden_directories/.hidden/uncommunicative_method_name.rb +0 -5
  284. data/samples/source_with_non_ruby_files/uncommunicative_parameter_name.rb +0 -6
  285. /data/{samples/configuration/more_than_one_configuration_file/regular.reek → .reek.yml} +0 -0
  286. /data/samples/{clean.rb → clean_source/clean.rb} +0 -0
  287. /data/samples/{exceptions.reek → configuration/home/home.reek.yml} +0 -0
  288. /data/samples/configuration/{more_than_one_configuration_file/todo.reek → regular_configuration/empty_sub_directory/.gitignore} +0 -0
  289. /data/samples/{configuration/single_configuration_file/.reek → no_config_file/.keep} +0 -0
  290. /data/samples/{inline.rb → smelly_source/inline.rb} +0 -0
  291. /data/samples/{optparse.rb → smelly_source/optparse.rb} +0 -0
  292. /data/samples/{redcloth.rb → smelly_source/redcloth.rb} +0 -0
  293. /data/samples/{smelly.rb → smelly_source/smelly.rb} +0 -0
  294. /data/samples/{source_with_hidden_directories/uncommunicative_parameter_name.rb → source_with_non_ruby_files/ruby.rb} +0 -0
@@ -1,12 +1,14 @@
1
- # encoding: UTF-8
2
-
3
1
  require 'fileutils'
4
2
  require 'pathname'
5
3
  require 'tmpdir'
6
4
  require_relative '../../spec_helper'
5
+ require_lib 'reek/configuration/app_configuration'
7
6
 
8
7
  RSpec.describe Reek::Configuration::ConfigurationFileFinder do
9
8
  describe '.find' do
9
+ let(:regular_configuration_dir) { CONFIGURATION_DIR.join('regular_configuration') }
10
+ let(:regular_configuration_file) { regular_configuration_dir.join('.reek.yml') }
11
+
10
12
  it 'returns any explicitely passed path' do
11
13
  path = Pathname.new 'foo/bar'
12
14
  found = described_class.find(path: path)
@@ -15,42 +17,44 @@ RSpec.describe Reek::Configuration::ConfigurationFileFinder do
15
17
 
16
18
  it 'prefers an explicitely passed path over a file in current dir' do
17
19
  path = Pathname.new 'foo/bar'
18
- found = described_class.find(path: path, current: SAMPLES_PATH)
20
+ found = described_class.find(path: path, current: regular_configuration_dir)
19
21
  expect(found).to eq(path)
20
22
  end
21
23
 
22
24
  it 'returns the file in current dir if path is not set' do
23
- found = described_class.find(current: SAMPLES_PATH)
24
- expect(found).to eq(SAMPLES_PATH.join('exceptions.reek'))
25
+ found = described_class.find(current: regular_configuration_dir)
26
+ expect(found).to eq(regular_configuration_file)
25
27
  end
26
28
 
27
29
  it 'returns the file in a parent dir if none in current dir' do
28
- Dir.mktmpdir(nil, SAMPLES_PATH) do |tempdir|
29
- found = described_class.find(current: Pathname.new(tempdir))
30
- expect(found).to eq(SAMPLES_PATH.join('exceptions.reek'))
31
- end
30
+ empty_sub_directory = CONFIGURATION_DIR.join('regular_configuration').join('empty_sub_directory')
31
+ found = described_class.find(current: empty_sub_directory)
32
+ expect(found).to eq(regular_configuration_file)
32
33
  end
33
34
 
34
- it 'returns the file even if it’s just ‘.reek’' do
35
- single_configuration_file_dir = CONFIG_PATH.join('single_configuration_file')
36
- found = described_class.find(current: single_configuration_file_dir)
37
- expect(found).to eq(single_configuration_file_dir.join('.reek'))
35
+ it 'skips files ending in .reek.yml in current dir' do
36
+ Dir.mktmpdir(nil, regular_configuration_dir) do |tempdir|
37
+ current = Pathname.new(tempdir)
38
+ bad_config = current.join('ignoreme.reek.yml')
39
+ FileUtils.touch bad_config
40
+ found = described_class.find(current: Pathname.new(tempdir))
41
+ expect(found).to eq(regular_configuration_file)
42
+ end
38
43
  end
39
44
 
40
45
  it 'returns the file in home if traversing from the current dir fails' do
41
46
  skip_if_a_config_in_tempdir
42
47
 
43
- Dir.mktmpdir(nil, SAMPLES_PATH) do |tempdir|
44
- found = described_class.find(current: Pathname.new(tempdir))
45
- expect(found).to eq(SAMPLES_PATH.join('exceptions.reek'))
48
+ Dir.mktmpdir do |tempdir|
49
+ found = described_class.find(current: Pathname.new(tempdir), home: regular_configuration_dir)
50
+ expect(found).to eq(regular_configuration_file)
46
51
  end
47
52
  end
48
53
 
49
54
  it 'prefers the file in :current over one in :home' do
50
- found = described_class.find(current: SAMPLES_PATH, home: CONFIG_PATH)
51
- file_in_current = SAMPLES_PATH.join('exceptions.reek')
52
-
53
- expect(found).to eq(file_in_current)
55
+ home_dir = CONFIGURATION_DIR.join('home')
56
+ found = described_class.find(current: regular_configuration_dir, home: home_dir)
57
+ expect(found).to eq(regular_configuration_file)
54
58
  end
55
59
 
56
60
  it 'returns nil when there are no files to find' do
@@ -70,12 +74,10 @@ RSpec.describe Reek::Configuration::ConfigurationFileFinder do
70
74
  skip_if_a_config_in_tempdir
71
75
 
72
76
  Dir.mktmpdir do |tempdir|
73
- dir = Pathname.new(tempdir)
74
- subdir = dir.join('subdir')
75
-
76
- FileUtils.mkdir(subdir)
77
+ current = Pathname.new(tempdir)
78
+ home = SAMPLES_DIR.join('no_config_file')
77
79
 
78
- found = described_class.find(current: subdir, home: dir)
80
+ found = described_class.find(current: current, home: home)
79
81
 
80
82
  expect(found).to be_nil
81
83
  end
@@ -83,7 +85,7 @@ RSpec.describe Reek::Configuration::ConfigurationFileFinder do
83
85
 
84
86
  it 'works with paths that need escaping' do
85
87
  Dir.mktmpdir("ma\ngic d*r") do |tempdir|
86
- config = Pathname.new("#{tempdir}/ma\ngic f*le.reek")
88
+ config = Pathname.new("#{tempdir}/.reek.yml")
87
89
  subdir = Pathname.new("#{tempdir}/ma\ngic subd*r")
88
90
  FileUtils.touch config
89
91
  FileUtils.mkdir subdir
@@ -91,42 +93,122 @@ RSpec.describe Reek::Configuration::ConfigurationFileFinder do
91
93
  expect(found).to eq(config)
92
94
  end
93
95
  end
94
-
95
- context 'more than one configuration file' do
96
- let(:path) { CONFIG_PATH.join('more_than_one_configuration_file') }
97
-
98
- it 'prints a message on STDERR' do
99
- expected_message = "Error: Found multiple configuration files 'regular.reek', 'todo.reek'"
100
- expect do
101
- begin
102
- described_class.find(current: path)
103
- rescue SystemExit
104
- end
105
- end.to output(/#{expected_message}/).to_stderr
106
- end
107
-
108
- it 'exits' do
109
- Reek::CLI::Silencer.silently do
110
- expect do
111
- described_class.find(current: path)
112
- end.to raise_error(SystemExit)
113
- end
114
- end
115
- end
116
96
  end
117
97
 
118
98
  describe '.load_from_file' do
119
99
  let(:sample_configuration_loaded) do
120
100
  {
121
- 'UncommunicativeVariableName' => { 'enabled' => false },
122
- 'UncommunicativeMethodName' => { 'enabled' => false }
101
+ Reek::Configuration::AppConfiguration::DETECTORS_KEY => {
102
+ 'UncommunicativeVariableName' => { 'enabled' => false },
103
+ 'UncommunicativeMethodName' => { 'enabled' => false }
104
+ }
123
105
  }
124
106
  end
125
107
 
126
108
  it 'loads the configuration from given file' do
127
- configuration = described_class.load_from_file(CONFIG_PATH.join('full_mask.reek'))
109
+ configuration = described_class.load_from_file(CONFIGURATION_DIR.join('full_mask.reek'))
128
110
  expect(configuration).to eq(sample_configuration_loaded)
129
111
  end
112
+
113
+ context 'with exclude, accept and reject settings' do
114
+ context 'when configuring top level detectors' do
115
+ let(:configuration) do
116
+ described_class.
117
+ load_from_file(CONFIGURATION_DIR.join('accepts_rejects_and_excludes_for_detectors.reek.yml')).
118
+ fetch(Reek::Configuration::AppConfiguration::DETECTORS_KEY)
119
+ end
120
+
121
+ let(:expected) do
122
+ {
123
+ 'UnusedPrivateMethod' => { 'exclude' => [/exclude regexp/] },
124
+ 'UncommunicativeMethodName' => { 'reject' => ['reject name'],
125
+ 'accept' => ['accept name'] },
126
+ 'UncommunicativeModuleName' => { 'reject' => ['reject name 1', 'reject name 2'],
127
+ 'accept' => ['accept name 1', 'accept name 2'] },
128
+ 'UncommunicativeParameterName' => { 'reject' => ['reject name', /reject regexp/],
129
+ 'accept' => ['accept name', /accept regexp/] },
130
+ 'UncommunicativeVariableName' => { 'reject' => [/^reject regexp$/],
131
+ 'accept' => [/accept(.*)regexp/] }
132
+ }
133
+ end
134
+
135
+ it 'converts marked strings to regexes' do
136
+ expect(configuration['UnusedPrivateMethod']).to eq(expected['UnusedPrivateMethod'])
137
+ end
138
+
139
+ it 'leaves regular single strings untouched' do
140
+ expect(configuration['UncommunicativeMethodName']).to eq(expected['UncommunicativeMethodName'])
141
+ end
142
+
143
+ it 'leaves regular multiple strings untouched' do
144
+ expect(configuration['UncommunicativeModuleName']).to eq(expected['UncommunicativeModuleName'])
145
+ end
146
+
147
+ it 'allows mixing of regular strings and marked strings' do
148
+ expect(configuration['UncommunicativeParameterName']).to eq(expected['UncommunicativeParameterName'])
149
+ end
150
+
151
+ it 'converts more complex marked strings correctly to regexes' do
152
+ expect(configuration['UncommunicativeVariableName']).to eq(expected['UncommunicativeVariableName'])
153
+ end
154
+ end
155
+
156
+ context 'when configuring directory directives' do
157
+ let(:directory_name) { 'app/controllers' }
158
+ let(:configuration) do
159
+ described_class.
160
+ load_from_file(CONFIGURATION_DIR.join('accepts_rejects_and_excludes_for_directory_directives.reek.yml')).
161
+ fetch(Reek::Configuration::AppConfiguration::DIRECTORIES_KEY)
162
+ end
163
+
164
+ let(:expected) do
165
+ {
166
+ directory_name => {
167
+ 'UnusedPrivateMethod' => { 'exclude' => [/exclude regexp/] },
168
+ 'UncommunicativeMethodName' => { 'reject' => ['reject name'],
169
+ 'accept' => ['accept name'] },
170
+ 'UncommunicativeModuleName' => { 'reject' => ['reject name 1', 'reject name 2'],
171
+ 'accept' => ['accept name 1', 'accept name 2'] },
172
+ 'UncommunicativeParameterName' => { 'reject' => ['reject name', /reject regexp/],
173
+ 'accept' => ['accept name', /accept regexp/] },
174
+ 'UncommunicativeVariableName' => { 'reject' => [/^reject regexp$/],
175
+ 'accept' => [/accept(.*)regexp/] }
176
+ }
177
+ }
178
+ end
179
+
180
+ it 'converts marked strings to regexes' do
181
+ expect(configuration[directory_name]['UnusedPrivateMethod']).
182
+ to eq(expected[directory_name]['UnusedPrivateMethod'])
183
+ end
184
+
185
+ it 'leaves regular single strings untouched' do
186
+ expect(configuration[directory_name]['UncommunicativeMethodName']).
187
+ to eq(expected[directory_name]['UncommunicativeMethodName'])
188
+ end
189
+
190
+ it 'leaves regular multiple strings untouched' do
191
+ expect(configuration[directory_name]['UncommunicativeModuleName']).
192
+ to eq(expected[directory_name]['UncommunicativeModuleName'])
193
+ end
194
+
195
+ it 'allows mixing of regular strings and marked strings' do
196
+ expect(configuration[directory_name]['UncommunicativeParameterName']).
197
+ to eq(expected[directory_name]['UncommunicativeParameterName'])
198
+ end
199
+
200
+ it 'converts more complex marked strings correctly to regexes' do
201
+ expect(configuration[directory_name]['UncommunicativeVariableName']).
202
+ to eq(expected[directory_name]['UncommunicativeVariableName'])
203
+ end
204
+ end
205
+ end
206
+
207
+ it 'returns blank hash when no file is found' do
208
+ config = described_class.load_from_file(nil)
209
+
210
+ expect(config).to eq({})
211
+ end
130
212
  end
131
213
 
132
214
  private
@@ -6,7 +6,7 @@ RSpec.describe Reek::Configuration::DefaultDirective do
6
6
  let(:directives) { {}.extend(described_class) }
7
7
 
8
8
  it 'adds a smell configuration' do
9
- directives.add :UncommunicativeVariableName, enabled: false
9
+ directives.add(UncommunicativeVariableName: { enabled: false })
10
10
  expect(directives).to eq(Reek::SmellDetectors::UncommunicativeVariableName => { enabled: false })
11
11
  end
12
12
  end
@@ -1,4 +1,5 @@
1
1
  require_relative '../../spec_helper'
2
+ require_lib 'reek/errors/config_file_error'
2
3
  require_lib 'reek/configuration/directory_directives'
3
4
 
4
5
  RSpec.describe Reek::Configuration::DirectoryDirectives do
@@ -13,13 +14,13 @@ RSpec.describe Reek::Configuration::DirectoryDirectives do
13
14
  end
14
15
  let(:source_via) { 'foo/bar/bang/dummy.rb' }
15
16
 
16
- context 'our source is in a directory for which we have a directive' do
17
+ context 'when our source is in a directory for which we have a directive' do
17
18
  it 'returns the corresponding directive' do
18
19
  expect(directives.directive_for(source_via)).to eq(bang_config)
19
20
  end
20
21
  end
21
22
 
22
- context 'our source is not in a directory for which we have a directive' do
23
+ context 'when our source is not in a directory for which we have a directive' do
23
24
  it 'returns nil' do
24
25
  expect(directives.directive_for('does/not/exist')).to eq(nil)
25
26
  end
@@ -31,13 +32,11 @@ RSpec.describe Reek::Configuration::DirectoryDirectives do
31
32
  {}.extend(described_class)
32
33
  end
33
34
 
34
- context 'one of given paths is a file' do
35
- let(:file_as_path) { SAMPLES_PATH.join('inline.rb') }
35
+ context 'when one of given paths is a file' do
36
+ let(:file_as_path) { SAMPLES_DIR.join('smelly_source').join('inline.rb') }
36
37
 
37
38
  it 'raises an error' do
38
- Reek::CLI::Silencer.silently do
39
- expect { directives.add(file_as_path, {}) }.to raise_error(SystemExit)
40
- end
39
+ expect { directives.add(file_as_path => nil) }.to raise_error(Reek::Errors::ConfigFileError)
41
40
  end
42
41
  end
43
42
  end
@@ -1,18 +1,18 @@
1
1
  require_relative '../../spec_helper'
2
+ require_lib 'reek/errors/config_file_error'
2
3
  require_lib 'reek/configuration/excluded_paths'
3
4
 
4
5
  RSpec.describe Reek::Configuration::ExcludedPaths do
5
6
  describe '#add' do
6
7
  let(:exclusions) { [].extend(described_class) }
7
8
 
8
- context 'one of given paths is a file' do
9
- let(:file_as_path) { SAMPLES_PATH.join('inline.rb') }
10
- let(:paths) { [SAMPLES_PATH, file_as_path] }
9
+ context 'when one of given paths is a file' do
10
+ let(:smelly_source_dir) { SAMPLES_DIR.join('smelly_source') }
11
+ let(:file_as_path) { smelly_source_dir.join('inline.rb') }
12
+ let(:paths) { [smelly_source_dir, file_as_path] }
11
13
 
12
14
  it 'raises an error if one of the given paths is a file' do
13
- Reek::CLI::Silencer.silently do
14
- expect { exclusions.add(paths) }.to raise_error(SystemExit)
15
- end
15
+ expect { exclusions.add(paths) }.to raise_error(Reek::Errors::ConfigFileError)
16
16
  end
17
17
  end
18
18
  end
@@ -0,0 +1,33 @@
1
+ require_relative '../../spec_helper'
2
+ require_lib 'reek/configuration/rake_task_converter'
3
+
4
+ RSpec.describe Reek::Configuration::RakeTaskConverter do
5
+ describe 'convert' do
6
+ let(:configuration_for_smell_detector) do
7
+ {
8
+ 'exclude' => [/Klass#foobar$/, /^Klass#omg$/],
9
+ 'reject' => [/^[a-z]$/, /[0-9]$/, /[A-Z]/],
10
+ 'accept' => [/^_$/]
11
+ }
12
+ end
13
+
14
+ let(:expected_exclude) { ['/Klass#foobar$/', '/^Klass#omg$/'] }
15
+ let(:expected_reject) { ['/^[a-z]$/', '/[0-9]$/', '/[A-Z]/'] }
16
+ let(:expected_accept) { ['/^_$/'] }
17
+
18
+ it 'converts exclude regexes to strings' do
19
+ converted_configuration = described_class.convert configuration_for_smell_detector
20
+ expect(converted_configuration['exclude']).to eq(expected_exclude)
21
+ end
22
+
23
+ it 'converts reject regexes to strings' do
24
+ converted_configuration = described_class.convert configuration_for_smell_detector
25
+ expect(converted_configuration['reject']).to eq(expected_reject)
26
+ end
27
+
28
+ it 'converts accept regexes to strings' do
29
+ converted_configuration = described_class.convert configuration_for_smell_detector
30
+ expect(converted_configuration['accept']).to eq(expected_accept)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,165 @@
1
+ require_relative '../../spec_helper'
2
+ require_lib 'reek/configuration/schema_validator'
3
+ require_lib 'reek/errors/config_file_error'
4
+
5
+ RSpec.describe Reek::Configuration::SchemaValidator do
6
+ describe 'validate' do
7
+ subject(:validator) { described_class.new configuration }
8
+
9
+ context 'when configuration is valid' do
10
+ let(:configuration) do
11
+ {
12
+ Reek::Configuration::AppConfiguration::DETECTORS_KEY => {
13
+ 'UncommunicativeVariableName' => { 'enabled' => false },
14
+ 'UncommunicativeMethodName' => { 'enabled' => false }
15
+ }
16
+ }
17
+ end
18
+
19
+ it 'returns nil' do
20
+ expect(validator.validate).to eq(nil)
21
+ end
22
+ end
23
+
24
+ context 'when detector is invalid' do
25
+ let(:configuration) do
26
+ {
27
+ Reek::Configuration::AppConfiguration::DETECTORS_KEY => {
28
+ 'DoesNotExist' => { 'enabled' => false }
29
+ }
30
+ }
31
+ end
32
+
33
+ it 'raises an error' do
34
+ message = %r{\[/detectors/DoesNotExist\] key 'DoesNotExist:' is undefined}
35
+ expect { validator.validate }.to raise_error(Reek::Errors::ConfigFileError, message)
36
+ end
37
+ end
38
+
39
+ context 'when `enabled` has a non-boolean value' do
40
+ let(:configuration) do
41
+ {
42
+ Reek::Configuration::AppConfiguration::DETECTORS_KEY => {
43
+ 'FeatureEnvy' => { 'enabled' => 'foo' }
44
+ }
45
+ }
46
+ end
47
+
48
+ it 'raises an error' do
49
+ message = %r{\[/detectors/FeatureEnvy/enabled\] 'foo': not a boolean}
50
+ expect { validator.validate }.to raise_error(Reek::Errors::ConfigFileError, message)
51
+ end
52
+ end
53
+
54
+ context 'when detector has an unknown option' do
55
+ let(:configuration) do
56
+ {
57
+ Reek::Configuration::AppConfiguration::DETECTORS_KEY => {
58
+ 'DataClump' => { 'does_not_exist' => 42 }
59
+ }
60
+ }
61
+ end
62
+
63
+ it 'raises an error' do
64
+ message = %r{\[/detectors/DataClump/does_not_exist\] key 'does_not_exist:' is undefined}
65
+ expect { validator.validate }.to raise_error(Reek::Errors::ConfigFileError, message)
66
+ end
67
+ end
68
+
69
+ context 'when `exclude`, `reject` and `accept`' do
70
+ %w(exclude reject accept).each do |attribute|
71
+ context 'when a scalar' do
72
+ let(:configuration) do
73
+ {
74
+ Reek::Configuration::AppConfiguration::DETECTORS_KEY => {
75
+ 'UncommunicativeMethodName' => { attribute => 42 }
76
+ }
77
+ }
78
+ end
79
+
80
+ it 'raises an error' do
81
+ message = %r{\[/detectors/UncommunicativeMethodName/#{attribute}\] '42': not a sequence}
82
+ expect { validator.validate }.to raise_error(Reek::Errors::ConfigFileError, message)
83
+ end
84
+ end
85
+
86
+ context 'when types are mixed' do
87
+ let(:configuration) do
88
+ {
89
+ Reek::Configuration::AppConfiguration::DETECTORS_KEY => {
90
+ 'UncommunicativeMethodName' => { attribute => [42, 'foo'] }
91
+ }
92
+ }
93
+ end
94
+
95
+ it 'raises an error' do
96
+ message = %r{\[/detectors/UncommunicativeMethodName/#{attribute}/0\] '42': not a string}
97
+ expect { validator.validate }.to raise_error(Reek::Errors::ConfigFileError, message)
98
+ end
99
+ end
100
+ end
101
+ end
102
+
103
+ context 'when `exclude_paths` is a scalar' do
104
+ let(:configuration) do
105
+ {
106
+ Reek::Configuration::AppConfiguration::EXCLUDE_PATHS_KEY => 42
107
+ }
108
+ end
109
+
110
+ it 'raises an error' do
111
+ message = %r{\[/exclude_paths\] '42': not a sequence}
112
+ expect { validator.validate }.to raise_error(Reek::Errors::ConfigFileError, message)
113
+ end
114
+ end
115
+
116
+ context 'when `exclude_paths` mixes types' do
117
+ let(:configuration) do
118
+ {
119
+ Reek::Configuration::AppConfiguration::EXCLUDE_PATHS_KEY => [42, 'foo']
120
+ }
121
+ end
122
+
123
+ it 'raises an error' do
124
+ message = %r{\[/exclude_paths/0\] '42': not a string}
125
+ expect { validator.validate }.to raise_error(Reek::Errors::ConfigFileError, message)
126
+ end
127
+ end
128
+
129
+ context 'with directory directives' do
130
+ context 'when bad detector' do
131
+ let(:configuration) do
132
+ {
133
+ Reek::Configuration::AppConfiguration::DIRECTORIES_KEY => {
134
+ 'web_app/app/helpers' => {
135
+ 'Bar' => { 'enabled' => false }
136
+ }
137
+ }
138
+ }
139
+ end
140
+
141
+ it 'raises an error' do
142
+ message = %r{\[/directories/web_app/app/helpers/Bar\] key 'Bar:' is undefined}
143
+ expect { validator.validate }.to raise_error(Reek::Errors::ConfigFileError, message)
144
+ end
145
+ end
146
+
147
+ context 'when unknown attribute' do
148
+ let(:configuration) do
149
+ {
150
+ Reek::Configuration::AppConfiguration::DIRECTORIES_KEY => {
151
+ 'web_app/app/controllers' => {
152
+ 'NestedIterators' => { 'foo' => false }
153
+ }
154
+ }
155
+ }
156
+ end
157
+
158
+ it 'raises an error' do
159
+ message = %r{\[/directories/web_app/app/controllers/NestedIterators/foo\] key 'foo:' is undefined}
160
+ expect { validator.validate }.to raise_error(Reek::Errors::ConfigFileError, message)
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end