reek 2.2.1 → 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 (157) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/.travis.yml +9 -4
  4. data/CHANGELOG +8 -0
  5. data/Gemfile +6 -4
  6. data/README.md +6 -0
  7. data/docs/API.md +51 -22
  8. data/docs/Configuration-Files.md +12 -1
  9. data/docs/Feature-Envy.md +30 -10
  10. data/docs/How-reek-works-internally.md +109 -39
  11. data/docs/RSpec-matchers.md +26 -22
  12. data/docs/Reek-Driven-Development.md +0 -8
  13. data/docs/Utility-Function.md +8 -10
  14. data/features/{ruby_api/api.feature → command_line_interface/basic_usage.feature} +2 -2
  15. data/features/programmatic_access.feature +21 -2
  16. data/features/samples.feature +3 -1
  17. data/lib/reek.rb +2 -2
  18. data/lib/reek/{core → ast}/ast_node_class_map.rb +8 -8
  19. data/lib/reek/{sexp/sexp_node.rb → ast/node.rb} +47 -6
  20. data/lib/reek/{core → ast}/object_refs.rb +2 -1
  21. data/lib/reek/{core → ast}/reference_collector.rb +2 -1
  22. data/lib/reek/{sexp → ast}/sexp_extensions.rb +10 -5
  23. data/lib/reek/{sexp → ast}/sexp_formatter.rb +7 -5
  24. data/lib/reek/cli/application.rb +1 -0
  25. data/lib/reek/cli/command.rb +1 -0
  26. data/lib/reek/cli/input.rb +4 -1
  27. data/lib/reek/cli/option_interpreter.rb +6 -4
  28. data/lib/reek/cli/options.rb +2 -1
  29. data/lib/reek/cli/reek_command.rb +3 -2
  30. data/lib/reek/cli/silencer.rb +1 -0
  31. data/lib/reek/{core → cli}/warning_collector.rb +2 -1
  32. data/lib/reek/code_comment.rb +36 -0
  33. data/lib/reek/configuration/app_configuration.rb +17 -2
  34. data/lib/reek/configuration/configuration_file_finder.rb +1 -0
  35. data/lib/reek/{core → context}/code_context.rb +7 -5
  36. data/lib/reek/{core → context}/method_context.rb +5 -3
  37. data/lib/reek/{core → context}/module_context.rb +8 -3
  38. data/lib/reek/{core/stop_context.rb → context/root_context.rb} +4 -2
  39. data/lib/reek/{core → context}/singleton_method_context.rb +2 -1
  40. data/lib/reek/examiner.rb +82 -0
  41. data/lib/reek/report/formatter.rb +70 -0
  42. data/lib/reek/report/heading_formatter.rb +45 -0
  43. data/lib/reek/report/location_formatter.rb +35 -0
  44. data/lib/reek/report/report.rb +198 -0
  45. data/lib/reek/smells.rb +24 -13
  46. data/lib/reek/smells/attribute.rb +6 -4
  47. data/lib/reek/smells/boolean_parameter.rb +3 -1
  48. data/lib/reek/smells/class_variable.rb +3 -1
  49. data/lib/reek/smells/control_parameter.rb +3 -1
  50. data/lib/reek/smells/data_clump.rb +3 -1
  51. data/lib/reek/smells/duplicate_method_call.rb +3 -1
  52. data/lib/reek/smells/feature_envy.rb +3 -1
  53. data/lib/reek/smells/irresponsible_module.rb +12 -7
  54. data/lib/reek/smells/long_parameter_list.rb +5 -3
  55. data/lib/reek/smells/long_yield_list.rb +3 -1
  56. data/lib/reek/smells/module_initialize.rb +3 -1
  57. data/lib/reek/smells/nested_iterators.rb +3 -1
  58. data/lib/reek/smells/nil_check.rb +3 -1
  59. data/lib/reek/smells/prima_donna_method.rb +3 -1
  60. data/lib/reek/smells/repeated_conditional.rb +5 -3
  61. data/lib/reek/{core → smells}/smell_configuration.rb +3 -1
  62. data/lib/reek/smells/smell_detector.rb +9 -7
  63. data/lib/reek/{core → smells}/smell_repository.rb +3 -2
  64. data/lib/reek/smells/smell_warning.rb +6 -4
  65. data/lib/reek/smells/too_many_instance_variables.rb +3 -1
  66. data/lib/reek/smells/too_many_methods.rb +3 -1
  67. data/lib/reek/smells/too_many_statements.rb +3 -1
  68. data/lib/reek/smells/uncommunicative_method_name.rb +3 -1
  69. data/lib/reek/smells/uncommunicative_module_name.rb +3 -1
  70. data/lib/reek/smells/uncommunicative_parameter_name.rb +3 -1
  71. data/lib/reek/smells/uncommunicative_variable_name.rb +3 -1
  72. data/lib/reek/smells/unused_parameters.rb +3 -1
  73. data/lib/reek/smells/utility_function.rb +5 -2
  74. data/lib/reek/source/source_code.rb +40 -9
  75. data/lib/reek/source/source_locator.rb +30 -12
  76. data/lib/reek/spec/should_reek.rb +5 -4
  77. data/lib/reek/spec/should_reek_of.rb +3 -2
  78. data/lib/reek/spec/should_reek_only_of.rb +5 -4
  79. data/lib/reek/tree_dresser.rb +32 -0
  80. data/lib/reek/tree_walker.rb +182 -0
  81. data/lib/reek/version.rb +1 -1
  82. data/reek.gemspec +3 -3
  83. data/spec/factories/factories.rb +2 -0
  84. data/spec/reek/{sexp/sexp_node_spec.rb → ast/node_spec.rb} +2 -2
  85. data/spec/reek/{core → ast}/object_refs_spec.rb +3 -3
  86. data/spec/reek/{core → ast}/reference_collector_spec.rb +2 -2
  87. data/spec/reek/{sexp → ast}/sexp_extensions_spec.rb +6 -16
  88. data/spec/reek/{sexp → ast}/sexp_formatter_spec.rb +2 -2
  89. data/spec/reek/cli/option_interpreter_spec.rb +2 -1
  90. data/spec/reek/{core → cli}/warning_collector_spec.rb +3 -3
  91. data/spec/reek/{core/code_comment_spec.rb → code_comment_spec.rb} +3 -3
  92. data/spec/reek/configuration/app_configuration_spec.rb +31 -18
  93. data/spec/reek/{core → context}/code_context_spec.rb +14 -15
  94. data/spec/reek/{core → context}/method_context_spec.rb +8 -8
  95. data/spec/reek/{core → context}/module_context_spec.rb +4 -4
  96. data/spec/reek/context/root_context_spec.rb +14 -0
  97. data/spec/reek/{core → context}/singleton_method_context_spec.rb +4 -4
  98. data/spec/reek/{core/examiner_spec.rb → examiner_spec.rb} +3 -42
  99. data/spec/reek/{cli → report}/html_report_spec.rb +5 -5
  100. data/spec/reek/report/json_report_spec.rb +20 -0
  101. data/spec/reek/{cli → report}/text_report_spec.rb +14 -14
  102. data/spec/reek/{cli → report}/xml_report_spec.rb +7 -7
  103. data/spec/reek/report/yaml_report_spec.rb +20 -0
  104. data/spec/reek/smells/attribute_spec.rb +2 -1
  105. data/spec/reek/smells/boolean_parameter_spec.rb +1 -1
  106. data/spec/reek/smells/class_variable_spec.rb +5 -5
  107. data/spec/reek/smells/control_parameter_spec.rb +1 -1
  108. data/spec/reek/smells/data_clump_spec.rb +1 -1
  109. data/spec/reek/smells/duplicate_method_call_spec.rb +3 -3
  110. data/spec/reek/smells/feature_envy_spec.rb +1 -1
  111. data/spec/reek/smells/irresponsible_module_spec.rb +24 -28
  112. data/spec/reek/smells/long_parameter_list_spec.rb +2 -2
  113. data/spec/reek/smells/long_yield_list_spec.rb +2 -2
  114. data/spec/reek/smells/nested_iterators_spec.rb +1 -1
  115. data/spec/reek/smells/nil_check_spec.rb +2 -2
  116. data/spec/reek/smells/prima_donna_method_spec.rb +3 -3
  117. data/spec/reek/smells/repeated_conditional_spec.rb +6 -6
  118. data/spec/reek/{core → smells}/smell_configuration_spec.rb +4 -4
  119. data/spec/reek/smells/smell_detector_shared.rb +2 -2
  120. data/spec/reek/{core → smells}/smell_repository_spec.rb +5 -4
  121. data/spec/reek/smells/too_many_instance_variables_spec.rb +1 -1
  122. data/spec/reek/smells/too_many_methods_spec.rb +4 -4
  123. data/spec/reek/smells/too_many_statements_spec.rb +2 -2
  124. data/spec/reek/smells/uncommunicative_method_name_spec.rb +1 -1
  125. data/spec/reek/smells/uncommunicative_module_name_spec.rb +4 -4
  126. data/spec/reek/smells/uncommunicative_parameter_name_spec.rb +2 -2
  127. data/spec/reek/smells/uncommunicative_variable_name_spec.rb +3 -3
  128. data/spec/reek/smells/utility_function_spec.rb +23 -1
  129. data/spec/reek/source/source_code_spec.rb +1 -1
  130. data/spec/reek/source/source_locator_spec.rb +30 -0
  131. data/spec/reek/spec/should_reek_of_spec.rb +0 -17
  132. data/spec/reek/spec/should_reek_spec.rb +0 -25
  133. data/spec/reek/tree_dresser_spec.rb +16 -0
  134. data/spec/reek/{core/tree_walker_spec.rb → tree_walker_spec.rb} +5 -5
  135. data/spec/samples/{simple_configuration.reek → configuration/simple_configuration.reek} +0 -0
  136. data/spec/samples/configuration/with_excluded_paths.reek +4 -0
  137. data/spec/samples/source_with_exclude_paths/ignore_me/uncommunicative_method_name.rb +5 -0
  138. data/spec/samples/source_with_exclude_paths/nested/ignore_me_as_well/irresponsible_module.rb +2 -0
  139. data/spec/samples/source_with_exclude_paths/nested/uncommunicative_parameter_name.rb +6 -0
  140. data/spec/spec_helper.rb +6 -7
  141. data/tasks/develop.rake +2 -2
  142. metadata +71 -69
  143. data/lib/reek/cli/report/formatter.rb +0 -69
  144. data/lib/reek/cli/report/heading_formatter.rb +0 -45
  145. data/lib/reek/cli/report/location_formatter.rb +0 -34
  146. data/lib/reek/cli/report/report.rb +0 -191
  147. data/lib/reek/core/ast_node.rb +0 -38
  148. data/lib/reek/core/code_comment.rb +0 -37
  149. data/lib/reek/core/examiner.rb +0 -85
  150. data/lib/reek/core/tree_dresser.rb +0 -24
  151. data/lib/reek/core/tree_walker.rb +0 -180
  152. data/lib/reek/source/source_repository.rb +0 -43
  153. data/spec/reek/cli/json_report_spec.rb +0 -20
  154. data/spec/reek/cli/yaml_report_spec.rb +0 -20
  155. data/spec/reek/core/object_source_spec.rb +0 -18
  156. data/spec/reek/core/stop_context_spec.rb +0 -14
  157. data/spec/reek/core/tree_dresser_spec.rb +0 -16
@@ -1,24 +0,0 @@
1
- require_relative 'ast_node_class_map'
2
-
3
- module Reek
4
- module Core
5
- #
6
- # Adorns an abstract syntax tree with mix-in modules to make accessing
7
- # the tree more understandable and less implementation-dependent.
8
- #
9
- class TreeDresser
10
- def initialize(klass_map = ASTNodeClassMap.new)
11
- @klass_map = klass_map
12
- end
13
-
14
- def dress(sexp, comment_map)
15
- return sexp unless sexp.is_a? AST::Node
16
- type = sexp.type
17
- children = sexp.children.map { |child| dress(child, comment_map) }
18
- comments = comment_map[sexp]
19
- @klass_map.klass_for(type).new(type, children,
20
- location: sexp.loc, comments: comments)
21
- end
22
- end
23
- end
24
- end
@@ -1,180 +0,0 @@
1
- require_relative 'method_context'
2
- require_relative 'module_context'
3
- require_relative 'stop_context'
4
- require_relative 'singleton_method_context'
5
-
6
- module Reek
7
- module Core
8
- #
9
- # Traverses a Sexp abstract syntax tree and fires events whenever
10
- # it encounters specific node types.
11
- #
12
- # SMELL: This class is responsible for counting statements and for feeding
13
- # each context to the smell repository.
14
- class TreeWalker
15
- def initialize(smell_repository = Core::SmellRepository.new)
16
- @smell_repository = smell_repository
17
- @element = StopContext.new
18
- end
19
-
20
- def process(exp)
21
- context_processor = "process_#{exp.type}"
22
- if context_processor_exists?(context_processor)
23
- send(context_processor, exp)
24
- else
25
- process_default exp
26
- end
27
- @element
28
- end
29
-
30
- def process_default(exp)
31
- exp.children.each do |child|
32
- process(child) if child.is_a? AST::Node
33
- end
34
- end
35
-
36
- def process_module(exp)
37
- inside_new_context(ModuleContext, exp) do
38
- process_default(exp)
39
- end
40
- end
41
-
42
- alias_method :process_class, :process_module
43
-
44
- def process_def(exp)
45
- inside_new_context(MethodContext, exp) do
46
- count_clause(exp.body)
47
- process_default(exp)
48
- end
49
- end
50
-
51
- def process_defs(exp)
52
- inside_new_context(SingletonMethodContext, exp) do
53
- count_clause(exp.body)
54
- process_default(exp)
55
- end
56
- end
57
-
58
- def process_args(_) end
59
-
60
- #
61
- # Recording of calls to methods and self
62
- #
63
-
64
- def process_send(exp)
65
- @element.record_call_to(exp)
66
- process_default(exp)
67
- end
68
-
69
- alias_method :process_attrasgn, :process_send
70
- alias_method :process_op_asgn, :process_send
71
-
72
- def process_ivar(exp)
73
- @element.record_use_of_self
74
- process_default(exp)
75
- end
76
-
77
- alias_method :process_ivasgn, :process_ivar
78
-
79
- def process_self(_)
80
- @element.record_use_of_self
81
- end
82
-
83
- alias_method :process_zsuper, :process_self
84
-
85
- #
86
- # Statement counting
87
- #
88
-
89
- def process_block(exp)
90
- count_clause(exp.block)
91
- process_default(exp)
92
- end
93
-
94
- def process_begin(exp)
95
- count_statement_list(exp.children)
96
- @element.count_statements(-1)
97
- process_default(exp)
98
- end
99
-
100
- alias_method :process_kwbegin, :process_begin
101
-
102
- def process_if(exp)
103
- count_clause(exp[2])
104
- count_clause(exp[3])
105
- @element.count_statements(-1)
106
- process_default(exp)
107
- end
108
-
109
- def process_while(exp)
110
- count_clause(exp[2])
111
- @element.count_statements(-1)
112
- process_default(exp)
113
- end
114
-
115
- alias_method :process_until, :process_while
116
-
117
- def process_for(exp)
118
- count_clause(exp[3])
119
- @element.count_statements(-1)
120
- process_default(exp)
121
- end
122
-
123
- def process_rescue(exp)
124
- count_clause(exp[1])
125
- @element.count_statements(-1)
126
- process_default(exp)
127
- end
128
-
129
- def process_resbody(exp)
130
- count_statement_list(exp[2..-1].compact)
131
- process_default(exp)
132
- end
133
-
134
- def process_case(exp)
135
- count_clause(exp.else_body)
136
- @element.count_statements(-1)
137
- process_default(exp)
138
- end
139
-
140
- def process_when(exp)
141
- count_clause(exp.body)
142
- process_default(exp)
143
- end
144
-
145
- private
146
-
147
- def context_processor_exists?(name)
148
- respond_to?(name)
149
- end
150
-
151
- def count_clause(sexp)
152
- @element.count_statements(1) if sexp
153
- end
154
-
155
- def count_statement_list(statement_list)
156
- @element.count_statements statement_list.length
157
- end
158
-
159
- def inside_new_context(klass, exp)
160
- scope = klass.new(@element, exp)
161
- push(scope) do
162
- yield
163
- check_smells(exp.type)
164
- end
165
- scope
166
- end
167
-
168
- def check_smells(type)
169
- @smell_repository.examine(@element, type)
170
- end
171
-
172
- def push(scope)
173
- orig = @element
174
- @element = scope
175
- yield
176
- @element = orig
177
- end
178
- end
179
- end
180
- end
@@ -1,43 +0,0 @@
1
- require_relative 'source_code'
2
- require_relative 'source_locator'
3
-
4
- module Reek
5
- module Source
6
- #
7
- # A collection of source code. If the collection is initialized with an array
8
- # it is assumed to be a list of file paths. Otherwise it is assumed to be a
9
- # single unit of Ruby source code.
10
- #
11
- class SourceRepository
12
- attr_reader :description
13
-
14
- def initialize(description, sources)
15
- @description = description
16
- @sources = sources
17
- end
18
-
19
- # TODO: This method is a least partially broken.
20
- # Regardless of how you call reek, be it:
21
- # reek lib/
22
- # reek lib/file_one.rb lib/file_two.rb
23
- # echo "def m; end" | reek
24
- # we *always* end up in the "when Source::SourceCode" branch.
25
- # So it seems like 80% of this method is never used.
26
- def self.parse(source)
27
- case source
28
- when Array
29
- new 'dir', Source::SourceLocator.new(source).sources
30
- when Source::SourceCode
31
- new source.description, [source]
32
- else
33
- src = Source::SourceCode.from source
34
- new src.description, [src]
35
- end
36
- end
37
-
38
- def each(&block)
39
- @sources.each(&block)
40
- end
41
- end
42
- end
43
- end
@@ -1,20 +0,0 @@
1
- require_relative '../../spec_helper'
2
- require_relative '../../../lib/reek/core/examiner'
3
- require_relative '../../../lib/reek/cli/report/report'
4
- require_relative '../../../lib/reek/cli/report/formatter'
5
-
6
- RSpec.describe Reek::CLI::Report::JSONReport do
7
- let(:instance) { Reek::CLI::Report::JSONReport.new }
8
-
9
- context 'empty source' do
10
- let(:examiner) { Reek::Core::Examiner.new('') }
11
-
12
- before do
13
- instance.add_examiner examiner
14
- end
15
-
16
- it 'prints empty json' do
17
- expect { instance.show }.to output(/^\[\]$/).to_stdout
18
- end
19
- end
20
- end
@@ -1,20 +0,0 @@
1
- require_relative '../../spec_helper'
2
- require_relative '../../../lib/reek/core/examiner'
3
- require_relative '../../../lib/reek/cli/report/report'
4
- require_relative '../../../lib/reek/cli/report/formatter'
5
-
6
- RSpec.describe Reek::CLI::Report::YAMLReport do
7
- let(:instance) { Reek::CLI::Report::YAMLReport.new }
8
-
9
- context 'empty source' do
10
- let(:examiner) { Reek::Core::Examiner.new('') }
11
-
12
- before do
13
- instance.add_examiner examiner
14
- end
15
-
16
- it 'prints empty yaml' do
17
- expect { instance.show }.to output(/^--- \[\]\n.*$/).to_stdout
18
- end
19
- end
20
- end
@@ -1,18 +0,0 @@
1
- require_relative '../../spec_helper'
2
-
3
- RSpec.describe Dir do
4
- it 'reports correct smells via the Dir matcher' do
5
- files = Dir['spec/samples/two_smelly_files/*.rb']
6
- expect(files).to reek
7
- expect(files).to reek_of(:UncommunicativeVariableName)
8
- expect(files).not_to reek_of(:LargeClass)
9
- end
10
-
11
- it 'copes with daft file specs' do
12
- expect(Dir['spec/samples/two_smelly_files/*/.rb']).not_to reek
13
- end
14
-
15
- it 'copes with empty array' do
16
- expect([]).not_to reek
17
- end
18
- end
@@ -1,14 +0,0 @@
1
- require_relative '../../spec_helper'
2
- require_relative '../../../lib/reek/core/stop_context'
3
-
4
- RSpec.describe Reek::Core::StopContext do
5
- before :each do
6
- @stop = Reek::Core::StopContext.new
7
- end
8
-
9
- context 'full_name' do
10
- it 'reports full context' do
11
- expect(@stop.full_name).to eq('')
12
- end
13
- end
14
- end
@@ -1,16 +0,0 @@
1
- require_relative '../../spec_helper'
2
- require_relative '../../../lib/reek/core/tree_dresser'
3
-
4
- RSpec.describe Reek::Core::TreeDresser do
5
- let(:ifnode) { Parser::AST::Node.new(:if) }
6
- let(:sendnode) { Parser::AST::Node.new(:send) }
7
- let(:dresser) { described_class.new }
8
-
9
- it 'dresses :if sexp with IfNode' do
10
- expect(dresser.dress(ifnode, {})).to be_a Reek::Sexp::SexpExtensions::IfNode
11
- end
12
-
13
- it 'dresses :send sexp with SendNode' do
14
- expect(dresser.dress(sendnode, {})).to be_a Reek::Sexp::SexpExtensions::SendNode
15
- end
16
- end