reek 4.2.3 → 4.2.4
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/.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
|