cuke_modeler 3.2.0 → 3.7.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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +48 -3
  3. data/LICENSE.txt +1 -1
  4. data/README.md +7 -7
  5. data/cuke_modeler.gemspec +35 -16
  6. data/lib/cuke_modeler.rb +1 -2
  7. data/lib/cuke_modeler/adapters/gherkin_10_adapter.rb +2 -1
  8. data/lib/cuke_modeler/adapters/gherkin_11_adapter.rb +2 -1
  9. data/lib/cuke_modeler/adapters/gherkin_12_adapter.rb +2 -1
  10. data/lib/cuke_modeler/adapters/gherkin_13_adapter.rb +2 -1
  11. data/lib/cuke_modeler/adapters/gherkin_14_adapter.rb +2 -1
  12. data/lib/cuke_modeler/adapters/gherkin_15_adapter.rb +13 -0
  13. data/lib/cuke_modeler/adapters/gherkin_16_adapter.rb +13 -0
  14. data/lib/cuke_modeler/adapters/gherkin_17_adapter.rb +13 -0
  15. data/lib/cuke_modeler/adapters/gherkin_9_adapter.rb +261 -243
  16. data/lib/cuke_modeler/containing.rb +47 -90
  17. data/lib/cuke_modeler/described.rb +40 -2
  18. data/lib/cuke_modeler/models/background.rb +11 -12
  19. data/lib/cuke_modeler/models/cell.rb +13 -8
  20. data/lib/cuke_modeler/models/comment.rb +5 -6
  21. data/lib/cuke_modeler/models/directory.rb +13 -18
  22. data/lib/cuke_modeler/models/doc_string.rb +10 -8
  23. data/lib/cuke_modeler/models/example.rb +63 -46
  24. data/lib/cuke_modeler/models/feature.rb +23 -17
  25. data/lib/cuke_modeler/models/feature_file.rb +5 -8
  26. data/lib/cuke_modeler/models/model.rb +2 -3
  27. data/lib/cuke_modeler/models/outline.rb +19 -15
  28. data/lib/cuke_modeler/models/row.rb +15 -8
  29. data/lib/cuke_modeler/models/rule.rb +11 -10
  30. data/lib/cuke_modeler/models/scenario.rb +17 -13
  31. data/lib/cuke_modeler/models/step.rb +40 -19
  32. data/lib/cuke_modeler/models/table.rb +9 -7
  33. data/lib/cuke_modeler/models/tag.rb +9 -6
  34. data/lib/cuke_modeler/named.rb +5 -2
  35. data/lib/cuke_modeler/nested.rb +22 -19
  36. data/lib/cuke_modeler/parsed.rb +8 -1
  37. data/lib/cuke_modeler/parsing.rb +38 -29
  38. data/lib/cuke_modeler/sourceable.rb +8 -1
  39. data/lib/cuke_modeler/stepped.rb +8 -1
  40. data/lib/cuke_modeler/taggable.rb +9 -2
  41. data/lib/cuke_modeler/version.rb +1 -1
  42. metadata +91 -36
@@ -1,11 +1,28 @@
1
+ # It's the module that has functionality for every possible model type. So, yes, it's long.
2
+ # rubocop:disable Metrics/ModuleLength
3
+
1
4
  module CukeModeler
2
5
 
3
6
  # NOT A PART OF THE PUBLIC API
4
7
  # A mix-in module containing methods used by models that contain other models.
5
-
6
8
  module Containing
7
9
 
10
+ include Enumerable
11
+
12
+ # Executes the given code block with this model and every model that is a child of this model. Exact
13
+ # order of model tree traversal is not guaranteed beyond the first model traversed, which will be the
14
+ # model that called this method. If no block is provided, an `Enumerator` is returned instead.
15
+ def each(&block)
16
+ if block
17
+ block.call(self)
18
+ children.each { |child| child.each(&block) }
19
+ else
20
+ to_enum(:each)
21
+ end
22
+ end
23
+
8
24
  # Executes the given code block with every model that is a child of this model.
25
+ # DEPRECATED: use `Enumerable` module methods instead
9
26
  def each_descendant(&block)
10
27
  children.each do |child_model|
11
28
  block.call(child_model)
@@ -14,6 +31,7 @@ module CukeModeler
14
31
  end
15
32
 
16
33
  # Executes the given code block with this model and every model that is a child of this model.
34
+ # DEPRECATED: use `Enumerable` module methods instead
17
35
  def each_model(&block)
18
36
  block.call(self)
19
37
 
@@ -74,14 +92,11 @@ module CukeModeler
74
92
  end
75
93
 
76
94
  def populate_block(step_object, parsed_step_data)
77
- case
78
- when parsed_step_data['table']
79
- step_object.block = build_child_model(Table, parsed_step_data['table'])
80
- when parsed_step_data['doc_string']
81
- step_object.block = build_child_model(DocString, parsed_step_data['doc_string'])
82
- else
83
- step_object.block = nil
84
- end
95
+ step_object.block = if parsed_step_data['table']
96
+ build_child_model(Table, parsed_step_data['table'])
97
+ elsif parsed_step_data['doc_string']
98
+ build_child_model(DocString, parsed_step_data['doc_string'])
99
+ end
85
100
  end
86
101
 
87
102
  def populate_table(table_object, parsed_table_data)
@@ -154,7 +169,9 @@ module CukeModeler
154
169
  populate_parsing_data(feature_file_object, processed_feature_file_data)
155
170
  feature_file_object.path = processed_feature_file_data['path']
156
171
 
157
- feature_file_object.feature = build_child_model(Feature, processed_feature_file_data['feature']) unless processed_feature_file_data['feature'].nil?
172
+ if processed_feature_file_data['feature']
173
+ feature_file_object.feature = build_child_model(Feature, processed_feature_file_data['feature'])
174
+ end
158
175
 
159
176
  processed_feature_file_data['comments'].each do |comment_data|
160
177
  feature_file_object.comments << build_child_model(Comment, comment_data)
@@ -202,7 +219,7 @@ module CukeModeler
202
219
  end
203
220
 
204
221
  def populate_content_type(doc_string_model, parsed_doc_string_data)
205
- doc_string_model.content_type = parsed_doc_string_data['content_type'] == "" ? nil : parsed_doc_string_data['content_type']
222
+ doc_string_model.content_type = parsed_doc_string_data['content_type']
206
223
  end
207
224
 
208
225
  def populate_content(doc_string_model, parsed_doc_string_data)
@@ -221,89 +238,29 @@ module CukeModeler
221
238
  end
222
239
  end
223
240
 
224
- def populate_children(model, parsed_feature_data)
225
- elements = parsed_feature_data['elements']
226
-
227
- if elements
228
- elements.each do |element|
229
- case element['type']
230
- when 'Scenario', 'scenario'
231
- model.tests << build_child_model(Scenario, element)
232
- when 'ScenarioOutline', 'scenario_outline'
233
- model.tests << build_child_model(Outline, element)
234
- when 'Background', 'background'
235
- model.background = build_child_model(Background, element)
236
- when 'Rule'
237
- model.rules << build_child_model(Rule, element)
238
- else
239
- raise(ArgumentError, "Unknown element type: #{element['type']}")
240
- end
241
- end
242
- end
243
- end
244
-
245
- def populate_parsing_data(model, parsed_model_data)
246
- model.parsing_data = parsed_model_data['cuke_modeler_parsing_data']
247
- end
248
-
249
- def populate_source_line(model, parsed_model_data)
250
- model.source_line = parsed_model_data['line']
251
- end
252
-
253
- def populate_name(model, parsed_model_data)
254
- model.name = parsed_model_data['name']
255
- end
256
-
257
- def populate_description(model, parsed_model_data)
258
- model.description = trimmed_description(parsed_model_data['description'])
259
- end
260
-
261
- def trimmed_description(description)
262
- description = description.split("\n")
263
-
264
- trim_leading_blank_lines(description)
265
- trim_trailing_blank_lines(description)
266
- trim_leading_spaces(description)
267
- trim_trailing_spaces(description)
268
-
269
- description.join("\n")
270
- end
271
-
272
- def trim_leading_blank_lines(description)
273
- description.replace(description.drop_while { |line| line !~ /\S/ })
274
- end
275
-
276
- def trim_trailing_blank_lines(description)
277
- # Nothing to do. Already done by the parser but leaving this here in case that changes in future versions.
278
- end
279
-
280
- def trim_leading_spaces(description)
281
- non_blank_lines = description.select { |line| line =~ /\S/ }
282
-
283
- fewest_spaces = non_blank_lines.collect { |line| line[/^\s*/].length }.min || 0
241
+ # It's not getting better any time soon
242
+ # rubocop:disable Metrics/AbcSize
284
243
 
285
- description.each { |line| line.slice!(0..(fewest_spaces - 1)) } if fewest_spaces > 0
286
- end
287
-
288
- def trim_trailing_spaces(description)
289
- description.map! { |line| line.rstrip }
290
- end
291
-
292
- def populate_tags(model, parsed_model_data)
293
- if parsed_model_data['tags']
294
- parsed_model_data['tags'].each do |tag|
295
- model.tags << build_child_model(Tag, tag)
296
- end
297
- end
298
- end
299
-
300
- def populate_steps(model, parsed_model_data)
301
- if parsed_model_data['steps']
302
- parsed_model_data['steps'].each do |step_data|
303
- model.steps << build_child_model(Step, step_data)
244
+ def populate_children(model, parsed_feature_data)
245
+ return unless parsed_feature_data['elements']
246
+
247
+ parsed_feature_data['elements'].each do |element|
248
+ case element['type']
249
+ when 'Scenario', 'scenario'
250
+ model.tests << build_child_model(Scenario, element)
251
+ when 'ScenarioOutline', 'scenario_outline'
252
+ model.tests << build_child_model(Outline, element)
253
+ when 'Background', 'background'
254
+ model.background = build_child_model(Background, element)
255
+ when 'Rule'
256
+ model.rules << build_child_model(Rule, element)
257
+ else
258
+ raise(ArgumentError, "Unknown element type: #{element['type']}")
304
259
  end
305
260
  end
306
261
  end
262
+ # rubocop:enable Metrics/AbcSize
307
263
 
308
264
  end
309
265
  end
266
+ # rubocop:enable Metrics/ModuleLength
@@ -2,7 +2,6 @@ module CukeModeler
2
2
 
3
3
  # NOT A PART OF THE PUBLIC API
4
4
  # A mix-in module containing methods used by models that represent an element that has a description.
5
-
6
5
  module Described
7
6
 
8
7
  # The description of the element
@@ -19,11 +18,50 @@ module CukeModeler
19
18
  description_lines = description.split("\n")
20
19
 
21
20
  text << "\n" if description_lines.first =~ /\S/
22
- text << description_lines.collect { |line| "#{line}" }.join("\n")
21
+ text << description_lines.join("\n")
23
22
  end
24
23
 
25
24
  text
26
25
  end
27
26
 
27
+ def no_description_to_output?
28
+ description.nil? || description.empty?
29
+ end
30
+
31
+ def populate_description(model, parsed_model_data)
32
+ model.description = trimmed_description(parsed_model_data['description'])
33
+ end
34
+
35
+ def trimmed_description(description)
36
+ description = description.split("\n")
37
+
38
+ trim_leading_blank_lines(description)
39
+ trim_trailing_blank_lines(description)
40
+ trim_leading_spaces(description)
41
+ trim_trailing_spaces(description)
42
+
43
+ description.join("\n")
44
+ end
45
+
46
+ def trim_leading_blank_lines(description)
47
+ description.replace(description.drop_while { |line| line !~ /\S/ })
48
+ end
49
+
50
+ def trim_trailing_blank_lines(_description)
51
+ # Nothing to do. Already done by the parser but leaving this here in case that changes in future versions.
52
+ end
53
+
54
+ def trim_leading_spaces(description)
55
+ non_blank_lines = description.select { |line| line =~ /\S/ }
56
+
57
+ fewest_spaces = non_blank_lines.collect { |line| line[/^\s*/].length }.min || 0
58
+
59
+ description.each { |line| line.slice!(0..(fewest_spaces - 1)) } if fewest_spaces.positive?
60
+ end
61
+
62
+ def trim_trailing_spaces(description)
63
+ description.map!(&:rstrip)
64
+ end
65
+
28
66
  end
29
67
  end
@@ -1,7 +1,6 @@
1
1
  module CukeModeler
2
2
 
3
3
  # A class modeling a feature's background.
4
-
5
4
  class Background < Model
6
5
 
7
6
  include Parsing
@@ -23,17 +22,17 @@ module CukeModeler
23
22
 
24
23
  super(source_text)
25
24
 
26
- if source_text
27
- parsed_background_data = parse_source(source_text)
28
- populate_background(self, parsed_background_data)
29
- end
25
+ return unless source_text
26
+
27
+ parsed_background_data = parse_source(source_text)
28
+ populate_background(self, parsed_background_data)
30
29
  end
31
30
 
32
31
  # Returns *true* if the two models have equivalent steps and *false* otherwise.
33
- def ==(other_model)
34
- return false unless other_model.respond_to?(:steps)
32
+ def ==(other)
33
+ return false unless other.respond_to?(:steps)
35
34
 
36
- steps == other_model.steps
35
+ steps == other.steps
37
36
  end
38
37
 
39
38
  # Returns the model objects that belong to this model.
@@ -47,9 +46,9 @@ module CukeModeler
47
46
  text = ''
48
47
 
49
48
  text << "#{@keyword}:#{name_output_string}"
50
- text << "\n" + description_output_string unless (description.nil? || description.empty?)
51
- text << "\n" unless (steps.empty? || description.nil? || description.empty?)
52
- text << "\n" + steps_output_string unless steps.empty?
49
+ text << "\n#{description_output_string}" unless no_description_to_output?
50
+ text << "\n" unless steps.empty? || no_description_to_output?
51
+ text << "\n#{steps_output_string}" unless steps.empty?
53
52
 
54
53
  text
55
54
  end
@@ -62,7 +61,7 @@ module CukeModeler
62
61
  base_file_string = "# language: #{Parsing.dialect}\n#{dialect_feature_keyword}: Fake feature to parse\n"
63
62
  source_text = base_file_string + source_text
64
63
 
65
- parsed_file = Parsing::parse_text(source_text, 'cuke_modeler_stand_alone_background.feature')
64
+ parsed_file = Parsing.parse_text(source_text, 'cuke_modeler_stand_alone_background.feature')
66
65
 
67
66
  parsed_file['feature']['elements'].first
68
67
  end
@@ -1,7 +1,6 @@
1
1
  module CukeModeler
2
2
 
3
3
  # A class modeling a single cell of a row.
4
-
5
4
  class Cell < Model
6
5
 
7
6
  include Sourceable
@@ -18,10 +17,10 @@ module CukeModeler
18
17
  def initialize(source_text = nil)
19
18
  super(source_text)
20
19
 
21
- if source_text
22
- parsed_cell_data = parse_source(source_text)
23
- populate_cell(self, parsed_cell_data)
24
- end
20
+ return unless source_text
21
+
22
+ parsed_cell_data = parse_source(source_text)
23
+ populate_cell(self, parsed_cell_data)
25
24
  end
26
25
 
27
26
  # Returns a string representation of this model. For a cell model,
@@ -35,14 +34,20 @@ module CukeModeler
35
34
  private
36
35
 
37
36
 
37
+ # It's only considered complex because of how deeply nested cells are in the tree. It's not REALLY complex.
38
+ # rubocop:disable Metrics/AbcSize
38
39
  def parse_source(source_text)
39
- base_file_string = "# language: #{Parsing.dialect}\n#{dialect_feature_keyword}: Fake feature to parse\n#{dialect_scenario_keyword}:\n#{dialect_step_keyword} fake step\n"
40
- source_text = base_file_string + '|' + source_text + '|'
40
+ base_file_string = "# language: #{Parsing.dialect}
41
+ #{dialect_feature_keyword}: Fake feature to parse
42
+ #{dialect_scenario_keyword}:
43
+ #{dialect_step_keyword} fake step\n"
44
+ source_text = "#{base_file_string}|#{source_text}|"
41
45
 
42
- parsed_file = Parsing::parse_text(source_text, 'cuke_modeler_stand_alone_cell.feature')
46
+ parsed_file = Parsing.parse_text(source_text, 'cuke_modeler_stand_alone_cell.feature')
43
47
 
44
48
  parsed_file['feature']['elements'].first['steps'].first['table']['rows'].first['cells'].first
45
49
  end
50
+ # rubocop:enable Metrics/AbcSize
46
51
 
47
52
  end
48
53
 
@@ -1,7 +1,6 @@
1
1
  module CukeModeler
2
2
 
3
3
  # A class modeling a comment in a feature file.
4
-
5
4
  class Comment < Model
6
5
 
7
6
  include Parsing
@@ -18,10 +17,10 @@ module CukeModeler
18
17
  def initialize(source_text = nil)
19
18
  super(source_text)
20
19
 
21
- if source_text
22
- parsed_comment_data = parse_source(source_text)
23
- populate_comment(self, parsed_comment_data)
24
- end
20
+ return unless source_text
21
+
22
+ parsed_comment_data = parse_source(source_text)
23
+ populate_comment(self, parsed_comment_data)
25
24
  end
26
25
 
27
26
  # Returns a string representation of this model. For a comment model,
@@ -38,7 +37,7 @@ module CukeModeler
38
37
  base_file_string = "\n#{dialect_feature_keyword}: Fake feature to parse"
39
38
  source_text = "# language: #{Parsing.dialect}\n" + source_text + base_file_string
40
39
 
41
- parsed_file = Parsing::parse_text(source_text, 'cuke_modeler_stand_alone_comment.feature')
40
+ parsed_file = Parsing.parse_text(source_text, 'cuke_modeler_stand_alone_comment.feature')
42
41
 
43
42
  parsed_file['comments'].last
44
43
  end
@@ -1,7 +1,6 @@
1
1
  module CukeModeler
2
2
 
3
3
  # A class modeling a directory in a Cucumber suite.
4
-
5
4
  class Directory < Model
6
5
 
7
6
 
@@ -24,12 +23,11 @@ module CukeModeler
24
23
 
25
24
  super(directory_path)
26
25
 
27
- if directory_path
28
- raise(ArgumentError, "Unknown directory: #{directory_path.inspect}") unless File.exists?(directory_path)
26
+ return unless directory_path
27
+ raise(ArgumentError, "Unknown directory: #{directory_path.inspect}") unless File.exist?(directory_path)
29
28
 
30
- processed_directory_data = process_directory(directory_path)
31
- populate_directory(self, processed_directory_data)
32
- end
29
+ processed_directory_data = process_directory(directory_path)
30
+ populate_directory(self, processed_directory_data)
33
31
  end
34
32
 
35
33
  # Returns the name of the modeled directory.
@@ -53,10 +51,9 @@ module CukeModeler
53
51
 
54
52
 
55
53
  def process_directory(directory_path)
56
- directory_data = {'path' => directory_path,
57
- 'directories' => [],
58
- 'feature_files' => []
59
- }
54
+ directory_data = { 'path' => directory_path,
55
+ 'directories' => [],
56
+ 'feature_files' => [] }
60
57
 
61
58
  entries = Dir.entries(directory_path)
62
59
  entries.delete '.'
@@ -65,13 +62,11 @@ module CukeModeler
65
62
  entries.each do |entry|
66
63
  entry = "#{directory_path}/#{entry}"
67
64
 
68
- case
69
- when File.directory?(entry)
70
- directory_data['directories'] << process_directory(entry)
71
- when entry =~ /\.feature$/
72
- directory_data['feature_files'] << process_feature_file(entry)
73
- else
74
- # Ignore anything that isn't a directory or a feature file
65
+ # Ignore anything that isn't a directory or a feature file
66
+ if File.directory?(entry)
67
+ directory_data['directories'] << process_directory(entry)
68
+ elsif entry =~ /\.feature$/
69
+ directory_data['feature_files'] << process_feature_file(entry)
75
70
  end
76
71
  end
77
72
 
@@ -82,7 +77,7 @@ module CukeModeler
82
77
  def process_feature_file(file_path)
83
78
  source_text = IO.read(file_path)
84
79
 
85
- feature_file_data = Parsing::parse_text(source_text, file_path)
80
+ feature_file_data = Parsing.parse_text(source_text, file_path)
86
81
  feature_file_data = feature_file_data.merge({ 'path' => file_path })
87
82
 
88
83
  feature_file_data
@@ -1,7 +1,6 @@
1
1
  module CukeModeler
2
2
 
3
3
  # A class modeling a step's doc string.
4
-
5
4
  class DocString < Model
6
5
 
7
6
  include Parsing
@@ -21,10 +20,10 @@ module CukeModeler
21
20
  def initialize(source_text = nil)
22
21
  super(source_text)
23
22
 
24
- if source_text
25
- parsed_doc_string_data = parse_source(source_text)
26
- populate_docstring(self, parsed_doc_string_data)
27
- end
23
+ return unless source_text
24
+
25
+ parsed_doc_string_data = parse_source(source_text)
26
+ populate_docstring(self, parsed_doc_string_data)
28
27
  end
29
28
 
30
29
  # Returns a string representation of this model. For a doc string model,
@@ -40,10 +39,13 @@ module CukeModeler
40
39
 
41
40
 
42
41
  def parse_source(source_text)
43
- base_file_string = "# language: #{Parsing.dialect}\n#{dialect_feature_keyword}:\n#{dialect_scenario_keyword}:\n#{dialect_step_keyword} step\n"
42
+ base_file_string = "# language: #{Parsing.dialect}
43
+ #{dialect_feature_keyword}:
44
+ #{dialect_scenario_keyword}:
45
+ #{dialect_step_keyword} step\n"
44
46
  source_text = base_file_string + source_text
45
47
 
46
- parsed_file = Parsing::parse_text(source_text, 'cuke_modeler_stand_alone_doc_string.feature')
48
+ parsed_file = Parsing.parse_text(source_text, 'cuke_modeler_stand_alone_doc_string.feature')
47
49
 
48
50
  parsed_file['feature']['elements'].first['steps'].first['doc_string']
49
51
  end
@@ -53,7 +55,7 @@ module CukeModeler
53
55
  end
54
56
 
55
57
  def content_output_string
56
- (content.nil? || content.empty?) ? '' : content.gsub('"""', '\"\"\"') + "\n"
58
+ content.nil? || content.empty? ? '' : content.gsub('"""', '\"\"\"') + "\n"
57
59
  end
58
60
 
59
61
  end