Use XSD-compatible type names for Hash#to_xml and make the converters extendable #8047 [Tim Pope]

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@6546 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
上级 eb7a3045
*SVN*
* Use XSD-compatible type names for Hash#to_xml and make the converters extendable #8047 [Tim Pope]
* Added yielding of builder in Hash#to_xml [DHH]
* Hash#with_indifferent_access now also converts hashes kept in arrays to indifferent access (makes it easier to treat HTML and XML parameters the same) [DHH]
......
require 'date'
require 'xml_simple'
require 'cgi'
require 'base64'
# Extensions needed for Hash#to_query
class Object
......@@ -26,21 +27,40 @@ module Conversions
XML_TYPE_NAMES = {
"Fixnum" => "integer",
"Bignum" => "integer",
"BigDecimal" => "numeric",
"BigDecimal" => "decimal",
"Float" => "float",
"Date" => "date",
"DateTime" => "datetime",
"Time" => "datetime",
"TrueClass" => "boolean",
"FalseClass" => "boolean"
} unless defined? XML_TYPE_NAMES
} unless defined?(XML_TYPE_NAMES)
XML_FORMATTING = {
"date" => Proc.new { |date| date.to_s(:db) },
"datetime" => Proc.new { |time| time.xmlschema },
"binary" => Proc.new { |binary| Base64.encode64(binary) },
"yaml" => Proc.new { |yaml| yaml.to_yaml }
} unless defined? XML_FORMATTING
} unless defined?(XML_FORMATTING)
unless defined?(XML_PARSING)
XML_PARSING = {
"date" => Proc.new { |date| ::Date.parse(date) },
"datetime" => Proc.new { |time| ::Time.parse(time).utc },
"integer" => Proc.new { |integer| integer.to_i },
"float" => Proc.new { |float| float.to_f },
"decimal" => Proc.new { |number| BigDecimal(number) },
"boolean" => Proc.new { |boolean| %w(1 true).include?(boolean.strip) },
"string" => Proc.new { |string| string.to_s },
"yaml" => Proc.new { |yaml| YAML::load(yaml) rescue yaml },
"base64Binary" => Proc.new { |bin| Base64.decode64(bin) }
}
XML_PARSING.update(
"double" => XML_PARSING["float"],
"dateTime" => XML_PARSING["datetime"]
)
end
def self.included(klass)
klass.extend(ClassMethods)
......@@ -127,14 +147,13 @@ def typecast_xml_value(value)
when "Hash"
if value.has_key?("__content__")
content = translate_xml_entities(value["__content__"])
case value["type"]
when "integer" then content.to_i
when "boolean" then content.strip == "true"
when "datetime" then ::Time.parse(content).utc
when "date" then ::Date.parse(content)
when "yaml" then YAML::load(content) rescue content
else content
if XML_PARSING[value["type"]]
XML_PARSING[value["type"]].call(content)
else
content
end
elsif value['type'] == 'string' && value['nil'] != 'true'
""
else
(value.blank? || value['type'] || value['nil'] == 'true') ? nil : value.inject({}) do |h,(k,v)|
h[k] = typecast_xml_value(v)
......@@ -180,4 +199,4 @@ def undasherize_keys(params)
end
end
end
end
end
\ No newline at end of file
......@@ -126,7 +126,7 @@ def test_to_xml
assert xml.include?(%(<age-in-millis type="integer">820497600000</age-in-millis>)), xml
assert xml.include?(%(<name>David</name>)), xml
assert xml.include?(%(<age type="integer">31</age>)), xml
assert xml.include?(%(<age-in-millis type="numeric">1.0</age-in-millis>)), xml
assert xml.include?(%(<age-in-millis type="decimal">1.0</age-in-millis>)), xml
assert xml.include?(%(<name>Jason</name>)), xml
end
......
......@@ -395,6 +395,8 @@ def test_single_record_from_xml
<content type="yaml">--- \n1: should be an integer\n:message: Have a nice day\narray: \n- should-have-dashes: true\n should_have_underscores: true\n</content>
<author-email-address>david@loudthinking.com</author-email-address>
<parent-id></parent-id>
<ad-revenue type="decimal">1.5</ad-revenue>
<optimum-viewing-angle type="float">135</optimum-viewing-angle>
</topic>
EOT
......@@ -409,7 +411,9 @@ def test_single_record_from_xml
:viewed_at => Time.utc(2003, 7, 16, 9, 28),
:content => { :message => "Have a nice day", 1 => "should be an integer", "array" => [{ "should-have-dashes" => true, "should_have_underscores" => true }] },
:author_email_address => "david@loudthinking.com",
:parent_id => nil
:parent_id => nil,
:ad_revenue => BigDecimal("1.50"),
:optimum_viewing_angle => 135.0
}.stringify_keys
assert_equal expected_topic_hash, Hash.from_xml(topic_xml)["topic"]
......@@ -513,6 +517,30 @@ def test_single_record_from_xml_with_attributes_other_than_type
assert_equal expected_topic_hash, Hash.from_xml(topic_xml)["rsp"]["photos"]["photo"]
end
def test_xsd_like_types_from_xml
bacon_xml = <<-EOT
<bacon>
<weight type="double">0.5</weight>
<price type="decimal">12.50</price>
<chunky type="boolean"> 1 </chunky>
<expires-at type="dateTime">2007-12-25T12:34:56+0000</expires-at>
<notes type="string"></notes>
<illustration type="base64Binary">YmFiZS5wbmc=</illustration>
</bacon>
EOT
expected_bacon_hash = {
:weight => 0.5,
:chunky => true,
:price => BigDecimal("12.50"),
:expires_at => Time.utc(2007,12,25,12,34,56),
:notes => "",
:illustration => "babe.png"
}.stringify_keys
assert_equal expected_bacon_hash, Hash.from_xml(bacon_xml)["bacon"]
end
def test_should_use_default_value_for_unknown_key
hash_wia = HashWithIndifferentAccess.new(3)
assert_equal 3, hash_wia[:new_key]
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册