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.