reek 3.6.1 → 3.7.0

Sign up to get free protection for your applications and to get access to all the features.
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