reek 4.2.3 → 4.2.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +6 -0
- data/CONTRIBUTING.md +7 -6
- data/Gemfile +2 -2
- data/README.md +8 -15
- data/Rakefile +1 -1
- data/defaults.reek +1 -0
- data/features/command_line_interface/basic_usage.feature +8 -14
- data/features/command_line_interface/smell_selection.feature +4 -4
- data/features/command_line_interface/smells_count.feature +12 -14
- data/features/configuration_files/masking_smells.feature +31 -24
- data/features/configuration_loading.feature +15 -18
- data/features/programmatic_access.feature +7 -9
- data/features/rake_task/rake_task.feature +20 -24
- data/features/reports/json.feature +16 -28
- data/features/reports/reports.feature +56 -67
- data/features/reports/yaml.feature +13 -26
- data/features/samples.feature +3 -3
- data/features/step_definitions/sample_file_steps.rb +22 -156
- data/features/todo_list.feature +13 -14
- data/lib/reek/cli/options.rb +1 -1
- data/lib/reek/examiner.rb +45 -10
- data/lib/reek/smells/attribute.rb +3 -4
- data/lib/reek/smells/boolean_parameter.rb +2 -2
- data/lib/reek/smells/class_variable.rb +1 -1
- data/lib/reek/smells/control_parameter.rb +4 -4
- data/lib/reek/smells/data_clump.rb +2 -3
- data/lib/reek/smells/duplicate_method_call.rb +1 -1
- data/lib/reek/smells/feature_envy.rb +2 -2
- data/lib/reek/smells/irresponsible_module.rb +2 -3
- data/lib/reek/smells/long_parameter_list.rb +1 -1
- data/lib/reek/smells/long_yield_list.rb +1 -1
- data/lib/reek/smells/module_initialize.rb +1 -1
- data/lib/reek/smells/nested_iterators.rb +2 -2
- data/lib/reek/smells/nil_check.rb +1 -1
- data/lib/reek/smells/prima_donna_method.rb +5 -2
- data/lib/reek/smells/repeated_conditional.rb +1 -1
- data/lib/reek/smells/smell_detector.rb +1 -1
- data/lib/reek/smells/smell_warning.rb +6 -5
- data/lib/reek/smells/subclassed_from_core_class.rb +3 -3
- data/lib/reek/smells/too_many_constants.rb +1 -1
- data/lib/reek/smells/too_many_instance_variables.rb +1 -1
- data/lib/reek/smells/too_many_methods.rb +1 -1
- data/lib/reek/smells/too_many_statements.rb +1 -1
- data/lib/reek/smells/uncommunicative_method_name.rb +1 -1
- data/lib/reek/smells/uncommunicative_module_name.rb +1 -1
- data/lib/reek/smells/uncommunicative_parameter_name.rb +1 -1
- data/lib/reek/smells/uncommunicative_variable_name.rb +1 -1
- data/lib/reek/smells/unused_parameters.rb +1 -1
- data/lib/reek/smells/unused_private_method.rb +1 -1
- data/lib/reek/smells/utility_function.rb +2 -3
- data/lib/reek/spec/should_reek_of.rb +14 -1
- data/lib/reek/version.rb +1 -1
- data/samples/checkstyle.xml +7 -0
- data/samples/clean.rb +6 -0
- data/samples/configuration/.reek +0 -0
- data/samples/configuration/corrupt.reek +1 -0
- data/samples/configuration/empty.reek +0 -0
- data/samples/configuration/full_configuration.reek +9 -0
- data/{spec/samples/configuration/simple_configuration.reek → samples/configuration/full_mask.reek} +2 -2
- data/samples/configuration/non_public_modifiers_mask.reek +3 -0
- data/samples/configuration/partial_mask.reek +3 -0
- data/samples/configuration/with_excluded_paths.reek +4 -0
- data/{spec/samples → samples}/exceptions.reek +0 -0
- data/{spec/samples → samples}/inline.rb +0 -0
- data/{spec/samples → samples}/optparse.rb +0 -0
- data/samples/paths.rb +4 -0
- data/{spec/samples → samples}/redcloth.rb +0 -0
- data/samples/smelly.rb +7 -0
- data/samples/smelly_with_inline_mask.rb +8 -0
- data/samples/smelly_with_modifiers.rb +12 -0
- data/{spec/samples → samples}/source_with_exclude_paths/ignore_me/uncommunicative_method_name.rb +0 -0
- data/{spec/samples → samples}/source_with_exclude_paths/nested/ignore_me_as_well/irresponsible_module.rb +0 -0
- data/{spec/samples → samples}/source_with_exclude_paths/nested/uncommunicative_parameter_name.rb +0 -0
- data/{spec/samples → samples}/source_with_hidden_directories/.hidden/uncommunicative_method_name.rb +0 -0
- data/{spec/samples → samples}/source_with_hidden_directories/uncommunicative_parameter_name.rb +0 -0
- data/{spec/samples → samples}/source_with_non_ruby_files/gibberish +0 -0
- data/{spec/samples → samples}/source_with_non_ruby_files/python_source.py +0 -0
- data/{spec/samples → samples}/source_with_non_ruby_files/uncommunicative_parameter_name.rb +0 -0
- data/spec/reek/cli/application_spec.rb +1 -1
- data/spec/reek/cli/command/report_command_spec.rb +2 -5
- data/spec/reek/configuration/app_configuration_spec.rb +10 -8
- data/spec/reek/configuration/configuration_file_finder_spec.rb +24 -17
- data/spec/reek/examiner_spec.rb +84 -5
- data/spec/reek/report/json_report_spec.rb +1 -3
- data/spec/reek/report/xml_report_spec.rb +2 -3
- data/spec/reek/report/yaml_report_spec.rb +0 -2
- data/spec/reek/smells/attribute_spec.rb +21 -10
- data/spec/reek/smells/boolean_parameter_spec.rb +13 -12
- data/spec/reek/smells/class_variable_spec.rb +4 -4
- data/spec/reek/smells/control_parameter_spec.rb +25 -18
- data/spec/reek/smells/data_clump_spec.rb +5 -5
- data/spec/reek/smells/duplicate_method_call_spec.rb +1 -1
- data/spec/reek/smells/feature_envy_spec.rb +8 -2
- data/spec/reek/smells/irresponsible_module_spec.rb +16 -14
- data/spec/reek/smells/long_parameter_list_spec.rb +5 -1
- data/spec/reek/smells/long_yield_list_spec.rb +5 -2
- data/spec/reek/smells/nested_iterators_spec.rb +37 -13
- data/spec/reek/smells/nil_check_spec.rb +50 -53
- data/spec/reek/smells/prima_donna_method_spec.rb +9 -1
- data/spec/reek/smells/too_many_instance_variables_spec.rb +1 -1
- data/spec/reek/smells/too_many_methods_spec.rb +4 -4
- data/spec/reek/smells/too_many_statements_spec.rb +1 -1
- data/spec/reek/smells/uncommunicative_method_name_spec.rb +3 -3
- data/spec/reek/smells/uncommunicative_module_name_spec.rb +3 -3
- data/spec/reek/smells/uncommunicative_parameter_name_spec.rb +3 -3
- data/spec/reek/smells/uncommunicative_variable_name_spec.rb +3 -3
- data/spec/reek/smells/utility_function_spec.rb +12 -8
- data/spec/reek/source/source_locator_spec.rb +5 -1
- data/spec/reek/spec/should_reek_of_spec.rb +20 -13
- data/spec/reek/spec/should_reek_spec.rb +6 -11
- data/spec/spec_helper.rb +2 -2
- metadata +28 -36
- data/spec/samples/all_but_one_masked/clean_one.rb +0 -7
- data/spec/samples/all_but_one_masked/dirty.rb +0 -8
- data/spec/samples/all_but_one_masked/masked.reek +0 -9
- data/spec/samples/checkstyle.xml +0 -13
- data/spec/samples/clean_due_to_masking/clean_one.rb +0 -7
- data/spec/samples/clean_due_to_masking/clean_three.rb +0 -7
- data/spec/samples/clean_due_to_masking/clean_two.rb +0 -7
- data/spec/samples/clean_due_to_masking/dirty_one.rb +0 -7
- data/spec/samples/clean_due_to_masking/dirty_two.rb +0 -7
- data/spec/samples/clean_due_to_masking/masked.reek +0 -11
- data/spec/samples/configuration/full_configuration.reek +0 -9
- data/spec/samples/configuration/with_excluded_paths.reek +0 -4
- data/spec/samples/masked_by_dotfile/.reek +0 -9
- data/spec/samples/masked_by_dotfile/dirty.rb +0 -8
- data/spec/samples/no_config_file/dirty.rb +0 -8
- data/spec/samples/three_clean_files/clean_one.rb +0 -7
- data/spec/samples/three_clean_files/clean_three.rb +0 -7
- data/spec/samples/three_clean_files/clean_two.rb +0 -7
- data/spec/samples/two_smelly_files/dirty_one.rb +0 -8
- data/spec/samples/two_smelly_files/dirty_two.rb +0 -8
@@ -33,7 +33,7 @@ module Reek
|
|
33
33
|
#
|
34
34
|
# @return [Array<SmellWarning>]
|
35
35
|
#
|
36
|
-
def
|
36
|
+
def sniff(ctx)
|
37
37
|
max_allowed_ivars = value(MAX_ALLOWED_IVARS_KEY, ctx)
|
38
38
|
count = ctx.local_nodes(:ivasgn).map { |ivasgn| ivasgn.children.first }.uniq.length
|
39
39
|
return [] if count <= max_allowed_ivars
|
@@ -58,7 +58,7 @@ module Reek
|
|
58
58
|
#
|
59
59
|
# :reek:FeatureEnvy
|
60
60
|
# :reek:TooManyStatements: { max_statements: 6 }
|
61
|
-
def
|
61
|
+
def sniff(ctx)
|
62
62
|
return [] if ctx.singleton_method? || ctx.module_function?
|
63
63
|
return [] if ctx.references_self?
|
64
64
|
return [] if num_helper_methods(ctx).zero?
|
@@ -67,8 +67,7 @@ module Reek
|
|
67
67
|
[smell_warning(
|
68
68
|
context: ctx,
|
69
69
|
lines: [ctx.exp.line],
|
70
|
-
message: "doesn't depend on instance state (maybe move it to another class?)"
|
71
|
-
parameters: { name: ctx.full_name })]
|
70
|
+
message: "doesn't depend on instance state (maybe move it to another class?)")]
|
72
71
|
end
|
73
72
|
|
74
73
|
private
|
@@ -74,11 +74,24 @@ module Reek
|
|
74
74
|
|
75
75
|
def set_failure_messages_for_smell_details
|
76
76
|
self.failure_message = "Expected #{origin} to reek of #{smell_type} "\
|
77
|
-
"(which it did) with smell details #{smell_details}, which it didn't"
|
77
|
+
"(which it did) with smell details #{smell_details}, which it didn't.\n"\
|
78
|
+
"The number of smell details I had to compare with the given one was #{matching_smell_types.count} "\
|
79
|
+
"and here they are:\n"\
|
80
|
+
"#{all_relevant_smell_details_formatted}"
|
78
81
|
self.failure_message_when_negated = "Expected #{origin} not to reek of "\
|
79
82
|
"#{smell_type} with smell details #{smell_details}, but it did"
|
80
83
|
end
|
81
84
|
|
85
|
+
# :reek:FeatureEnvy
|
86
|
+
def all_relevant_smell_details_formatted
|
87
|
+
matching_smell_types.each_with_object([]).with_index do |(smell, accumulator), index|
|
88
|
+
accumulator << "#{index + 1}.)\n"
|
89
|
+
warning_as_hash = smell.smell_warning.to_hash
|
90
|
+
warning_as_hash.delete('smell_type')
|
91
|
+
accumulator << "#{warning_as_hash}\n"
|
92
|
+
end.join
|
93
|
+
end
|
94
|
+
|
82
95
|
def origin
|
83
96
|
examiner.description
|
84
97
|
end
|
data/lib/reek/version.rb
CHANGED
@@ -0,0 +1,7 @@
|
|
1
|
+
<?xml version='1.0'?>
|
2
|
+
<checkstyle>
|
3
|
+
<file name='samples/smelly.rb'>
|
4
|
+
<error column='0' line='4' message='has the name 'x'' severity='warning' source='UncommunicativeMethodName'/>
|
5
|
+
<error column='0' line='5' message='has the variable name 'y'' severity='warning' source='UncommunicativeVariableName'/>
|
6
|
+
</file>
|
7
|
+
</checkstyle>
|
data/samples/clean.rb
ADDED
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
Not a valid configuration file
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
data/samples/paths.rb
ADDED
File without changes
|
data/samples/smelly.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# Smelly class for testing purposes
|
2
|
+
#
|
3
|
+
# Not necessary for the feature per se but for
|
4
|
+
# removing distracting output. :reek:UnusedPrivateMethod
|
5
|
+
#
|
6
|
+
class Klass
|
7
|
+
def public_method(arg) arg.to_s; end
|
8
|
+
protected
|
9
|
+
def protected_method(arg) arg.to_s; end
|
10
|
+
private
|
11
|
+
def private_method(arg) arg.to_s; end
|
12
|
+
end
|
data/{spec/samples → samples}/source_with_exclude_paths/ignore_me/uncommunicative_method_name.rb
RENAMED
File without changes
|
File without changes
|
data/{spec/samples → samples}/source_with_exclude_paths/nested/uncommunicative_parameter_name.rb
RENAMED
File without changes
|
data/{spec/samples → samples}/source_with_hidden_directories/.hidden/uncommunicative_method_name.rb
RENAMED
File without changes
|
data/{spec/samples → samples}/source_with_hidden_directories/uncommunicative_parameter_name.rb
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -19,7 +19,7 @@ RSpec.describe Reek::CLI::Application do
|
|
19
19
|
SAMPLES_PATH.join('source_with_exclude_paths/ignore_me/uncommunicative_method_name.rb')
|
20
20
|
end
|
21
21
|
|
22
|
-
let(:configuration) { test_configuration_for(
|
22
|
+
let(:configuration) { test_configuration_for(CONFIG_PATH.join('with_excluded_paths.reek')) }
|
23
23
|
|
24
24
|
describe '#execute' do
|
25
25
|
let(:command) { double 'reek_command' }
|
@@ -10,9 +10,6 @@ RSpec.describe Reek::CLI::Command::ReportCommand do
|
|
10
10
|
let(:configuration) { double 'configuration' }
|
11
11
|
let(:sources) { [source_file] }
|
12
12
|
|
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
13
|
let(:command) do
|
17
14
|
described_class.new(options: options,
|
18
15
|
sources: sources,
|
@@ -24,7 +21,7 @@ RSpec.describe Reek::CLI::Command::ReportCommand do
|
|
24
21
|
end
|
25
22
|
|
26
23
|
context 'when no smells are found' do
|
27
|
-
let(:source_file) {
|
24
|
+
let(:source_file) { CLEAN_FILE }
|
28
25
|
|
29
26
|
it 'returns a success code' do
|
30
27
|
result = command.execute
|
@@ -33,7 +30,7 @@ RSpec.describe Reek::CLI::Command::ReportCommand do
|
|
33
30
|
end
|
34
31
|
|
35
32
|
context 'when smells are found' do
|
36
|
-
let(:source_file) {
|
33
|
+
let(:source_file) { SMELLY_FILE }
|
37
34
|
|
38
35
|
it 'returns a failure code' do
|
39
36
|
result = Reek::CLI::Silencer.silently do
|
@@ -11,11 +11,13 @@ RSpec.describe Reek::Configuration::AppConfiguration do
|
|
11
11
|
[SAMPLES_PATH.join('two_smelly_files'),
|
12
12
|
SAMPLES_PATH.join('source_with_non_ruby_files')]
|
13
13
|
end
|
14
|
+
|
14
15
|
let(:expected_default_directive) do
|
15
16
|
{ Reek::Smells::IrresponsibleModule => { 'enabled' => false } }
|
16
17
|
end
|
18
|
+
|
17
19
|
let(:expected_directory_directives) do
|
18
|
-
{
|
20
|
+
{ SAMPLES_PATH.join('three_clean_files') =>
|
19
21
|
{ Reek::Smells::UtilityFunction => { 'enabled' => false } } }
|
20
22
|
end
|
21
23
|
|
@@ -24,13 +26,13 @@ RSpec.describe Reek::Configuration::AppConfiguration do
|
|
24
26
|
end
|
25
27
|
|
26
28
|
let(:directory_directives_value) do
|
27
|
-
{ '
|
29
|
+
{ 'samples/three_clean_files' =>
|
28
30
|
{ 'UtilityFunction' => { 'enabled' => false } } }
|
29
31
|
end
|
30
32
|
|
31
33
|
let(:exclude_paths_value) do
|
32
|
-
['
|
33
|
-
'
|
34
|
+
['samples/two_smelly_files',
|
35
|
+
'samples/source_with_non_ruby_files']
|
34
36
|
end
|
35
37
|
|
36
38
|
let(:combined_value) do
|
@@ -40,7 +42,7 @@ RSpec.describe Reek::Configuration::AppConfiguration do
|
|
40
42
|
end
|
41
43
|
|
42
44
|
describe '#from_path' do
|
43
|
-
let(:full_configuration_path) {
|
45
|
+
let(:full_configuration_path) { CONFIG_PATH.join('full_configuration.reek') }
|
44
46
|
|
45
47
|
it 'properly loads configuration and processes it' do
|
46
48
|
config = described_class.from_path full_configuration_path
|
@@ -74,14 +76,14 @@ RSpec.describe Reek::Configuration::AppConfiguration do
|
|
74
76
|
|
75
77
|
describe '#directive_for' do
|
76
78
|
context 'multiple directory directives and no default directive present' do
|
77
|
-
let(:source_via) { '
|
79
|
+
let(:source_via) { 'samples/three_clean_files/dummy.rb' }
|
78
80
|
let(:baz_config) { { Reek::Smells::IrresponsibleModule => { enabled: false } } }
|
79
81
|
let(:bang_config) { { Reek::Smells::Attribute => { enabled: true } } }
|
80
82
|
|
81
83
|
let(:directory_directives) do
|
82
84
|
{
|
83
|
-
'
|
84
|
-
'
|
85
|
+
'samples/two_smelly_files' => baz_config,
|
86
|
+
'samples/three_clean_files' => bang_config
|
85
87
|
}
|
86
88
|
end
|
87
89
|
|
@@ -25,47 +25,57 @@ RSpec.describe Reek::Configuration::ConfigurationFileFinder do
|
|
25
25
|
end
|
26
26
|
|
27
27
|
it 'returns the file in a parent dir if none in current dir' do
|
28
|
-
|
29
|
-
|
28
|
+
Dir.mktmpdir(nil, SAMPLES_PATH) do |tempdir|
|
29
|
+
found = described_class.find(current: Pathname.new(tempdir))
|
30
|
+
expect(found).to eq(SAMPLES_PATH.join('exceptions.reek'))
|
31
|
+
end
|
30
32
|
end
|
31
33
|
|
32
34
|
it 'returns the file even if it’s just ‘.reek’' do
|
33
|
-
found = described_class.find(current:
|
34
|
-
expect(found).to eq(
|
35
|
+
found = described_class.find(current: CONFIG_PATH)
|
36
|
+
expect(found).to eq(CONFIG_PATH.join('.reek'))
|
35
37
|
end
|
36
38
|
|
37
39
|
it 'returns the file in home if traversing from the current dir fails' do
|
38
40
|
skip_if_a_config_in_tempdir
|
39
|
-
|
40
|
-
|
41
|
-
found = described_class.find(current:
|
41
|
+
|
42
|
+
Dir.mktmpdir(nil, SAMPLES_PATH) do |tempdir|
|
43
|
+
found = described_class.find(current: Pathname.new(tempdir))
|
42
44
|
expect(found).to eq(SAMPLES_PATH.join('exceptions.reek'))
|
43
45
|
end
|
44
46
|
end
|
45
47
|
|
46
48
|
it 'prefers the file in :current over one in :home' do
|
47
|
-
found = described_class.find(current: SAMPLES_PATH, home:
|
48
|
-
file_in_home = SAMPLES_PATH.join('masked_by_dotfile/.reek')
|
49
|
+
found = described_class.find(current: SAMPLES_PATH, home: CONFIG_PATH)
|
49
50
|
file_in_current = SAMPLES_PATH.join('exceptions.reek')
|
50
|
-
|
51
|
+
|
51
52
|
expect(found).to eq(file_in_current)
|
52
53
|
end
|
53
54
|
|
54
55
|
it 'returns nil when there are no files to find' do
|
55
56
|
skip_if_a_config_in_tempdir
|
57
|
+
|
56
58
|
Dir.mktmpdir do |tempdir|
|
57
59
|
current = Pathname.new(tempdir)
|
58
|
-
home
|
60
|
+
home = Pathname.new(tempdir)
|
61
|
+
|
59
62
|
found = described_class.find(current: current, home: home)
|
63
|
+
|
60
64
|
expect(found).to be_nil
|
61
65
|
end
|
62
66
|
end
|
63
67
|
|
64
68
|
it 'does not traverse up from :home' do
|
65
69
|
skip_if_a_config_in_tempdir
|
70
|
+
|
66
71
|
Dir.mktmpdir do |tempdir|
|
67
|
-
|
68
|
-
|
72
|
+
dir = Pathname.new(tempdir)
|
73
|
+
subdir = dir.join('subdir')
|
74
|
+
|
75
|
+
FileUtils.mkdir(subdir)
|
76
|
+
|
77
|
+
found = described_class.find(current: subdir, home: dir)
|
78
|
+
|
69
79
|
expect(found).to be_nil
|
70
80
|
end
|
71
81
|
end
|
@@ -82,9 +92,6 @@ RSpec.describe Reek::Configuration::ConfigurationFileFinder do
|
|
82
92
|
end
|
83
93
|
|
84
94
|
describe '.load_from_file' do
|
85
|
-
let(:sample_configuration_path) do
|
86
|
-
SAMPLES_PATH.join('configuration/simple_configuration.reek')
|
87
|
-
end
|
88
95
|
let(:sample_configuration_loaded) do
|
89
96
|
{
|
90
97
|
'UncommunicativeVariableName' => { 'enabled' => false },
|
@@ -93,7 +100,7 @@ RSpec.describe Reek::Configuration::ConfigurationFileFinder do
|
|
93
100
|
end
|
94
101
|
|
95
102
|
it 'loads the configuration from given file' do
|
96
|
-
configuration = described_class.load_from_file(
|
103
|
+
configuration = described_class.load_from_file(CONFIG_PATH.join('full_mask.reek'))
|
97
104
|
expect(configuration).to eq(sample_configuration_loaded)
|
98
105
|
end
|
99
106
|
end
|