# Portions of this code were copied and modified from Ruby on Rails, released # under the MIT license, copyright (c) 2005-2009 David Heinemeier Hansson module Braintree module Xml module Generator # :nodoc: XML_TYPE_NAMES = { "Fixnum" => "integer", "Bignum" => "integer", "TrueClass" => "boolean", "FalseClass" => "boolean", "DateTime" => "datetime", "Time" => "datetime", } XML_FORMATTING_NAMES = { "BigDecimal" => "bigdecimal", "Symbol" => "symbol" }.merge(XML_TYPE_NAMES) XML_FORMATTING = { "symbol" => Proc.new { |symbol| symbol.to_s }, "datetime" => Proc.new { |time| time.xmlschema }, "bigdecimal" => Proc.new do |bigdecimal| str = bigdecimal.to_s('F') if str =~ /\.\d$/ str += "0" end str end } def self.hash_to_xml(hash) root, contents = hash.keys[0], hash.values[0] if contents.is_a?(String) builder = Builder::XmlMarkup.new builder.__send__(_xml_escape(root)) { |b| b.text! contents } else _convert_to_xml contents, :root => root end end def self._convert_to_xml(hash_to_convert, options = {}) raise ArgumentError, "need root" unless options[:root] options[:indent] ||= 2 options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent]) options[:builder].instruct! unless options.delete(:skip_instruct) root = _xml_escape(options[:root]) options[:builder].__send__(:method_missing, root) do hash_to_convert.each do |key, value| case value when ::Hash _convert_to_xml(value, options.merge(:root => key, :skip_instruct => true)) when ::Array _array_to_xml(value, options.merge(:root => key, :skip_instruct => true)) else type_name = XML_TYPE_NAMES[value.class.name] attributes = ((value.nil? || type_name.nil?) ? {} : { :type => type_name }) if value.nil? attributes[:nil] = true end formatting_name = XML_FORMATTING_NAMES[value.class.name] options[:builder].tag!(_xml_escape(key), XML_FORMATTING[formatting_name] ? XML_FORMATTING[formatting_name].call(value) : value, attributes ) end end end end def self._array_to_xml(array, options = {}) raise "expected options[:root]" unless options[:root] raise "expected options[:builder]" unless options[:builder] options[:indent] ||= 2 root = options.delete(:root).to_s.tr("_", "-") if array.empty? options[:builder].tag!(root, :type => "array") else options[:builder].tag!(root, :type => "array") do array.each do |e| if e.is_a?(Hash) _convert_to_xml(e, options.merge(:root => "item", :skip_instruct => true)) else options[:builder].tag!("item", e) end end end end end def self._xml_escape(key) key.to_s.tr("_", "-").to_xs end end end end