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
@@ -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$/