Skip to content

Instantly share code, notes, and snippets.

@ernesto-jimenez
Created November 10, 2008 01:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ernesto-jimenez/23400 to your computer and use it in GitHub Desktop.
Save ernesto-jimenez/23400 to your computer and use it in GitHub Desktop.
module ActiveSupport #:nodoc:
module CoreExtensions #:nodoc:
module Hash #:nodoc:
module Conversions
module ClassMethods
def typecast_xml_value(value)
case value.class.to_s
when 'Hash'
if value['type'] == 'array'
# If it has more than 2 keys tag has attributes
if value.keys.size != 2
value.delete('type')
typecast_xml_value(value)
else
child_key, entries = value.detect { |k,v| k != 'type' } # child_key is throwaway
if entries.nil? || (c = value['__content__'] && c.blank?)
[]
else
case entries.class.to_s # something weird with classes not matching here. maybe singleton methods breaking is_a?
when "Array"
entries.collect { |v| typecast_xml_value(v) }
when "Hash"
[typecast_xml_value(entries)]
else
raise "can't typecast #{entries.inspect}"
end
end
end
elsif value.has_key?("__content__")
content = value["__content__"]
if parser = XML_PARSING[value["type"]]
if parser.arity == 2
XML_PARSING[value["type"]].call(content, value)
else
XML_PARSING[value["type"]].call(content)
end
else
content
end
elsif value['type'] == 'string' && value['nil'] != 'true'
""
# blank or nil parsed values are represented by nil
elsif value.blank? || value['nil'] == 'true'
nil
# If the type is the only element which makes it then
# this still makes the value nil, except if type is
# a XML node(where type['value'] is a Hash)
elsif value['type'] && value.size == 1 && !value['type'].is_a?(::Hash)
nil
else
xml_value = value.inject({}) do |h,(k,v)|
h[k] = typecast_xml_value(v)
h
end
# 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
when 'Array'
value.map! { |i| typecast_xml_value(i) }
case value.length
when 0 then nil
when 1 then value.first
else value
end
when 'String'
value
else
raise "can't typecast #{value.class.name} - #{value.inspect}"
end
end
end
end
end
end
end
require 'active_support'
# Works like before
Hash.from_xml('
<projects type="array">
<project>
<name>project 1</name>
</project>
<project>
<name>project 2</name>
</project>
<project>
<name>project 3</name>
</project>
</projects>
')
# => {"projects"=>[{"name"=>"project 1"}, {"name"=>"project 2"}, {"name"=>"project 3"}]}
# Works like before
Hash.from_xml('
<projects page="1" per_page="3">
<project>
<name>project 1</name>
</project>
<project>
<name>project 2</name>
</project>
<project>
<name>project 3</name>
</project>
</projects>
')
# => {"projects"=>{"project"=>[{"name"=>"project 1"}, {"name"=>"project 2"}, {"name"=>"project 3"}], "per_page"=>"3", "page"=>"1"}}
# Modified behaviour: type="array" is ignored when other attributes are provided
# This behaviour doesn't break previous tests
Hash.from_xml('
<projects page="1" per_page="3" type="array">
<project>
<name>project 1</name>
</project>
<project>
<name>project 2</name>
</project>
<project>
<name>project 3</name>
</project>
</projects>
')
# => {"projects"=>{"project"=>[{"name"=>"project 1"}, {"name"=>"project 2"}, {"name"=>"project 3"}], "per_page"=>"3", "page"=>"1"}}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment