class ComfortableMexicanSofa::Content::ParamsParser

Constants

ARRAY_CLOSE
ARRAY_OPEN
COMMA
HASH_CLOSE
HASH_KEY
HASH_OPEN
IDENTIFIER
STRING_LITERAL

Public Class Methods

new(string = "") click to toggle source

@param <String> string

# File lib/comfortable_mexican_sofa/content/params_parser.rb, line 19
def initialize(string = "")
  @string = string
end

Public Instance Methods

params() click to toggle source

Takes CMS content tag parameters and converts them into array of strings, hashes and arrays.

@return [Array<String, {String => String}>] @raise [Error] if the given `text` is malformed.

@example

new("a, b, c").parse
#=> ["a", "b", "c"]

new("a, b: c, d: e").parse
#=> ["a", {"b" => "c", "d" => "e"}]

new("a, b: {c: [d, e]}").parse
#=> ["a", {"b" => {"c" => ["d", "e"]}}]
# File lib/comfortable_mexican_sofa/content/params_parser.rb, line 39
def params
  @tokens = tokenize(@string)
  parse_params
end

Private Instance Methods

parse_array() click to toggle source

Array construction method. Will handle nested arrays

# File lib/comfortable_mexican_sofa/content/params_parser.rb, line 100
def parse_array
  opens = 1
  array = []
  while (token = @tokens.shift)
    case token&.first
    when :array_close
      opens -= 1
    else
      array << parse_value(token)
    end

    return array if opens.zero?
  end

  raise Error, "Unclosed array param: #{@string}"
end
parse_hash() click to toggle source

Hash constructor method. Will handle nested hashes as well

# File lib/comfortable_mexican_sofa/content/params_parser.rb, line 73
def parse_hash
  opens = 1
  hash = {}

  while (token = @tokens.shift)
    case token&.first
    when :hash_key
      hash[token[1]] = parse_value(@tokens.shift)
    when :hash_close
      opens -= 1
    when :hash_open
      opens += 1
    else
      raise Error, "Invalid params: #{@string}"
    end

    return hash if opens.zero?
  end

  # We're can't really detect unclosed hashes as we can construct them without
  # opening brakets. For example, `a: b, c: d` is same as `{a: b, c: d}` and
  # `{a: b, c: d` is also ends up to be a valid hash. It will error out if
  # unclosed hash is followed by some other unexpected token. Like: `{a: b, c`
  hash
end
parse_params() click to toggle source

Contructs root-level list of arguments sent via content tag

# File lib/comfortable_mexican_sofa/content/params_parser.rb, line 47
def parse_params
  params = []
  while (token = @tokens.shift)
    params << parse_value(token)
  end
  params
end
parse_value(token) click to toggle source

Gets token value. Will trigger parsing of hash and array structures

# File lib/comfortable_mexican_sofa/content/params_parser.rb, line 56
def parse_value(token)
  case token&.first
  when :string
    token[1]
  when :hash_key
    @tokens.unshift(token)
    parse_hash
  when :hash_open
    parse_hash
  when :array_open
    parse_array
  else
    raise Error, "Invalid params: #{@string}"
  end
end
tokenize(args_string) click to toggle source

Tokenizing input string into a list of touples Also args_string is stripped of “smart” quotes coming from wysiwyg

@param [String] args_string @return [Array<String>] tokens

# File lib/comfortable_mexican_sofa/content/params_parser.rb, line 122
def tokenize(args_string)
  args_string = args_string.tr("“”‘’", %q(""''))
  ss = StringScanner.new(args_string)
  tokens = []
  loop do
    ss.skip(%r{\s*})
    break if ss.eos?

    # commas are just separators like spaces
    next if ss.scan(COMMA)

    tokens <<
      if    (t = ss.scan(STRING_LITERAL)) then [:string, t[1...-1]]
      elsif (t = ss.scan(HASH_KEY))       then [:hash_key, t[0...-1]]
      elsif (t = ss.scan(IDENTIFIER))     then [:string, t]
      elsif (t = ss.scan(HASH_OPEN))      then [:hash_open, t]
      elsif (t = ss.scan(HASH_CLOSE))     then [:hash_close, t]
      elsif (t = ss.scan(ARRAY_OPEN))     then [:array_open, t]
      elsif (t = ss.scan(ARRAY_CLOSE))    then [:array_close, t]
      else
        raise Error, "Unexpected char: #{ss.getch}"
      end
  end

  tokens
end