reek 3.6.1 → 3.7.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 (32) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/features/command_line_interface/basic_usage.feature +8 -8
  4. data/features/command_line_interface/options.feature +2 -1
  5. data/features/command_line_interface/smell_selection.feature +1 -1
  6. data/features/command_line_interface/smells_count.feature +8 -8
  7. data/features/command_line_interface/stdin.feature +3 -3
  8. data/features/configuration_files/directory_specific_directives.feature +9 -9
  9. data/features/configuration_files/masking_smells.feature +5 -5
  10. data/features/configuration_loading.feature +6 -6
  11. data/features/programmatic_access.feature +3 -3
  12. data/features/rake_task/rake_task.feature +9 -9
  13. data/features/reports/json.feature +4 -0
  14. data/features/reports/reports.feature +49 -37
  15. data/features/reports/yaml.feature +4 -0
  16. data/features/samples.feature +265 -265
  17. data/lib/reek/cli/options.rb +5 -4
  18. data/lib/reek/cli/warning_collector.rb +1 -1
  19. data/lib/reek/report.rb +2 -1
  20. data/lib/reek/report/code_climate_formatter.rb +46 -0
  21. data/lib/reek/report/formatter.rb +13 -8
  22. data/lib/reek/report/report.rb +12 -0
  23. data/lib/reek/smells/smell_warning.rb +2 -2
  24. data/lib/reek/version.rb +1 -1
  25. data/reek.gemspec +5 -4
  26. data/spec/reek/report/code_climate_formatter_spec.rb +63 -0
  27. data/spec/reek/report/code_climate_report_spec.rb +68 -0
  28. data/spec/reek/report/json_report_spec.rb +18 -18
  29. data/spec/reek/report/yaml_report_spec.rb +17 -17
  30. data/spec/reek/smells/smell_warning_spec.rb +11 -11
  31. data/spec/samples/checkstyle.xml +1 -1
  32. metadata +20 -3
@@ -9,7 +9,7 @@ module Reek
9
9
  #
10
10
  # See {file:docs/Command-Line-Options.md} for details.
11
11
  #
12
- # :reek:TooManyInstanceVariables: { max_instance_variables: 6 }
12
+ # :reek:TooManyInstanceVariables: { max_instance_variables: 7 }
13
13
  # :reek:Attribute: { enabled: false }
14
14
  class Options
15
15
  attr_reader :argv, :parser, :smells_to_detect
@@ -26,6 +26,7 @@ module Reek
26
26
  @parser = OptionParser.new
27
27
  @report_format = :text
28
28
  @location_format = :numbers
29
+ @show_links = true
29
30
  @smells_to_detect = []
30
31
  @colored = color_support?
31
32
 
@@ -72,9 +73,9 @@ module Reek
72
73
  def set_alternative_formatter_options
73
74
  parser.separator "\nReport format:"
74
75
  parser.on(
75
- '-f', '--format FORMAT', [:html, :text, :yaml, :json, :xml],
76
+ '-f', '--format FORMAT', [:html, :text, :yaml, :json, :xml, :code_climate],
76
77
  'Report smells in the given format:',
77
- ' html', ' text (default)', ' yaml', ' json', ' xml'
78
+ ' html', ' text (default)', ' yaml', ' json', ' xml', ' code_climate'
78
79
  ) do |opt|
79
80
  self.report_format = opt
80
81
  end
@@ -112,7 +113,7 @@ module Reek
112
113
  self.show_empty = show_empty
113
114
  end
114
115
  parser.on('-U', '--[no-]wiki-links',
115
- 'Show link to related wiki page for each smell (default: false)') do |show_links|
116
+ 'Show link to related wiki page for each smell (default: true)') do |show_links|
116
117
  self.show_links = show_links
117
118
  end
118
119
  end
@@ -16,7 +16,7 @@ module Reek
16
16
  end
17
17
 
18
18
  def warnings
19
- warnings_set.to_a.sort
19
+ warnings_set.sort
20
20
  end
21
21
 
22
22
  private
data/lib/reek/report.rb CHANGED
@@ -12,7 +12,8 @@ module Reek
12
12
  json: JSONReport,
13
13
  html: HTMLReport,
14
14
  xml: XMLReport,
15
- text: TextReport
15
+ text: TextReport,
16
+ code_climate: CodeClimateReport
16
17
  }
17
18
 
18
19
  LOCATION_FORMATTERS = {
@@ -0,0 +1,46 @@
1
+ require 'codeclimate_engine'
2
+ require 'private_attr'
3
+
4
+ module Reek
5
+ module Report
6
+ # Generates a hash in the structure specified by the Code Climate engine spec
7
+ class CodeClimateFormatter
8
+ private_attr_reader :warning
9
+
10
+ def initialize(warning)
11
+ @warning = warning
12
+ end
13
+
14
+ def to_hash
15
+ CCEngine::Issue.new(check_name: check_name,
16
+ description: description,
17
+ categories: categories,
18
+ location: location
19
+ ).to_hash
20
+ end
21
+
22
+ private
23
+
24
+ def description
25
+ [warning.context, warning.message].join(' ')
26
+ end
27
+
28
+ def check_name
29
+ [warning.smell_category, warning.smell_type].join('/')
30
+ end
31
+
32
+ def categories
33
+ # TODO: provide mappings for Reek's smell categories
34
+ ['Complexity']
35
+ end
36
+
37
+ def location
38
+ warning_lines = warning.lines
39
+ CCEngine::Location::LineRange.new(
40
+ path: warning.source,
41
+ line_range: warning_lines.first..warning_lines.last
42
+ )
43
+ end
44
+ end
45
+ end
46
+ end
@@ -1,5 +1,6 @@
1
1
  require 'private_attr/everywhere'
2
2
  require_relative 'location_formatter'
3
+ require_relative '../report/code_climate_formatter'
3
4
 
4
5
  module Reek
5
6
  module Report
@@ -9,13 +10,13 @@ module Reek
9
10
  # passed-in warning formatter.
10
11
  #
11
12
  module Formatter
12
- def self.format_list(warnings, formatter: SimpleWarningFormatter.new)
13
- warnings.map do |warning|
14
- " #{formatter.format warning}"
15
- end.join("\n")
13
+ module_function
14
+
15
+ def format_list(warnings, formatter: SimpleWarningFormatter.new)
16
+ warnings.map { |warning| " #{formatter.format(warning)}" }.join("\n")
16
17
  end
17
18
 
18
- def self.header(examiner)
19
+ def header(examiner)
19
20
  count = examiner.smells_count
20
21
  result = Rainbow("#{examiner.description} -- ").cyan +
21
22
  Rainbow("#{count} warning").yellow
@@ -42,6 +43,11 @@ module Reek
42
43
  warning.yaml_hash
43
44
  end
44
45
 
46
+ # :reek:UtilityFunction
47
+ def format_code_climate_hash(warning)
48
+ CodeClimateFormatter.new(warning).to_hash
49
+ end
50
+
45
51
  private_attr_reader :location_formatter
46
52
  end
47
53
 
@@ -53,12 +59,11 @@ module Reek
53
59
  BASE_URL_FOR_HELP_LINK = 'https://github.com/troessner/reek/blob/master/docs/'
54
60
 
55
61
  def format(warning)
56
- "#{super} " \
57
- "[#{explanatory_link(warning)}]"
62
+ "#{super} [#{explanatory_link(warning)}]"
58
63
  end
59
64
 
60
65
  def format_hash(warning)
61
- super(warning).merge('wiki_link' => explanatory_link(warning))
66
+ super.merge('wiki_link' => explanatory_link(warning))
62
67
  end
63
68
 
64
69
  private
@@ -143,6 +143,18 @@ module Reek
143
143
  end
144
144
  end
145
145
 
146
+ #
147
+ # Displays a list of smells in Code Climate engine format
148
+ # (https://github.com/codeclimate/spec/blob/master/SPEC.md)
149
+ # JSON with empty array for 0 smells
150
+ #
151
+ class CodeClimateReport < Base
152
+ # @public
153
+ def show(out = $stdout)
154
+ out.print ::JSON.generate smells.map { |smell| warning_formatter.format_code_climate_hash(smell) }
155
+ end
156
+ end
157
+
146
158
  #
147
159
  # Saves the report as a HTML file
148
160
  #
@@ -61,13 +61,13 @@ module Reek
61
61
  end
62
62
 
63
63
  def base_message
64
- "#{context} #{message} (#{smell_type})"
64
+ "#{smell_type}: #{context} #{message}"
65
65
  end
66
66
 
67
67
  protected
68
68
 
69
69
  def sort_key
70
- [context, message, smell_category]
70
+ [smell_type, context, message]
71
71
  end
72
72
 
73
73
  private
data/lib/reek/version.rb CHANGED
@@ -6,6 +6,6 @@ module Reek
6
6
  # @public
7
7
  module Version
8
8
  # @public
9
- STRING = '3.6.1'
9
+ STRING = '3.7.0'
10
10
  end
11
11
  end
data/reek.gemspec CHANGED
@@ -22,10 +22,11 @@ Gem::Specification.new do |s|
22
22
  s.required_ruby_version = '>= 2.0.0'
23
23
  s.summary = 'Code smell detector for Ruby'
24
24
 
25
- s.add_runtime_dependency 'parser', '~> 2.2', '>= 2.2.2.5'
26
- s.add_runtime_dependency 'private_attr', '~> 1.1'
27
- s.add_runtime_dependency 'rainbow', '~> 2.0'
28
- s.add_runtime_dependency 'unparser', '~> 0.2.2'
25
+ s.add_runtime_dependency 'codeclimate-engine-rb', '~> 0.1.0'
26
+ s.add_runtime_dependency 'parser', '~> 2.2', '>= 2.2.2.5'
27
+ s.add_runtime_dependency 'private_attr', '~> 1.1'
28
+ s.add_runtime_dependency 'rainbow', '~> 2.0'
29
+ s.add_runtime_dependency 'unparser', '~> 0.2.2'
29
30
 
30
31
  s.add_development_dependency 'activesupport', '~> 4.2'
31
32
  s.add_development_dependency 'aruba', '~> 0.10.0'
@@ -0,0 +1,63 @@
1
+ require_relative '../../spec_helper'
2
+ require_lib 'reek/report/code_climate_formatter'
3
+
4
+ RSpec.describe Reek::Report::CodeClimateFormatter, '#to_hash' do
5
+ it "sets the type as 'issue'" do
6
+ warning = FactoryGirl.build(:smell_warning)
7
+ issue = Reek::Report::CodeClimateFormatter.new(warning)
8
+
9
+ result = issue.to_hash
10
+
11
+ expect(result).to include(type: 'issue')
12
+ end
13
+
14
+ it 'sets the category' do
15
+ warning = FactoryGirl.build(:smell_warning)
16
+ issue = Reek::Report::CodeClimateFormatter.new(warning)
17
+
18
+ result = issue.to_hash
19
+
20
+ expect(result).to include(categories: ['Complexity'])
21
+ end
22
+
23
+ it 'constructs a description based on the context and message' do
24
+ warning = FactoryGirl.build(:smell_warning,
25
+ context: 'context foo',
26
+ message: 'message bar')
27
+ issue = Reek::Report::CodeClimateFormatter.new(warning)
28
+
29
+ result = issue.to_hash
30
+
31
+ expect(result).to include(
32
+ description: 'context foo message bar')
33
+ end
34
+
35
+ it 'sets a check name based on the smell detector' do
36
+ warning = FactoryGirl.build(:smell_warning,
37
+ smell_detector: Reek::Smells::UtilityFunction.new)
38
+ issue = Reek::Report::CodeClimateFormatter.new(warning)
39
+
40
+ result = issue.to_hash
41
+
42
+ expect(result).to include(check_name: 'LowCohesion/UtilityFunction')
43
+ end
44
+
45
+ it 'sets the location' do
46
+ warning = FactoryGirl.build(:smell_warning,
47
+ lines: [1, 2],
48
+ source: 'a/ruby/source/file.rb')
49
+ issue = Reek::Report::CodeClimateFormatter.new(warning)
50
+
51
+ result = issue.to_hash
52
+
53
+ expect(result).to include(
54
+ location: {
55
+ path: 'a/ruby/source/file.rb',
56
+ lines: {
57
+ begin: 1,
58
+ end: 2
59
+ }
60
+ }
61
+ )
62
+ end
63
+ end
@@ -0,0 +1,68 @@
1
+ require_relative '../../spec_helper'
2
+ require_lib 'reek/examiner'
3
+ require_lib 'reek/report/report'
4
+ require_lib 'reek/report/formatter'
5
+
6
+ require 'json'
7
+ require 'stringio'
8
+
9
+ RSpec.describe Reek::Report::CodeClimateReport do
10
+ let(:options) { {} }
11
+ let(:instance) { Reek::Report::CodeClimateReport.new(options) }
12
+ let(:examiner) { Reek::Examiner.new(source) }
13
+
14
+ before do
15
+ instance.add_examiner examiner
16
+ end
17
+
18
+ context 'with empty source' do
19
+ let(:source) { '' }
20
+
21
+ it 'prints empty json' do
22
+ expect { instance.show }.to output(/^\[\]$/).to_stdout
23
+ end
24
+ end
25
+
26
+ context 'with smelly source' do
27
+ let(:source) { 'def simple(a) a[3] end' }
28
+
29
+ it 'prints smells as json' do
30
+ out = StringIO.new
31
+ instance.show(out)
32
+ out.rewind
33
+ result = JSON.parse(out.read)
34
+ expected = JSON.parse <<-EOS
35
+ [
36
+ {
37
+ "type": "issue",
38
+ "check_name": "UncommunicativeName/UncommunicativeParameterName",
39
+ "description": "simple has the parameter name 'a'",
40
+ "categories": ["Complexity"],
41
+ "location": {
42
+ "path": "string",
43
+ "lines": {
44
+ "begin": 1,
45
+ "end": 1
46
+ }
47
+ }
48
+ },
49
+ {
50
+ "type": "issue",
51
+ "check_name": "LowCohesion/UtilityFunction",
52
+ "description": "simple doesn't depend on instance state (maybe move it to another class?)",
53
+ "categories": ["Complexity"],
54
+ "location": {
55
+ "path": "string",
56
+ "lines": {
57
+ "begin": 1,
58
+ "end": 1
59
+ }
60
+ }
61
+ }
62
+ ]
63
+ EOS
64
+
65
+ expect(result).to eq expected
66
+ end
67
+ end
68
+ end
@@ -36,20 +36,20 @@ RSpec.describe Reek::Report::JSONReport do
36
36
  {
37
37
  "context": "simple",
38
38
  "lines": [1],
39
- "message": "doesn't depend on instance state (maybe move it to another class?)",
40
- "smell_category": "LowCohesion",
41
- "smell_type": "UtilityFunction",
39
+ "message": "has the parameter name 'a'",
40
+ "smell_category": "UncommunicativeName",
41
+ "smell_type": "UncommunicativeParameterName",
42
42
  "source": "string",
43
- "name": "simple"
43
+ "name": "a"
44
44
  },
45
45
  {
46
46
  "context": "simple",
47
47
  "lines": [1],
48
- "message": "has the parameter name 'a'",
49
- "smell_category": "UncommunicativeName",
50
- "smell_type": "UncommunicativeParameterName",
48
+ "message": "doesn't depend on instance state (maybe move it to another class?)",
49
+ "smell_category": "LowCohesion",
50
+ "smell_type": "UtilityFunction",
51
51
  "source": "string",
52
- "name": "a"
52
+ "name": "simple"
53
53
  }
54
54
  ]
55
55
  EOS
@@ -67,16 +67,6 @@ RSpec.describe Reek::Report::JSONReport do
67
67
  result = JSON.parse(out.read)
68
68
  expected = JSON.parse <<-EOS
69
69
  [
70
- {
71
- "context": "simple",
72
- "lines": [1],
73
- "message": "doesn't depend on instance state (maybe move it to another class?)",
74
- "smell_category": "LowCohesion",
75
- "smell_type": "UtilityFunction",
76
- "source": "string",
77
- "name": "simple",
78
- "wiki_link": "https://github.com/troessner/reek/blob/master/docs/Utility-Function.md"
79
- },
80
70
  {
81
71
  "context": "simple",
82
72
  "lines": [1],
@@ -86,6 +76,16 @@ RSpec.describe Reek::Report::JSONReport do
86
76
  "source": "string",
87
77
  "name": "a",
88
78
  "wiki_link": "https://github.com/troessner/reek/blob/master/docs/Uncommunicative-Parameter-Name.md"
79
+ },
80
+ {
81
+ "context": "simple",
82
+ "lines": [1],
83
+ "message": "doesn't depend on instance state (maybe move it to another class?)",
84
+ "smell_category": "LowCohesion",
85
+ "smell_type": "UtilityFunction",
86
+ "source": "string",
87
+ "name": "simple",
88
+ "wiki_link": "https://github.com/troessner/reek/blob/master/docs/Utility-Function.md"
89
89
  }
90
90
  ]
91
91
  EOS
@@ -33,14 +33,6 @@ RSpec.describe Reek::Report::YAMLReport do
33
33
  result = YAML.load(out.read)
34
34
  expected = YAML.load <<-EOS
35
35
  ---
36
- - context: "simple"
37
- lines:
38
- - 1
39
- message: "doesn't depend on instance state (maybe move it to another class?)"
40
- smell_category: "LowCohesion"
41
- smell_type: "UtilityFunction"
42
- source: "string"
43
- name: "simple"
44
36
  - context: "simple"
45
37
  lines:
46
38
  - 1
@@ -49,6 +41,14 @@ RSpec.describe Reek::Report::YAMLReport do
49
41
  smell_type: "UncommunicativeParameterName"
50
42
  source: "string"
51
43
  name: "a"
44
+ - context: "simple"
45
+ lines:
46
+ - 1
47
+ message: "doesn't depend on instance state (maybe move it to another class?)"
48
+ smell_category: "LowCohesion"
49
+ smell_type: "UtilityFunction"
50
+ source: "string"
51
+ name: "simple"
52
52
  EOS
53
53
 
54
54
  expect(result).to eq expected
@@ -63,15 +63,6 @@ RSpec.describe Reek::Report::YAMLReport do
63
63
  result = YAML.load(out.read)
64
64
  expected = YAML.load <<-EOS
65
65
  ---
66
- - context: "simple"
67
- lines:
68
- - 1
69
- message: "doesn't depend on instance state (maybe move it to another class?)"
70
- smell_category: "LowCohesion"
71
- smell_type: "UtilityFunction"
72
- source: "string"
73
- name: "simple"
74
- wiki_link: "https://github.com/troessner/reek/blob/master/docs/Utility-Function.md"
75
66
  - context: "simple"
76
67
  lines:
77
68
  - 1
@@ -81,6 +72,15 @@ RSpec.describe Reek::Report::YAMLReport do
81
72
  source: "string"
82
73
  name: "a"
83
74
  wiki_link: "https://github.com/troessner/reek/blob/master/docs/Uncommunicative-Parameter-Name.md"
75
+ - context: "simple"
76
+ lines:
77
+ - 1
78
+ message: "doesn't depend on instance state (maybe move it to another class?)"
79
+ smell_category: "LowCohesion"
80
+ smell_type: "UtilityFunction"
81
+ source: "string"
82
+ name: "simple"
83
+ wiki_link: "https://github.com/troessner/reek/blob/master/docs/Utility-Function.md"
84
84
  EOS
85
85
 
86
86
  expect(result).to eq expected