lib/apricoteatsgorilla.rb in smacks-apricoteatsgorilla-0.2.4 vs lib/apricoteatsgorilla.rb in smacks-apricoteatsgorilla-0.2.5
- old
+ new
@@ -8,111 +8,123 @@
#
# Its initial purpose was to convert SOAP response messages to Hashes,
# but it quickly evolved into a more general translation tool.
class ApricotEatsGorilla
- # Converts XML into a Hash. Starts parsing at root node by default.
- # Call with XPath expession (Hpricot search) as second parameter to define
- # a custom root node. The root node itself will not be included in the Hash.
- #
- # E.g.
- # xml = "<dude><likes>beer</likes><hates>appletini</hates></dude>"
- # ApricotEatsGorilla.xml_to_hash(xml)
- # # => { "hates" => "appletini", "likes" => "beer" }
- def self.xml_to_hash(xml, root_node = nil)
- xml = clean_xml(xml)
- doc = Hpricot.XML(xml)
- root = root_node ? doc.at(root_node) : doc.root
- xml_node_to_hash(root)
- end
+ class << self
+ attr_accessor :sort_keys
- # Converts a Hash to XML.
- #
- # E.g.
- # hash = { "dude" => { "likes" => "beer", "hates" => "appletini" } }
- # ApricotEatsGorilla.hash_to_xml(hash)
- # # => "<dude><hates>appletini</hates><likes>beer</likes></dude>"
- def self.hash_to_xml(hash)
- nested_data_to_xml(hash.keys.first, hash.values.first)
- end
+ # Converts XML into a Hash. Starts parsing at root node by default.
+ # Call with XPath expession (Hpricot search) as second parameter to define
+ # a custom root node. The root node itself will not be included in the Hash.
+ #
+ # E.g.
+ # xml = "<dude><likes>beer</likes><hates>appletini</hates></dude>"
+ # ApricotEatsGorilla.xml_to_hash(xml)
+ # # => { "hates" => "appletini", "likes" => "beer" }
+ def xml_to_hash(xml, root_node = nil)
+ xml = clean_xml(xml)
+ doc = Hpricot.XML(xml)
+ root = root_node ? doc.at(root_node) : doc.root
+ xml_node_to_hash(root)
+ end
-private
+ # Converts a Hash to XML.
+ #
+ # E.g.
+ # hash = { "dude" => { "likes" => "beer", "hates" => "appletini" } }
+ # ApricotEatsGorilla.hash_to_xml(hash)
+ # # => "<dude><hates>appletini</hates><likes>beer</likes></dude>"
+ def hash_to_xml(hash)
+ nested_data_to_xml(hash.keys.first, hash.values.first)
+ end
- # Converts XML into a Hash.
- def self.xml_node_to_hash(node)
- this_node = {}
+ private
- node.each_child do |child|
- if child.children.nil?
- key, value = child.name, nil
- elsif child.children.size == 1 && child.children.first.text?
- key, value = child.name, string_to_bool?(child.children.first.raw_string)
- else
- key, value = child.name, xml_node_to_hash(child)
- end
+ # Converts XML into a Hash.
+ def xml_node_to_hash(node)
+ this_node = {}
- current = this_node[key]
- case current
- when Array:
- this_node[key] << value
- when nil
- this_node[key] = value
+ node.each_child do |child|
+ if child.children.nil? || child.children.empty?
+ key, value = child.name, nil
+ elsif child.children.size == 1 && child.children.first.text?
+ key, value = child.name, string_to_bool?(child.children.first.inner_text)
else
- this_node[key] = [current.dup, value]
+ key, value = child.name, xml_node_to_hash(child)
+ end
+
+ current = this_node[key]
+ case current
+ when Array:
+ this_node[key] << value
+ when nil
+ this_node[key] = value
+ else
+ this_node[key] = [current.dup, value]
+ end
end
+
+ this_node
end
- this_node
- end
+ # Converts a Hash to XML.
+ def nested_data_to_xml(name, item)
+ case item
+ when String
+ make_tag(name) { item }
+ when Array
+ item.map { |subitem| nested_data_to_xml(name, subitem) }.join
+ when Hash
+ make_tag(name) do
+ opt_order(item).map { |tag, value|
+ case value
+ when String
+ make_tag(tag) { value }
+ when Array
+ value.map { |subitem| nested_data_to_xml(tag, subitem) }.join
+ when Hash
+ nested_data_to_xml(tag, value)
+ end
+ }.join
+ end
+ end
+ end
- # Converts a Hash to XML.
- def self.nested_data_to_xml(name, item)
- case item
- when String
- make_tag(name) { item }
- when Array
- item.map { |subitem| nested_data_to_xml(name, subitem) }.join
- when Hash
- make_tag(name) do
- item.map { |tag, value|
- case value
- when String
- make_tag(tag) { value }
- when Array
- value.map { |subitem| nested_data_to_xml(tag, subitem) }.join
- when Hash
- nested_data_to_xml(tag, value)
- end
- }.join
+ # Helper to create an XML tag. Expects a block for tag content.
+ # Defaults to an empty element tag in case no block was supplied.
+ def make_tag(name)
+ body = yield
+ if body && !body.empty?
+ "<#{name}>" << body << "</#{name}>"
+ else
+ "<#{name} />"
end
end
- end
- # Helper to create an XML tag. Expects a block for tag content.
- # Defaults to an empty element tag in case no block was supplied.
- def self.make_tag(name)
- body = yield
- if body && !body.empty?
- "<#{name}>" << body << "</#{name}>"
- else
- "<#{name} />"
+ def opt_order(hash)
+ if sort_keys
+ hash.sort_by{ |kv| kv.first }
+ else
+ hash
+ end
end
- end
-
- # Helper to remove line breaks and whitespace between XML tags.
- def self.clean_xml(xml)
- xml = xml.gsub(/\n+/, "")
- xml = xml.gsub(/(>)\s*(<)/, '\1\2')
- end
- # Helper to convert "true" and "false" strings to boolean values.
- # Returns the original string in case it doesn't match "true" or "false".
- def self.string_to_bool?(string)
- return true if string == "true"
- return false if string == "false"
- string
- end
+ # Helper to remove line breaks and whitespace between XML tags.
+ def clean_xml(xml)
+ xml = xml.gsub(/\n+/, "")
+ xml = xml.gsub(/(>)\s*(<)/, '\1\2')
+ end
+ # Helper to convert "true" and "false" strings to boolean values.
+ # Returns the original string in case it doesn't match "true" or "false".
+ def string_to_bool?(string)
+ return true if string == "true"
+ return false if string == "false"
+ string
+ end
+
+ end
end
# Shortcut method for translating between XML documents and Hashes.
# Calls xml_to_hash in case the first parameter is of type String.
# Calls hash_to_xml in case the first parameter is of type Hash.
\ No newline at end of file