cuke_modeler 3.2.0 → 3.7.0

Sign up to get free protection for your applications and to get access to all the features.
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