require "builder"
require "crack/xml"
require "gyoku"
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 SOAP envelope namespace.
attr_writer :env_namespace
# Returns the SOAP envelope namespace. Defaults to :env.
def env_namespace
@env_namespace ||= :env
end
# Sets the +namespaces+ Hash.
attr_writer :namespaces
# Returns the +namespaces+. Defaults to a Hash containing the SOAP envelope namespace.
def namespaces
@namespaces ||= begin
key = env_namespace.blank? ? "xmlns" : "xmlns:#{env_namespace}"
{ key => SOAP::Namespace[version] }
end
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 Gyoku.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 ||= tag(builder, :Envelope, complete_namespaces) do |xml|
tag(xml, :Header) { xml << header_for_xml } unless header_for_xml.empty?
tag(xml, :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
# Expects a builder +xml+ instance, a tag +name+ and accepts optional +namespaces+
# and a block to create an XML tag.
def tag(xml, name, namespaces = {}, &block)
return xml.tag! name, namespaces, &block if env_namespace.blank?
xml.tag! env_namespace, name, namespaces, &block
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 ||= Gyoku.xml(header) + 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.kind_of?(Hash) ? Gyoku.xml(body) : body.to_s
end
end
end
end