diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb index cd4b56a2a0..65b0d368d8 100644 --- a/actionpack/test/template/asset_tag_helper_test.rb +++ b/actionpack/test/template/asset_tag_helper_test.rb @@ -364,4 +364,11 @@ class AssetTagHelperNonVhostTest < Test::Unit::TestCase ensure ActionController::Base.asset_host = nil end + + def test_asset_host_without_protocol_should_use_request_protocol_even_if_path_present + ActionController::Base.asset_host = 'a.example.com/files/go/here' + assert_equal 'gopher://a.example.com/files/go/here/collaboration/hieraki/images/xml.png', image_path('xml.png') + ensure + ActionController::Base.asset_host = nil + end end diff --git a/activerecord/lib/active_record/xml_serialization.rb b/activerecord/lib/active_record/xml_serialization.rb index 42fe46bca3..7e2d5dd01e 100644 --- a/activerecord/lib/active_record/xml_serialization.rb +++ b/activerecord/lib/active_record/xml_serialization.rb @@ -211,7 +211,13 @@ module ActiveRecord #:nodoc: builder.tag!(tag, :type => :array) else builder.tag!(tag, :type => :array) do - records.each { |r| r.to_xml(opts.merge(:root=>r.class.to_s.underscore)) } + association_name = association.to_s.singularize + records.each do |record| + record.to_xml opts.merge( + :root => association_name, + :type => (record.class.to_s.underscore == association_name ? nil : record.class.name) + ) + end end end when :has_one, :belongs_to @@ -247,6 +253,10 @@ module ActiveRecord #:nodoc: if options[:namespace] args << {:xmlns=>options[:namespace]} end + + if options[:type] + args << {:type=>options[:type]} + end builder.tag!(*args) do add_attributes diff --git a/activerecord/test/xml_serialization_test.rb b/activerecord/test/xml_serialization_test.rb index 807daddabb..f6e3d0dd9d 100644 --- a/activerecord/test/xml_serialization_test.rb +++ b/activerecord/test/xml_serialization_test.rb @@ -2,6 +2,7 @@ require 'abstract_unit' require 'fixtures/post' require 'fixtures/author' require 'fixtures/tagging' +require 'fixtures/comment' class Contact < ActiveRecord::Base # mock out self.columns so no pesky db is needed for these tests @@ -147,8 +148,8 @@ class DatabaseConnectedXmlSerializationTest < Test::Unit::TestCase def test_include_uses_association_name xml = authors(:david).to_xml :include=>:hello_posts, :indent => 0 assert_match %r{}, xml - assert_match %r{}, xml - assert_match %r{}, xml + assert_match %r{}, xml + assert_match %r{}, xml end def test_methods_are_called_on_object @@ -171,4 +172,18 @@ class DatabaseConnectedXmlSerializationTest < Test::Unit::TestCase assert_match %r{^ }, xml end + def test_should_has_many_array_elements_should_include_type_when_different_from_guessed_value + xml = authors(:david).to_xml :include=>:posts_with_comments, :indent => 2 + + assert Hash.from_xml(xml) + assert_match %r{^ }, xml + assert_match %r{^ }, xml + assert_match %r{^ }, xml + + types = Hash.from_xml(xml)['author']['posts_with_comments'].collect {|t| t['type'] } + assert types.include?('SpecialPost') + assert types.include?('Post') + assert types.include?('StiPost') + end + end \ No newline at end of file diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb index 929dd45e98..d8c6852e25 100644 --- a/activesupport/lib/active_support/core_ext/hash/conversions.rb +++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb @@ -167,7 +167,7 @@ module ActiveSupport #:nodoc: private def typecast_xml_value(value) case value.class.to_s - when "Hash" + when 'Hash' if value.has_key?("__content__") content = translate_xml_entities(value["__content__"]) if parser = XML_PARSING[value["type"]] @@ -195,31 +195,34 @@ module ActiveSupport #:nodoc: 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 + elsif value['type'] && value.size == 1 + nil else - xml_value = (value.blank? || value['type'] || value['nil'] == 'true') ? nil : value.inject({}) do |h,(k,v)| + xml_value = value.inject({}) do |h,(k,v)| h[k] = typecast_xml_value(v) h end # Turn { :files => { :file => # } into { :files => # } so it is compatible with # how multipart uploaded files from HTML appear - if xml_value.is_a?(Hash) && xml_value["file"].is_a?(StringIO) - xml_value["file"] - else - xml_value - end + xml_value["file"].is_a?(StringIO) ? xml_value["file"] : xml_value end - when "Array" + 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" + when 'String' value else - raise "can't typecast #{value.inspect}" + raise "can't typecast #{value.class.name} - #{value.inspect}" end end diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb index c2b7a7f1eb..17bfb3a92c 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -588,6 +588,23 @@ class HashToXmlTest < Test::Unit::TestCase assert_equal expected_bacon_hash, Hash.from_xml(bacon_xml)["bacon"] end + + def test_type_trickles_through_when_unknown + product_xml = <<-EOT + + 0.5 + image.gif + + + EOT + + expected_product_hash = { + :weight => 0.5, + :image => {'type' => 'ProductImage', 'filename' => 'image.gif' }, + }.stringify_keys + + assert_equal expected_product_hash, Hash.from_xml(product_xml)["product"] + end def test_should_use_default_value_for_unknown_key hash_wia = HashWithIndifferentAccess.new(3)