macros4cuke 0.3.42 → 0.4.00

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 (35) hide show
  1. checksums.yaml +8 -8
  2. data/.rubocop.yml +20 -3
  3. data/CHANGELOG.md +15 -1
  4. data/examples/i18n/fr/features/step_definitions/use_macro_steps.rb +1 -1
  5. data/features/demo06.feature +75 -75
  6. data/features/demo07.feature +15 -0
  7. data/features/support/env.rb +6 -0
  8. data/features/support/macro_support.rb +2 -2
  9. data/lib/macro_steps.rb +19 -2
  10. data/lib/macros4cuke/coll-walker-factory.rb +119 -0
  11. data/lib/macros4cuke/constants.rb +1 -1
  12. data/lib/macros4cuke/exceptions.rb +23 -1
  13. data/lib/macros4cuke/formatter/all-notifications.rb +30 -0
  14. data/lib/macros4cuke/formatter/to-gherkin.rb +80 -0
  15. data/lib/macros4cuke/formatter/to-null.rb +86 -0
  16. data/lib/macros4cuke/formatter/to-trace.rb +100 -0
  17. data/lib/macros4cuke/formatting-service.rb +74 -0
  18. data/lib/macros4cuke/macro-collection.rb +3 -3
  19. data/lib/macros4cuke/macro-step-support.rb +1 -1
  20. data/lib/macros4cuke/macro-step.rb +10 -26
  21. data/lib/macros4cuke/templating/engine.rb +74 -29
  22. data/spec/macros4cuke/coll-walker-factory_spec.rb +269 -0
  23. data/spec/macros4cuke/formatter/to-gherkin_spec.rb +129 -0
  24. data/spec/macros4cuke/formatter/to-null_spec.rb +62 -0
  25. data/spec/macros4cuke/formatter/to-trace_spec.rb +155 -0
  26. data/spec/macros4cuke/formatting-service_spec.rb +138 -0
  27. data/spec/macros4cuke/macro-collection_spec.rb +7 -4
  28. data/spec/macros4cuke/macro-step-support_spec.rb +41 -24
  29. data/spec/macros4cuke/macro-step_spec.rb +13 -6
  30. data/spec/macros4cuke/templating/engine_spec.rb +11 -7
  31. data/spec/macros4cuke/templating/placeholder_spec.rb +1 -1
  32. data/spec/macros4cuke/templating/section_spec.rb +3 -1
  33. data/spec/macros4cuke/use-sample-collection.rb +79 -0
  34. data/spec/spec_helper.rb +3 -0
  35. metadata +15 -2
@@ -3,7 +3,7 @@
3
3
 
4
4
  module Macros4Cuke # Module used as a namespace
5
5
  # The version number of the gem.
6
- Version = '0.3.42'
6
+ Version = '0.4.00'
7
7
 
8
8
  # Brief description of the gem.
9
9
  Description = 'Macro-steps for Cucumber'
@@ -84,7 +84,8 @@ end # class
84
84
 
85
85
 
86
86
 
87
- # Raised when one invokes a macro-step without a required data table argument
87
+ # Raised when one invokes a macro-step without a required data table argument.
88
+ # The exception is raised only only via Cucumber (steps)
88
89
  class DataTableNotFound < Macros4CukeError
89
90
  def initialize(phrase)
90
91
  msg = "The step with phrase [#{phrase}]: requires a data table."
@@ -93,6 +94,27 @@ class DataTableNotFound < Macros4CukeError
93
94
  end # class
94
95
 
95
96
 
97
+ # Raised when the list of formatting events provided by a macro-step formatter
98
+ # is nil or empty.
99
+ class NoFormattingEventForFormatter < Macros4CukeError
100
+ def initialize(aFormatter)
101
+ msg = "Formatter #{aFormatter.class}"
102
+ msg << ' does not support any formatting event.'
103
+ super(msg)
104
+ end
105
+ end # class
106
+
107
+
108
+ # Raised when a macro-step formatter uses an unknown formatting event.
109
+ class UnknownFormattingEvent < Macros4CukeError
110
+ def initialize(aFormatter, anEvent)
111
+ msg = "Formatter #{aFormatter.class}"
112
+ msg << " uses the unknown formatting event '#{anEvent}'."
113
+ super(msg)
114
+ end
115
+ end # class
116
+
117
+
96
118
  # Raised when Macros4Cuke encountered an issue
97
119
  # that it can't handle properly.
98
120
  class InternalError < Macros4CukeError
@@ -0,0 +1,30 @@
1
+ # File: all_notifications.rb
2
+
3
+ module Macros4Cuke # Module used as a namespace
4
+
5
+ # Namespace for all formatters of MacroCollection and MacroStep objects
6
+ module Formatter
7
+
8
+ # The list of all formatting notifications
9
+ AllNotifications = [
10
+ :on_collection,
11
+ :on_collection_end,
12
+ :on_step,
13
+ :on_step_end,
14
+ :on_phrase,
15
+ :on_renderer,
16
+ :on_renderer_end,
17
+ :on_source,
18
+ :on_static_text,
19
+ :on_eol,
20
+ :on_comment,
21
+ :on_placeholder,
22
+ :on_section,
23
+ :on_section_end
24
+ ]
25
+
26
+ end # module
27
+
28
+ end # module
29
+
30
+ # End of file
@@ -0,0 +1,80 @@
1
+ # File: to-gherkin.rb
2
+
3
+ require_relative '../constants'
4
+
5
+ module Macros4Cuke # Module used as a namespace
6
+
7
+ # Namespace for all formatters of MacroCollection and MacroStep objects
8
+ module Formatter
9
+
10
+ # A macro-step formatter that outputs to the given IO the macro-steps from a
11
+ # macro collection into a Gherkin feature file.
12
+ class ToGherkin
13
+ # The IO where the formatter's output will be written to.
14
+ attr_reader(:io)
15
+
16
+ # The number of macro-step encountered by the formatter.
17
+ attr_reader(:step_count)
18
+
19
+ def initialize(anIO)
20
+ @io = anIO
21
+ @step_count = 0
22
+ end
23
+
24
+ public
25
+ # Tell which notifications this formatter subscribes to.
26
+ def implements()
27
+ return [:on_collection, :on_step, :on_step_end, :on_phrase, :on_source]
28
+ end
29
+
30
+ def on_collection(aLevel, aMacroCollection)
31
+ io.print "# Feature file generated by Macro4Cuke #{Macros4Cuke::Version}"
32
+ io.puts " on #{Time.now.strftime('%d/%m/%Y %H:%M:%S')}"
33
+ io.puts ''
34
+ io.puts 'Feature: the set of macro-step definitions'
35
+ io.puts "#{indentation(1)}As a feature file writer"
36
+ io.puts "#{indentation(1)}So that I write higher-level steps"
37
+ io.puts ''
38
+ end
39
+
40
+ def on_step(aLevel, aMacroStep)
41
+ @step_count += 1
42
+ io.puts "#{indentation(aLevel)}Scenario: Macro #{step_count}"
43
+ end
44
+
45
+ def on_step_end(aLevel)
46
+ io.puts ''
47
+ end
48
+
49
+
50
+ def on_phrase(aLevel, aPhraseText, useTable)
51
+ suffix = useTable ? ':' : ''
52
+ io.print "#{indentation(aLevel)}Given I define the step "
53
+ io.puts %Q|"* I [#{aPhraseText}]#{suffix}" to mean:|
54
+ end
55
+
56
+ def on_source(aLevel, aSourceText)
57
+ ljust = indentation(aLevel)
58
+ triple_quotes = %Q|#{ljust}"""|
59
+ io.puts triple_quotes
60
+
61
+ # Indent source text
62
+ indented_text = aSourceText.gsub(/^/m, "#{ljust}")
63
+
64
+ io.puts indented_text
65
+ io.puts triple_quotes
66
+ end
67
+
68
+ private
69
+ def indentation(aLevel)
70
+ return ' ' * (aLevel)
71
+ end
72
+
73
+
74
+ end # class
75
+
76
+ end # module
77
+
78
+ end # module
79
+
80
+ # End of file
@@ -0,0 +1,86 @@
1
+ # File: to-null.rb
2
+
3
+ require_relative 'all-notifications'
4
+
5
+ module Macros4Cuke # Module used as a namespace
6
+
7
+ # Namespace for all formatters of MacroCollection and MacroStep objects
8
+ module Formatter
9
+
10
+ # A macro-step formatter that doesn't produce any output.
11
+ # It fully implements the expected interface of formatters but
12
+ # its methods are NOOP (i.e. they do nothing).
13
+ # This formatter can be useful when one wants to discard
14
+ # any formatted output.
15
+ class ToNull
16
+
17
+ public
18
+ # Tell which notifications the formatter subscribes to.
19
+ def implements()
20
+ return Formatter::AllNotifications
21
+ end
22
+
23
+ def on_collection(aLevel, aMacroCollection)
24
+ ; # Do nothing
25
+ end
26
+
27
+ def on_collection_end(aLevel)
28
+ ; # Do nothing
29
+ end
30
+
31
+ def on_step(aLevel, aMacroStep)
32
+ ; # Do nothing
33
+ end
34
+
35
+ def on_step_end(aLevel)
36
+ ; # Do nothing
37
+ end
38
+
39
+ def on_phrase(aLevel, aPhraseText, useTable)
40
+ ; # Do nothing
41
+ end
42
+
43
+ def on_renderer(aLevel, aRenderer)
44
+ ; # Do nothing
45
+ end
46
+
47
+ def on_renderer_end(aLevel)
48
+ ; # Do nothing
49
+ end
50
+
51
+ def on_source(aLevel, aSourceText)
52
+ ; # Do nothing
53
+ end
54
+
55
+ def on_static_text(aLevel, aText)
56
+ ; # Do nothing
57
+ end
58
+
59
+ def on_comment(aLevel, aComment)
60
+ ; # Do nothing
61
+ end
62
+
63
+ def on_eol(aLevel)
64
+ ; # Do nothing
65
+ end
66
+
67
+ def on_placeholder(aLevel, aPlaceHolderName)
68
+ ; # Do nothing
69
+ end
70
+
71
+ def on_section(aLevel, aSectionName)
72
+ ; # Do Nothing
73
+ end
74
+
75
+ def on_section_end(aLevel)
76
+ ; # Do Nothing
77
+ end
78
+
79
+
80
+ end # class
81
+
82
+ end # module
83
+
84
+ end # module
85
+
86
+ # End of file
@@ -0,0 +1,100 @@
1
+ # File: to-trace.rb
2
+
3
+ require_relative 'all-notifications'
4
+
5
+ module Macros4Cuke # Module used as a namespace
6
+
7
+ # Namespace for all formatters of MacroCollection and MacroStep objects
8
+ module Formatter
9
+
10
+ # A macro-step formatter that outputs in the given IO the formatting events.
11
+ # Can be useful in tracing the visit sequence inside
12
+ # a given macro-step collection.
13
+ class ToTrace
14
+ # The IO where the formatter's output will be written to.
15
+ attr_reader(:io)
16
+
17
+
18
+ def initialize(anIO)
19
+ @io = anIO
20
+ end
21
+
22
+ public
23
+ # Tell which notifications the formatter subscribes to.
24
+ def implements()
25
+ return Formatter::AllNotifications
26
+ end
27
+
28
+ def on_collection(aLevel, aMacroCollection)
29
+ trace_event(aLevel, __method__)
30
+ end
31
+
32
+ def on_collection_end(aLevel)
33
+ trace_event(aLevel, __method__)
34
+ end
35
+
36
+ def on_step(aLevel, aMacroStep)
37
+ trace_event(aLevel, __method__)
38
+ end
39
+
40
+ def on_step_end(aLevel)
41
+ trace_event(aLevel, __method__)
42
+ end
43
+
44
+ def on_phrase(aLevel, aPhraseText, useTable)
45
+ trace_event(aLevel, __method__)
46
+ end
47
+
48
+ def on_renderer(aLevel, aRenderer)
49
+ trace_event(aLevel, __method__)
50
+ end
51
+
52
+ def on_renderer_end(aLevel)
53
+ trace_event(aLevel, __method__)
54
+ end
55
+
56
+ def on_source(aLevel, aSourceText)
57
+ trace_event(aLevel, __method__)
58
+ end
59
+
60
+ def on_static_text(aLevel, aStaticText)
61
+ trace_event(aLevel, __method__)
62
+ end
63
+
64
+ def on_comment(aLevel, aComment)
65
+ trace_event(aLevel, __method__)
66
+ end
67
+
68
+ def on_eol(aLevel)
69
+ trace_event(aLevel, __method__)
70
+ end
71
+
72
+ def on_placeholder(aLevel, aPlaceHolderName)
73
+ trace_event(aLevel, __method__)
74
+ end
75
+
76
+ def on_section(aLevel, aSectionName)
77
+ trace_event(aLevel, __method__)
78
+ end
79
+
80
+ def on_section_end(aLevel)
81
+ trace_event(aLevel, __method__)
82
+ end
83
+
84
+ private
85
+ def indentation(aLevel)
86
+ return ' ' * aLevel
87
+ end
88
+
89
+ def trace_event(aLevel, anEvent)
90
+ io.puts "#{indentation(aLevel)}#{anEvent}"
91
+ end
92
+
93
+
94
+ end # class
95
+
96
+ end # module
97
+
98
+ end # module
99
+
100
+ # End of file
@@ -0,0 +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
@@ -32,7 +32,7 @@ public
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
34
  # An exception is raised if the phrase syntax of both macros are the
35
- raise DuplicateMacroError.new(aPhrase) if find_macro(aPhrase, useTable)
35
+ fail(DuplicateMacroError, aPhrase) if find_macro(aPhrase, useTable)
36
36
 
37
37
  macro_steps[new_macro.key] = new_macro
38
38
 
@@ -47,9 +47,9 @@ public
47
47
  # Multiple rows with same argument name are acceptable.
48
48
  # @return [String]
49
49
  def render_steps(aPhrase, rawData = nil)
50
- use_table = ! rawData.nil?
50
+ use_table = !rawData.nil?
51
51
  macro = find_macro(aPhrase, use_table)
52
- raise UnknownMacroError.new(aPhrase) if macro.nil?
52
+ fail(UnknownMacroError, aPhrase) if macro.nil?
53
53
 
54
54
  # Render the steps
55
55
  return macro.expand(aPhrase, rawData)
@@ -32,7 +32,7 @@ public
32
32
  # Invoke a macro with given phrase and (optionally) a table of values
33
33
  # @param aPhraseInstance [String] an instance of the macro phrase.
34
34
  # That is, the text between [...] and with zero or more actual values.
35
- # @param rawData [Array or nil] An Array with coupples of the form:
35
+ # @param rawData [Array or nil] An Array with couples of the form:
36
36
  # [macro argument name, a value].
37
37
  # Multiple rows with same argument name are acceptable.
38
38
  def invoke_macro(aPhraseInstance, rawData = nil)