reek 4.0.1 → 4.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/Gemfile +3 -3
- data/README.md +1 -1
- data/docs/Attribute.md +1 -1
- data/docs/Boolean-Parameter.md +8 -6
- data/docs/Class-Variable.md +1 -1
- data/docs/Command-Line-Options.md +26 -1
- data/docs/Control-Couple.md +13 -9
- data/docs/Control-Parameter.md +8 -5
- data/docs/Data-Clump.md +9 -7
- data/docs/Duplicate-Method-Call.md +1 -1
- data/docs/Irresponsible-Module.md +4 -3
- data/docs/Large-Class.md +7 -12
- data/docs/Long-Parameter-List.md +7 -6
- data/docs/Long-Yield-List.md +7 -6
- data/docs/Nested-Iterators.md +7 -7
- data/docs/Nil-Check.md +8 -4
- data/docs/Prima-Donna-Method.md +19 -9
- data/docs/Reek-Driven-Development.md +3 -1
- data/docs/Repeated-Conditional.md +5 -2
- data/docs/Too-Many-Instance-Variables.md +7 -7
- data/docs/Too-Many-Methods.md +10 -9
- data/docs/Too-Many-Statements.md +8 -4
- data/docs/Uncommunicative-Method-Name.md +8 -7
- data/docs/Uncommunicative-Module-Name.md +8 -8
- data/docs/Uncommunicative-Name.md +5 -3
- data/docs/Uncommunicative-Parameter-Name.md +9 -9
- data/docs/Uncommunicative-Variable-Name.md +8 -7
- data/docs/Unused-Parameters.md +5 -4
- data/docs/Unused-Private-Method.md +3 -3
- data/docs/Utility-Function.md +3 -3
- data/lib/reek/ast/object_refs.rb +4 -4
- data/lib/reek/cli/application.rb +40 -4
- data/lib/reek/cli/command/base_command.rb +3 -2
- data/lib/reek/cli/command/report_command.rb +35 -5
- data/lib/reek/cli/command/todo_list_command.rb +4 -4
- data/lib/reek/configuration/app_configuration.rb +15 -15
- data/lib/reek/configuration/default_directive.rb +2 -1
- data/lib/reek/context/code_context.rb +1 -1
- data/lib/reek/context/module_context.rb +5 -6
- data/lib/reek/spec.rb +6 -4
- data/lib/reek/spec/should_reek_of.rb +11 -3
- data/lib/reek/version.rb +1 -1
- data/spec/factories/factories.rb +0 -11
- data/spec/reek/cli/application_spec.rb +83 -9
- data/spec/reek/cli/command/report_command_spec.rb +17 -14
- data/spec/reek/cli/command/todo_list_command_spec.rb +12 -10
- data/spec/reek/smells/duplicate_method_call_spec.rb +8 -14
- data/spec/reek/smells/nested_iterators_spec.rb +12 -16
- data/spec/reek/smells/uncommunicative_method_name_spec.rb +2 -4
- data/spec/reek/smells/uncommunicative_module_name_spec.rb +2 -4
- data/spec/reek/smells/uncommunicative_parameter_name_spec.rb +2 -4
- data/spec/reek/smells/unused_private_method_spec.rb +47 -48
- data/spec/reek/smells/utility_function_spec.rb +5 -10
- data/spec/reek/spec/should_reek_of_spec.rb +27 -0
- metadata +2 -6
- data/lib/reek/cli/input.rb +0 -49
- data/lib/reek/cli/option_interpreter.rb +0 -58
- data/spec/reek/cli/input_spec.rb +0 -71
- data/spec/reek/cli/option_interpreter_spec.rb +0 -20
@@ -6,14 +6,15 @@ module Reek
|
|
6
6
|
# Base class for all commands
|
7
7
|
#
|
8
8
|
class BaseCommand
|
9
|
-
def initialize(options
|
9
|
+
def initialize(options:, sources:, configuration:)
|
10
10
|
@options = options
|
11
11
|
@sources = sources
|
12
|
+
@configuration = configuration
|
12
13
|
end
|
13
14
|
|
14
15
|
private
|
15
16
|
|
16
|
-
attr_reader :options, :sources
|
17
|
+
attr_reader :options, :sources, :configuration
|
17
18
|
|
18
19
|
def smell_names
|
19
20
|
@smell_names ||= options.smells_to_detect
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require_relative 'base_command'
|
3
3
|
require_relative '../../examiner'
|
4
|
+
require_relative '../../report'
|
4
5
|
|
5
6
|
module Reek
|
6
7
|
module CLI
|
@@ -10,19 +11,19 @@ module Reek
|
|
10
11
|
# text report format.
|
11
12
|
#
|
12
13
|
class ReportCommand < BaseCommand
|
13
|
-
def execute
|
14
|
-
populate_reporter_with_smells
|
14
|
+
def execute
|
15
|
+
populate_reporter_with_smells
|
15
16
|
reporter.show
|
16
17
|
result_code
|
17
18
|
end
|
18
19
|
|
19
20
|
private
|
20
21
|
|
21
|
-
def populate_reporter_with_smells
|
22
|
+
def populate_reporter_with_smells
|
22
23
|
sources.each do |source|
|
23
24
|
reporter.add_examiner Examiner.new(source,
|
24
25
|
filter_by_smells: smell_names,
|
25
|
-
configuration:
|
26
|
+
configuration: configuration)
|
26
27
|
end
|
27
28
|
end
|
28
29
|
|
@@ -31,7 +32,36 @@ module Reek
|
|
31
32
|
end
|
32
33
|
|
33
34
|
def reporter
|
34
|
-
@reporter ||=
|
35
|
+
@reporter ||=
|
36
|
+
report_class.new(
|
37
|
+
warning_formatter: warning_formatter,
|
38
|
+
report_formatter: Report::Formatter,
|
39
|
+
sort_by_issue_count: sort_by_issue_count,
|
40
|
+
heading_formatter: heading_formatter)
|
41
|
+
end
|
42
|
+
|
43
|
+
def report_class
|
44
|
+
Report.report_class(options.report_format)
|
45
|
+
end
|
46
|
+
|
47
|
+
def warning_formatter
|
48
|
+
warning_formatter_class.new(location_formatter: location_formatter)
|
49
|
+
end
|
50
|
+
|
51
|
+
def warning_formatter_class
|
52
|
+
Report.warning_formatter_class(options.show_links ? :wiki_links : :simple)
|
53
|
+
end
|
54
|
+
|
55
|
+
def location_formatter
|
56
|
+
Report.location_formatter(options.location_format)
|
57
|
+
end
|
58
|
+
|
59
|
+
def heading_formatter
|
60
|
+
Report.heading_formatter(options.show_empty ? :verbose : :quiet)
|
61
|
+
end
|
62
|
+
|
63
|
+
def sort_by_issue_count
|
64
|
+
options.sorting == :smelliness
|
35
65
|
end
|
36
66
|
end
|
37
67
|
end
|
@@ -12,8 +12,8 @@ module Reek
|
|
12
12
|
class TodoListCommand < BaseCommand
|
13
13
|
FILE_NAME = '.todo.reek'.freeze
|
14
14
|
|
15
|
-
def execute
|
16
|
-
smells = scan_for_smells
|
15
|
+
def execute
|
16
|
+
smells = scan_for_smells
|
17
17
|
if smells.empty?
|
18
18
|
puts "\n'.todo.reek' not generated because "\
|
19
19
|
'there were no smells found!'
|
@@ -27,11 +27,11 @@ module Reek
|
|
27
27
|
|
28
28
|
private
|
29
29
|
|
30
|
-
def scan_for_smells
|
30
|
+
def scan_for_smells
|
31
31
|
sources.map do |source|
|
32
32
|
Examiner.new(source,
|
33
33
|
filter_by_smells: smell_names,
|
34
|
-
configuration:
|
34
|
+
configuration: configuration)
|
35
35
|
end.map(&:smells).flatten
|
36
36
|
end
|
37
37
|
|
@@ -70,6 +70,19 @@ module Reek
|
|
70
70
|
excluded_paths.include?(path)
|
71
71
|
end
|
72
72
|
|
73
|
+
def load_values(configuration_hash)
|
74
|
+
configuration_hash.each do |key, value|
|
75
|
+
case
|
76
|
+
when key == EXCLUDE_PATHS_KEY
|
77
|
+
excluded_paths.add value
|
78
|
+
when smell_type?(key)
|
79
|
+
default_directive.add key, value
|
80
|
+
else
|
81
|
+
directory_directives.add key, value
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
73
86
|
private
|
74
87
|
|
75
88
|
attr_writer :directory_directives, :default_directive, :excluded_paths
|
@@ -87,22 +100,9 @@ module Reek
|
|
87
100
|
end
|
88
101
|
|
89
102
|
def find_and_load(path: nil)
|
90
|
-
|
103
|
+
configuration_hash = ConfigurationFileFinder.find_and_load(path: path)
|
91
104
|
|
92
|
-
load_values(
|
93
|
-
end
|
94
|
-
|
95
|
-
def load_values(configuration_file)
|
96
|
-
configuration_file.each do |key, value|
|
97
|
-
case
|
98
|
-
when key == EXCLUDE_PATHS_KEY
|
99
|
-
excluded_paths.add value
|
100
|
-
when smell_type?(key)
|
101
|
-
default_directive.add key, value
|
102
|
-
else
|
103
|
-
directory_directives.add key, value
|
104
|
-
end
|
105
|
-
end
|
105
|
+
load_values(configuration_hash)
|
106
106
|
end
|
107
107
|
end
|
108
108
|
end
|
@@ -24,7 +24,7 @@ module Reek
|
|
24
24
|
|
25
25
|
# Initializes a new CodeContext.
|
26
26
|
#
|
27
|
-
# @param
|
27
|
+
# @param _parent [CodeContext, nil] The parent context
|
28
28
|
# @param exp [Reek::AST::Node] The code described by this context
|
29
29
|
#
|
30
30
|
# For example, given the following code:
|
@@ -45,16 +45,15 @@ module Reek
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def defined_instance_methods(visibility: :public)
|
48
|
-
|
49
|
-
context.
|
50
|
-
context.visibility == visibility
|
48
|
+
instance_method_children.select do |context|
|
49
|
+
context.visibility == visibility
|
51
50
|
end
|
52
51
|
end
|
53
52
|
|
54
53
|
def instance_method_calls
|
55
|
-
|
56
|
-
grep(SendContext)
|
57
|
-
|
54
|
+
instance_method_children.flat_map do |context|
|
55
|
+
context.children.grep(SendContext)
|
56
|
+
end
|
58
57
|
end
|
59
58
|
|
60
59
|
#
|
data/lib/reek/spec.rb
CHANGED
@@ -16,9 +16,9 @@ module Reek
|
|
16
16
|
# Here's a spec that ensures there are no active code smells in the current project:
|
17
17
|
#
|
18
18
|
# describe 'source code quality' do
|
19
|
-
#
|
19
|
+
# Pathname.glob('lib/**/*.rb').each do |path|
|
20
20
|
# it "reports no smells in #{path}" do
|
21
|
-
#
|
21
|
+
# expect(path).not_to reek
|
22
22
|
# end
|
23
23
|
# end
|
24
24
|
# end
|
@@ -26,7 +26,9 @@ module Reek
|
|
26
26
|
# And here's an even simpler way to do the same:
|
27
27
|
#
|
28
28
|
# it 'has no code smells' do
|
29
|
-
#
|
29
|
+
# Pathname.glob('lib/**/*.rb').each do |path|
|
30
|
+
# expect(path).not_to reek
|
31
|
+
# end
|
30
32
|
# end
|
31
33
|
#
|
32
34
|
# Here's a simple check of a code fragment:
|
@@ -63,7 +65,7 @@ module Reek
|
|
63
65
|
# much sense.
|
64
66
|
#
|
65
67
|
# @param smell_type [Symbol, String, Class] The "smell type" to check for.
|
66
|
-
# @param
|
68
|
+
# @param smell_details [Hash] A hash containing "smell warning" parameters
|
67
69
|
#
|
68
70
|
# @example Without smell_details
|
69
71
|
#
|
@@ -15,14 +15,22 @@ module Reek
|
|
15
15
|
smell_details = {},
|
16
16
|
configuration = Configuration::AppConfiguration.default)
|
17
17
|
@smell_type = normalize smell_type_or_class
|
18
|
-
@smell_details
|
19
|
-
@configuration
|
18
|
+
@smell_details = smell_details
|
19
|
+
@configuration = configuration
|
20
|
+
@configuration.load_values(smell_type => { Smells::SmellConfiguration::ENABLED_KEY => true })
|
20
21
|
end
|
21
22
|
|
22
23
|
def matches?(source)
|
24
|
+
@matching_smell_types = nil
|
23
25
|
self.examiner = Examiner.new(source, configuration: configuration)
|
24
26
|
set_failure_messages
|
25
|
-
|
27
|
+
matching_smell_details?
|
28
|
+
end
|
29
|
+
|
30
|
+
def with_config(config_hash)
|
31
|
+
new_configuration = Configuration::AppConfiguration.default
|
32
|
+
new_configuration.load_values(smell_type => config_hash)
|
33
|
+
self.class.new(smell_type, smell_details, new_configuration)
|
26
34
|
end
|
27
35
|
|
28
36
|
private
|
data/lib/reek/version.rb
CHANGED
data/spec/factories/factories.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
require_relative '../../lib/reek/smells'
|
2
2
|
require_relative '../../lib/reek/smells/smell_detector'
|
3
3
|
require_relative '../../lib/reek/smells/smell_warning'
|
4
|
-
require_relative '../../lib/reek/cli/option_interpreter'
|
5
4
|
require_relative '../../lib/reek/cli/options'
|
6
5
|
|
7
6
|
FactoryGirl.define do
|
@@ -53,14 +52,4 @@ FactoryGirl.define do
|
|
53
52
|
parameters: parameters)
|
54
53
|
end
|
55
54
|
end
|
56
|
-
|
57
|
-
factory :options_interpreter_with_empty_sources, class: Reek::CLI::OptionInterpreter do
|
58
|
-
transient do
|
59
|
-
options { Reek::CLI::Options.new [] }
|
60
|
-
end
|
61
|
-
|
62
|
-
initialize_with do
|
63
|
-
Reek::CLI::OptionInterpreter.new(options)
|
64
|
-
end
|
65
|
-
end
|
66
55
|
end
|
@@ -15,18 +15,92 @@ RSpec.describe Reek::CLI::Application do
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
let(:path_excluded_in_configuration) do
|
19
|
+
SAMPLES_PATH.join('source_with_exclude_paths/ignore_me/uncommunicative_method_name.rb')
|
20
|
+
end
|
21
|
+
|
22
|
+
let(:configuration) { test_configuration_for(SAMPLES_PATH.join('configuration/with_excluded_paths.reek')) }
|
23
|
+
|
24
|
+
describe '#execute' do
|
25
|
+
let(:command) { double 'reek_command' }
|
26
|
+
let(:app) { Reek::CLI::Application.new [] }
|
27
|
+
|
28
|
+
before do
|
29
|
+
allow(Reek::CLI::Command::ReportCommand).to receive(:new).and_return command
|
30
|
+
allow(command).to receive(:execute).and_return 'foo'
|
31
|
+
end
|
32
|
+
|
33
|
+
it "returns the command's result code" do
|
34
|
+
expect(app.execute).to eq 'foo'
|
35
|
+
end
|
22
36
|
|
23
|
-
|
24
|
-
|
37
|
+
context 'when no source files given' do
|
38
|
+
context 'and input was piped' do
|
39
|
+
before do
|
40
|
+
allow_any_instance_of(IO).to receive(:tty?).and_return(false)
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should use source form pipe' do
|
44
|
+
app.execute
|
45
|
+
expect(Reek::CLI::Command::ReportCommand).to have_received(:new).
|
46
|
+
with(sources: [$stdin],
|
47
|
+
configuration: Reek::Configuration::AppConfiguration,
|
48
|
+
options: Reek::CLI::Options)
|
49
|
+
end
|
25
50
|
end
|
26
51
|
|
27
|
-
|
28
|
-
|
29
|
-
|
52
|
+
context 'and input was not piped' do
|
53
|
+
before do
|
54
|
+
allow_any_instance_of(IO).to receive(:tty?).and_return(true)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should use working directory as source' do
|
58
|
+
expected_sources = Reek::Source::SourceLocator.new(['.']).sources
|
59
|
+
app.execute
|
60
|
+
expect(Reek::CLI::Command::ReportCommand).to have_received(:new).
|
61
|
+
with(sources: expected_sources,
|
62
|
+
configuration: Reek::Configuration::AppConfiguration,
|
63
|
+
options: Reek::CLI::Options)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'when source files are excluded through configuration' do
|
68
|
+
let(:app) { Reek::CLI::Application.new ['--config', 'some_file.reek'] }
|
69
|
+
|
70
|
+
before do
|
71
|
+
allow(File).to receive(:exist?).and_call_original
|
72
|
+
allow(File).to receive(:exist?).with('some_file.reek').and_return true
|
73
|
+
allow(Reek::Configuration::AppConfiguration).
|
74
|
+
to receive(:from_path).
|
75
|
+
with(Pathname.new('some_file.reek')).
|
76
|
+
and_return configuration
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'should use configuration for excluded paths' do
|
80
|
+
expected_sources = Reek::Source::SourceLocator.
|
81
|
+
new(['.'], configuration: configuration).sources
|
82
|
+
expect(expected_sources).not_to include(path_excluded_in_configuration)
|
83
|
+
|
84
|
+
app.execute
|
85
|
+
|
86
|
+
expect(Reek::CLI::Command::ReportCommand).to have_received(:new).
|
87
|
+
with(sources: expected_sources,
|
88
|
+
configuration: configuration,
|
89
|
+
options: Reek::CLI::Options)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context 'when source files given' do
|
95
|
+
let(:app) { Reek::CLI::Application.new ['.'] }
|
96
|
+
|
97
|
+
it 'should use sources from argv' do
|
98
|
+
expected_sources = Reek::Source::SourceLocator.new(['.']).sources
|
99
|
+
app.execute
|
100
|
+
expect(Reek::CLI::Command::ReportCommand).to have_received(:new).
|
101
|
+
with(sources: expected_sources,
|
102
|
+
configuration: Reek::Configuration::AppConfiguration,
|
103
|
+
options: Reek::CLI::Options)
|
30
104
|
end
|
31
105
|
end
|
32
106
|
end
|
@@ -1,41 +1,44 @@
|
|
1
1
|
require_relative '../../../spec_helper'
|
2
2
|
require_lib 'reek/cli/command/report_command'
|
3
3
|
require_lib 'reek/cli/options'
|
4
|
-
require_lib 'reek/cli/option_interpreter'
|
5
4
|
|
6
5
|
RSpec.describe Reek::CLI::Command::ReportCommand do
|
7
6
|
describe '#execute' do
|
8
7
|
let(:options) { Reek::CLI::Options.new [] }
|
9
|
-
let(:option_interpreter) { Reek::CLI::OptionInterpreter.new(options) }
|
10
8
|
|
11
9
|
let(:reporter) { double 'reporter' }
|
12
|
-
let(:
|
10
|
+
let(:configuration) { double 'configuration' }
|
11
|
+
let(:sources) { [source_file] }
|
13
12
|
|
14
|
-
let(:
|
13
|
+
let(:clean_file) { Pathname.glob("#{SAMPLES_PATH}/three_clean_files/*.rb").first }
|
14
|
+
let(:smelly_file) { Pathname.glob("#{SAMPLES_PATH}/two_smelly_files/*.rb").first }
|
15
|
+
|
16
|
+
let(:command) do
|
17
|
+
described_class.new(options: options,
|
18
|
+
sources: sources,
|
19
|
+
configuration: configuration)
|
20
|
+
end
|
15
21
|
|
16
22
|
before do
|
17
|
-
allow(
|
18
|
-
allow(reporter).to receive(:show)
|
23
|
+
allow(configuration).to receive(:directive_for).and_return({})
|
19
24
|
end
|
20
25
|
|
21
26
|
context 'when no smells are found' do
|
22
|
-
|
23
|
-
allow(reporter).to receive(:smells?).and_return false
|
24
|
-
end
|
27
|
+
let(:source_file) { clean_file }
|
25
28
|
|
26
29
|
it 'returns a success code' do
|
27
|
-
result = command.execute
|
30
|
+
result = command.execute
|
28
31
|
expect(result).to eq Reek::CLI::Options::DEFAULT_SUCCESS_EXIT_CODE
|
29
32
|
end
|
30
33
|
end
|
31
34
|
|
32
35
|
context 'when smells are found' do
|
33
|
-
|
34
|
-
allow(reporter).to receive(:smells?).and_return true
|
35
|
-
end
|
36
|
+
let(:source_file) { smelly_file }
|
36
37
|
|
37
38
|
it 'returns a failure code' do
|
38
|
-
result =
|
39
|
+
result = Reek::CLI::Silencer.silently do
|
40
|
+
command.execute
|
41
|
+
end
|
39
42
|
expect(result).to eq Reek::CLI::Options::DEFAULT_FAILURE_EXIT_CODE
|
40
43
|
end
|
41
44
|
end
|