class Macros4Cuke::MacroStep

A macro-step object is a Cucumber step that is itself an aggregation of lower-level sub-steps. When a macro-step is used in a scenario, then its execution is equivalent to the execution of its sub-steps. A macro-step may have zero or more arguments. The actual values bound to these arguments are passed to the sub-steps at execution time.

Constants

BuiltinParameters

The set of predefined macro argument constant values.

Attributes

args[R]

The list of macro argument names (as appearing in the substeps AND in the macro phrase).

key[R]

Unique key of the macro as derived from the macro phrase.

phrase[R]

The sentence fragment that defines the syntax of the macro-step

phrase_args[R]

The list of macro arguments that appears in the macro phrase.

renderer[R]

A template engine that expands the sub-steps upon request.

Public Class Methods

macro_key(aMacroPhrase, useTable, mode) click to toggle source

Compute the identifier of the macro from the given macro phrase. A macro phrase is a text that may contain zero or more placeholders. In definition mode, a placeholder is delimited by chevrons <..>. In invokation mode, a value bound to a placeholder is delimited by double quotes. The rule for building the identifying key are:

  • Leading and trailing space(s) are removed.

  • Each underscore character is removed.

  • Every sequence of one or more space(s) is converted into an underscore

  • Each placeholder (i.e. = delimiters + enclosed text)

    is converted into a letter X.
  • when useTable is true, concatenate: _T

@example:

Consider the macro phrase: 'create the following "contactType" contact'
The resulting macro_key is: 'create_the_following_X_contact_T'

@param aMacroPhrase [String] The text from the macro step definition

that is between the square brackets.

@param useTable [boolean] A flag indicating whether a table

should be used to pass actual values.

@param mode [:definition, :invokation] @return [String] the key of the phrase/macro.

# File lib/macros4cuke/macro-step.rb, line 82
def self.macro_key(aMacroPhrase, useTable, mode)
  stripped_phrase = aMacroPhrase.strip # Remove leading ... trailing space(s)

  # Remove every underscore
  stripped_phrase.delete!('_')

  # Replace all consecutive whitespaces by an underscore
  stripped_phrase.gsub!(/\s+/, '_')

  # Determine the pattern to isolate
  # each argument/parameter with its delimiters
  pattern = case mode
              when :definition
                /<(?:[^\\<>]|\\.)*>/
              when :invokation
                /"([^\\"]|\\.)*"/

            end

  # Each text between quotes or chevron is replaced by the letter X
  normalized = stripped_phrase.gsub(pattern, 'X')

  key = normalized + (useTable ? '_T' : '')

  return key
end
new(aMacroPhrase, theSubsteps, useTable) click to toggle source

Constructor. @param aMacroPhrase The text from the macro step definition

that is between the square brackets.

@param theSubsteps [String] The source text of the steps to be expanded

upon macro invokation.

@param useTable [boolean] A flag indicating whether a data table

must be used to pass actual values.
# File lib/macros4cuke/macro-step.rb, line 46
def initialize(aMacroPhrase, theSubsteps, useTable)
  @phrase = aMacroPhrase
  @key = self.class.macro_key(aMacroPhrase, useTable, :definition)

  # Retrieve the macro arguments embedded in the phrase.
  @phrase_args = scan_arguments(aMacroPhrase, :definition)
  @renderer = Templating::Engine.new(theSubsteps)
  substeps_vars = renderer.variables

  @args = validate_phrase_args(@phrase_args, substeps_vars)
  @args.concat(substeps_vars)
  @args.uniq!
end

Public Instance Methods

expand(aPhrase, rawData) click to toggle source

Render the steps from the template, given the values taken by the parameters @param aPhrase [String] an instance of the macro phrase. @param rawData [Array or nil] An Array with couples of the form: [macro argument name, a value].

Multiple rows with same argument name are acceptable.
# File lib/macros4cuke/macro-step.rb, line 115
def expand(aPhrase, rawData)
  params = validate_params(aPhrase, rawData)

  # Add built-in constants if necessary.
  params = BuiltinParameters.merge(params)

  return renderer.render(nil, params)
end

Private Instance Methods

scan_arguments(aMacroPhrase, mode) click to toggle source

Retrieve from the macro phrase, all the text between <..> or double quotes. Returns an array. Each of its elements corresponds to quoted text. Example: aMacroPhrase = 'a “qualifier” text with “quantity” placeholders.' Results in : [“qualifier”, “quantity”]

aMacroPhrase

A phrase

mode

one of the following: :definition, :invokation

# File lib/macros4cuke/macro-step.rb, line 180
def scan_arguments(aMacroPhrase, mode)
  # determine the syntax of the arguments/parameters
  # as a regular expression with one capturing group
  pattern = case mode
              when :definition
                /<((?:[^\\<>]|\\.)*)>/
                # /{{{([^}]*)}}}|{{([^}]*)}}/ # Two capturing groups!...
              when :invokation
                /"((?:[^\\"]|\\.)*)"/
            end
  raw_result = aMacroPhrase.scan(pattern)
  args = raw_result.flatten.compact

  # Replace escaped quotes by quote character.
  args.map! { |arg| arg.sub(/\\"/, '"') } if mode == :invokation

  return args
end
use_table?() click to toggle source

Return true, if the macro-step requires a data table to pass actual values of the arguments.

# File lib/macros4cuke/macro-step.rb, line 224
def use_table?()
  return key =~ /_T$/
end
validate_params(aPhrase, rawData) click to toggle source

Build a Hash from the given raw data. @param aPhrase [String] an instance of the macro phrase. @param rawData [Array or nil] An Array with coupples of the form: [macro argument name, a value]. Multiple rows with same argument name are acceptable.

# File lib/macros4cuke/macro-step.rb, line 131
def validate_params(aPhrase, rawData)
  macro_parameters = {}

  # Retrieve the value(s) per variable in the phrase.
  quoted_values = scan_arguments(aPhrase, :invokation)
  quoted_values.each_with_index do |val, index|
    macro_parameters[phrase_args[index]] = val
  end

  if rawData
    rawData.each do |a_row|
      (a_key, value) = validate_row(a_row, macro_parameters)
      if macro_parameters.include? a_key
        if macro_parameters[a_key].is_a?(Array)
          macro_parameters[a_key] << value
        else
          macro_parameters[a_key] = [macro_parameters[a_key], value]
        end
      else
        macro_parameters[a_key] = value
      end
    end
  end

  return macro_parameters
end
validate_phrase_args(thePhraseArgs, substepsVars) click to toggle source

Check for inconsistencies between the argument names in the phrase and the substeps part.

# File lib/macros4cuke/macro-step.rb, line 201
def validate_phrase_args(thePhraseArgs, substepsVars)
  # Error when the phrase names an argument that never occurs in the substeps
  thePhraseArgs.each do |phrase_arg|
    next if substepsVars.include? phrase_arg
    
    raise(UselessPhraseArgument.new(phrase_arg))
  end
  # Error when a substep has an argument that never appears in the phrase
  # and the macro-step does not use data table.
  unless use_table?
    substepsVars.each do |substep_arg|
      next if thePhraseArgs.include?(substep_arg) ||
              BuiltinParameters.include?(substep_arg)
      
      raise(UnreachableSubstepArgument.new(substep_arg))
    end
  end

  return thePhraseArgs.dup
end
validate_row(a_row, params) click to toggle source

Validate a row from the data table. Return the validated row. @param a_row [Array] A 2-elements Array (i.e. a couple) of the form: [macro argument name, a value]. @param params [Hash] The pairs phrase argument name => value

# File lib/macros4cuke/macro-step.rb, line 163
def validate_row(a_row, params)
  (a_key, value) = a_row
  raise(UnknownArgumentError.new(a_key)) unless args.include? a_key
  if (phrase_args.include? a_key) && (params[a_key] != value)
    raise(AmbiguousArgumentValue.new(a_key, params[a_key], value))
  end

  return a_row
end