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