Skip to content

Commit

Permalink
Fixes #31
Browse files Browse the repository at this point in the history
This is a pretty hefty refactor. I found it easier to understand toml
generation with the majority of the generation code within the
monkey_patch rather than the generator class.
  • Loading branch information
Daniel Paul Searles committed Sep 23, 2014
1 parent d39648b commit dc1b51f
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 67 deletions.
50 changes: 1 addition & 49 deletions lib/toml/generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,8 @@ def initialize(doc)
# used by TOML.
self.class.inject!

@body = ""
@doc = doc

visit(@doc)
@body = doc.to_toml

return @body
end
Expand All @@ -27,51 +25,5 @@ def self.inject!
require 'toml/monkey_patch'
@@injected = true
end

def visit(hash, path = "")
hash_pairs = [] # Sub-hashes
other_pairs = []

hash.keys.sort.each do |key|
val = hash[key]
# TODO: Refactor for other hash-likes (OrderedHash)
if val.is_a? Hash
hash_pairs << [key, val]
else
other_pairs << [key, val]
end
end

# Handle all the key-values
if !path.empty? && !other_pairs.empty?
@body += "[#{path}]\n"
end
other_pairs.each do |pair|
key, val = pair
if key.include? '.'
raise SyntaxError, "Periods are not allowed in keys (failed on key: #{key.inspect})"
end
unless val.nil?
@body += "#{key} = #{format(val)}\n"
end
end
@body += "\n" unless other_pairs.empty?

# Then deal with sub-hashes
hash_pairs.each do |pair|
key, hash = pair
if hash.empty?
@body += "[#{path.empty? ? key : [path, key].join(".")}]\n"
else
visit(hash, (path.empty? ? key : [path, key].join(".")))
end
end
end#visit

# Returns the value formatted for TOML.
def format(val)
val.to_toml
end

end#Generator
end#TOML
87 changes: 74 additions & 13 deletions lib/toml/monkey_patch.rb
Original file line number Diff line number Diff line change
@@ -1,26 +1,87 @@
# Adds to_toml methods to base Ruby classes used by the generator.
class Object
def toml_table?
self.kind_of?(Hash)
end
def toml_table_array?
self.kind_of?(Array) && self.first.toml_table?
end
end
class Hash
def to_toml(path = "")
return "" if self.empty?

tables = {}
values = {}
self.keys.sort.each do |key|
val = self[key]
if val.kind_of?(NilClass)
next
elsif val.toml_table? || val.toml_table_array?
tables[key] = val
else
values[key] = val
end
end

toml = ""
values.each do |key, val|
toml << "#{key} = #{val.to_toml(key)}\n"
end

tables.each do |key, val|
key = "#{path}.#{key}" unless path.empty?
toml_val = val.to_toml(key)
unless toml_val.empty?
if val.toml_table?
non_table_vals = val.values.reject do |v|
v.toml_table? || v.toml_table_array?
end

# Only add the table key if there are non table values.
if non_table_vals.length > 0
toml << "\n[#{key}]\n"
end
end
toml << toml_val
end
end

toml
end
end
class Array
def to_toml(path = "")
unless self.map(&:class).uniq.length == 1
raise "All array values must be the same type"
end

if self.first.toml_table?
toml = ""
self.each do |val|
toml << "\n[[#{path}]]\n"
toml << val.to_toml(path)
end
return toml
else
"[" + self.map {|v| v.to_toml(path) }.join(",") + "]"
end
end
end
class TrueClass
def to_toml; "true"; end
def to_toml(path = ""); "true"; end
end
class FalseClass
def to_toml; "false"; end
def to_toml(path = ""); "false"; end
end
class String
def to_toml; self.inspect; end
def to_toml(path = ""); self.inspect; end
end
class Numeric
def to_toml; self.to_s; end
end
class Array
def to_toml
unless self.map(&:class).uniq.length < 2
raise "All array values must be the same type"
end
"[" + self.map {|v| v.to_toml }.join(",") + "]"
end
def to_toml(path = ""); self.to_s; end
end
class DateTime
def to_toml
def to_toml(path = "")
self.to_time.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
end
end
30 changes: 25 additions & 5 deletions test/test_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,22 @@ def setup
"false" => false,
"string" => "hi",
"array" => [[1], [2], [3]],
"table_array" => [
{
"name" => "first"
},
{
"name" => "second",
"sub_table_array" => [
{
"sub_name" => "sub first",
},
{
"sub_name" => "sub second"
}
]
}
],
"key" => {
"group" => {
"value" => "lol"
Expand All @@ -32,14 +48,18 @@ def test_generator

doc_parsed = TOML::Parser.new(body).parsed

# Extracting dates since Ruby's DateTime equality testing sucks.
original_date = doc.delete "date"
parsed_date = doc_parsed.delete "date"

# removing the nil value
remove_nil = doc.delete "nil"
remove_nil_table = doc["key"].delete "nil_table"

assert_equal doc, doc_parsed
# Extracting dates since Ruby's DateTime equality testing sucks.
original_date = doc.delete "date"
parsed_date = doc_parsed.delete "date"
assert_equal original_date.to_time.to_s, parsed_date.to_time.to_s

refute doc_parsed.length > doc.length, "Parsed doc has more items than we started with."
doc.each do |key, val|
assert_equal val, doc_parsed[key]
end
end
end

0 comments on commit dc1b51f

Please sign in to comment.