macros4cuke 0.4.08 → 0.4.09

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 (49) hide show
  1. checksums.yaml +8 -8
  2. data/.rubocop.yml +3 -0
  3. data/.travis.yml +6 -0
  4. data/CHANGELOG.md +4 -0
  5. data/README.md +5 -3
  6. data/features/{1_Basics → 1_the_basics}/README.md +0 -0
  7. data/features/{1_Basics → 1_the_basics}/demo01.feature +5 -5
  8. data/features/{2_Macros_with_arguments → 2_macros_with_arguments}/README.md +0 -0
  9. data/features/{2_Macros_with_arguments → 2_macros_with_arguments}/demo02.feature +10 -10
  10. data/features/{2_Macros_with_arguments → 2_macros_with_arguments}/demo03.feature +5 -5
  11. data/features/{3_Macros_with_table_arguments → 3_macros_with_table_arguments}/README.md +0 -0
  12. data/features/{3_Macros_with_table_arguments → 3_macros_with_table_arguments}/demo04.feature +5 -5
  13. data/features/{3_Macros_with_table_arguments → 3_macros_with_table_arguments}/demo05.feature +4 -4
  14. data/features/{4_Conditional_steps → 4_conditional_steps}/demo06.feature +18 -16
  15. data/features/{5_Goodies → 5_goodies}/demo07.feature +6 -5
  16. data/features/{5_Goodies → 5_goodies}/demo08.feature +0 -0
  17. data/features/step_definitions/demo_steps.rb +5 -4
  18. data/lib/macro_steps.rb +6 -6
  19. data/lib/macros4cuke/coll-walker-factory.rb +12 -12
  20. data/lib/macros4cuke/constants.rb +4 -4
  21. data/lib/macros4cuke/formatter/all-notifications.rb +8 -8
  22. data/lib/macros4cuke/formatter/to-gherkin.rb +2 -2
  23. data/lib/macros4cuke/formatter/to-null.rb +10 -10
  24. data/lib/macros4cuke/formatting-service.rb +74 -74
  25. data/lib/macros4cuke/macro-collection.rb +13 -13
  26. data/lib/macros4cuke/macro-step-support.rb +19 -12
  27. data/lib/macros4cuke/macro-step.rb +48 -48
  28. data/lib/macros4cuke/templating/engine.rb +79 -79
  29. data/lib/macros4cuke/templating/placeholder.rb +52 -52
  30. data/lib/macros4cuke/templating/section.rb +116 -116
  31. data/lib/macros4cuke/templating/template-element.rb +88 -88
  32. data/lib/macros4cuke/templating/unary-element.rb +37 -37
  33. data/lib/macros4cuke.rb +1 -1
  34. data/spec/macros4cuke/coll-walker-factory_spec.rb +269 -269
  35. data/spec/macros4cuke/formatter/to-gherkin_spec.rb +5 -5
  36. data/spec/macros4cuke/formatter/to-null_spec.rb +62 -62
  37. data/spec/macros4cuke/formatter/to-trace_spec.rb +8 -8
  38. data/spec/macros4cuke/formatting-service_spec.rb +7 -7
  39. data/spec/macros4cuke/macro-collection_spec.rb +3 -3
  40. data/spec/macros4cuke/macro-step-support_spec.rb +4 -4
  41. data/spec/macros4cuke/macro-step_spec.rb +8 -8
  42. data/spec/macros4cuke/templating/comment_spec.rb +45 -0
  43. data/spec/macros4cuke/templating/engine_spec.rb +23 -23
  44. data/spec/macros4cuke/templating/eo-line_spec.rb +39 -0
  45. data/spec/macros4cuke/templating/section_spec.rb +1 -1
  46. data/spec/macros4cuke/templating/static_text_spec.rb +45 -0
  47. data/spec/macros4cuke/use-sample-collection.rb +7 -7
  48. data/spec/spec_helper.rb +3 -3
  49. metadata +31 -14
@@ -13,19 +13,19 @@ module Macros4Cuke # Module used as a namespace
13
13
 
14
14
 
15
15
  # Module containing all classes implementing the simple template engine
16
- # used internally in Macros4Cuke.
16
+ # used internally in Macros4Cuke.
17
17
  module Templating
18
18
 
19
- # Class used internally by the template engine.
20
- # Represents a static piece of text from a template.
21
- # A static text is a text that is reproduced verbatim
19
+ # Class used internally by the template engine.
20
+ # Represents a static piece of text from a template.
21
+ # A static text is a text that is reproduced verbatim
22
22
  # when rendering a template.
23
23
  class StaticText
24
24
  # The static text extracted from the original template.
25
25
  attr_reader(:source)
26
-
27
26
 
28
- # @param aSourceText [String] A piece of text extracted
27
+
28
+ # @param aSourceText [String] A piece of text extracted
29
29
  # from the template that must be rendered verbatim.
30
30
  def initialize(aSourceText)
31
31
  @source = aSourceText
@@ -42,16 +42,16 @@ class StaticText
42
42
  end # class
43
43
 
44
44
 
45
- # Class used internally by the template engine.
46
- # Represents a comment from a template.
47
- # A static text is a text that is reproduced verbatim
45
+ # Class used internally by the template engine.
46
+ # Represents a comment from a template.
47
+ # A static text is a text that is reproduced verbatim
48
48
  # when rendering a template.
49
49
  class Comment
50
50
  # The comment as extracted from the original template.
51
51
  attr_reader(:source)
52
-
53
52
 
54
- # @param aSourceText [String] A piece of text extracted
53
+
54
+ # @param aSourceText [String] A piece of text extracted
55
55
  # from the template that must be rendered verbatim.
56
56
  def initialize(aSourceText)
57
57
  @source = aSourceText
@@ -70,12 +70,12 @@ class Comment
70
70
  end # class
71
71
 
72
72
 
73
- # Class used internally by the template engine.
73
+ # Class used internally by the template engine.
74
74
  # Represents an end of line that must be rendered as such.
75
75
  class EOLine
76
76
 
77
77
  public
78
-
78
+
79
79
  # Render an end of line.
80
80
  # This method has the same signature as the {Engine#render} method.
81
81
  # @return [String] An end of line marker. Its exact value is OS-dependent.
@@ -86,24 +86,24 @@ end # class
86
86
 
87
87
 
88
88
 
89
- # A very simple implementation of a templating engine.
90
- # Earlier versions of Macros4Cuke relied on the logic-less
91
- # Mustache template engine.
92
- # But it was decided afterwards to replace it by a very simple
93
- # template engine.
94
- # The reasons were the following:
95
- # - Be closer to the usual Gherkin syntax (parameters of scenario outlines
96
- # use chevrons <...>,
97
- # while Mustache use !{{...}} delimiters),
98
- # - Feature files are meant to be simple, so should the template engine be.
89
+ # A very simple implementation of a templating engine.
90
+ # Earlier versions of Macros4Cuke relied on the logic-less
91
+ # Mustache template engine.
92
+ # But it was decided afterwards to replace it by a very simple
93
+ # template engine.
94
+ # The reasons were the following:
95
+ # - Be closer to the usual Gherkin syntax (parameters of scenario outlines
96
+ # use chevrons <...>,
97
+ # while Mustache use !{{...}} delimiters),
98
+ # - Feature files are meant to be simple, so should the template engine be.
99
99
  class Engine
100
- # The regular expression that matches a space,
100
+ # The regular expression that matches a space,
101
101
  # any punctuation sign or delimiter that is forbidden
102
102
  # between chevrons <...> template tags.
103
- DisallowedSigns = begin
103
+ DisallowedSigns = begin
104
104
  # Use concatenation (+) to work around Ruby bug!
105
- forbidden = ' !"#' + "$%&'()*+,-./:;<=>?[\\]^`{|}~"
106
- all_escaped = []
105
+ forbidden = ' !"#' + "$%&'()*+,-./:;<=>?[\\]^`{|}~"
106
+ all_escaped = []
107
107
  forbidden.each_char { |ch| all_escaped << Regexp.escape(ch) }
108
108
  pattern = all_escaped.join('|')
109
109
  Regexp.new(pattern)
@@ -111,13 +111,13 @@ class Engine
111
111
 
112
112
  # The original text of the template is kept here.
113
113
  attr_reader(:source)
114
-
114
+
115
115
  # The internal representation of the template text
116
116
  attr_reader(:representation)
117
-
117
+
118
118
  # Builds an Engine and compiles the given template text into
119
119
  # an internal representation.
120
- # @param aSourceTemplate [String] The template source text.
120
+ # @param aSourceTemplate [String] The template source text.
121
121
  # It may contain zero or tags enclosed between chevrons <...>.
122
122
  def initialize(aSourceTemplate)
123
123
  @source = aSourceTemplate
@@ -126,14 +126,14 @@ class Engine
126
126
 
127
127
  public
128
128
 
129
- # Render the template within the given scope object and
129
+ # Render the template within the given scope object and
130
130
  # with the locals specified.
131
131
  # The method mimicks the signature of the Tilt::Template#render method.
132
132
  # @param aContextObject [anything] context object to get actual values
133
133
  # (when not present in the locals Hash).
134
134
  # @param theLocals [Hash] Contains one or more pairs of the form:
135
135
  # tag/placeholder name => actual value.
136
- # @return [String] The rendition of the template given
136
+ # @return [String] The rendition of the template given
137
137
  # the passed argument values.
138
138
  def render(aContextObject = Object.new, theLocals)
139
139
  return '' if @representation.empty?
@@ -143,7 +143,7 @@ class Engine
143
143
  # Output compaction rules:
144
144
  # -In case of consecutive eol's only one is rendered.
145
145
  # -In case of comment followed by one eol, both aren't rendered
146
- unless element.is_a?(EOLine) &&
146
+ unless element.is_a?(EOLine) &&
147
147
  (prev.is_a?(EOLine) || prev.is_a?(Comment))
148
148
  subResult << element.render(aContextObject, theLocals)
149
149
  end
@@ -153,7 +153,7 @@ class Engine
153
153
  return result
154
154
  end
155
155
 
156
-
156
+
157
157
  # Retrieve all placeholder names that appear in the template.
158
158
  # @return [Array] The list of placeholder names.
159
159
  def variables()
@@ -161,20 +161,20 @@ class Engine
161
161
  @variables ||= begin
162
162
  vars = @representation.each_with_object([]) do |element, subResult|
163
163
  case element
164
- when Placeholder
164
+ when Placeholder
165
165
  subResult << element.name
166
-
166
+
167
167
  when Section
168
168
  subResult.concat(element.variables)
169
-
169
+
170
170
  else
171
171
  # Do nothing
172
172
  end
173
173
  end
174
-
174
+
175
175
  vars.flatten.uniq
176
176
  end
177
-
177
+
178
178
  return @variables
179
179
  end
180
180
 
@@ -192,12 +192,12 @@ class Engine
192
192
  # Scan tag at current position...
193
193
  tag_literal = scanner.scan(/<(?:[^\\<>]|\\.)*>/)
194
194
  unless tag_literal.nil?
195
- result << [:dynamic, tag_literal.gsub(/^<|>$/, '')]
195
+ result << [:dynamic, tag_literal.gsub(/^<|>$/, '')]
196
196
  end
197
-
197
+
198
198
  # ... or scan plain text at current position
199
199
  literal = scanner.scan(/(?:[^\\<>]|\\.)+/)
200
- result << [:static, literal] unless literal.nil?
200
+ result << [:static, literal] unless literal.nil?
201
201
  identify_parse_error(aTextLine) if tag_literal.nil? && literal.nil?
202
202
  end
203
203
  end
@@ -209,7 +209,7 @@ class Engine
209
209
 
210
210
  # Called when the given text line could not be parsed.
211
211
  # Raises an exception with the syntax issue identified.
212
- # @param aTextLine [String] A text line from the template.
212
+ # @param aTextLine [String] A text line from the template.
213
213
  def self.identify_parse_error(aTextLine)
214
214
  # Unsuccessful scanning: we typically have improperly balanced chevrons.
215
215
  # We will analyze the opening and closing chevrons...
@@ -221,25 +221,25 @@ class Engine
221
221
 
222
222
  no_escaped.each_char do |ch|
223
223
  case ch
224
- when '<' then unbalance += 1
225
- when '>' then unbalance -= 1
224
+ when '<' then unbalance += 1
225
+ when '>' then unbalance -= 1
226
226
  end
227
-
227
+
228
228
  fail(StandardError, "Nested opening chevron '<'.") if unbalance > 1
229
229
  fail(StandardError, "Missing opening chevron '<'.") if unbalance < 0
230
230
  end
231
-
232
- fail(StandardError, "Missing closing chevron '>'.") if unbalance == 1
231
+
232
+ fail(StandardError, "Missing closing chevron '>'.") if unbalance == 1
233
233
  end
234
-
235
-
234
+
235
+
236
236
  # Create the internal representation of the given template.
237
237
  def compile(aSourceTemplate)
238
238
  # Split the input text into lines.
239
239
  input_lines = aSourceTemplate.split(/\r\n?|\n/)
240
-
240
+
241
241
  # Parse the input text into raw data.
242
- raw_lines = input_lines.map do |line|
242
+ raw_lines = input_lines.map do |line|
243
243
  line_items = self.class.parse(line)
244
244
  line_items.each do |(kind, text)|
245
245
  # A tag text cannot be empty nor blank
@@ -247,27 +247,27 @@ class Engine
247
247
  fail(EmptyArgumentError.new(line.strip))
248
248
  end
249
249
  end
250
-
250
+
251
251
  line_items
252
252
  end
253
-
253
+
254
254
  compiled_lines = raw_lines.map { |line| compile_line(line) }
255
255
  return compile_sections(compiled_lines.flatten)
256
256
  end
257
-
258
- # Convert the array of raw entries (per line)
257
+
258
+ # Convert the array of raw entries (per line)
259
259
  # into full-fledged template elements.
260
260
  def compile_line(aRawLine)
261
261
  line_rep = aRawLine.map { |couple| compile_couple(couple) }
262
-
263
- # Apply the rule: when a line just consist of spaces
262
+
263
+ # Apply the rule: when a line just consist of spaces
264
264
  # and a section element, then remove all the spaces from that line.
265
265
  section_item = nil
266
266
  line_to_squeeze = line_rep.all? do |item|
267
267
  case item
268
268
  when StaticText
269
269
  item.source =~ /\s+/
270
-
270
+
271
271
  when Section, SectionEndMarker
272
272
  if section_item.nil?
273
273
  section_item = item
@@ -284,14 +284,14 @@ class Engine
284
284
  else
285
285
  line_rep_ending(line_rep)
286
286
  end
287
-
287
+
288
288
  return line_rep
289
289
  end
290
290
 
291
291
 
292
- # Apply rule: if last item in line is an end of section marker,
293
- # then place eoline before that item.
294
- # Otherwise, end the line with a eoline marker.
292
+ # Apply rule: if last item in line is an end of section marker,
293
+ # then place eoline before that item.
294
+ # Otherwise, end the line with a eoline marker.
295
295
  def line_rep_ending(theLineRep)
296
296
  if theLineRep.last.is_a?(SectionEndMarker)
297
297
  section_end = theLineRep.pop
@@ -299,10 +299,10 @@ class Engine
299
299
  theLineRep << section_end
300
300
  else
301
301
  theLineRep << EOLine.new
302
- end
302
+ end
303
303
  end
304
304
 
305
-
305
+
306
306
  # @param aCouple [Array] a two-element array of the form: [kind, text]
307
307
  # Where kind must be one of :static, :dynamic
308
308
  def compile_couple(aCouple)
@@ -329,11 +329,11 @@ class Engine
329
329
  matching = DisallowedSigns.match(aText)
330
330
  end
331
331
  fail(InvalidCharError.new(aText, matching[0])) if matching
332
-
332
+
333
333
  result = case aText[0, 1]
334
334
  when '?'
335
335
  ConditionalSection.new(aText[1..-1], true)
336
-
336
+
337
337
  when '/'
338
338
  SectionEndMarker.new(aText[1..-1])
339
339
  else
@@ -342,43 +342,43 @@ class Engine
342
342
 
343
343
  return result
344
344
  end
345
-
345
+
346
346
  # Transform a flat sequence of elements into a hierarchy of sections.
347
347
  # @param flat_sequence [Array] a linear list of elements (including sections)
348
348
  def compile_sections(flat_sequence)
349
349
  open_sections = [] # The list of nested open sections
350
-
350
+
351
351
  compiled = flat_sequence.each_with_object([]) do |element, subResult|
352
352
  case element
353
353
  when Section
354
354
  open_sections << element
355
-
356
- when SectionEndMarker
355
+
356
+ when SectionEndMarker
357
357
  validate_section_end(element, open_sections)
358
358
  subResult << open_sections.pop
359
-
359
+
360
360
  else
361
361
  if open_sections.empty?
362
362
  subResult << element
363
363
  else
364
364
  open_sections.last.add_child(element)
365
365
  end
366
- end
366
+ end
367
367
  end
368
-
368
+
369
369
  unless open_sections.empty?
370
370
  error_message = "Unterminated section #{open_sections.last}."
371
371
  fail(StandardError, error_message)
372
372
  end
373
-
373
+
374
374
  return compiled
375
375
  end
376
-
377
- # Validate the given end of section marker taking into account
376
+
377
+ # Validate the given end of section marker taking into account
378
378
  # the open sections.
379
379
  def validate_section_end(marker, sections)
380
380
  msg_prefix = "End of section</#{marker.name}> "
381
-
381
+
382
382
  if sections.empty?
383
383
  msg = 'found while no corresponding section is open.'
384
384
  fail(StandardError, msg_prefix + msg)
@@ -388,7 +388,7 @@ class Engine
388
388
  fail(StandardError, msg_prefix + msg)
389
389
  end
390
390
  end
391
-
391
+
392
392
  end # class
393
393
 
394
394
  end # module
@@ -1,52 +1,52 @@
1
- # File: placeholder.rb
2
- # Purpose: Implementation of the Placeholder class.
3
-
4
- require_relative 'unary-element' # Load the superclass
5
-
6
-
7
- module Macros4Cuke # Module used as a namespace
8
-
9
- # Module containing all classes implementing the simple template engine
10
- # used internally in Macros4Cuke.
11
- module Templating
12
-
13
- # Class used internally by the template engine.
14
- # Represents a named placeholder in a template, that is,
15
- # a name placed between <..> in the template.
16
- # At rendition, a placeholder is replaced by the text value
17
- # that is associated with it.
18
- class Placeholder < UnaryElement
19
-
20
- public
21
-
22
- # Render the placeholder given the passed arguments.
23
- # This method has the same signature as the {Engine#render} method.
24
- # @return [String] The text value assigned to the placeholder.
25
- # Returns an empty string when no value is assigned to the placeholder.
26
- def render(aContextObject, theLocals)
27
- actual_value = retrieve_value_from(aContextObject, theLocals)
28
-
29
- result = case actual_value
30
- when NilClass
31
- ''
32
-
33
- when Array
34
- # TODO: Move away from hard-coded separator.
35
- actual_value.join('<br/>')
36
-
37
- when String
38
- actual_value
39
- else
40
- actual_value.to_s
41
- end
42
-
43
- return result
44
- end
45
-
46
- end # class
47
-
48
- end # module
49
-
50
- end # module
51
-
52
- # End of file
1
+ # File: placeholder.rb
2
+ # Purpose: Implementation of the Placeholder class.
3
+
4
+ require_relative 'unary-element' # Load the superclass
5
+
6
+
7
+ module Macros4Cuke # Module used as a namespace
8
+
9
+ # Module containing all classes implementing the simple template engine
10
+ # used internally in Macros4Cuke.
11
+ module Templating
12
+
13
+ # Class used internally by the template engine.
14
+ # Represents a named placeholder in a template, that is,
15
+ # a name placed between <..> in the template.
16
+ # At rendition, a placeholder is replaced by the text value
17
+ # that is associated with it.
18
+ class Placeholder < UnaryElement
19
+
20
+ public
21
+
22
+ # Render the placeholder given the passed arguments.
23
+ # This method has the same signature as the {Engine#render} method.
24
+ # @return [String] The text value assigned to the placeholder.
25
+ # Returns an empty string when no value is assigned to the placeholder.
26
+ def render(aContextObject, theLocals)
27
+ actual_value = retrieve_value_from(aContextObject, theLocals)
28
+
29
+ result = case actual_value
30
+ when NilClass
31
+ ''
32
+
33
+ when Array
34
+ # TODO: Move away from hard-coded separator.
35
+ actual_value.join('<br/>')
36
+
37
+ when String
38
+ actual_value
39
+ else
40
+ actual_value.to_s
41
+ end
42
+
43
+ return result
44
+ end
45
+
46
+ end # class
47
+
48
+ end # module
49
+
50
+ end # module
51
+
52
+ # End of file