diff options
-rw-r--r-- | activesupport/CHANGELOG | 2 | ||||
-rw-r--r-- | activesupport/lib/active_support/core_ext/hash/conversions.rb | 41 | ||||
-rw-r--r-- | activesupport/test/core_ext/array_ext_test.rb | 2 | ||||
-rw-r--r-- | activesupport/test/core_ext/hash_ext_test.rb | 30 |
4 files changed, 62 insertions, 13 deletions
diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index 58bfefe90d..bfac524d95 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -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] diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb index 4e076dce6e..4fb3cbe4d3 100644 --- a/activesupport/lib/active_support/core_ext/hash/conversions.rb +++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb @@ -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 ActiveSupport #:nodoc: 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 @@ module ActiveSupport #:nodoc: 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 @@ module ActiveSupport #:nodoc: end end end -end +end
\ No newline at end of file diff --git a/activesupport/test/core_ext/array_ext_test.rb b/activesupport/test/core_ext/array_ext_test.rb index 5a273b83d6..26bc232b81 100644 --- a/activesupport/test/core_ext/array_ext_test.rb +++ b/activesupport/test/core_ext/array_ext_test.rb @@ -126,7 +126,7 @@ class ArrayToXmlTests < Test::Unit::TestCase 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 diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb index 813e23af5f..ba121cc352 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -395,6 +395,8 @@ class HashToXmlTest < Test::Unit::TestCase <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 @@ class HashToXmlTest < Test::Unit::TestCase :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 @@ class HashToXmlTest < Test::Unit::TestCase 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] |