Module: Chop::RegexTemplates

Defined in:
lib/chop/regex_templates.rb

Constant Summary collapse

TOKEN =
/
  (?<!\\)       # not preceded by backslash
  \#\{          # start of token
  \/            # opening slash
  (.*?)         # pattern (non-greedy)
  \/            # closing slash
  ([imx]*)      # optional flags
  \}            # end of token
/mx

Class Method Summary collapse

Class Method Details

.allowed?(allowed_columns, j) ⇒ Boolean

Returns:

  • (Boolean)


69
70
71
72
# File 'lib/chop/regex_templates.rb', line 69

def allowed?(allowed_columns, j)
  return true if allowed_columns == :all
  allowed_columns.include?(j)
end

.apply(cucumber_table, actual, fields) ⇒ Object



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/chop/regex_templates.rb', line 15

def apply(cucumber_table, actual, fields)
  allowed_columns = columns_for(fields, cucumber_table.raw.first)

  expected = cucumber_table.raw.map.with_index do |row, i|
    row.map.with_index do |cell, j|
      str = cell.to_s
      # De-escape literal token markers so \#{/.../} becomes literal '#{...}'
      deescaped = str.gsub('\\#{', '#{')

      if allowed?(allowed_columns, j) && deescaped.include?('#{') && deescaped.match?(TOKEN)
        regex = expand_template(deescaped)
        actual_cell = (actual.dig(i, j) || "").to_s
        if regex.match?(actual_cell)
          actual_cell
        else
          deescaped
        end
      else
        deescaped
      end
    end
  end

  Cucumber::MultilineArgument::DataTable.from(expected)
end

.columns_for(fields, expected_header) ⇒ Object



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/chop/regex_templates.rb', line 41

def columns_for(fields, expected_header)
  return :all if fields.nil? || fields.empty?

  idxs = []

  fields.each do |f|
    case f
    when Integer
      idxs << (f - 1)
    when Symbol, String
      next unless expected_header
      normalized = f.to_s.parameterize.underscore
      header_keys = expected_header.map.with_index do |text, idx|
        t = text.to_s
        key = t.parameterize.underscore
        key = t if key.blank? && t.present?
        key = (idx + 1).to_s if key.blank?
        key
      end
      header_keys.each_with_index do |key, idx|
        idxs << idx if key == normalized
      end
    end
  end

  idxs.uniq
end

.expand_template(str) ⇒ Object



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/chop/regex_templates.rb', line 74

def expand_template(str)
  parts = []
  last = 0

  str.to_enum(:scan, TOKEN).each do
    m = Regexp.last_match
    literal = str[last...m.begin(0)]
    parts << Regexp.escape(literal)
    pattern, flags = m.captures
    if flags.to_s.empty?
      parts << "(?:#{pattern})"
    else
      parts << "(?#{flags}:#{pattern})"
    end
    last = m.end(0)
  end

  tail = str[last..-1] || ""
  parts << Regexp.escape(tail)

  Regexp.new("\\A(?:#{parts.join})\\z")
end