class Hash
# Error message for missing :@inorder elements.
InOrderMissing = "Missing elements in :@inorder %s"
# Error message for spurious :@inorder elements.
InOrderSpurious = "Spurious elements in :@inorder %s"
# Returns the values from the 'soap:Body' element or an empty Hash
# in case the node could not be found.
def find_soap_body
envelope = self[self.keys.first] || {}
body_key = envelope.keys.find { |key| /.+:Body/ =~ key } rescue nil
body_key ? envelope[body_key].map_soap_response : {}
end
# Returns the Hash translated into SOAP request compatible XML.
#
# To control the order of output, add a key of :@inorder with
# the value being an Array listing keys in order.
#
# === Examples
#
# { :find_user => { :id => 666 } }.to_soap_xml
# => "666"
#
# { :find_user => { :name => "Lucy", :id => 666, :@inorder => [:id, :name] } }.to_soap_xml
# => "666Lucy"
def to_soap_xml
@soap_xml = Builder::XmlMarkup.new
inorder(self).each { |key| nested_data_to_soap_xml key, self[key] }
@soap_xml.target!
end
# Maps keys and values of a Hash created from SOAP response XML to
# more convenient Ruby Objects.
def map_soap_response
inject({}) do |hash, (key, value)|
key = key.strip_namespace.snakecase.to_sym
value = case value
when Hash then value["xsi:nil"] ? nil : value.map_soap_response
when Array then value.map { |a_value| a_value.map_soap_response rescue a_value }
when String then value.map_soap_response
end
hash.merge key => value
end
end
private
# Expects a Hash +key+ and +value+ and recursively creates an XML structure
# representing the Hash content.
def nested_data_to_soap_xml(key, value)
case value
when Array
value.map { |subitem| nested_data_to_soap_xml key, subitem }
when Hash
@soap_xml.tag!(key.to_soap_key) do
inorder(value).each { |subkey| nested_data_to_soap_xml subkey, value[subkey] }
end
else
@soap_xml.tag!(key.to_soap_key) { @soap_xml << value.to_soap_value }
end
end
# Takes a +hash+, removes the :@inorder marker and returns its keys.
# Raises an error in case an :@inorder Array does not match the Hash keys.
def inorder(hash)
inorder = hash.delete :@inorder
hash_keys = hash.keys
inorder = hash_keys unless inorder.kind_of? Array
raise InOrderMissing % (hash_keys - inorder).inspect unless (hash_keys - inorder).empty?
raise InOrderSpurious % (inorder - hash_keys).inspect unless (inorder - hash_keys).empty?
inorder
end
end