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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +217 -211
- data/LICENSE.txt +21 -21
- data/README.md +160 -160
- data/cuke_linter.gemspec +59 -58
- data/exe/cuke_linter +112 -112
- data/lib/cuke_linter/configuration.rb +45 -45
- data/lib/cuke_linter/default_linters.rb +32 -32
- data/lib/cuke_linter/formatters/pretty_formatter.rb +84 -84
- data/lib/cuke_linter/gherkin.rb +10 -10
- data/lib/cuke_linter/linter_registration.rb +32 -32
- data/lib/cuke_linter/linters/background_does_more_than_setup_linter.rb +35 -35
- data/lib/cuke_linter/linters/element_with_common_tags_linter.rb +49 -49
- data/lib/cuke_linter/linters/element_with_duplicate_tags_linter.rb +48 -48
- data/lib/cuke_linter/linters/element_with_too_many_tags_linter.rb +46 -46
- data/lib/cuke_linter/linters/example_without_name_linter.rb +19 -19
- data/lib/cuke_linter/linters/feature_file_with_invalid_name_linter.rb +20 -20
- data/lib/cuke_linter/linters/feature_file_with_mismatched_name_linter.rb +25 -25
- data/lib/cuke_linter/linters/feature_with_too_many_different_tags_linter.rb +35 -35
- data/lib/cuke_linter/linters/feature_without_description_linter.rb +19 -19
- data/lib/cuke_linter/linters/feature_without_name_linter.rb +19 -19
- data/lib/cuke_linter/linters/feature_without_scenarios_linter.rb +23 -23
- data/lib/cuke_linter/linters/linter.rb +42 -42
- data/lib/cuke_linter/linters/outline_with_single_example_row_linter.rb +22 -22
- data/lib/cuke_linter/linters/single_test_background_linter.rb +19 -19
- data/lib/cuke_linter/linters/step_with_end_period_linter.rb +19 -19
- data/lib/cuke_linter/linters/step_with_too_many_characters_linter.rb +38 -38
- data/lib/cuke_linter/linters/test_name_with_too_many_characters_linter.rb +38 -38
- data/lib/cuke_linter/linters/test_should_use_background_linter.rb +80 -80
- data/lib/cuke_linter/linters/test_with_action_step_as_final_step_linter.rb +33 -33
- data/lib/cuke_linter/linters/test_with_bad_name_linter.rb +23 -23
- data/lib/cuke_linter/linters/test_with_no_action_step_linter.rb +33 -33
- data/lib/cuke_linter/linters/test_with_no_name_linter.rb +19 -19
- data/lib/cuke_linter/linters/test_with_no_verification_step_linter.rb +33 -33
- data/lib/cuke_linter/linters/test_with_setup_step_after_action_step_linter.rb +46 -46
- data/lib/cuke_linter/linters/test_with_setup_step_after_verification_step_linter.rb +46 -46
- data/lib/cuke_linter/linters/test_with_setup_step_as_final_step_linter.rb +33 -33
- data/lib/cuke_linter/linters/test_with_too_many_steps_linter.rb +27 -27
- data/lib/cuke_linter/version.rb +4 -4
- data/lib/cuke_linter.rb +196 -196
- data/testing/cucumber/features/command_line.feature +202 -202
- data/testing/cucumber/features/configuration/configuring_linters.feature +58 -58
- data/testing/cucumber/features/configuration/locally_scoping_linters.feature +55 -55
- data/testing/cucumber/features/configuration/using_configurations.feature +41 -41
- data/testing/cucumber/features/custom_linters.feature +56 -56
- data/testing/cucumber/features/default_linters.feature +57 -57
- data/testing/cucumber/features/formatters/pretty_formatter.feature +26 -26
- data/testing/cucumber/features/linters/background_does_more_than_setup.feature +84 -84
- data/testing/cucumber/features/linters/element_with_common_tags.feature +28 -28
- data/testing/cucumber/features/linters/element_with_duplicate_tags.feature +71 -71
- data/testing/cucumber/features/linters/element_with_too_many_tags.feature +70 -70
- data/testing/cucumber/features/linters/example_without_name.feature +34 -34
- data/testing/cucumber/features/linters/feature_file_with_invalid_name.feature +20 -20
- data/testing/cucumber/features/linters/feature_file_with_mismatched_name.feature +32 -32
- data/testing/cucumber/features/linters/feature_with_too_many_different_tags.feature +56 -56
- data/testing/cucumber/features/linters/feature_without_description.feature +17 -17
- data/testing/cucumber/features/linters/feature_without_name.feature +18 -18
- data/testing/cucumber/features/linters/feature_without_scenarios.feature +39 -39
- data/testing/cucumber/features/linters/outline_with_single_example_row.feature +23 -23
- data/testing/cucumber/features/linters/single_test_background.feature +24 -24
- data/testing/cucumber/features/linters/step_too_long.feature +43 -43
- data/testing/cucumber/features/linters/step_with_end_period.feature +21 -21
- data/testing/cucumber/features/linters/test_name_too_long.feature +41 -41
- data/testing/cucumber/features/linters/test_should_use_background.feature +29 -29
- data/testing/cucumber/features/linters/test_with_action_as_final_step.feature +50 -50
- data/testing/cucumber/features/linters/test_with_bad_name.feature +29 -29
- data/testing/cucumber/features/linters/test_with_no_action_step.feature +56 -56
- data/testing/cucumber/features/linters/test_with_no_name.feature +23 -23
- data/testing/cucumber/features/linters/test_with_no_verification_step.feature +58 -58
- data/testing/cucumber/features/linters/test_with_setup_step_after_action_step.feature +57 -57
- data/testing/cucumber/features/linters/test_with_setup_step_after_verification_step.feature +57 -57
- data/testing/cucumber/features/linters/test_with_setup_step_as_final_step.feature +50 -50
- data/testing/cucumber/features/linters/test_with_too_many_steps.feature +61 -61
- metadata +21 -25
- data/testing/cucumber/features/linters/rule_without_name.feature +0 -18
data/lib/cuke_linter.rb
CHANGED
|
@@ -1,196 +1,196 @@
|
|
|
1
|
-
require 'yaml'
|
|
2
|
-
require 'cuke_modeler'
|
|
3
|
-
|
|
4
|
-
require 'cuke_linter/version'
|
|
5
|
-
require 'cuke_linter/formatters/pretty_formatter'
|
|
6
|
-
require 'cuke_linter/linters/linter'
|
|
7
|
-
require 'cuke_linter/linters/background_does_more_than_setup_linter'
|
|
8
|
-
require 'cuke_linter/linters/element_with_common_tags_linter'
|
|
9
|
-
require 'cuke_linter/linters/element_with_duplicate_tags_linter'
|
|
10
|
-
require 'cuke_linter/linters/element_with_too_many_tags_linter'
|
|
11
|
-
require 'cuke_linter/linters/example_without_name_linter'
|
|
12
|
-
require 'cuke_linter/linters/feature_file_with_invalid_name_linter'
|
|
13
|
-
require 'cuke_linter/linters/feature_file_with_mismatched_name_linter'
|
|
14
|
-
require 'cuke_linter/linters/feature_with_too_many_different_tags_linter'
|
|
15
|
-
require 'cuke_linter/linters/feature_without_name_linter'
|
|
16
|
-
require 'cuke_linter/linters/feature_without_description_linter'
|
|
17
|
-
require 'cuke_linter/linters/feature_without_scenarios_linter'
|
|
18
|
-
require 'cuke_linter/linters/outline_with_single_example_row_linter'
|
|
19
|
-
require 'cuke_linter/linters/single_test_background_linter'
|
|
20
|
-
require 'cuke_linter/linters/step_with_end_period_linter'
|
|
21
|
-
require 'cuke_linter/linters/step_with_too_many_characters_linter'
|
|
22
|
-
require 'cuke_linter/linters/test_name_with_too_many_characters_linter'
|
|
23
|
-
require 'cuke_linter/linters/test_should_use_background_linter'
|
|
24
|
-
require 'cuke_linter/linters/test_with_action_step_as_final_step_linter'
|
|
25
|
-
require 'cuke_linter/linters/test_with_bad_name_linter'
|
|
26
|
-
require 'cuke_linter/linters/test_with_no_action_step_linter'
|
|
27
|
-
require 'cuke_linter/linters/test_with_no_name_linter'
|
|
28
|
-
require 'cuke_linter/linters/test_with_no_verification_step_linter'
|
|
29
|
-
require 'cuke_linter/linters/test_with_setup_step_after_action_step_linter'
|
|
30
|
-
require 'cuke_linter/linters/test_with_setup_step_after_verification_step_linter'
|
|
31
|
-
require 'cuke_linter/linters/test_with_setup_step_as_final_step_linter'
|
|
32
|
-
require 'cuke_linter/linters/test_with_too_many_steps_linter'
|
|
33
|
-
require 'cuke_linter/configuration'
|
|
34
|
-
require 'cuke_linter/default_linters'
|
|
35
|
-
require 'cuke_linter/gherkin'
|
|
36
|
-
require 'cuke_linter/linter_registration'
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
# The top level namespace used by this gem
|
|
40
|
-
module CukeLinter
|
|
41
|
-
|
|
42
|
-
extend CukeLinter::Configuration
|
|
43
|
-
extend CukeLinter::LinterRegistration
|
|
44
|
-
|
|
45
|
-
class << self
|
|
46
|
-
|
|
47
|
-
# Lints the given model trees and file paths using the given linting objects and formatting
|
|
48
|
-
# the results with the given formatters and their respective output locations
|
|
49
|
-
def lint(file_paths: [], model_trees: [], linters: registered_linters.values, formatters: [[CukeLinter::PrettyFormatter.new]]) # rubocop:disable
|
|
50
|
-
# TODO: Test this?
|
|
51
|
-
# Because directive memoization is based on a model's `#object_id` and Ruby reuses object IDs over the
|
|
52
|
-
# life of a program as objects are garbage collected, it is not safe to remember the IDs forever. However,
|
|
53
|
-
# models shouldn't get GC'd in the middle of the linting process and so the start of the linting process is
|
|
54
|
-
# a good time to reset things
|
|
55
|
-
@directives_for_feature_file = {}
|
|
56
|
-
|
|
57
|
-
model_trees = [CukeModeler::Directory.new(Dir.pwd)] if model_trees.empty? && file_paths.empty?
|
|
58
|
-
file_path_models = collect_file_path_models(file_paths)
|
|
59
|
-
model_sets = model_trees + file_path_models
|
|
60
|
-
|
|
61
|
-
linting_data = lint_models(model_sets, linters)
|
|
62
|
-
format_data(formatters, linting_data)
|
|
63
|
-
|
|
64
|
-
linting_data
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
private
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
def collect_file_path_models(file_paths)
|
|
72
|
-
file_paths.collect do |file_path|
|
|
73
|
-
# TODO: raise exception unless path exists?
|
|
74
|
-
if File.directory?(file_path)
|
|
75
|
-
CukeModeler::Directory.new(file_path)
|
|
76
|
-
elsif File.file?(file_path) && File.extname(file_path) == '.feature'
|
|
77
|
-
CukeModeler::FeatureFile.new(file_path)
|
|
78
|
-
end
|
|
79
|
-
end.compact # Compacting in order to get rid of any `nil` values left over from non-feature files
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
def lint_models(model_sets, linters)
|
|
83
|
-
[].tap do |linting_data|
|
|
84
|
-
model_sets.each do |model_tree|
|
|
85
|
-
model_tree.each_model do |model|
|
|
86
|
-
applicable_linters = relevant_linters_for_model(linters, model)
|
|
87
|
-
applicable_linters.each do |linter|
|
|
88
|
-
# TODO: have linters lint only certain types of models?
|
|
89
|
-
# linting_data.concat(linter.lint(model)) if relevant_model?(linter, model)
|
|
90
|
-
|
|
91
|
-
result = linter.lint(model)
|
|
92
|
-
|
|
93
|
-
if result
|
|
94
|
-
result[:linter] = linter.name
|
|
95
|
-
linting_data << result
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
def relevant_linters_for_model(base_linters, model)
|
|
104
|
-
feature_file_model = model.get_ancestor(:feature_file)
|
|
105
|
-
|
|
106
|
-
# Linter directives are not applicable for directory and feature file models. Every other
|
|
107
|
-
# model type should have a feature file ancestor from which to grab linter directive comments.
|
|
108
|
-
return base_linters if feature_file_model.nil?
|
|
109
|
-
|
|
110
|
-
linter_modifications_for_model = {}
|
|
111
|
-
|
|
112
|
-
linter_directives_for_feature_file(feature_file_model).each do |directive|
|
|
113
|
-
# Assuming that the directives are in the same order that they appear in the file
|
|
114
|
-
break if directive[:source_line] > model.source_line
|
|
115
|
-
|
|
116
|
-
linter_modifications_for_model[directive[:linter_class]] = directive[:enabled_status]
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
disabled_linter_classes = linter_modifications_for_model.reject { |_name, status| status }.keys
|
|
120
|
-
enabled_linter_classes = linter_modifications_for_model.select { |_name, status| status }.keys
|
|
121
|
-
|
|
122
|
-
determine_final_linters(base_linters, disabled_linter_classes, enabled_linter_classes)
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
def determine_final_linters(base_linters, disabled_linter_classes, enabled_linter_classes)
|
|
126
|
-
final_linters = base_linters.reject { |linter| disabled_linter_classes.include?(linter.class) }
|
|
127
|
-
|
|
128
|
-
enabled_linter_classes.each do |clazz|
|
|
129
|
-
final_linters << dynamic_linters[clazz] unless final_linters.map(&:class).include?(clazz)
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
final_linters
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
def linter_directives_for_feature_file(feature_file_model)
|
|
136
|
-
# IMPORTANT ASSUMPTION: Models never change during the life of a linting, so data only has to be gathered once
|
|
137
|
-
existing_directives = @directives_for_feature_file[feature_file_model
|
|
138
|
-
|
|
139
|
-
return existing_directives if existing_directives
|
|
140
|
-
|
|
141
|
-
directives = gather_directives_in_feature(feature_file_model)
|
|
142
|
-
|
|
143
|
-
# Make sure that the directives are in the same order as they appear in the source file
|
|
144
|
-
directives = directives.sort_by { |a| a[:source_line] }
|
|
145
|
-
|
|
146
|
-
@directives_for_feature_file[feature_file_model
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
def gather_directives_in_feature(feature_file_model)
|
|
150
|
-
[].tap do |directives|
|
|
151
|
-
feature_file_model.comments.each do |comment|
|
|
152
|
-
pieces = comment.text.match(/#\s*cuke_linter:(disable|enable)\s+(.*)/)
|
|
153
|
-
next unless pieces # Skipping non-directive file comments
|
|
154
|
-
|
|
155
|
-
linter_classes = pieces[2].tr(',', ' ').split
|
|
156
|
-
linter_classes.each do |clazz|
|
|
157
|
-
directives << { linter_class: Kernel.const_get(clazz),
|
|
158
|
-
enabled_status: pieces[1] != 'disable',
|
|
159
|
-
source_line: comment.source_line }
|
|
160
|
-
end
|
|
161
|
-
end
|
|
162
|
-
end
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
def dynamic_linters
|
|
166
|
-
# No need to keep making new ones over and over...
|
|
167
|
-
@dynamic_linters ||= Hash.new { |hash, key| hash[key] = key.new }
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
def format_data(formatters, linting_data)
|
|
171
|
-
formatters.each do |formatter_output_pair|
|
|
172
|
-
formatter = formatter_output_pair[0]
|
|
173
|
-
location = formatter_output_pair[1]
|
|
174
|
-
|
|
175
|
-
formatted_data = formatter.format(linting_data)
|
|
176
|
-
|
|
177
|
-
if location
|
|
178
|
-
File.write(location, formatted_data)
|
|
179
|
-
else
|
|
180
|
-
puts formatted_data
|
|
181
|
-
end
|
|
182
|
-
end
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
# Not linting unused code
|
|
186
|
-
# rubocop:disable
|
|
187
|
-
# def self.relevant_model?(linter, model)
|
|
188
|
-
# model_classes = linter.class.target_model_types.map { |type| CukeModeler.const_get(type.to_s.capitalize.chop) }
|
|
189
|
-
# model_classes.any? { |clazz| model.is_a?(clazz) }
|
|
190
|
-
# end
|
|
191
|
-
#
|
|
192
|
-
# private_class_method(:relevant_model?)
|
|
193
|
-
# rubocop:enable
|
|
194
|
-
|
|
195
|
-
end
|
|
196
|
-
end
|
|
1
|
+
require 'yaml'
|
|
2
|
+
require 'cuke_modeler'
|
|
3
|
+
|
|
4
|
+
require 'cuke_linter/version'
|
|
5
|
+
require 'cuke_linter/formatters/pretty_formatter'
|
|
6
|
+
require 'cuke_linter/linters/linter'
|
|
7
|
+
require 'cuke_linter/linters/background_does_more_than_setup_linter'
|
|
8
|
+
require 'cuke_linter/linters/element_with_common_tags_linter'
|
|
9
|
+
require 'cuke_linter/linters/element_with_duplicate_tags_linter'
|
|
10
|
+
require 'cuke_linter/linters/element_with_too_many_tags_linter'
|
|
11
|
+
require 'cuke_linter/linters/example_without_name_linter'
|
|
12
|
+
require 'cuke_linter/linters/feature_file_with_invalid_name_linter'
|
|
13
|
+
require 'cuke_linter/linters/feature_file_with_mismatched_name_linter'
|
|
14
|
+
require 'cuke_linter/linters/feature_with_too_many_different_tags_linter'
|
|
15
|
+
require 'cuke_linter/linters/feature_without_name_linter'
|
|
16
|
+
require 'cuke_linter/linters/feature_without_description_linter'
|
|
17
|
+
require 'cuke_linter/linters/feature_without_scenarios_linter'
|
|
18
|
+
require 'cuke_linter/linters/outline_with_single_example_row_linter'
|
|
19
|
+
require 'cuke_linter/linters/single_test_background_linter'
|
|
20
|
+
require 'cuke_linter/linters/step_with_end_period_linter'
|
|
21
|
+
require 'cuke_linter/linters/step_with_too_many_characters_linter'
|
|
22
|
+
require 'cuke_linter/linters/test_name_with_too_many_characters_linter'
|
|
23
|
+
require 'cuke_linter/linters/test_should_use_background_linter'
|
|
24
|
+
require 'cuke_linter/linters/test_with_action_step_as_final_step_linter'
|
|
25
|
+
require 'cuke_linter/linters/test_with_bad_name_linter'
|
|
26
|
+
require 'cuke_linter/linters/test_with_no_action_step_linter'
|
|
27
|
+
require 'cuke_linter/linters/test_with_no_name_linter'
|
|
28
|
+
require 'cuke_linter/linters/test_with_no_verification_step_linter'
|
|
29
|
+
require 'cuke_linter/linters/test_with_setup_step_after_action_step_linter'
|
|
30
|
+
require 'cuke_linter/linters/test_with_setup_step_after_verification_step_linter'
|
|
31
|
+
require 'cuke_linter/linters/test_with_setup_step_as_final_step_linter'
|
|
32
|
+
require 'cuke_linter/linters/test_with_too_many_steps_linter'
|
|
33
|
+
require 'cuke_linter/configuration'
|
|
34
|
+
require 'cuke_linter/default_linters'
|
|
35
|
+
require 'cuke_linter/gherkin'
|
|
36
|
+
require 'cuke_linter/linter_registration'
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# The top level namespace used by this gem
|
|
40
|
+
module CukeLinter
|
|
41
|
+
|
|
42
|
+
extend CukeLinter::Configuration
|
|
43
|
+
extend CukeLinter::LinterRegistration
|
|
44
|
+
|
|
45
|
+
class << self
|
|
46
|
+
|
|
47
|
+
# Lints the given model trees and file paths using the given linting objects and formatting
|
|
48
|
+
# the results with the given formatters and their respective output locations
|
|
49
|
+
def lint(file_paths: [], model_trees: [], linters: registered_linters.values, formatters: [[CukeLinter::PrettyFormatter.new]]) # rubocop:disable Layout/LineLength
|
|
50
|
+
# TODO: Test this?
|
|
51
|
+
# Because directive memoization is based on a model's `#object_id` and Ruby reuses object IDs over the
|
|
52
|
+
# life of a program as objects are garbage collected, it is not safe to remember the IDs forever. However,
|
|
53
|
+
# models shouldn't get GC'd in the middle of the linting process and so the start of the linting process is
|
|
54
|
+
# a good time to reset things
|
|
55
|
+
@directives_for_feature_file = {}.compare_by_identity
|
|
56
|
+
|
|
57
|
+
model_trees = [CukeModeler::Directory.new(Dir.pwd)] if model_trees.empty? && file_paths.empty?
|
|
58
|
+
file_path_models = collect_file_path_models(file_paths)
|
|
59
|
+
model_sets = model_trees + file_path_models
|
|
60
|
+
|
|
61
|
+
linting_data = lint_models(model_sets, linters)
|
|
62
|
+
format_data(formatters, linting_data)
|
|
63
|
+
|
|
64
|
+
linting_data
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def collect_file_path_models(file_paths)
|
|
72
|
+
file_paths.collect do |file_path|
|
|
73
|
+
# TODO: raise exception unless path exists?
|
|
74
|
+
if File.directory?(file_path)
|
|
75
|
+
CukeModeler::Directory.new(file_path)
|
|
76
|
+
elsif File.file?(file_path) && File.extname(file_path) == '.feature'
|
|
77
|
+
CukeModeler::FeatureFile.new(file_path)
|
|
78
|
+
end
|
|
79
|
+
end.compact # Compacting in order to get rid of any `nil` values left over from non-feature files
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def lint_models(model_sets, linters)
|
|
83
|
+
[].tap do |linting_data|
|
|
84
|
+
model_sets.each do |model_tree|
|
|
85
|
+
model_tree.each_model do |model|
|
|
86
|
+
applicable_linters = relevant_linters_for_model(linters, model)
|
|
87
|
+
applicable_linters.each do |linter|
|
|
88
|
+
# TODO: have linters lint only certain types of models?
|
|
89
|
+
# linting_data.concat(linter.lint(model)) if relevant_model?(linter, model)
|
|
90
|
+
|
|
91
|
+
result = linter.lint(model)
|
|
92
|
+
|
|
93
|
+
if result
|
|
94
|
+
result[:linter] = linter.name
|
|
95
|
+
linting_data << result
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def relevant_linters_for_model(base_linters, model) # rubocop:disable Metrics/AbcSize -- Maybe I'll revisit this later
|
|
104
|
+
feature_file_model = model.get_ancestor(:feature_file)
|
|
105
|
+
|
|
106
|
+
# Linter directives are not applicable for directory and feature file models. Every other
|
|
107
|
+
# model type should have a feature file ancestor from which to grab linter directive comments.
|
|
108
|
+
return base_linters if feature_file_model.nil?
|
|
109
|
+
|
|
110
|
+
linter_modifications_for_model = {}
|
|
111
|
+
|
|
112
|
+
linter_directives_for_feature_file(feature_file_model).each do |directive|
|
|
113
|
+
# Assuming that the directives are in the same order that they appear in the file
|
|
114
|
+
break if directive[:source_line] > model.source_line
|
|
115
|
+
|
|
116
|
+
linter_modifications_for_model[directive[:linter_class]] = directive[:enabled_status]
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
disabled_linter_classes = linter_modifications_for_model.reject { |_name, status| status }.keys
|
|
120
|
+
enabled_linter_classes = linter_modifications_for_model.select { |_name, status| status }.keys
|
|
121
|
+
|
|
122
|
+
determine_final_linters(base_linters, disabled_linter_classes, enabled_linter_classes)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def determine_final_linters(base_linters, disabled_linter_classes, enabled_linter_classes)
|
|
126
|
+
final_linters = base_linters.reject { |linter| disabled_linter_classes.include?(linter.class) }
|
|
127
|
+
|
|
128
|
+
enabled_linter_classes.each do |clazz|
|
|
129
|
+
final_linters << dynamic_linters[clazz] unless final_linters.map(&:class).include?(clazz)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
final_linters
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def linter_directives_for_feature_file(feature_file_model)
|
|
136
|
+
# IMPORTANT ASSUMPTION: Models never change during the life of a linting, so data only has to be gathered once
|
|
137
|
+
existing_directives = @directives_for_feature_file[feature_file_model]
|
|
138
|
+
|
|
139
|
+
return existing_directives if existing_directives
|
|
140
|
+
|
|
141
|
+
directives = gather_directives_in_feature(feature_file_model)
|
|
142
|
+
|
|
143
|
+
# Make sure that the directives are in the same order as they appear in the source file
|
|
144
|
+
directives = directives.sort_by { |a| a[:source_line] }
|
|
145
|
+
|
|
146
|
+
@directives_for_feature_file[feature_file_model] = directives
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def gather_directives_in_feature(feature_file_model)
|
|
150
|
+
[].tap do |directives|
|
|
151
|
+
feature_file_model.comments.each do |comment|
|
|
152
|
+
pieces = comment.text.match(/#\s*cuke_linter:(disable|enable)\s+(.*)/)
|
|
153
|
+
next unless pieces # Skipping non-directive file comments
|
|
154
|
+
|
|
155
|
+
linter_classes = pieces[2].tr(',', ' ').split
|
|
156
|
+
linter_classes.each do |clazz|
|
|
157
|
+
directives << { linter_class: Kernel.const_get(clazz),
|
|
158
|
+
enabled_status: pieces[1] != 'disable',
|
|
159
|
+
source_line: comment.source_line }
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def dynamic_linters
|
|
166
|
+
# No need to keep making new ones over and over...
|
|
167
|
+
@dynamic_linters ||= Hash.new { |hash, key| hash[key] = key.new }
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def format_data(formatters, linting_data)
|
|
171
|
+
formatters.each do |formatter_output_pair|
|
|
172
|
+
formatter = formatter_output_pair[0]
|
|
173
|
+
location = formatter_output_pair[1]
|
|
174
|
+
|
|
175
|
+
formatted_data = formatter.format(linting_data)
|
|
176
|
+
|
|
177
|
+
if location
|
|
178
|
+
File.write(location, formatted_data)
|
|
179
|
+
else
|
|
180
|
+
puts formatted_data
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Not linting unused code
|
|
186
|
+
# rubocop:disable Layout/LineLength
|
|
187
|
+
# def self.relevant_model?(linter, model)
|
|
188
|
+
# model_classes = linter.class.target_model_types.map { |type| CukeModeler.const_get(type.to_s.capitalize.chop) }
|
|
189
|
+
# model_classes.any? { |clazz| model.is_a?(clazz) }
|
|
190
|
+
# end
|
|
191
|
+
#
|
|
192
|
+
# private_class_method(:relevant_model?)
|
|
193
|
+
# rubocop:enable Layout/LineLength
|
|
194
|
+
|
|
195
|
+
end
|
|
196
|
+
end
|