require "builder"
require "crack/xml"
require "savon/soap"
require "savon/core_ext/hash"
module Savon
module SOAP
# = Savon::SOAP::XML
#
# Represents the SOAP request XML. Contains various global and per request/instance settings
# like the SOAP version, header, body and namespaces.
class XML
# XML Schema Type namespaces.
SchemaTypes = {
"xmlns:xsd" => "http://www.w3.org/2001/XMLSchema",
"xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance"
}
def self.to_hash(xml)
(Crack::XML.parse(xml) rescue {}).find_soap_body
end
# Accepts an +endpoint+, an +input+ tag and a SOAP +body+.
def initialize(endpoint = nil, input = nil, body = nil)
self.endpoint = endpoint if endpoint
self.input = input if input
self.body = body if body
end
# Accessor for the SOAP +input+ tag.
attr_accessor :input
# Accessor for the SOAP +endpoint+.
attr_accessor :endpoint
# Sets the SOAP +version+.
def version=(version)
raise ArgumentError, "Invalid SOAP version: #{version}" unless SOAP::Versions.include? version
@version = version
end
# Returns the SOAP +version+. Defaults to Savon.soap_version.
def version
@version ||= Savon.soap_version
end
# Sets the SOAP +header+ Hash.
attr_writer :header
# Returns the SOAP +header+. Defaults to an empty Hash.
def header
@header ||= {}
end
# Sets the +namespaces+ Hash.
attr_writer :namespaces
# Returns the +namespaces+. Defaults to a Hash containing the xmlns:env namespace.
def namespaces
@namespaces ||= { "xmlns:env" => SOAP::Namespace[version] }
end
# Sets the default namespace identifier.
attr_writer :namespace_identifier
# Returns the default namespace identifier.
def namespace_identifier
@namespace_identifier ||= :wsdl
end
# Accessor for the default namespace URI.
attr_accessor :namespace
# Accessor for the Savon::WSSE object.
attr_accessor :wsse
# Accessor for the SOAP +body+. Expected to be a Hash that can be translated to XML via Hash.to_soap_xml
# or any other Object responding to to_s.
attr_accessor :body
# Accepts a +block+ and yields a Builder::XmlMarkup object to let you create custom XML.
def xml
@xml = yield builder if block_given?
end
# Accepts an XML String and lets you specify a completely custom request body.
attr_writer :xml
# Returns the XML for a SOAP request.
def to_xml
@xml ||= builder.env :Envelope, complete_namespaces do |xml|
xml.env(:Header) { xml << header_for_xml } unless header_for_xml.empty?
xml.env(:Body) { xml.tag!(*input) { xml << body_to_xml } }
end
end
private
# Returns a new Builder::XmlMarkup object.
def builder
builder = Builder::XmlMarkup.new
builder.instruct!
builder
end
# Returns the complete Hash of namespaces.
def complete_namespaces
defaults = SchemaTypes.dup
defaults["xmlns:#{namespace_identifier}"] = namespace if namespace
defaults.merge namespaces
end
# Returns the SOAP header as an XML String.
def header_for_xml
@header_for_xml ||= header.to_soap_xml + wsse_header
end
# Returns the WSSE header or an empty String in case WSSE was not set.
def wsse_header
wsse.respond_to?(:to_xml) ? wsse.to_xml : ""
end
# Returns the SOAP body as an XML String.
def body_to_xml
body.respond_to?(:to_soap_xml) ? body.to_soap_xml : body.to_s
end
end
end
end