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.
- checksums.yaml +8 -8
- data/.rubocop.yml +3 -0
- data/.travis.yml +6 -0
- data/CHANGELOG.md +4 -0
- data/README.md +5 -3
- data/features/{1_Basics → 1_the_basics}/README.md +0 -0
- data/features/{1_Basics → 1_the_basics}/demo01.feature +5 -5
- data/features/{2_Macros_with_arguments → 2_macros_with_arguments}/README.md +0 -0
- data/features/{2_Macros_with_arguments → 2_macros_with_arguments}/demo02.feature +10 -10
- data/features/{2_Macros_with_arguments → 2_macros_with_arguments}/demo03.feature +5 -5
- data/features/{3_Macros_with_table_arguments → 3_macros_with_table_arguments}/README.md +0 -0
- data/features/{3_Macros_with_table_arguments → 3_macros_with_table_arguments}/demo04.feature +5 -5
- data/features/{3_Macros_with_table_arguments → 3_macros_with_table_arguments}/demo05.feature +4 -4
- data/features/{4_Conditional_steps → 4_conditional_steps}/demo06.feature +18 -16
- data/features/{5_Goodies → 5_goodies}/demo07.feature +6 -5
- data/features/{5_Goodies → 5_goodies}/demo08.feature +0 -0
- data/features/step_definitions/demo_steps.rb +5 -4
- data/lib/macro_steps.rb +6 -6
- data/lib/macros4cuke/coll-walker-factory.rb +12 -12
- data/lib/macros4cuke/constants.rb +4 -4
- data/lib/macros4cuke/formatter/all-notifications.rb +8 -8
- data/lib/macros4cuke/formatter/to-gherkin.rb +2 -2
- data/lib/macros4cuke/formatter/to-null.rb +10 -10
- data/lib/macros4cuke/formatting-service.rb +74 -74
- data/lib/macros4cuke/macro-collection.rb +13 -13
- data/lib/macros4cuke/macro-step-support.rb +19 -12
- data/lib/macros4cuke/macro-step.rb +48 -48
- data/lib/macros4cuke/templating/engine.rb +79 -79
- data/lib/macros4cuke/templating/placeholder.rb +52 -52
- data/lib/macros4cuke/templating/section.rb +116 -116
- data/lib/macros4cuke/templating/template-element.rb +88 -88
- data/lib/macros4cuke/templating/unary-element.rb +37 -37
- data/lib/macros4cuke.rb +1 -1
- data/spec/macros4cuke/coll-walker-factory_spec.rb +269 -269
- data/spec/macros4cuke/formatter/to-gherkin_spec.rb +5 -5
- data/spec/macros4cuke/formatter/to-null_spec.rb +62 -62
- data/spec/macros4cuke/formatter/to-trace_spec.rb +8 -8
- data/spec/macros4cuke/formatting-service_spec.rb +7 -7
- data/spec/macros4cuke/macro-collection_spec.rb +3 -3
- data/spec/macros4cuke/macro-step-support_spec.rb +4 -4
- data/spec/macros4cuke/macro-step_spec.rb +8 -8
- data/spec/macros4cuke/templating/comment_spec.rb +45 -0
- data/spec/macros4cuke/templating/engine_spec.rb +23 -23
- data/spec/macros4cuke/templating/eo-line_spec.rb +39 -0
- data/spec/macros4cuke/templating/section_spec.rb +1 -1
- data/spec/macros4cuke/templating/static_text_spec.rb +45 -0
- data/spec/macros4cuke/use-sample-collection.rb +7 -7
- data/spec/spec_helper.rb +3 -3
- 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
|
-
|
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
|
-
|
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
|