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
@@ -1,74 +1,74 @@
1
- # File: formatting-service.rb
2
- # Purpose: Implementation of the WalkEventPublisher class.
3
-
4
- require_relative 'exceptions'
5
- require_relative 'coll-walker-factory'
6
- require_relative './formatter/all-notifications'
7
-
8
-
9
- module Macros4Cuke # Module used as a namespace
10
-
11
- # A worker class that drives the rendering of macro-steps in
12
- # any registered format.
13
- class FormattingService
14
- # The list of registered formatters
15
- attr_reader(:formatters)
16
-
17
- # Link to a factory of walker objects that visit macro collections
18
- attr_reader(:walker_factory)
19
-
20
-
21
- # Constructor.
22
- def initialize()
23
- @formatters = []
24
- @walker_factory = CollWalkerFactory.new
25
- end
26
-
27
-
28
- # Register a formatter.
29
- # Raises an exception when the formatter implements
30
- # an unknown formatting event.
31
- def register(aFormatter)
32
- # Get the list of formatted events supported by the formatter
33
- supported_events = aFormatter.implements
34
-
35
- # Complain if list is empty or nil
36
- if supported_events.nil? || supported_events.empty?
37
- fail(NoFormattingEventForFormatter.new(aFormatter))
38
- end
39
-
40
- # Check that each event from the event list the formatter is known.
41
- supported_events.each do |event|
42
- unless Formatter::AllNotifications.include? event
43
- fail(UnknownFormattingEvent.new(aFormatter, event))
44
- end
45
- end
46
-
47
- formatters << aFormatter
48
- end
49
-
50
- # Launch the rendering(s) of the given macro collection.
51
- def start!(aMacroCollection)
52
- # Create a walker (that will visit the collection)
53
- walker = walker_factory.build_walker(aMacroCollection)
54
-
55
- walker.each do |visit_event|
56
- (msg, nesting_level) = visit_event[0..1]
57
-
58
- # Notify each formatter of the visit event.
59
- formatters.each do |fmt|
60
- accepted_notifications = fmt.implements
61
- if accepted_notifications.include? msg
62
- # Assumption: all nil argument(s) are at the end
63
- arg_vector = visit_event[2..-1].reject { |an_arg| an_arg.nil? }
64
- fmt.send(msg, nesting_level, *arg_vector)
65
- end
66
- end
67
- end
68
- end
69
-
70
- end # class
71
-
72
- end # module
73
-
74
- # End of file
1
+ # File: formatting-service.rb
2
+ # Purpose: Implementation of the WalkEventPublisher class.
3
+
4
+ require_relative 'exceptions'
5
+ require_relative 'coll-walker-factory'
6
+ require_relative './formatter/all-notifications'
7
+
8
+
9
+ module Macros4Cuke # Module used as a namespace
10
+
11
+ # A worker class that drives the rendering of macro-steps in
12
+ # any registered format.
13
+ class FormattingService
14
+ # The list of registered formatters
15
+ attr_reader(:formatters)
16
+
17
+ # Link to a factory of walker objects that visit macro collections
18
+ attr_reader(:walker_factory)
19
+
20
+
21
+ # Constructor.
22
+ def initialize()
23
+ @formatters = []
24
+ @walker_factory = CollWalkerFactory.new
25
+ end
26
+
27
+
28
+ # Register a formatter.
29
+ # Raises an exception when the formatter implements
30
+ # an unknown formatting event.
31
+ def register(aFormatter)
32
+ # Get the list of formatted events supported by the formatter
33
+ supported_events = aFormatter.implements
34
+
35
+ # Complain if list is empty or nil
36
+ if supported_events.nil? || supported_events.empty?
37
+ fail(NoFormattingEventForFormatter.new(aFormatter))
38
+ end
39
+
40
+ # Check that each event from the event list the formatter is known.
41
+ supported_events.each do |event|
42
+ unless Formatter::AllNotifications.include? event
43
+ fail(UnknownFormattingEvent.new(aFormatter, event))
44
+ end
45
+ end
46
+
47
+ formatters << aFormatter
48
+ end
49
+
50
+ # Launch the rendering(s) of the given macro collection.
51
+ def start!(aMacroCollection)
52
+ # Create a walker (that will visit the collection)
53
+ walker = walker_factory.build_walker(aMacroCollection)
54
+
55
+ walker.each do |visit_event|
56
+ (msg, nesting_level) = visit_event[0..1]
57
+
58
+ # Notify each formatter of the visit event.
59
+ formatters.each do |fmt|
60
+ accepted_notifications = fmt.implements
61
+ if accepted_notifications.include? msg
62
+ # Assumption: all nil argument(s) are at the end
63
+ arg_vector = visit_event[2..-1].reject { |an_arg| an_arg.nil? }
64
+ fmt.send(msg, nesting_level, *arg_vector)
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ end # class
71
+
72
+ end # module
73
+
74
+ # End of file
@@ -6,7 +6,7 @@ require_relative 'macro-step'
6
6
 
7
7
  module Macros4Cuke # Module used as a namespace
8
8
 
9
- # Represents a container of macros.
9
+ # Represents a container of macros.
10
10
  # It gathers all the macros encountered by Cucumber while "executing"
11
11
  # the feature files.
12
12
  # @note This is a singleton class: there is only one macro collection object.
@@ -16,25 +16,25 @@ class MacroCollection
16
16
  # @!attribute [r] macro_steps.
17
17
  # A Hash with pairs of the form: macro key => MacroStep object
18
18
 
19
-
19
+
20
20
  public
21
21
 
22
22
  # Add a new macro.
23
23
  # Pre-condition: there is no existing macro with the same key.
24
- # @param aPhrase [String] The text that is enclosed between
24
+ # @param aPhrase [String] The text that is enclosed between
25
25
  # the square brackets.
26
26
  # @param aTemplate [String] A text that consists of a sequence of sub-steps.
27
- # @param useTable [boolean] A flag indicating whether a table should be
27
+ # @param useTable [boolean] A flag indicating whether a table should be
28
28
  # used to pass actual values.
29
29
  def add_macro(aPhrase, aTemplate, useTable)
30
30
  new_macro = MacroStep.new(aPhrase, aTemplate, useTable)
31
-
31
+
32
32
  # Prevent collision of macros (macros with same phrase).
33
33
  # This can occur if a macro was defined in a background section.
34
- # An exception is raised if the phrase syntax of both macros are the
34
+ # An exception is raised if the phrase syntax of both macros are the
35
35
  fail(DuplicateMacroError, aPhrase) if find_macro(aPhrase, useTable)
36
-
37
- macro_steps[new_macro.key] = new_macro
36
+
37
+ macro_steps[new_macro.key] = new_macro
38
38
  end
39
39
 
40
40
  # Render the steps associated to the macro with given phrase
@@ -42,12 +42,12 @@ class MacroCollection
42
42
  # Return the rendered steps as a text.
43
43
  # @param aPhrase [String] an instance of the macro phrase.
44
44
  # @param rawData [Array or nil] An Array with couples of the form:
45
- # [macro argument name, a value].
45
+ # [macro argument name, a value].
46
46
  # Multiple rows with same argument name are acceptable.
47
47
  # @return [String]
48
48
  def render_steps(aPhrase, rawData = nil)
49
49
  use_table = !rawData.nil?
50
- macro = find_macro(aPhrase, use_table)
50
+ macro = find_macro(aPhrase, use_table)
51
51
  fail(UnknownMacroError, aPhrase) if macro.nil?
52
52
 
53
53
  # Render the steps
@@ -56,19 +56,19 @@ class MacroCollection
56
56
 
57
57
 
58
58
  # Clear/remove all macro definitions from the collection.
59
- # Post-condition: we are back to the same situation as
59
+ # Post-condition: we are back to the same situation as
60
60
  # no macro was ever defined.
61
61
  def clear()
62
62
  macro_steps.clear
63
63
  end
64
64
 
65
-
65
+
66
66
  # Read accessor for the @macro_steps attribute.
67
67
  def macro_steps()
68
68
  @macro_steps ||= {}
69
69
  return @macro_steps
70
70
  end
71
-
71
+
72
72
  private
73
73
 
74
74
  # Retrieve the macro, given a phrase.
@@ -5,21 +5,23 @@ require_relative 'macro-collection'
5
5
 
6
6
  module Macros4Cuke # Module used as a namespace
7
7
 
8
- # Mix-in module that should be extending World objects in Cucumber.
9
- # Synopsis (in env.rb):
10
- #
11
- # require 'macros4cuke'
12
- # ...
8
+ # Mix-in module that should be extending World objects in Cucumber.
9
+ # Synopsis (in env.rb):
10
+ #
11
+ # require 'macros4cuke'
12
+ # ...
13
13
  # # Extend the world object with this module.
14
- # World(Macros4Cuke::MacroStepSupport)
15
- #
14
+ # World(Macros4Cuke::MacroStepSupport)
15
+ #
16
16
  module MacroStepSupport
17
+ # The substeps being executed in the scenario represented as text.
18
+ attr_reader(:substeps_trace)
17
19
 
18
20
  public
19
21
 
20
22
  # Add a new macro.
21
23
  # Pre-condition: there is no existing macro with the same key.
22
- # @param aPhrase [String] The text that is enclosed between
24
+ # @param aPhrase [String] The text that is enclosed between
23
25
  # the square brackets [...].
24
26
  # @param aTemplate [String] The text template that consists of a sequence
25
27
  # of sub-steps.
@@ -31,23 +33,28 @@ module MacroStepSupport
31
33
 
32
34
 
33
35
  # Invoke a macro with given phrase and (optionally) a table of values
34
- # @param aPhraseInstance [String] an instance of the macro phrase.
36
+ # @param aPhraseInstance [String] an instance of the macro phrase.
35
37
  # That is, the text between [...] and with zero or more actual values.
36
- # @param rawData [Array or nil] An Array with couples of the form:
38
+ # @param rawData [Array or nil] An Array with couples of the form:
37
39
  # [macro argument name, a value].
38
- # Multiple rows with same argument name are acceptable.
40
+ # Multiple rows with same argument name are acceptable.
39
41
  def invoke_macro(aPhraseInstance, rawData = nil)
40
42
  # Generate a text rendition of the step to be executed.
41
43
  collection = MacroCollection.instance
42
44
  rendered_steps = collection.render_steps(aPhraseInstance, rawData)
43
45
 
46
+ # Keep track of the sub-steps to execute
47
+ @substeps_trace = '' if @substeps_trace.nil?
48
+ @substeps_trace << rendered_steps
49
+
50
+
44
51
  # Let Cucumber execute the sub-steps
45
52
  steps(rendered_steps)
46
53
  end
47
54
 
48
55
 
49
56
  # Clear (remove) all the macro-step definitions.
50
- # After this, we are in the same situation when no macro-step
57
+ # After this, we are in the same situation when no macro-step
51
58
  # was ever defined.
52
59
  def clear_macros()
53
60
  MacroCollection.instance.clear
@@ -11,7 +11,7 @@ module Macros4Cuke # Module used as a namespace
11
11
  # an aggregation of lower-level sub-steps.
12
12
  # When a macro-step is used in a scenario, then its execution is equivalent
13
13
  # to the execution of its sub-steps.
14
- # A macro-step may have zero or more arguments.
14
+ # A macro-step may have zero or more arguments.
15
15
  # The actual values bound to these arguments
16
16
  # are passed to the sub-steps at execution time.
17
17
  class MacroStep
@@ -19,29 +19,29 @@ class MacroStep
19
19
  BuiltinParameters = {
20
20
  'quotes' => '"""'
21
21
  }
22
-
22
+
23
23
  # A template engine that expands the sub-steps upon request.
24
24
  attr_reader(:renderer)
25
-
25
+
26
26
  # The sentence fragment that defines the syntax of the macro-step
27
27
  attr_reader(:phrase)
28
-
28
+
29
29
  # Unique key of the macro as derived from the macro phrase.
30
30
  attr_reader(:key)
31
-
31
+
32
32
  # The list of macro arguments that appears in the macro phrase.
33
33
  attr_reader(:phrase_args)
34
-
35
- # The list of macro argument names (as appearing in the substeps
34
+
35
+ # The list of macro argument names (as appearing in the substeps
36
36
  # AND in the macro phrase).
37
37
  attr_reader(:args)
38
-
38
+
39
39
  # Constructor.
40
- # @param aMacroPhrase[String] The text from the macro step definition
40
+ # @param aMacroPhrase[String] The text from the macro step definition
41
41
  # that is between the square brackets.
42
42
  # @param theSubsteps [String] The source text of the steps to be expanded
43
43
  # upon macro invokation.
44
- # @param useTable [boolean] A flag indicating whether a data table
44
+ # @param useTable [boolean] A flag indicating whether a data table
45
45
  # must be used to pass actual values.
46
46
  def initialize(aMacroPhrase, theSubsteps, useTable)
47
47
  @phrase = aMacroPhrase
@@ -59,23 +59,23 @@ class MacroStep
59
59
  end
60
60
 
61
61
 
62
- # Compute the identifier of the macro from the given macro phrase.
63
- # A macro phrase is a text that may contain zero or more placeholders.
64
- # In definition mode, a placeholder is delimited by chevrons <..>.
65
- # In invokation mode, a value bound to a placeholder is delimited
66
- # by double quotes.
67
- # The rule for building the identifying key are:
68
- # - Leading and trailing space(s) are removed.
69
- # - Each underscore character is removed.
70
- # - Every sequence of one or more space(s) is converted into an underscore
71
- # - Each placeholder (i.e. = delimiters + enclosed text)
72
- # is converted into a letter X.
73
- # - when useTable is true, concatenate: _T
62
+ # Compute the identifier of the macro from the given macro phrase.
63
+ # A macro phrase is a text that may contain zero or more placeholders.
64
+ # In definition mode, a placeholder is delimited by chevrons <..>.
65
+ # In invokation mode, a value bound to a placeholder is delimited
66
+ # by double quotes.
67
+ # The rule for building the identifying key are:
68
+ # - Leading and trailing space(s) are removed.
69
+ # - Each underscore character is removed.
70
+ # - Every sequence of one or more space(s) is converted into an underscore
71
+ # - Each placeholder (i.e. = delimiters + enclosed text)
72
+ # is converted into a letter X.
73
+ # - when useTable is true, concatenate: _T
74
74
  # @example:
75
- # Consider the macro phrase: 'create the following "contactType" contact'
75
+ # Consider the macro phrase: 'create the following "contactType" contact'
76
76
  # The resulting macro_key is: 'create_the_following_X_contact_T'
77
77
  #
78
- # @param aMacroPhrase [String] The text from the macro step definition
78
+ # @param aMacroPhrase [String] The text from the macro step definition
79
79
  # that is between the square brackets.
80
80
  # @param useTable [boolean] A flag indicating whether a table
81
81
  # should be used to pass actual values.
@@ -87,11 +87,11 @@ class MacroStep
87
87
  # Remove every underscore
88
88
  stripped_phrase.gsub!(/_/, '')
89
89
 
90
- # Replace all consecutive whitespaces by an underscore
90
+ # Replace all consecutive whitespaces by an underscore
91
91
  stripped_phrase.gsub!(/\s+/, '_')
92
92
 
93
93
 
94
- # Determine the pattern to isolate
94
+ # Determine the pattern to isolate
95
95
  # each argument/parameter with its delimiters
96
96
  pattern = case mode
97
97
  when :definition
@@ -113,12 +113,12 @@ class MacroStep
113
113
  # Render the steps from the template, given the values
114
114
  # taken by the parameters
115
115
  # @param aPhrase [String] an instance of the macro phrase.
116
- # @param rawData [Array or nil] An Array with couples of the form:
116
+ # @param rawData [Array or nil] An Array with couples of the form:
117
117
  # [macro argument name, a value].
118
118
  # Multiple rows with same argument name are acceptable.
119
119
  def expand(aPhrase, rawData)
120
120
  params = validate_params(aPhrase, rawData)
121
-
121
+
122
122
  # Add built-in constants if necessary.
123
123
  params = BuiltinParameters.merge(params)
124
124
 
@@ -129,18 +129,18 @@ class MacroStep
129
129
 
130
130
  # Build a Hash from the given raw data.
131
131
  # @param aPhrase [String] an instance of the macro phrase.
132
- # @param rawData [Array or nil] An Array with coupples of the form:
132
+ # @param rawData [Array or nil] An Array with coupples of the form:
133
133
  # [macro argument name, a value].
134
134
  # Multiple rows with same argument name are acceptable.
135
135
  def validate_params(aPhrase, rawData)
136
136
  macro_parameters = {}
137
137
 
138
138
  # Retrieve the value(s) per variable in the phrase.
139
- quoted_values = scan_arguments(aPhrase, :invokation)
139
+ quoted_values = scan_arguments(aPhrase, :invokation)
140
140
  quoted_values.each_with_index do |val, index|
141
141
  macro_parameters[phrase_args[index]] = val
142
142
  end
143
-
143
+
144
144
  unless rawData.nil?
145
145
  rawData.each do |a_row|
146
146
  (a_key, value) = validate_row(a_row, macro_parameters)
@@ -155,22 +155,22 @@ class MacroStep
155
155
  end
156
156
  end
157
157
  end
158
-
158
+
159
159
  return macro_parameters
160
160
  end
161
-
161
+
162
162
  # Validate a row from the data table.
163
163
  # Return the validated row.
164
- # @param a_row [Array] A 2-elements Array (i.e. a couple) of the form:
164
+ # @param a_row [Array] A 2-elements Array (i.e. a couple) of the form:
165
165
  # [macro argument name, a value].
166
166
  # @param params [Hash] The pairs phrase argument name => value
167
167
  def validate_row(a_row, params)
168
168
  (a_key, value) = a_row
169
169
  fail(UnknownArgumentError.new(a_key)) unless args.include? a_key
170
170
  if (phrase_args.include? a_key) && (params[a_key] != value)
171
- fail(AmbiguousArgumentValue.new(a_key, params[a_key], value))
172
- end
173
-
171
+ fail(AmbiguousArgumentValue.new(a_key, params[a_key], value))
172
+ end
173
+
174
174
  return a_row
175
175
  end
176
176
 
@@ -194,14 +194,14 @@ class MacroStep
194
194
  end
195
195
  raw_result = aMacroPhrase.scan(pattern)
196
196
  args = raw_result.flatten.compact
197
-
197
+
198
198
  # Replace escaped quotes by quote character.
199
199
  args.map! { |arg| arg.sub(/\\"/, '"') } if mode == :invokation
200
-
200
+
201
201
  return args
202
202
  end
203
-
204
- # Check for inconsistencies between the argument names
203
+
204
+ # Check for inconsistencies between the argument names
205
205
  # in the phrase and the substeps part.
206
206
  def validate_phrase_args(thePhraseArgs, substepsVars)
207
207
  # Error when the phrase names an argument that never occurs in the substeps
@@ -209,8 +209,8 @@ class MacroStep
209
209
  unless substepsVars.include? phrase_arg
210
210
  fail(UselessPhraseArgument.new(phrase_arg))
211
211
  end
212
- end
213
- # Error when a substep has an argument that never appears in the phrase
212
+ end
213
+ # Error when a substep has an argument that never appears in the phrase
214
214
  # and the macro-step does not use data table.
215
215
  unless use_table?
216
216
  substepsVars.each do |substep_arg|
@@ -218,14 +218,14 @@ class MacroStep
218
218
  BuiltinParameters.include?(substep_arg)
219
219
  fail(UnreachableSubstepArgument.new(substep_arg))
220
220
  end
221
- end
221
+ end
222
222
  end
223
-
223
+
224
224
  return thePhraseArgs.dup
225
225
  end
226
-
227
-
228
- # Return true, if the macro-step requires a data table
226
+
227
+
228
+ # Return true, if the macro-step requires a data table
229
229
  # to pass actual values of the arguments.
230
230
  def use_table?()
231
231
  return key =~ /_T$/