macros4cuke 0.3.42 → 0.4.00

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