require "builder"
require "gyoku"
require "rexml/document"
require "nori"
require "savon/soap"
Nori.configure do |config|
config.strip_namespaces = true
config.convert_tags_to { |tag| tag.snakecase.to_sym }
end
module Savon
module SOAP
class XML
SCHEMA_TYPES = {
"xmlns:xsd" => "http://www.w3.org/2001/XMLSchema",
"xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance"
}
def initialize(config)
self.config = config
end
attr_accessor :config
attr_accessor :input
attr_accessor :endpoint
def version=(version)
raise ArgumentError, "Invalid SOAP version: #{version}" unless SOAP::VERSIONS.include? version
@version = version
end
def version
@version ||= config.soap_version
end
attr_writer :header
def
@header ||= config..nil? ? {} : config.
end
attr_writer :env_namespace
def env_namespace
@env_namespace ||= config.env_namespace.nil? ? :env : config.env_namespace
end
attr_writer :namespaces
def namespaces
@namespaces ||= begin
key = ["xmlns"]
key << env_namespace if env_namespace && env_namespace != ""
{ key.join(":") => SOAP::NAMESPACE[version] }
end
end
def namespace_by_uri(uri)
namespaces.each do |candidate_identifier, candidate_uri|
return candidate_identifier.gsub(/^xmlns:/, '') if candidate_uri == uri
end
nil
end
def used_namespaces
@used_namespaces ||= {}
end
def use_namespace(path, uri)
@internal_namespace_count ||= 0
unless identifier = namespace_by_uri(uri)
identifier = "ins#{@internal_namespace_count}"
namespaces["xmlns:#{identifier}"] = uri
@internal_namespace_count += 1
end
used_namespaces[path] = identifier
end
def types
@types ||= {}
end
attr_writer :namespace_identifier
def namespace_identifier
@namespace_identifier ||= :wsdl
end
attr_accessor :element_form_default
attr_accessor :namespace
attr_accessor :wsse
def signature?
wsse.respond_to?(:signature?) && wsse.signature?
end
def encoding
@encoding ||= "UTF-8"
end
attr_writer :encoding
def body
@body = yield builder(nil) if block_given?
@body
end
attr_writer :body
def xml(directive_tag = :xml, attrs = {})
@xml = yield builder(directive_tag, attrs) if block_given?
end
attr_writer :xml
def to_xml(clear_cache = false)
if clear_cache
@xml = nil
@header_for_xml = nil
end
@xml ||= tag(builder, :Envelope, complete_namespaces) do |xml|
tag(xml, :Header) { xml << } unless .empty?
body_attributes = (signature? ? wsse.signature.body_attributes : {})
if input.nil?
tag(xml, :Body, body_attributes)
else
tag(xml, :Body, body_attributes) { xml.tag!(*add_namespace_to_input) { xml << body_to_xml } }
end
end
end
private
def builder(directive_tag = :xml, attrs = { :encoding => encoding })
builder = Builder::XmlMarkup.new
builder.instruct!(directive_tag, attrs) if directive_tag
builder
end
def tag(xml, name, namespaces = {}, &block)
if env_namespace && env_namespace != ""
xml.tag! env_namespace, name, namespaces, &block
else
xml.tag! name, namespaces, &block
end
end
def complete_namespaces
defaults = SCHEMA_TYPES.dup
defaults["xmlns:#{namespace_identifier}"] = namespace if namespace
defaults.merge namespaces
end
def
@header_for_xml ||= (Hash === ? Gyoku.xml() : ) +
end
def
wsse.respond_to?(:to_xml) ? wsse.to_xml : ""
end
def body_to_xml
return body.to_s unless body.kind_of? Hash
body_to_xml = element_form_default == :qualified ? add_namespaces_to_body(body) : body
Gyoku.xml body_to_xml, :element_form_default => element_form_default, :namespace => namespace_identifier
end
def add_namespaces_to_body(hash, path = [input[1].to_s])
return unless hash
return hash if hash.kind_of?(Array)
return hash.to_s unless hash.kind_of? Hash
hash.inject({}) do |newhash, (key, value)|
camelcased_key = Gyoku::XMLKey.create(key)
newpath = path + [camelcased_key]
if used_namespaces[newpath]
newhash.merge(
"#{used_namespaces[newpath]}:#{camelcased_key}" =>
add_namespaces_to_body(value, types[newpath] ? [types[newpath]] : newpath)
)
else
add_namespaces_to_values(value, path) if key == :order!
newhash.merge(key => value)
end
end
end
def add_namespace_to_input
return input.compact unless used_namespaces[[input[1].to_s]]
[used_namespaces[[input[1].to_s]], input[1], input[2]]
end
def add_namespaces_to_values(values, path)
values.collect! { |value|
camelcased_value = Gyoku::XMLKey.create(value)
namespace_path = path + [camelcased_value.to_s]
namespace = used_namespaces[namespace_path]
"#{namespace.blank? ? '' : namespace + ":"}#{camelcased_value}"
}
end
end
end
end