lib/apricoteatsgorilla.rb in smacks-apricoteatsgorilla-0.1.2 vs lib/apricoteatsgorilla.rb in smacks-apricoteatsgorilla-0.2.0
- old
+ new
@@ -1,26 +1,45 @@
# -*- coding: utf-8 -*-
require 'rubygems'
require 'hpricot'
-# Apricot eats Gorilla converts SOAP response messages to Ruby hashes.
-# It's based on CobraVsMongoose but uses Hpricot instead of REXML and it
-# also doesn't follow the BadgerFish convention.
+# Apricot eats Gorilla translates between XML documents and Ruby hashes.
+# It's based on CobraVsMongoose but uses Hpricot instead of REXML to parse
+# XML and it also doesn't follow the BadgerFish convention.
+#
+# It's initial purpose was to convert SOAP response messages to Ruby hashes,
+# but it quickly evolved into a more general translation tool.
class ApricotEatsGorilla
- # Converts a given SOAP response message into a Ruby Hash.
- def self.soap_response_to_hash(xml, root_node = nil)
- xml = prepare_xml(xml)
- root_node ||= "//return"
+ # Converts XML into a Ruby 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 is not 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)
- xml_node_to_hash doc.at(root_node)
+ root = root_node ? doc.at(root_node) : doc.root
+ xml_node_to_hash(root)
end
+ # Converts a Ruby 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
+
private
- # Converts XML nodes into a Ruby Hash. Recursively calls itself to grab
- # nested XML nodes and values.
+ # Converts XML nodes into a Ruby Hash.
def self.xml_node_to_hash(node)
this_node = {}
node.each_child do |child|
if child.children.nil?
@@ -43,11 +62,46 @@
end
this_node
end
+ # Converts a Ruby Hash to XML.
+ def self.nested_data_to_xml(name, item)
+ children = {}
+ 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
+ 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} />"
+ end
+ end
+
# Helper to remove line breaks and whitespace between XML nodes.
- def self.prepare_xml(xml)
+ 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.