lib/apricoteatsgorilla.rb in smacks-apricoteatsgorilla-0.2.5 vs lib/apricoteatsgorilla.rb in smacks-apricoteatsgorilla-0.2.6

- old
+ new

@@ -1,25 +1,23 @@ # -*- coding: utf-8 -*- require 'rubygems' require 'hpricot' -# Apricot eats Gorilla translates between XML documents and Hashes. -# It's based on CobraVsMongoose but uses Hpricot instead of REXML to parse -# XML and it also doesn't follow the BadgerFish convention. -# -# Its initial purpose was to convert SOAP response messages to Hashes, -# but it quickly evolved into a more general translation tool. +# Apricot eats Gorilla is a helper for working with XML and SOAP messages. +# It's based on CobraVsMongoose but without REXML and the BadgerFish convention. +# Also it offers some extras for working with SOAP messages. class ApricotEatsGorilla - class << self + + # Flag to enable optional sorting of Hash keys. Especially useful for + # comparing expected values with actual results while testing. attr_accessor :sort_keys # 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) @@ -27,82 +25,98 @@ root = root_node ? doc.at(root_node) : doc.root xml_node_to_hash(root) end # 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 + # Builds a SOAP envelope and includes the content from an expected block + # into the envelope body. Pass in a Hash of namespaces and their URI to + # set custom namespaces. + # + # <env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"> + # <env:Body> + # yield + # </env:Body> + # </env:Envelope> + def soap_envelope(namespaces = {}) + namespaces["env"] = "http://schemas.xmlsoap.org/soap/envelope/" + tag("env:Envelope", namespaces) do + tag("env:Body") do + yield if block_given? + end + end + end + + # Creates an XML tag. Expects a block for tag content. + # Defaults to an empty element tag in case no block was supplied. + def tag(name, attributes = {}) + return "<#{name} />" unless block_given? + + attr = opt_order(attributes).map { |k, v| sprintf(' xmlns:%s="%s"', k, v) }.to_s + body = (yield && !yield.empty?) ? yield : "" + "<#{name}#{attr}>" << body << "</#{name}>" + end + private # Converts XML into a Hash. def xml_node_to_hash(node) this_node = {} - node.each_child do |child| + # hpricot 0.6.1 returns an empty array, while 0.8 returns nil 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) + key, value = child.name, booleanize(child.children.first.inner_text) else key, value = child.name, xml_node_to_hash(child) end current = this_node[key] case current - when Array: + when Array this_node[key] << value when nil this_node[key] = value else this_node[key] = [current.dup, value] end end - this_node end # Converts a Hash to XML. def nested_data_to_xml(name, item) case item when String - make_tag(name) { item } + tag(name) { item } when Array item.map { |subitem| nested_data_to_xml(name, subitem) }.join when Hash - make_tag(name) do + tag(name) do opt_order(item).map { |tag, value| case value when String - make_tag(tag) { value } + 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 make_tag(name) - body = yield - if body && !body.empty? - "<#{name}>" << body << "</#{name}>" - else - "<#{name} />" - end - end - + # Returns a sorted version of a given Hash in case :sort_keys is enabled. def opt_order(hash) if sort_keys hash.sort_by{ |kv| kv.first } else hash @@ -115,10 +129,10 @@ 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) + def booleanize(string) return true if string == "true" return false if string == "false" string end \ No newline at end of file