cuke_linter 1.3.0 → 1.4.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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +217 -211
  3. data/LICENSE.txt +21 -21
  4. data/README.md +160 -160
  5. data/cuke_linter.gemspec +59 -58
  6. data/exe/cuke_linter +112 -112
  7. data/lib/cuke_linter/configuration.rb +45 -45
  8. data/lib/cuke_linter/default_linters.rb +32 -32
  9. data/lib/cuke_linter/formatters/pretty_formatter.rb +84 -84
  10. data/lib/cuke_linter/gherkin.rb +10 -10
  11. data/lib/cuke_linter/linter_registration.rb +32 -32
  12. data/lib/cuke_linter/linters/background_does_more_than_setup_linter.rb +35 -35
  13. data/lib/cuke_linter/linters/element_with_common_tags_linter.rb +49 -49
  14. data/lib/cuke_linter/linters/element_with_duplicate_tags_linter.rb +48 -48
  15. data/lib/cuke_linter/linters/element_with_too_many_tags_linter.rb +46 -46
  16. data/lib/cuke_linter/linters/example_without_name_linter.rb +19 -19
  17. data/lib/cuke_linter/linters/feature_file_with_invalid_name_linter.rb +20 -20
  18. data/lib/cuke_linter/linters/feature_file_with_mismatched_name_linter.rb +25 -25
  19. data/lib/cuke_linter/linters/feature_with_too_many_different_tags_linter.rb +35 -35
  20. data/lib/cuke_linter/linters/feature_without_description_linter.rb +19 -19
  21. data/lib/cuke_linter/linters/feature_without_name_linter.rb +19 -19
  22. data/lib/cuke_linter/linters/feature_without_scenarios_linter.rb +23 -23
  23. data/lib/cuke_linter/linters/linter.rb +42 -42
  24. data/lib/cuke_linter/linters/outline_with_single_example_row_linter.rb +22 -22
  25. data/lib/cuke_linter/linters/single_test_background_linter.rb +19 -19
  26. data/lib/cuke_linter/linters/step_with_end_period_linter.rb +19 -19
  27. data/lib/cuke_linter/linters/step_with_too_many_characters_linter.rb +38 -38
  28. data/lib/cuke_linter/linters/test_name_with_too_many_characters_linter.rb +38 -38
  29. data/lib/cuke_linter/linters/test_should_use_background_linter.rb +80 -80
  30. data/lib/cuke_linter/linters/test_with_action_step_as_final_step_linter.rb +33 -33
  31. data/lib/cuke_linter/linters/test_with_bad_name_linter.rb +23 -23
  32. data/lib/cuke_linter/linters/test_with_no_action_step_linter.rb +33 -33
  33. data/lib/cuke_linter/linters/test_with_no_name_linter.rb +19 -19
  34. data/lib/cuke_linter/linters/test_with_no_verification_step_linter.rb +33 -33
  35. data/lib/cuke_linter/linters/test_with_setup_step_after_action_step_linter.rb +46 -46
  36. data/lib/cuke_linter/linters/test_with_setup_step_after_verification_step_linter.rb +46 -46
  37. data/lib/cuke_linter/linters/test_with_setup_step_as_final_step_linter.rb +33 -33
  38. data/lib/cuke_linter/linters/test_with_too_many_steps_linter.rb +27 -27
  39. data/lib/cuke_linter/version.rb +4 -4
  40. data/lib/cuke_linter.rb +196 -196
  41. data/testing/cucumber/features/command_line.feature +202 -202
  42. data/testing/cucumber/features/configuration/configuring_linters.feature +58 -58
  43. data/testing/cucumber/features/configuration/locally_scoping_linters.feature +55 -55
  44. data/testing/cucumber/features/configuration/using_configurations.feature +41 -41
  45. data/testing/cucumber/features/custom_linters.feature +56 -56
  46. data/testing/cucumber/features/default_linters.feature +57 -57
  47. data/testing/cucumber/features/formatters/pretty_formatter.feature +26 -26
  48. data/testing/cucumber/features/linters/background_does_more_than_setup.feature +84 -84
  49. data/testing/cucumber/features/linters/element_with_common_tags.feature +28 -28
  50. data/testing/cucumber/features/linters/element_with_duplicate_tags.feature +71 -71
  51. data/testing/cucumber/features/linters/element_with_too_many_tags.feature +70 -70
  52. data/testing/cucumber/features/linters/example_without_name.feature +34 -34
  53. data/testing/cucumber/features/linters/feature_file_with_invalid_name.feature +20 -20
  54. data/testing/cucumber/features/linters/feature_file_with_mismatched_name.feature +32 -32
  55. data/testing/cucumber/features/linters/feature_with_too_many_different_tags.feature +56 -56
  56. data/testing/cucumber/features/linters/feature_without_description.feature +17 -17
  57. data/testing/cucumber/features/linters/feature_without_name.feature +18 -18
  58. data/testing/cucumber/features/linters/feature_without_scenarios.feature +39 -39
  59. data/testing/cucumber/features/linters/outline_with_single_example_row.feature +23 -23
  60. data/testing/cucumber/features/linters/single_test_background.feature +24 -24
  61. data/testing/cucumber/features/linters/step_too_long.feature +43 -43
  62. data/testing/cucumber/features/linters/step_with_end_period.feature +21 -21
  63. data/testing/cucumber/features/linters/test_name_too_long.feature +41 -41
  64. data/testing/cucumber/features/linters/test_should_use_background.feature +29 -29
  65. data/testing/cucumber/features/linters/test_with_action_as_final_step.feature +50 -50
  66. data/testing/cucumber/features/linters/test_with_bad_name.feature +29 -29
  67. data/testing/cucumber/features/linters/test_with_no_action_step.feature +56 -56
  68. data/testing/cucumber/features/linters/test_with_no_name.feature +23 -23
  69. data/testing/cucumber/features/linters/test_with_no_verification_step.feature +58 -58
  70. data/testing/cucumber/features/linters/test_with_setup_step_after_action_step.feature +57 -57
  71. data/testing/cucumber/features/linters/test_with_setup_step_after_verification_step.feature +57 -57
  72. data/testing/cucumber/features/linters/test_with_setup_step_as_final_step.feature +50 -50
  73. data/testing/cucumber/features/linters/test_with_too_many_steps.feature +61 -61
  74. metadata +21 -25
  75. data/testing/cucumber/features/linters/rule_without_name.feature +0 -18
data/exe/cuke_linter CHANGED
@@ -1,112 +1,112 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'cuke_linter'
4
- require 'optparse'
5
-
6
- params = {}
7
- params[:paths] = []
8
- params[:formatters] = []
9
- params[:outs] = []
10
- params[:requires] = []
11
-
12
- # rubocop:disable Metrics/BlockLength
13
- parser = OptionParser.new do |options|
14
-
15
- options.set_summary_width(30)
16
-
17
- options.on('-p', '--path PATH', String,
18
- 'The file path that should be linted. Can be a file or directory.',
19
- 'This option can be specified multiple times in order to lint',
20
- 'multiple, unconnected locations.') do |path|
21
- params[:paths] << path
22
- end
23
-
24
- options.on('-f', '--formatter FORMATTER', String,
25
- 'The formatter used for generating linting output. This option',
26
- 'can be specified multiple times in order to use more than one',
27
- 'formatter. Formatters must be specified using their fully',
28
- 'qualified class name (e.g CukeLinter::PrettyFormatter). Uses',
29
- 'the default formatter if none are specified.') do |format|
30
- params[:formatters] << format
31
- end
32
-
33
- options.on('-o', '--out OUT', String,
34
- 'The file path to which linting results are output. Can be specified',
35
- 'multiple times. Specified files are matched to formatters in the',
36
- 'same order that the formatters are specified. Any formatter without',
37
- 'a corresponding file path will output to STDOUT instead.') do |out|
38
- params[:outs] << out
39
- end
40
-
41
- options.on('-r', '--require FILEPATH', String,
42
- 'A file that will be required before further processing. Likely',
43
- 'needed when using custom linters or formatters in order to ensure',
44
- 'that the specified classes have been read into memory. This option',
45
- 'can be specified multiple times in order to load more than one file.') do |file_path|
46
- params[:requires] << file_path
47
- end
48
-
49
- options.on('-c', '--config FILEPATH', String,
50
- 'The configuration file that will be used. Will use the default',
51
- 'configuration file (if present) if this option is not specified.') do |file_path|
52
-
53
- if params[:config]
54
- puts 'Cannot specify more than one configuration file!'
55
- exit(2)
56
- end
57
-
58
- params[:config] = file_path
59
- end
60
-
61
- options.on('-h', '--help', 'Display the help that you are reading now.') do
62
- puts options.help
63
- exit
64
- end
65
-
66
- options.on('-v', '--version', 'Display the version of the gem being used.') do
67
- puts CukeLinter::VERSION
68
- exit
69
- end
70
-
71
- end
72
- # rubocop:enable Metrics/BlockLength
73
-
74
- begin
75
- parser.parse!
76
- rescue OptionParser::InvalidOption, OptionParser::MissingArgument => e
77
- puts e.message
78
- puts parser.help
79
-
80
- exit(2)
81
- end
82
-
83
-
84
- require_files = params[:requires]
85
-
86
- require_files.each do |file|
87
- require file
88
- end
89
-
90
- file_paths = params[:paths]
91
- formatters = params[:formatters].map { |formatter| Kernel.const_get(formatter).new }
92
- output_paths = params[:outs]
93
-
94
- bundled_formatters = [].tap do |formatter_output_pairs|
95
- [formatters.count, output_paths.count].max.times do |count|
96
- formatter_output_pairs << [formatters[count] || CukeLinter::PrettyFormatter.new, output_paths[count]]
97
- end
98
- end
99
-
100
- options = {}
101
- options[:formatters] = bundled_formatters unless bundled_formatters.empty?
102
- options[:file_paths] = file_paths
103
-
104
- if params[:config]
105
- CukeLinter.load_configuration(config_file_path: params[:config])
106
- elsif File.exist?("#{Dir.pwd}/.cuke_linter")
107
- CukeLinter.load_configuration
108
- end
109
-
110
- results = CukeLinter.lint(**options)
111
-
112
- exit(1) unless results.empty?
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'cuke_linter'
4
+ require 'optparse'
5
+
6
+ params = {}
7
+ params[:paths] = []
8
+ params[:formatters] = []
9
+ params[:outs] = []
10
+ params[:requires] = []
11
+
12
+ # rubocop:disable Metrics/BlockLength
13
+ parser = OptionParser.new do |options|
14
+
15
+ options.set_summary_width(30)
16
+
17
+ options.on('-p', '--path PATH', String,
18
+ 'The file path that should be linted. Can be a file or directory.',
19
+ 'This option can be specified multiple times in order to lint',
20
+ 'multiple, unconnected locations.') do |path|
21
+ params[:paths] << path
22
+ end
23
+
24
+ options.on('-f', '--formatter FORMATTER', String,
25
+ 'The formatter used for generating linting output. This option',
26
+ 'can be specified multiple times in order to use more than one',
27
+ 'formatter. Formatters must be specified using their fully',
28
+ 'qualified class name (e.g CukeLinter::PrettyFormatter). Uses',
29
+ 'the default formatter if none are specified.') do |format|
30
+ params[:formatters] << format
31
+ end
32
+
33
+ options.on('-o', '--out OUT', String,
34
+ 'The file path to which linting results are output. Can be specified',
35
+ 'multiple times. Specified files are matched to formatters in the',
36
+ 'same order that the formatters are specified. Any formatter without',
37
+ 'a corresponding file path will output to STDOUT instead.') do |out|
38
+ params[:outs] << out
39
+ end
40
+
41
+ options.on('-r', '--require FILEPATH', String,
42
+ 'A file that will be required before further processing. Likely',
43
+ 'needed when using custom linters or formatters in order to ensure',
44
+ 'that the specified classes have been read into memory. This option',
45
+ 'can be specified multiple times in order to load more than one file.') do |file_path|
46
+ params[:requires] << file_path
47
+ end
48
+
49
+ options.on('-c', '--config FILEPATH', String,
50
+ 'The configuration file that will be used. Will use the default',
51
+ 'configuration file (if present) if this option is not specified.') do |file_path|
52
+
53
+ if params[:config]
54
+ puts 'Cannot specify more than one configuration file!'
55
+ exit(2)
56
+ end
57
+
58
+ params[:config] = file_path
59
+ end
60
+
61
+ options.on('-h', '--help', 'Display the help that you are reading now.') do
62
+ puts options.help
63
+ exit
64
+ end
65
+
66
+ options.on('-v', '--version', 'Display the version of the gem being used.') do
67
+ puts CukeLinter::VERSION
68
+ exit
69
+ end
70
+
71
+ end
72
+ # rubocop:enable Metrics/BlockLength
73
+
74
+ begin
75
+ parser.parse!
76
+ rescue OptionParser::InvalidOption, OptionParser::MissingArgument => e
77
+ puts e.message
78
+ puts parser.help
79
+
80
+ exit(2)
81
+ end
82
+
83
+
84
+ require_files = params[:requires]
85
+
86
+ require_files.each do |file|
87
+ require file
88
+ end
89
+
90
+ file_paths = params[:paths]
91
+ formatters = params[:formatters].map { |formatter| Kernel.const_get(formatter).new }
92
+ output_paths = params[:outs]
93
+
94
+ bundled_formatters = [].tap do |formatter_output_pairs|
95
+ [formatters.count, output_paths.count].max.times do |count|
96
+ formatter_output_pairs << [formatters[count] || CukeLinter::PrettyFormatter.new, output_paths[count]]
97
+ end
98
+ end
99
+
100
+ options = {}
101
+ options[:formatters] = bundled_formatters unless bundled_formatters.empty?
102
+ options[:file_paths] = file_paths
103
+
104
+ if params[:config]
105
+ CukeLinter.load_configuration(config_file_path: params[:config])
106
+ elsif File.exist?("#{Dir.pwd}/.cuke_linter")
107
+ CukeLinter.load_configuration
108
+ end
109
+
110
+ results = CukeLinter.lint(**options)
111
+
112
+ exit(1) unless results.empty?
@@ -1,45 +1,45 @@
1
- module CukeLinter
2
-
3
- # Mix-in module containing methods related to configuring linters
4
- module Configuration
5
-
6
- # Configures linters based on the given options
7
- def load_configuration(config_file_path: nil, config: nil)
8
- # TODO: define what happens if both a configuration file and a configuration are
9
- # provided. Merge them or have direct config take precedence? Both?
10
-
11
- unless config || config_file_path
12
- config_file_path = "#{Dir.pwd}/.cuke_linter"
13
- message = 'No configuration or configuration file given and no .cuke_linter file found'
14
- raise message unless File.exist?(config_file_path)
15
- end
16
-
17
- config ||= YAML.load_file(config_file_path)
18
- configure_linters(config, registered_linters)
19
- end
20
-
21
-
22
- private
23
-
24
-
25
- def configure_linters(configuration, linters)
26
- common_config = configuration['AllLinters'] || {}
27
- to_delete = []
28
-
29
- linters.each_pair do |name, linter|
30
- linter_config = configuration[name] || {}
31
- final_config = common_config.merge(linter_config)
32
-
33
- disabled = (final_config.key?('Enabled') && !final_config['Enabled'])
34
-
35
- # Just save it for afterwards because modifying a collection while iterating through it is not a good idea
36
- to_delete << name if disabled
37
-
38
- linter.configure(final_config) if linter.respond_to?(:configure)
39
- end
40
-
41
- to_delete.each { |linter_name| unregister_linter(linter_name) }
42
- end
43
-
44
- end
45
- end
1
+ module CukeLinter
2
+
3
+ # Mix-in module containing methods related to configuring linters
4
+ module Configuration
5
+
6
+ # Configures linters based on the given options
7
+ def load_configuration(config_file_path: nil, config: nil)
8
+ # TODO: define what happens if both a configuration file and a configuration are
9
+ # provided. Merge them or have direct config take precedence? Both?
10
+
11
+ unless config || config_file_path
12
+ config_file_path = "#{Dir.pwd}/.cuke_linter"
13
+ message = 'No configuration or configuration file given and no .cuke_linter file found'
14
+ raise message unless File.exist?(config_file_path)
15
+ end
16
+
17
+ config ||= YAML.load_file(config_file_path)
18
+ configure_linters(config, registered_linters)
19
+ end
20
+
21
+
22
+ private
23
+
24
+
25
+ def configure_linters(configuration, linters) # rubocop:disable Metrics/CyclomaticComplexity -- Maybe I'll revisit this later
26
+ common_config = configuration['AllLinters'] || {}
27
+ to_delete = []
28
+
29
+ linters.each_pair do |name, linter|
30
+ linter_config = configuration[name] || {}
31
+ final_config = common_config.merge(linter_config)
32
+
33
+ disabled = final_config.key?('Enabled') && !final_config['Enabled']
34
+
35
+ # Just save it for afterwards because modifying a collection while iterating through it is not a good idea
36
+ to_delete << name if disabled
37
+
38
+ linter.configure(final_config) if linter.respond_to?(:configure)
39
+ end
40
+
41
+ to_delete.each { |linter_name| unregister_linter(linter_name) }
42
+ end
43
+
44
+ end
45
+ end
@@ -1,32 +1,32 @@
1
- module CukeLinter # rubocop:disable Style/Documentation
2
-
3
- # Long names inherently result in long lines
4
- # rubocop:disable Metrics/LineLength
5
- @original_linters = { 'BackgroundDoesMoreThanSetupLinter' => BackgroundDoesMoreThanSetupLinter.new,
6
- 'ElementWithCommonTagsLinter' => ElementWithCommonTagsLinter.new,
7
- 'ElementWithDuplicateTagsLinter' => ElementWithDuplicateTagsLinter.new,
8
- 'ElementWithTooManyTagsLinter' => ElementWithTooManyTagsLinter.new,
9
- 'ExampleWithoutNameLinter' => ExampleWithoutNameLinter.new,
10
- 'FeatureFileWithInvalidNameLinter' => FeatureFileWithInvalidNameLinter.new,
11
- 'FeatureFileWithMismatchedNameLinter' => FeatureFileWithMismatchedNameLinter.new,
12
- 'FeatureWithTooManyDifferentTagsLinter' => FeatureWithTooManyDifferentTagsLinter.new,
13
- 'FeatureWithoutDescriptionLinter' => FeatureWithoutDescriptionLinter.new,
14
- 'FeatureWithoutNameLinter' => FeatureWithoutNameLinter.new,
15
- 'FeatureWithoutScenariosLinter' => FeatureWithoutScenariosLinter.new,
16
- 'OutlineWithSingleExampleRowLinter' => OutlineWithSingleExampleRowLinter.new,
17
- 'SingleTestBackgroundLinter' => SingleTestBackgroundLinter.new,
18
- 'StepWithEndPeriodLinter' => StepWithEndPeriodLinter.new,
19
- 'StepWithTooManyCharactersLinter' => StepWithTooManyCharactersLinter.new,
20
- 'TestShouldUseBackgroundLinter' => TestShouldUseBackgroundLinter.new,
21
- 'TestWithActionStepAsFinalStepLinter' => TestWithActionStepAsFinalStepLinter.new,
22
- 'TestWithBadNameLinter' => TestWithBadNameLinter.new,
23
- 'TestWithNoActionStepLinter' => TestWithNoActionStepLinter.new,
24
- 'TestWithNoNameLinter' => TestWithNoNameLinter.new,
25
- 'TestWithNoVerificationStepLinter' => TestWithNoVerificationStepLinter.new,
26
- 'TestWithSetupStepAfterActionStepLinter' => TestWithSetupStepAfterActionStepLinter.new,
27
- 'TestWithSetupStepAfterVerificationStepLinter' => TestWithSetupStepAfterVerificationStepLinter.new,
28
- 'TestWithSetupStepAsFinalStepLinter' => TestWithSetupStepAsFinalStepLinter.new,
29
- 'TestWithTooManyStepsLinter' => TestWithTooManyStepsLinter.new }
30
- # rubocop:enable Metrics/LineLength
31
-
32
- end
1
+ module CukeLinter # rubocop:disable Style/Documentation
2
+
3
+ # Long names inherently result in long lines
4
+ # rubocop:disable Layout/LineLength
5
+ @original_linters = { 'BackgroundDoesMoreThanSetupLinter' => BackgroundDoesMoreThanSetupLinter.new,
6
+ 'ElementWithCommonTagsLinter' => ElementWithCommonTagsLinter.new,
7
+ 'ElementWithDuplicateTagsLinter' => ElementWithDuplicateTagsLinter.new,
8
+ 'ElementWithTooManyTagsLinter' => ElementWithTooManyTagsLinter.new,
9
+ 'ExampleWithoutNameLinter' => ExampleWithoutNameLinter.new,
10
+ 'FeatureFileWithInvalidNameLinter' => FeatureFileWithInvalidNameLinter.new,
11
+ 'FeatureFileWithMismatchedNameLinter' => FeatureFileWithMismatchedNameLinter.new,
12
+ 'FeatureWithTooManyDifferentTagsLinter' => FeatureWithTooManyDifferentTagsLinter.new,
13
+ 'FeatureWithoutDescriptionLinter' => FeatureWithoutDescriptionLinter.new,
14
+ 'FeatureWithoutNameLinter' => FeatureWithoutNameLinter.new,
15
+ 'FeatureWithoutScenariosLinter' => FeatureWithoutScenariosLinter.new,
16
+ 'OutlineWithSingleExampleRowLinter' => OutlineWithSingleExampleRowLinter.new,
17
+ 'SingleTestBackgroundLinter' => SingleTestBackgroundLinter.new,
18
+ 'StepWithEndPeriodLinter' => StepWithEndPeriodLinter.new,
19
+ 'StepWithTooManyCharactersLinter' => StepWithTooManyCharactersLinter.new,
20
+ 'TestShouldUseBackgroundLinter' => TestShouldUseBackgroundLinter.new,
21
+ 'TestWithActionStepAsFinalStepLinter' => TestWithActionStepAsFinalStepLinter.new,
22
+ 'TestWithBadNameLinter' => TestWithBadNameLinter.new,
23
+ 'TestWithNoActionStepLinter' => TestWithNoActionStepLinter.new,
24
+ 'TestWithNoNameLinter' => TestWithNoNameLinter.new,
25
+ 'TestWithNoVerificationStepLinter' => TestWithNoVerificationStepLinter.new,
26
+ 'TestWithSetupStepAfterActionStepLinter' => TestWithSetupStepAfterActionStepLinter.new,
27
+ 'TestWithSetupStepAfterVerificationStepLinter' => TestWithSetupStepAfterVerificationStepLinter.new,
28
+ 'TestWithSetupStepAsFinalStepLinter' => TestWithSetupStepAsFinalStepLinter.new,
29
+ 'TestWithTooManyStepsLinter' => TestWithTooManyStepsLinter.new }
30
+ # rubocop:enable Layout/LineLength
31
+
32
+ end
@@ -1,84 +1,84 @@
1
- module CukeLinter
2
-
3
- # Formats linting data into organized, user readable text
4
- class PrettyFormatter
5
-
6
- # Formats the given linting data
7
- def format(data)
8
- format_data(categorize_problems(data), data.count)
9
- end
10
-
11
-
12
- private
13
-
14
-
15
- def categorize_problems(data)
16
- {}.tap do |categorized_problems|
17
- data.each do |lint_item|
18
- categorized_problems[lint_item[:linter]] ||= {}
19
- categorized_problems[lint_item[:linter]][lint_item[:problem]] ||= []
20
- categorized_problems[lint_item[:linter]][lint_item[:problem]] << lint_item[:location]
21
- end
22
- end
23
- end
24
-
25
- def format_data(problem_data, problem_count)
26
- ''.tap do |formatted_data|
27
- problem_data.each_pair do |linter, problems|
28
- formatted_data << "#{linter}\n"
29
-
30
- problems.each_pair do |problem, locations|
31
- formatted_data << " #{problem}\n"
32
-
33
- sort_locations(locations).each do |location|
34
- formatted_data << " #{location}\n"
35
- end
36
- end
37
- end
38
-
39
- formatted_data << "\n" unless problem_count.zero?
40
- formatted_data << "#{problem_count} issues found"
41
- end
42
- end
43
-
44
- def sort_locations(locations)
45
- locations.sort do |a, b|
46
- file_name_1 = a.match(/(.*?)(?::\d+)?$/)[1]
47
- line_number_1 = a =~ /:\d+$/ ? a.match(/:(\d+)$/)[1].to_i : 0
48
- file_name_2 = b.match(/(.*?)(?::\d+)?$/)[1]
49
- line_number_2 = b =~ /:\d+$/ ? b.match(/:(\d+)$/)[1].to_i : 0
50
-
51
- compare_locations(file_name_1, file_name_2, line_number_1, line_number_2)
52
- end
53
- end
54
-
55
- def compare_locations(file_name_1, file_name_2, line_number_1, line_number_2)
56
- if earlier_file(file_name_1, file_name_2) ||
57
- same_file_earlier_line(file_name_1, file_name_2, line_number_1, line_number_2)
58
- -1
59
- elsif later_file(file_name_1, file_name_2) ||
60
- same_file_later_line(file_name_1, file_name_2, line_number_1, line_number_2)
61
- 1
62
- else
63
- 0
64
- end
65
- end
66
-
67
- def earlier_file(file_name_1, file_name_2)
68
- (file_name_1 < file_name_2)
69
- end
70
-
71
- def same_file_earlier_line(file_name_1, file_name_2, line_number_1, line_number_2)
72
- (file_name_1 == file_name_2) && (line_number_1 < line_number_2)
73
- end
74
-
75
- def later_file(file_name_1, file_name_2)
76
- (file_name_1 > file_name_2)
77
- end
78
-
79
- def same_file_later_line(file_name_1, file_name_2, line_number_1, line_number_2)
80
- (file_name_1 == file_name_2) && (line_number_1 > line_number_2)
81
- end
82
-
83
- end
84
- end
1
+ module CukeLinter
2
+
3
+ # Formats linting data into organized, user readable text
4
+ class PrettyFormatter
5
+
6
+ # Formats the given linting data
7
+ def format(data)
8
+ format_data(categorize_problems(data), data.count)
9
+ end
10
+
11
+
12
+ private
13
+
14
+
15
+ def categorize_problems(data)
16
+ {}.tap do |categorized_problems|
17
+ data.each do |lint_item|
18
+ categorized_problems[lint_item[:linter]] ||= {}
19
+ categorized_problems[lint_item[:linter]][lint_item[:problem]] ||= []
20
+ categorized_problems[lint_item[:linter]][lint_item[:problem]] << lint_item[:location]
21
+ end
22
+ end
23
+ end
24
+
25
+ def format_data(problem_data, problem_count)
26
+ ''.tap do |formatted_data|
27
+ problem_data.each_pair do |linter, problems|
28
+ formatted_data << "#{linter}\n"
29
+
30
+ problems.each_pair do |problem, locations|
31
+ formatted_data << " #{problem}\n"
32
+
33
+ sort_locations(locations).each do |location|
34
+ formatted_data << " #{location}\n"
35
+ end
36
+ end
37
+ end
38
+
39
+ formatted_data << "\n" unless problem_count.zero?
40
+ formatted_data << "#{problem_count} issues found"
41
+ end
42
+ end
43
+
44
+ def sort_locations(locations)
45
+ locations.sort do |a, b|
46
+ file_name_1 = a.match(/(.*?)(?::\d+)?$/)[1]
47
+ line_number_1 = a =~ /:\d+$/ ? a.match(/:(\d+)$/)[1].to_i : 0
48
+ file_name_2 = b.match(/(.*?)(?::\d+)?$/)[1]
49
+ line_number_2 = b =~ /:\d+$/ ? b.match(/:(\d+)$/)[1].to_i : 0
50
+
51
+ compare_locations(file_name_1, file_name_2, line_number_1, line_number_2)
52
+ end
53
+ end
54
+
55
+ def compare_locations(file_name_1, file_name_2, line_number_1, line_number_2)
56
+ if earlier_file(file_name_1, file_name_2) ||
57
+ same_file_earlier_line(file_name_1, file_name_2, line_number_1, line_number_2)
58
+ -1
59
+ elsif later_file(file_name_1, file_name_2) ||
60
+ same_file_later_line(file_name_1, file_name_2, line_number_1, line_number_2)
61
+ 1
62
+ else
63
+ 0
64
+ end
65
+ end
66
+
67
+ def earlier_file(file_name_1, file_name_2)
68
+ (file_name_1 < file_name_2)
69
+ end
70
+
71
+ def same_file_earlier_line(file_name_1, file_name_2, line_number_1, line_number_2)
72
+ (file_name_1 == file_name_2) && (line_number_1 < line_number_2)
73
+ end
74
+
75
+ def later_file(file_name_1, file_name_2)
76
+ (file_name_1 > file_name_2)
77
+ end
78
+
79
+ def same_file_later_line(file_name_1, file_name_2, line_number_1, line_number_2)
80
+ (file_name_1 == file_name_2) && (line_number_1 > line_number_2)
81
+ end
82
+
83
+ end
84
+ end
@@ -1,10 +1,10 @@
1
- module CukeLinter
2
-
3
- # The default keyword that is considered a 'Given' keyword
4
- DEFAULT_GIVEN_KEYWORD = 'Given'.freeze
5
- # The default keyword that is considered a 'When' keyword
6
- DEFAULT_WHEN_KEYWORD = 'When'.freeze
7
- # The default keyword that is considered a 'Then' keyword
8
- DEFAULT_THEN_KEYWORD = 'Then'.freeze
9
-
10
- end
1
+ module CukeLinter
2
+
3
+ # The default keyword that is considered a 'Given' keyword
4
+ DEFAULT_GIVEN_KEYWORD = 'Given'.freeze
5
+ # The default keyword that is considered a 'When' keyword
6
+ DEFAULT_WHEN_KEYWORD = 'When'.freeze
7
+ # The default keyword that is considered a 'Then' keyword
8
+ DEFAULT_THEN_KEYWORD = 'Then'.freeze
9
+
10
+ end
@@ -1,32 +1,32 @@
1
- module CukeLinter
2
-
3
- # Mix-in module containing methods related to registering linters
4
- module LinterRegistration
5
-
6
- # Returns the registered linters to their default state
7
- def reset_linters
8
- @registered_linters = nil
9
- end
10
-
11
- # Registers for linting use the given linter object, tracked by the given name
12
- def register_linter(linter:, name:)
13
- registered_linters[name] = linter
14
- end
15
-
16
- # Unregisters the linter object tracked by the given name so that it is not used for linting
17
- def unregister_linter(name)
18
- registered_linters.delete(name)
19
- end
20
-
21
- # Lists the names of the currently registered linting objects
22
- def registered_linters
23
- @registered_linters ||= Marshal.load(Marshal.dump(@original_linters))
24
- end
25
-
26
- # Unregisters all currently registered linting objects
27
- def clear_registered_linters
28
- registered_linters.clear
29
- end
30
-
31
- end
32
- end
1
+ module CukeLinter
2
+
3
+ # Mix-in module containing methods related to registering linters
4
+ module LinterRegistration
5
+
6
+ # Returns the registered linters to their default state
7
+ def reset_linters
8
+ @registered_linters = nil
9
+ end
10
+
11
+ # Registers for linting use the given linter object, tracked by the given name
12
+ def register_linter(linter:, name:)
13
+ registered_linters[name] = linter
14
+ end
15
+
16
+ # Unregisters the linter object tracked by the given name so that it is not used for linting
17
+ def unregister_linter(name)
18
+ registered_linters.delete(name)
19
+ end
20
+
21
+ # Lists the names of the currently registered linting objects
22
+ def registered_linters
23
+ @registered_linters ||= Marshal.load(Marshal.dump(@original_linters))
24
+ end
25
+
26
+ # Unregisters all currently registered linting objects
27
+ def clear_registered_linters
28
+ registered_linters.clear
29
+ end
30
+
31
+ end
32
+ end