Permalink
Browse files

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
  • Loading branch information...
1 parent eb7a304 commit 178880ef7c4953470453c258abccf99b98c92bb9 @dhh dhh committed Apr 21, 2007
@@ -1,5 +1,7 @@
*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]
@@ -1,6 +1,7 @@
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
@@ -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]

0 comments on commit 178880e

Please sign in to comment.