class ActiveSupport::XMLConverter
:nodoc:
def become_array?(value)
def become_array?(value) value["type"] == "array" end
def become_content?(value)
def become_content?(value) value["type"] == "file" || (value["__content__"] && (value.keys.size == 1 || value["__content__"].present?)) end
def become_empty_string?(value)
def become_empty_string?(value) # { "string" => true } # No tests fail when the second term is removed. value["type"] == "string" && value["nil"] != "true" end
def become_hash?(value)
def become_hash?(value) !nothing?(value) && !garbage?(value) end
def deep_to_h(value)
def deep_to_h(value) case value when Hash process_hash(value) when Array process_array(value) when String value else raise "can't typecast #{value.class.name} - #{value.inspect}" end end
def garbage?(value)
def garbage?(value) # If the type is the only element which makes it then # this still makes the value nil, except if type is # an XML node(where type['value'] is a Hash) value["type"] && !value["type"].is_a?(::Hash) && value.size == 1 end
def initialize(xml, disallowed_types = nil)
def initialize(xml, disallowed_types = nil) @xml = normalize_keys(XmlMini.parse(xml)) @disallowed_types = disallowed_types || DISALLOWED_TYPES end
def normalize_keys(params)
def normalize_keys(params) case params when Hash Hash[params.map { |k, v| [k.to_s.tr("-", "_"), normalize_keys(v)] } ] when Array params.map { |v| normalize_keys(v) } else params end end
def nothing?(value)
def nothing?(value) # blank or nil parsed values are represented by nil value.blank? || value["nil"] == "true" end
def process_array(value)
def process_array(value) value.map! { |i| deep_to_h(i) } value.length > 1 ? value : value.first end
def process_content(value)
def process_content(value) content = value["__content__"] if parser = ActiveSupport::XmlMini::PARSING[value["type"]] parser.arity == 1 ? parser.call(content) : parser.call(content, value) else content end end
def process_hash(value)
def process_hash(value) if value.include?("type") && !value["type"].is_a?(Hash) && @disallowed_types.include?(value["type"]) raise DisallowedType, value["type"] end if become_array?(value) _, entries = Array.wrap(value.detect { |k, v| not v.is_a?(String) }) if entries.nil? || value["__content__"].try(:empty?) [] else case entries when Array entries.collect { |v| deep_to_h(v) } when Hash [deep_to_h(entries)] else raise "can't typecast #{entries.inspect}" end end elsif become_content?(value) process_content(value) elsif become_empty_string?(value) "" elsif become_hash?(value) xml_value = value.transform_values { |v| deep_to_h(v) } # Turn { files: { file: #<StringIO> } } into { files: #<StringIO> } so it is compatible with # how multipart uploaded files from HTML appear xml_value["file"].is_a?(StringIO) ? xml_value["file"] : xml_value end end
def to_h
def to_h deep_to_h(@xml) end