macros4cuke 0.4.08 → 0.4.09

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