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,7 +1,9 @@
1
+ # I'll take extra class length due to extra helper methods over having fewer but more complex methods
2
+ # rubocop:disable Metrics/ClassLength
3
+
1
4
  module CukeModeler
2
5
 
3
6
  # A class modeling an example table of an outline.
4
-
5
7
  class Example < Model
6
8
 
7
9
  include Parsing
@@ -27,10 +29,10 @@ module CukeModeler
27
29
 
28
30
  super(source_text)
29
31
 
30
- if source_text
31
- parsed_example_data = parse_source(source_text)
32
- populate_example(self, parsed_example_data)
33
- end
32
+ return unless source_text
33
+
34
+ parsed_example_data = parse_source(source_text)
35
+ populate_example(self, parsed_example_data)
34
36
  end
35
37
 
36
38
  # Adds a row to the example table. The row can be given as a Hash of
@@ -40,42 +42,39 @@ module CukeModeler
40
42
  raise('Cannot add a row. No parameters have been set.') if rows.empty?
41
43
 
42
44
  # A quick 'deep clone' so that the input isn't modified
43
- row = Marshal::load(Marshal.dump(row))
44
-
45
- case
46
- when row.is_a?(Array)
47
- # 'stringify' input
48
- row.collect! { |value| value.to_s }
49
-
50
- @rows << Row.new("|#{row.join('|')}|")
51
- when row.is_a?(Hash)
52
- # 'stringify' input
53
- row = row.inject({}) { |hash, (key, value)| hash[key.to_s] = value.to_s; hash }
54
-
55
- @rows << Row.new("|#{ordered_row_values(row).join('|')}|")
56
- else
57
- raise(ArgumentError, "Can only add row from a Hash or an Array but received #{row.class}")
58
- end
45
+ row = Marshal.load(Marshal.dump(row))
46
+
47
+ values = if row.is_a?(Array)
48
+ row
49
+ elsif row.is_a?(Hash)
50
+ # There is no guarantee that the user built up their hash with the keys in the same order as
51
+ # the parameter row and so the values have to be ordered by us. Additionally, the hash needs
52
+ # to have string keys in order for #order_row_values to work
53
+ ordered_row_values(stringify_keys(row))
54
+ else
55
+ raise(ArgumentError, "Can only add row from a Hash or an Array but received #{row.class}")
56
+ end
57
+
58
+ @rows << Row.new("|#{values.join('|')}|")
59
59
  end
60
60
 
61
61
  # Removes a row from the example table. The row can be given as a Hash of
62
62
  # parameters and their corresponding values or as an Array of values
63
63
  # which will be assigned in order.
64
64
  def remove_row(row_removed)
65
- return unless argument_rows
66
-
67
- case
68
- when row_removed.is_a?(Array)
69
- location = argument_rows.index { |row| row.cells.collect { |cell| cell.value } == row_removed.collect { |value| value.strip } }
70
- when row_removed.is_a?(Hash)
71
- # Note: the hash value order has to be manually calculated because Ruby 1.8.7 does not have ordered
72
- # hash keys. Alternatively, the hash may have simply been built up 'willy nilly' by the user instead
73
- # of being built up in order according to the parameter order.
74
- location = argument_rows.index { |row| row.cells.collect { |cell| cell.value } == ordered_row_values(row_removed.each_value { |value| value.strip! }) }
75
- else
76
- raise(ArgumentError, "Can only remove row from a Hash or an Array but received #{row_removed.class}")
77
- end
78
-
65
+ return if argument_rows.empty?
66
+
67
+ values = if row_removed.is_a?(Array)
68
+ row_removed
69
+ elsif row_removed.is_a?(Hash)
70
+ # There is no guarantee that the user built up their hash with the keys in the same order as
71
+ # the parameter row and so the values have to be ordered by us.
72
+ ordered_row_values(row_removed)
73
+ else
74
+ raise(ArgumentError, "Can only remove row from a Hash or an Array but received #{row_removed.class}")
75
+ end
76
+
77
+ location = index_for_values(values.map(&:to_s).map(&:strip))
79
78
  @rows.delete_at(location + 1) if location
80
79
  end
81
80
 
@@ -91,7 +90,7 @@ module CukeModeler
91
90
 
92
91
  # Returns the parameters of the example table
93
92
  def parameters
94
- parameter_row ? parameter_row.cells.collect { |cell| cell.value } : []
93
+ parameter_row ? parameter_row.cells.map(&:value) : []
95
94
  end
96
95
 
97
96
  # Returns the model objects that belong to this model.
@@ -99,30 +98,38 @@ module CukeModeler
99
98
  rows + tags
100
99
  end
101
100
 
101
+ # Building strings just isn't pretty
102
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
103
+
102
104
  # Returns a string representation of this model. For an example model,
103
105
  # this will be Gherkin text that is equivalent to the example being modeled.
104
106
  def to_s
105
107
  text = ''
106
108
 
107
- text << tag_output_string + "\n" unless tags.empty?
109
+ text << "#{tag_output_string}\n" unless tags.empty?
108
110
  text << "#{@keyword}:#{name_output_string}"
109
- text << "\n" + description_output_string unless (description.nil? || description.empty?)
110
- text << "\n" unless (rows.empty? || description.nil? || description.empty?)
111
- text << "\n" + parameters_output_string if parameter_row
112
- text << "\n" + rows_output_string unless argument_rows.empty?
111
+ text << "\n#{description_output_string}" unless no_description_to_output?
112
+ text << "\n" unless rows.empty? || no_description_to_output?
113
+ text << "\n#{parameters_output_string}" if parameter_row
114
+ text << "\n#{rows_output_string}" unless argument_rows.empty?
113
115
 
114
116
  text
115
117
  end
116
118
 
119
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
120
+
117
121
 
118
122
  private
119
123
 
120
124
 
121
125
  def parse_source(source_text)
122
- base_file_string = "# language: #{Parsing.dialect}\n#{dialect_feature_keyword}: Fake feature to parse\n#{dialect_outline_keyword}:\n#{dialect_step_keyword} fake step\n"
126
+ base_file_string = "# language: #{Parsing.dialect}
127
+ #{dialect_feature_keyword}: Fake feature to parse
128
+ #{dialect_outline_keyword}:
129
+ #{dialect_step_keyword} fake step\n"
123
130
  source_text = base_file_string + source_text
124
131
 
125
- parsed_file = Parsing::parse_text(source_text, 'cuke_modeler_stand_alone_example.feature')
132
+ parsed_file = Parsing.parse_text(source_text, 'cuke_modeler_stand_alone_example.feature')
126
133
 
127
134
  parsed_file['feature']['elements'].first['examples'].first
128
135
  end
@@ -135,7 +142,7 @@ module CukeModeler
135
142
  text = ''
136
143
 
137
144
  unless parameter_row.nil?
138
- text << " |"
145
+ text << ' |'
139
146
  parameter_row.cells.count.times { |index| text << " #{string_for(parameter_row.cells, index)} |" }
140
147
  end
141
148
 
@@ -148,7 +155,7 @@ module CukeModeler
148
155
  unless argument_rows.empty?
149
156
 
150
157
  argument_rows.each do |row|
151
- text << " |"
158
+ text << ' |'
152
159
  row.cells.count.times { |index| text << " #{string_for(row.cells, index)} |" }
153
160
  text << "\n"
154
161
  end
@@ -164,8 +171,18 @@ module CukeModeler
164
171
  end
165
172
 
166
173
  def ordered_row_values(row_hash)
167
- parameter_row.cells.collect { |cell| cell.value }.collect { |parameter| row_hash[parameter] }
174
+ parameter_row.cells.map(&:value).collect { |parameter| row_hash[parameter] }
175
+ end
176
+
177
+ def stringify_keys(hash)
178
+ hash.each_with_object({}) { |(key, value), new_hash| new_hash[key.to_s] = value }
179
+ end
180
+
181
+ def index_for_values(values)
182
+ argument_rows.index { |row| row.cells.map(&:value) == values }
168
183
  end
169
184
 
170
185
  end
171
186
  end
187
+
188
+ # rubocop:enable Metrics/ClassLength
@@ -1,7 +1,6 @@
1
1
  module CukeModeler
2
2
 
3
3
  # A class modeling a feature in a Cucumber suite.
4
-
5
4
  class Feature < Model
6
5
 
7
6
  include Parsed
@@ -33,17 +32,19 @@ module CukeModeler
33
32
 
34
33
  super(source_text)
35
34
 
36
- if source_text
37
- parsed_feature_data = parse_source(source_text)
38
- populate_feature(self, parsed_feature_data)
39
- end
35
+ return unless source_text
36
+
37
+ parsed_feature_data = parse_source(source_text)
38
+ populate_feature(self, parsed_feature_data)
40
39
  end
41
40
 
42
41
  # Returns *true* if the feature contains a background, *false* otherwise.
43
- def has_background?
42
+ def background?
44
43
  !@background.nil?
45
44
  end
46
45
 
46
+ alias has_background? background?
47
+
47
48
  # Returns the scenario models contained in the feature.
48
49
  def scenarios
49
50
  @tests.select { |test| test.is_a? Scenario }
@@ -60,11 +61,11 @@ module CukeModeler
60
61
  # single set of test values, such as an individual scenario or one example row
61
62
  # of an outline.
62
63
  def test_case_count
63
- scenarios.count + outlines.reduce(0) { |outline_sum, outline|
64
- outline_sum += outline.examples.reduce(0) { |example_sum, example|
65
- example_sum += example.argument_rows.count
66
- }
67
- }
64
+ scenarios.count + outlines.reduce(0) do |outline_sum, outline|
65
+ outline_sum + outline.examples.reduce(0) do |example_sum, example|
66
+ example_sum + example.argument_rows.count
67
+ end
68
+ end
68
69
  end
69
70
 
70
71
  # Returns the model objects that belong to this model.
@@ -75,27 +76,32 @@ module CukeModeler
75
76
  models
76
77
  end
77
78
 
79
+ # Building strings just isn't pretty
80
+ # rubocop:disable Metrics/AbcSize
81
+
78
82
  # Returns a string representation of this model. For a feature model,
79
83
  # this will be Gherkin text that is equivalent to the feature being modeled.
80
84
  def to_s
81
85
  text = ''
82
86
 
83
- text << tag_output_string + "\n" unless tags.empty?
87
+ text << "#{tag_output_string}\n" unless tags.empty?
84
88
  text << "#{@keyword}:#{name_output_string}"
85
- text << "\n" + description_output_string unless (description.nil? || description.empty?)
86
- text << "\n\n" + background_output_string if background
87
- text << "\n\n" + tests_output_string unless tests.empty?
88
- text << "\n\n" + rules_output_string unless rules.empty?
89
+ text << "\n#{description_output_string}" unless no_description_to_output?
90
+ text << "\n\n#{background_output_string}" if background
91
+ text << "\n\n#{tests_output_string}" unless tests.empty?
92
+ text << "\n\n#{rules_output_string}" unless rules.empty?
89
93
 
90
94
  text
91
95
  end
92
96
 
97
+ # rubocop:enable Metrics/AbcSize
98
+
93
99
 
94
100
  private
95
101
 
96
102
 
97
103
  def parse_source(source_text)
98
- parsed_file = Parsing::parse_text(source_text, 'cuke_modeler_stand_alone_feature.feature')
104
+ parsed_file = Parsing.parse_text(source_text, 'cuke_modeler_stand_alone_feature.feature')
99
105
 
100
106
  parsed_file['feature']
101
107
  end
@@ -1,7 +1,6 @@
1
1
  module CukeModeler
2
2
 
3
3
  # A class modeling a feature file in a Cucumber suite.
4
-
5
4
  class FeatureFile < Model
6
5
 
7
6
  include Parsed
@@ -25,13 +24,11 @@ module CukeModeler
25
24
 
26
25
  super(file_path)
27
26
 
28
- if file_path
29
- raise(ArgumentError, "Unknown file: #{file_path.inspect}") unless File.exists?(file_path)
30
-
31
- processed_feature_file_data = process_feature_file(file_path)
32
- populate_featurefile(self, processed_feature_file_data)
33
- end
27
+ return unless file_path
28
+ raise(ArgumentError, "Unknown file: #{file_path.inspect}") unless File.exist?(file_path)
34
29
 
30
+ processed_feature_file_data = process_feature_file(file_path)
31
+ populate_featurefile(self, processed_feature_file_data)
35
32
  end
36
33
 
37
34
  # Returns the name of the modeled feature file.
@@ -57,7 +54,7 @@ module CukeModeler
57
54
  def process_feature_file(file_path)
58
55
  source_text = IO.read(file_path)
59
56
 
60
- feature_file_data = Parsing::parse_text(source_text, file_path)
57
+ feature_file_data = Parsing.parse_text(source_text, file_path)
61
58
  feature_file_data = feature_file_data.merge({ 'path' => file_path })
62
59
 
63
60
  feature_file_data
@@ -1,7 +1,6 @@
1
1
  module CukeModeler
2
2
 
3
3
  # A class modeling an element of a Cucumber suite.
4
-
5
4
  class Model
6
5
 
7
6
  include Nested
@@ -11,7 +10,8 @@ module CukeModeler
11
10
  # Creates a new Model object and, if *source_text* is provided,
12
11
  # populates the object.
13
12
  def initialize(source_text = nil)
14
- raise(ArgumentError, "Can only create models from Strings but was given a #{source_text.class}.") if source_text && !source_text.is_a?(String)
13
+ error_message = "Can only create models from Strings but was given a #{source_text.class}."
14
+ raise(ArgumentError, error_message) if source_text && !source_text.is_a?(String)
15
15
 
16
16
  # This should be overridden by a child class
17
17
  end
@@ -24,7 +24,6 @@ module CukeModeler
24
24
 
25
25
  # Returns the model objects that belong to this model.
26
26
  def children
27
- # This should be overridden by a child class
28
27
  []
29
28
  end
30
29
 
@@ -1,7 +1,6 @@
1
1
  module CukeModeler
2
2
 
3
3
  # A class modeling an individual outline in a Cucumber suite.
4
-
5
4
  class Outline < Model
6
5
 
7
6
  include Parsing
@@ -29,17 +28,17 @@ module CukeModeler
29
28
 
30
29
  super(source_text)
31
30
 
32
- if source_text
33
- parsed_outline_data = parse_source(source_text)
34
- populate_outline(self, parsed_outline_data)
35
- end
31
+ return unless source_text
32
+
33
+ parsed_outline_data = parse_source(source_text)
34
+ populate_outline(self, parsed_outline_data)
36
35
  end
37
36
 
38
37
  # Returns *true* if the two models have equivalent steps and *false* otherwise.
39
- def ==(other_model)
40
- return false unless other_model.respond_to?(:steps)
38
+ def ==(other)
39
+ return false unless other.respond_to?(:steps)
41
40
 
42
- steps == other_model.steps
41
+ steps == other.steps
43
42
  end
44
43
 
45
44
  # Returns the model objects that belong to this model.
@@ -47,21 +46,26 @@ module CukeModeler
47
46
  examples + steps + tags
48
47
  end
49
48
 
49
+ # Building strings just isn't pretty
50
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
51
+
50
52
  # Returns a string representation of this model. For an outline model,
51
53
  # this will be Gherkin text that is equivalent to the outline being modeled.
52
54
  def to_s
53
55
  text = ''
54
56
 
55
- text << tag_output_string + "\n" unless tags.empty?
57
+ text << "#{tag_output_string}\n" unless tags.empty?
56
58
  text << "#{@keyword}:#{name_output_string}"
57
- text << "\n" + description_output_string unless (description.nil? || description.empty?)
58
- text << "\n" unless (steps.empty? || description.nil? || description.empty?)
59
- text << "\n" + steps_output_string unless steps.empty?
60
- text << "\n\n" + examples_output_string unless examples.empty?
59
+ text << "\n#{description_output_string}" unless no_description_to_output?
60
+ text << "\n" unless steps.empty? || no_description_to_output?
61
+ text << "\n#{steps_output_string}" unless steps.empty?
62
+ text << "\n\n#{examples_output_string}" unless examples.empty?
61
63
 
62
64
  text
63
65
  end
64
66
 
67
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
68
+
65
69
 
66
70
  private
67
71
 
@@ -70,13 +74,13 @@ module CukeModeler
70
74
  base_file_string = "# language: #{Parsing.dialect}\n#{dialect_feature_keyword}: Fake feature to parse\n"
71
75
  source_text = base_file_string + source_text
72
76
 
73
- parsed_file = Parsing::parse_text(source_text, 'cuke_modeler_stand_alone_outline.feature')
77
+ parsed_file = Parsing.parse_text(source_text, 'cuke_modeler_stand_alone_outline.feature')
74
78
 
75
79
  parsed_file['feature']['elements'].first
76
80
  end
77
81
 
78
82
  def examples_output_string
79
- examples.empty? ? '' : examples.collect { |example| example.to_s }.join("\n\n")
83
+ examples.empty? ? '' : examples.join("\n\n")
80
84
  end
81
85
 
82
86
  end
@@ -1,7 +1,6 @@
1
1
  module CukeModeler
2
2
 
3
3
  # A class modeling a single row of a step table or example table.
4
-
5
4
  class Row < Model
6
5
 
7
6
  include Sourceable
@@ -19,16 +18,21 @@ module CukeModeler
19
18
 
20
19
  super(source_text)
21
20
 
22
- if source_text
23
- parsed_row_data = parse_source(source_text)
24
- populate_row(self, parsed_row_data)
25
- end
21
+ return unless source_text
22
+
23
+ parsed_row_data = parse_source(source_text)
24
+ populate_row(self, parsed_row_data)
25
+ end
26
+
27
+ # Returns the model objects that belong to this model.
28
+ def children
29
+ @cells
26
30
  end
27
31
 
28
32
  # Returns a string representation of this model. For a row model,
29
33
  # this will be Gherkin text that is equivalent to the row being modeled.
30
34
  def to_s
31
- text_cells = cells.collect { |cell| cell.to_s }
35
+ text_cells = cells.map(&:to_s)
32
36
 
33
37
  "| #{text_cells.join(' | ')} |"
34
38
  end
@@ -38,10 +42,13 @@ module CukeModeler
38
42
 
39
43
 
40
44
  def parse_source(source_text)
41
- 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"
45
+ base_file_string = "# language: #{Parsing.dialect}
46
+ #{dialect_feature_keyword}: Fake feature to parse
47
+ #{dialect_scenario_keyword}:
48
+ #{dialect_step_keyword} fake step\n"
42
49
  source_text = base_file_string + source_text
43
50
 
44
- parsed_file = Parsing::parse_text(source_text, 'cuke_modeler_stand_alone_row.feature')
51
+ parsed_file = Parsing.parse_text(source_text, 'cuke_modeler_stand_alone_row.feature')
45
52
 
46
53
  parsed_file['feature']['elements'].first['steps'].first['table']['rows'].first
47
54
  end