reek 3.1 → 3.2

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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -2
  3. data/{CHANGELOG → CHANGELOG.md} +150 -123
  4. data/README.md +61 -21
  5. data/Rakefile +2 -1
  6. data/bin/reek +1 -0
  7. data/config/defaults.reek +2 -2
  8. data/docs/Attribute.md +9 -13
  9. data/docs/Basic-Smell-Options.md +2 -2
  10. data/docs/Command-Line-Options.md +2 -2
  11. data/docs/Too-Many-Instance-Variables.md +1 -1
  12. data/features/samples.feature +22 -31
  13. data/features/step_definitions/sample_file_steps.rb +2 -2
  14. data/features/support/env.rb +1 -0
  15. data/lib/reek.rb +1 -0
  16. data/lib/reek/ast/ast_node_class_map.rb +5 -1
  17. data/lib/reek/ast/node.rb +4 -2
  18. data/lib/reek/ast/object_refs.rb +9 -5
  19. data/lib/reek/ast/reference_collector.rb +4 -2
  20. data/lib/reek/cli/application.rb +12 -9
  21. data/lib/reek/cli/command.rb +4 -0
  22. data/lib/reek/cli/input.rb +4 -4
  23. data/lib/reek/cli/option_interpreter.rb +11 -7
  24. data/lib/reek/cli/options.rb +42 -40
  25. data/lib/reek/cli/reek_command.rb +3 -3
  26. data/lib/reek/cli/warning_collector.rb +7 -3
  27. data/lib/reek/code_comment.rb +5 -1
  28. data/lib/reek/configuration/app_configuration.rb +4 -4
  29. data/lib/reek/context/code_context.rb +19 -17
  30. data/lib/reek/examiner.rb +8 -6
  31. data/lib/reek/rake/task.rb +13 -22
  32. data/lib/reek/report/formatter.rb +5 -1
  33. data/lib/reek/report/report.rb +46 -44
  34. data/lib/reek/smells/attribute.rb +42 -24
  35. data/lib/reek/smells/control_parameter.rb +21 -13
  36. data/lib/reek/smells/data_clump.rb +17 -9
  37. data/lib/reek/smells/duplicate_method_call.rb +12 -6
  38. data/lib/reek/smells/long_parameter_list.rb +2 -2
  39. data/lib/reek/smells/long_yield_list.rb +4 -4
  40. data/lib/reek/smells/nested_iterators.rb +4 -2
  41. data/lib/reek/smells/nil_check.rb +6 -2
  42. data/lib/reek/smells/repeated_conditional.rb +2 -2
  43. data/lib/reek/smells/smell_configuration.rb +15 -7
  44. data/lib/reek/smells/smell_detector.rb +23 -10
  45. data/lib/reek/smells/smell_repository.rb +9 -16
  46. data/lib/reek/smells/smell_warning.rb +6 -6
  47. data/lib/reek/smells/too_many_instance_variables.rb +4 -4
  48. data/lib/reek/smells/too_many_methods.rb +2 -2
  49. data/lib/reek/smells/too_many_statements.rb +4 -4
  50. data/lib/reek/smells/uncommunicative_method_name.rb +5 -5
  51. data/lib/reek/smells/uncommunicative_module_name.rb +5 -5
  52. data/lib/reek/smells/uncommunicative_parameter_name.rb +8 -4
  53. data/lib/reek/smells/uncommunicative_variable_name.rb +8 -4
  54. data/lib/reek/source/source_code.rb +6 -2
  55. data/lib/reek/source/source_locator.rb +4 -4
  56. data/lib/reek/spec/should_reek.rb +9 -4
  57. data/lib/reek/spec/should_reek_of.rb +8 -5
  58. data/lib/reek/spec/should_reek_only_of.rb +12 -8
  59. data/lib/reek/tree_dresser.rb +6 -2
  60. data/lib/reek/tree_walker.rb +28 -22
  61. data/lib/reek/version.rb +1 -1
  62. data/reek.gemspec +6 -5
  63. data/spec/gem/yard_spec.rb +6 -9
  64. data/spec/reek/code_comment_spec.rb +1 -1
  65. data/spec/reek/report/xml_report_spec.rb +11 -21
  66. data/spec/reek/smells/attribute_spec.rb +73 -57
  67. data/spec/reek/smells/too_many_instance_variables_spec.rb +26 -12
  68. data/spec/reek/source/source_locator_spec.rb +2 -2
  69. data/spec/samples/checkstyle.xml +12 -1
  70. data/spec/spec_helper.rb +1 -0
  71. metadata +20 -7
  72. data/spec/samples/unusual_syntax.rb +0 -21
@@ -10,6 +10,10 @@ module Reek
10
10
  def initialize(options)
11
11
  @options = options
12
12
  end
13
+
14
+ private
15
+
16
+ private_attr_reader :options
13
17
  end
14
18
  end
15
19
  end
@@ -26,9 +26,9 @@ module Reek
26
26
  end
27
27
 
28
28
  def no_source_files_given?
29
- # At this point we have deleted all options from @argv. The only remaining entries
30
- # are paths to the source files. If @argv is empty, this means that no files were given.
31
- @argv.empty?
29
+ # At this point we have deleted all options from argv. The only remaining entries
30
+ # are paths to the source files. If argv is empty, this means that no files were given.
31
+ argv.empty?
32
32
  end
33
33
 
34
34
  def working_directory_as_source
@@ -36,7 +36,7 @@ module Reek
36
36
  end
37
37
 
38
38
  def sources_from_argv
39
- Source::SourceLocator.new(@argv).sources
39
+ Source::SourceLocator.new(argv).sources
40
40
  end
41
41
 
42
42
  def source_from_pipe
@@ -13,11 +13,11 @@ module Reek
13
13
 
14
14
  extend Forwardable
15
15
 
16
- def_delegators :@options, :smells_to_detect
16
+ def_delegators :options, :smells_to_detect
17
17
 
18
18
  def initialize(options)
19
19
  @options = options
20
- @argv = @options.argv
20
+ @argv = options.argv
21
21
  end
22
22
 
23
23
  def reporter
@@ -30,7 +30,7 @@ module Reek
30
30
  end
31
31
 
32
32
  def report_class
33
- Report.report_class(@options.report_format)
33
+ Report.report_class(options.report_format)
34
34
  end
35
35
 
36
36
  def warning_formatter
@@ -38,20 +38,24 @@ module Reek
38
38
  end
39
39
 
40
40
  def warning_formatter_class
41
- Report.warning_formatter_class(@options.show_links ? :wiki_links : :simple)
41
+ Report.warning_formatter_class(options.show_links ? :wiki_links : :simple)
42
42
  end
43
43
 
44
44
  def location_formatter
45
- Report.location_formatter(@options.location_format)
45
+ Report.location_formatter(options.location_format)
46
46
  end
47
47
 
48
48
  def heading_formatter
49
- Report.heading_formatter(@options.show_empty ? :verbose : :quiet)
49
+ Report.heading_formatter(options.show_empty ? :verbose : :quiet)
50
50
  end
51
51
 
52
52
  def sort_by_issue_count
53
- @options.sorting == :smelliness
53
+ options.sorting == :smelliness
54
54
  end
55
+
56
+ private
57
+
58
+ private_attr_reader :argv, :options
55
59
  end
56
60
  end
57
61
  end
@@ -23,14 +23,16 @@ module Reek
23
23
  end
24
24
 
25
25
  def parse
26
- @parser.parse!(@argv)
27
- @options.argv = @argv
28
- Rainbow.enabled = @options.colored
29
- @options
26
+ parser.parse!(argv)
27
+ options.argv = argv
28
+ Rainbow.enabled = options.colored
29
+ options
30
30
  end
31
31
 
32
32
  private
33
33
 
34
+ private_attr_reader :argv, :options, :parser
35
+
34
36
  def color_support?
35
37
  $stdout.tty?
36
38
  end
@@ -44,8 +46,8 @@ module Reek
44
46
  end
45
47
 
46
48
  def set_banner
47
- program_name = @parser.program_name
48
- @parser.banner = <<-EOB.gsub(/^[ ]+/, '')
49
+ program_name = parser.program_name
50
+ parser.banner = <<-EOB.gsub(/^[ ]+/, '')
49
51
  Usage: #{program_name} [options] [files]
50
52
 
51
53
  Examples:
@@ -60,29 +62,29 @@ module Reek
60
62
  end
61
63
 
62
64
  def set_alternative_formatter_options
63
- @parser.separator "\nReport format:"
64
- @parser.on(
65
+ parser.separator "\nReport format:"
66
+ parser.on(
65
67
  '-f', '--format FORMAT', [:html, :text, :yaml, :json, :xml],
66
68
  'Report smells in the given format:',
67
69
  ' html', ' text (default)', ' yaml', ' json', ' xml'
68
70
  ) do |opt|
69
- @options.report_format = opt
71
+ options.report_format = opt
70
72
  end
71
73
  end
72
74
 
73
75
  def set_configuration_options
74
- @parser.separator 'Configuration:'
75
- @parser.on('-c', '--config FILE', 'Read configuration options from FILE') do |file|
76
+ parser.separator 'Configuration:'
77
+ parser.on('-c', '--config FILE', 'Read configuration options from FILE') do |file|
76
78
  raise ArgumentError, "Config file #{file} doesn't exist" unless File.exist?(file)
77
- @options.config_file = Pathname.new(file)
79
+ options.config_file = Pathname.new(file)
78
80
  end
79
- @parser.on('--smell SMELL', 'Detect smell SMELL (default: all enabled smells)') do |smell|
80
- @options.smells_to_detect << smell
81
+ parser.on('--smell SMELL', 'Detect smell SMELL (default: all enabled smells)') do |smell|
82
+ options.smells_to_detect << smell
81
83
  end
82
84
  end
83
85
 
84
86
  def set_report_formatting_options
85
- @parser.separator "\nText format options:"
87
+ parser.separator "\nText format options:"
86
88
  set_up_color_option
87
89
  set_up_verbosity_options
88
90
  set_up_location_formatting_options
@@ -90,50 +92,50 @@ module Reek
90
92
  end
91
93
 
92
94
  def set_up_color_option
93
- @parser.on('--[no-]color', 'Use colors for the output (default: true)') do |opt|
94
- @options.colored = opt
95
+ parser.on('--[no-]color', 'Use colors for the output (default: true)') do |opt|
96
+ options.colored = opt
95
97
  end
96
98
  end
97
99
 
98
100
  def set_up_verbosity_options
99
- @parser.on('-V', '--[no-]empty-headings',
100
- 'Show headings for smell-free source files (default: false)') do |show_empty|
101
- @options.show_empty = show_empty
101
+ parser.on('-V', '--[no-]empty-headings',
102
+ 'Show headings for smell-free source files (default: false)') do |show_empty|
103
+ options.show_empty = show_empty
102
104
  end
103
- @parser.on('-U', '--[no-]wiki-links',
104
- 'Show link to related wiki page for each smell (default: false)') do |show_links|
105
- @options.show_links = show_links
105
+ parser.on('-U', '--[no-]wiki-links',
106
+ 'Show link to related wiki page for each smell (default: false)') do |show_links|
107
+ options.show_links = show_links
106
108
  end
107
109
  end
108
110
 
109
111
  def set_up_location_formatting_options
110
- @parser.on('-n', '--[no-]line-numbers',
111
- 'Show line numbers in the output (default: true)') do |show_numbers|
112
- @options.location_format = show_numbers ? :numbers : :plain
112
+ parser.on('-n', '--[no-]line-numbers',
113
+ 'Show line numbers in the output (default: true)') do |show_numbers|
114
+ options.location_format = show_numbers ? :numbers : :plain
113
115
  end
114
- @parser.on('-s', '--single-line',
115
- 'Show location in editor-compatible single-line-per-smell format') do
116
- @options.location_format = :single_line
116
+ parser.on('-s', '--single-line',
117
+ 'Show location in editor-compatible single-line-per-smell format') do
118
+ options.location_format = :single_line
117
119
  end
118
120
  end
119
121
 
120
122
  def set_up_sorting_option
121
- @parser.on('--sort-by SORTING', [:smelliness, :none],
122
- 'Sort reported files by the given criterium:',
123
- ' smelliness ("smelliest" files first)',
124
- ' none (default - output in processing order)') do |sorting|
125
- @options.sorting = sorting
126
- end
123
+ parser.on('--sort-by SORTING', [:smelliness, :none],
124
+ 'Sort reported files by the given criterium:',
125
+ ' smelliness ("smelliest" files first)',
126
+ ' none (default - output in processing order)') do |sorting|
127
+ options.sorting = sorting
128
+ end
127
129
  end
128
130
 
129
131
  def set_utility_options
130
- @parser.separator "\nUtility options:"
131
- @parser.on_tail('-h', '--help', 'Show this message') do
132
- puts @parser
132
+ parser.separator "\nUtility options:"
133
+ parser.on_tail('-h', '--help', 'Show this message') do
134
+ puts parser
133
135
  exit
134
136
  end
135
- @parser.on_tail('-v', '--version', 'Show version') do
136
- puts "#{@parser.program_name} #{Reek::Version::STRING}\n"
137
+ parser.on_tail('-v', '--version', 'Show version') do
138
+ puts "#{parser.program_name} #{Reek::Version::STRING}\n"
137
139
  exit
138
140
  end
139
141
  end
@@ -10,7 +10,7 @@ module Reek
10
10
  # @api private
11
11
  class ReekCommand < Command
12
12
  def execute(app)
13
- @options.sources.each do |source|
13
+ options.sources.each do |source|
14
14
  reporter.add_examiner Examiner.new(source, smell_names, configuration: app.configuration)
15
15
  end
16
16
  reporter.smells? ? app.report_smells : app.report_success
@@ -20,11 +20,11 @@ module Reek
20
20
  private
21
21
 
22
22
  def reporter
23
- @reporter ||= @options.reporter
23
+ @reporter ||= options.reporter
24
24
  end
25
25
 
26
26
  def smell_names
27
- @smell_names ||= @options.smells_to_detect
27
+ @smell_names ||= options.smells_to_detect
28
28
  end
29
29
  end
30
30
  end
@@ -8,16 +8,20 @@ module Reek
8
8
  # @api private
9
9
  class WarningCollector
10
10
  def initialize
11
- @warnings = Set.new
11
+ @warnings_set = Set.new
12
12
  end
13
13
 
14
14
  def found_smell(warning)
15
- @warnings.add(warning)
15
+ warnings_set.add(warning)
16
16
  end
17
17
 
18
18
  def warnings
19
- @warnings.to_a.sort
19
+ warnings_set.to_a.sort
20
20
  end
21
+
22
+ private
23
+
24
+ private_attr_reader :warnings_set
21
25
  end
22
26
  end
23
27
  end
@@ -21,7 +21,7 @@ module Reek
21
21
  end
22
22
 
23
23
  def descriptive?
24
- @text.split(/\s+/).length >= 2
24
+ text.split(/\s+/).length >= 2
25
25
  end
26
26
 
27
27
  protected
@@ -32,5 +32,9 @@ module Reek
32
32
  # TODO: extend this to all configs -------------------^
33
33
  # TODO: extend to allow configuration of whole smell class, not just subclass
34
34
  end
35
+
36
+ private
37
+
38
+ private_attr_reader :text
35
39
  end
36
40
  end
@@ -40,9 +40,9 @@ module Reek
40
40
  # config_file = #<Pathname:config/defaults.reek>,
41
41
  # argv = [ "lib/reek/spec" ]>
42
42
  def initialize(options = nil)
43
- self.directory_directives = {}
44
- self.default_directive = {}
45
- self.exclude_paths = []
43
+ @directory_directives = {}
44
+ @default_directive = {}
45
+ @exclude_paths = []
46
46
 
47
47
  load options
48
48
  end
@@ -56,7 +56,7 @@ module Reek
56
56
 
57
57
  private
58
58
 
59
- attr_writer :exclude_paths, :default_directive, :directory_directives
59
+ private_attr_writer :exclude_paths
60
60
 
61
61
  # @param source_via [String] - the source of the code inspected
62
62
  # Might be a string, STDIN or Filename / Pathname. We're only interested in the source
@@ -70,11 +70,11 @@ module Reek
70
70
  # @param child [CodeContext] the child context to register
71
71
  def append_child_context(child)
72
72
  child.visibility = tracked_visibility
73
- @children << child
73
+ children << child
74
74
  end
75
75
 
76
76
  def count_statements(num)
77
- @num_statements += num
77
+ self.num_statements += num
78
78
  end
79
79
 
80
80
  def record_call_to(exp)
@@ -83,19 +83,19 @@ module Reek
83
83
  case type
84
84
  when :lvar, :lvasgn
85
85
  unless exp.object_creation_call?
86
- @refs.record_reference_to(receiver.name, line: exp.line)
86
+ refs.record_reference_to(receiver.name, line: exp.line)
87
87
  end
88
88
  when :self
89
- @refs.record_reference_to(:self, line: exp.line)
89
+ refs.record_reference_to(:self, line: exp.line)
90
90
  end
91
91
  end
92
92
 
93
93
  def record_use_of_self
94
- @refs.record_reference_to(:self)
94
+ refs.record_reference_to(:self)
95
95
  end
96
96
 
97
97
  def name
98
- @exp.name
98
+ exp.name
99
99
  end
100
100
 
101
101
  def local_nodes(type, &blk)
@@ -103,7 +103,7 @@ module Reek
103
103
  end
104
104
 
105
105
  def each_node(type, ignoring, &blk)
106
- @exp.each_node(type, ignoring, &blk)
106
+ exp.each_node(type, ignoring, &blk)
107
107
  end
108
108
 
109
109
  def matches?(candidates)
@@ -119,8 +119,7 @@ module Reek
119
119
  end
120
120
 
121
121
  def full_name
122
- context = @context ? @context.full_name : ''
123
- exp.full_name(context)
122
+ exp.full_name(context ? context.full_name : '')
124
123
  end
125
124
 
126
125
  def config_for(detector_class)
@@ -140,46 +139,49 @@ module Reek
140
139
  # @param names [Array<Symbol>]
141
140
  def track_visibility(visibility, names = [])
142
141
  if names.any?
143
- @children.each do |child|
142
+ children.each do |child|
144
143
  child.visibility = visibility if names.include? child.name
145
144
  end
146
145
  else
147
- @tracked_visibility = visibility
146
+ self.tracked_visibility = visibility
148
147
  end
149
148
  end
150
149
 
151
150
  def type
152
- @exp.type
151
+ exp.type
153
152
  end
154
153
 
155
154
  # Iterate over +self+ and child contexts.
156
155
  def each(&block)
157
156
  yield self
158
- @children.each do |child|
157
+ children.each do |child|
159
158
  child.each(&block)
160
159
  end
161
160
  end
162
161
 
163
162
  protected
164
163
 
165
- attr_writer :visibility
164
+ attr_writer :num_statements, :visibility
166
165
 
167
166
  private
168
167
 
168
+ private_attr_writer :tracked_visibility
169
+ private_attr_reader :context, :refs
170
+
169
171
  def tracked_visibility
170
172
  @tracked_visibility ||= :public
171
173
  end
172
174
 
173
175
  def config
174
- @config ||= if @exp
175
- CodeComment.new(@exp.full_comment || '').config
176
+ @config ||= if exp
177
+ CodeComment.new(exp.full_comment || '').config
176
178
  else
177
179
  {}
178
180
  end
179
181
  end
180
182
 
181
183
  def context_config_for(detector_class)
182
- @context ? @context.config_for(detector_class) : {}
184
+ context ? context.config_for(detector_class) : {}
183
185
  end
184
186
  end
185
187
  end
@@ -39,14 +39,14 @@ module Reek
39
39
  # @return [String] description of the source being analysed
40
40
  #
41
41
  def description
42
- @description ||= @source.description
42
+ @description ||= source.description
43
43
  end
44
44
 
45
45
  #
46
46
  # @return [Array<SmellWarning>] the smells found in the source
47
47
  #
48
48
  def smells
49
- @smells ||= @collector.warnings
49
+ @smells ||= collector.warnings
50
50
  end
51
51
 
52
52
  #
@@ -65,13 +65,15 @@ module Reek
65
65
 
66
66
  private
67
67
 
68
+ private_attr_reader :configuration, :collector, :smell_types, :source
69
+
68
70
  def run
69
71
  smell_repository = Smells::SmellRepository.new(source_description: description,
70
- smell_types: @smell_types,
71
- configuration: @configuration)
72
- syntax_tree = @source.syntax_tree
72
+ smell_types: smell_types,
73
+ configuration: configuration)
74
+ syntax_tree = source.syntax_tree
73
75
  TreeWalker.new(smell_repository, syntax_tree).walk if syntax_tree
74
- smell_repository.report_on(@collector)
76
+ smell_repository.report_on(collector)
75
77
  end
76
78
 
77
79
  def eligible_smell_types(filter_by_smells = [])