Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for single nested hashes. #36

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
55 changes: 39 additions & 16 deletions lib/scrolls/parser.rb
Expand Up @@ -14,39 +14,37 @@ def unparse(data)
"#{k}=nil"
elsif v.is_a?(Time)
"#{k}=#{v.strftime("%FT%H:%M:%S%z")}"
elsif v.is_a?(Hash)
v.map { |(nk,nv)| "#{k}.#{nk.to_s}=#{handle_quotes(nv)}" }
else
v = v.to_s
has_single_quote = v.index("'")
has_double_quote = v.index('"')
if v =~ / /
if has_single_quote && has_double_quote
v = '"' + v.gsub(/\\|"/) { |c| "\\#{c}" } + '"'
elsif has_double_quote
v = "'" + v.gsub('\\', '\\\\\\') + "'"
else
v = '"' + v.gsub('\\', '\\\\\\') + '"'
end
elsif v =~ /=/
v = '"' + v + '"'
end
v = handle_quotes(v)
"#{k}=#{v}"
end
end.compact.join(" ")
end

def parse(data)
vals = {}
nvals = {}
str = data.dup if data.is_a?(String)

patterns = [
/([^= ]+)="((?:\\.|[^"\\])*)"/, # key="\"literal\" escaped val"
/([^= ]+)='((?:\\.|[^'\\])*)'/, # key='\'literal\' escaped val'
/([^= ]+)\.([^= ]+)=([^ =]+)/, # key.group=value
/([^= ]+)=([^ =]+)/ # key=value
]

patterns.each do |pattern|
str.scan(pattern) do |match|
v = match[1]
k = match[0] # Our key will always be our first match
if match.length == 3 # If we are parsing a nested value (ie: k.n=v)
n = match[1] # Then our nested key will be our second match
v = match[2] # And our nested value will be our third match
else
v = match[1] # Default
end

v.gsub!(/\\"/, '"') # unescape \"
v.gsub!(/\\\\/, "\\") # unescape \\

Expand All @@ -60,7 +58,12 @@ def parse(data)
v = true
end

vals[match[0]] = v
if n
nvals[n.to_sym] = v
vals[k] = nvals
else
vals[k] = v
end
end
# sub value, leaving keys in order
str.gsub!(pattern, "\\1")
Expand All @@ -72,5 +75,25 @@ def parse(data)
h
end
end

private

def handle_quotes(v)
v = v.to_s
has_single_quote = v.index("'")
has_double_quote = v.index('"')
if v =~ / /
if has_single_quote && has_double_quote
v = '"' + v.gsub(/\\|"/) { |c| "\\#{c}" } + '"'
elsif has_double_quote
v = "'" + v.gsub('\\', '\\\\\\') + "'"
else
v = '"' + v.gsub('\\', '\\\\\\') + '"'
end
elsif v =~ /=/
v = '"' + v + '"'
end
v
end
end
end
6 changes: 6 additions & 0 deletions test/test_parser.rb
Expand Up @@ -68,4 +68,10 @@ def test_parse_nil
data = { n: nil }
assert_equal "n=nil", unparse(data)
end

def test_nested_hashes
data = { t: { n: "v", o: "w" } }
assert_equal 't.n=v t.o=w', unparse(data)
assert_equal data.inspect, parse(unparse(data)).inspect
end
end