aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport')
-rw-r--r--activesupport/CHANGELOG2
-rw-r--r--activesupport/lib/active_support/core_ext/array/conversions.rb12
-rw-r--r--activesupport/lib/active_support/core_ext/hash/conversions.rb57
-rw-r--r--activesupport/test/core_ext/array_ext_test.rb20
-rw-r--r--activesupport/test/core_ext/hash_ext_test.rb39
5 files changed, 110 insertions, 20 deletions
diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG
index c6fb0f82b5..a9fd1a902e 100644
--- a/activesupport/CHANGELOG
+++ b/activesupport/CHANGELOG
@@ -1,5 +1,7 @@
*SVN*
+* to_xml fixes, features, and speedup: introduce :dasherize option that converts updated_at to updated-at if true (the existing default); binary columns get encoding="base64" attribute; nil values get nil="true" attribute to distinguish empty values; add type information for float columns; allow arbitrarily deep :include; include SQL type information as the type attribute. #4989 [Blair Zajac <blair@orcaware.com>]
+
* Add OrderedHash#values. [Sam Stephenson]
* Added Array#to_s(:db) that'll produce a comma-separated list of ids [DHH]. Example:
diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb
index 35d010b164..2b42a55ab1 100644
--- a/activesupport/lib/active_support/core_ext/array/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/array/conversions.rb
@@ -52,12 +52,20 @@ module ActiveSupport #:nodoc:
options[:indent] ||= 2
options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
- root = options.delete(:root)
+ root = options.delete(:root).to_s
children = options.delete(:children)
+ if !options.has_key?(:dasherize) || options[:dasherize]
+ root = root.dasherize
+ end
+
options[:builder].instruct! unless options.delete(:skip_instruct)
- options[:builder].tag!(root.to_s.dasherize) { each { |e| e.to_xml(options.merge({ :skip_instruct => true, :root => children })) } }
+
+ opts = options.merge({ :skip_instruct => true, :root => children })
+
+ options[:builder].tag!(root) { each { |e| e.to_xml(opts) } }
end
+
end
end
end
diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb
index d556bab09b..be0012ad45 100644
--- a/activesupport/lib/active_support/core_ext/hash/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb
@@ -3,40 +3,69 @@ module ActiveSupport #:nodoc:
module Hash #:nodoc:
module Conversions
XML_TYPE_NAMES = {
- "Fixnum" => "integer",
- "Date" => "date",
- "Time" => "datetime",
- "TrueClass" => "boolean",
- "FalseClass" => "boolean"
+ ::Fixnum => "integer",
+ ::Float => "float",
+ ::Date => "date",
+ ::DateTime => "datetime",
+ ::Time => "datetime",
+ ::TrueClass => "boolean",
+ ::FalseClass => "boolean"
}
-
+
XML_FORMATTING = {
"date" => Proc.new { |date| date.to_s(:db) },
"datetime" => Proc.new { |time| time.xmlschema }
}
-
+
def to_xml(options = {})
options[:indent] ||= 2
- options.reverse_merge!({ :builder => Builder::XmlMarkup.new(:indent => options[:indent]), :root => "hash" })
+ options.reverse_merge!({ :builder => Builder::XmlMarkup.new(:indent => options[:indent]),
+ :root => "hash" })
options[:builder].instruct! unless options.delete(:skip_instruct)
+ dasherize = !options.has_key?(:dasherize) || options[:dasherize]
+ root = dasherize ? options[:root].to_s.dasherize : options[:root].to_s
- options[:builder].__send__(options[:root].to_s.dasherize) do
+ options[:builder].__send__(root) do
each do |key, value|
case value
when ::Hash
value.to_xml(options.merge({ :root => key, :skip_instruct => true }))
when ::Array
value.to_xml(options.merge({ :root => key, :children => key.to_s.singularize, :skip_instruct => true}))
+ when ::Method, ::Proc
+ # If the Method or Proc takes two arguments, then
+ # pass the suggested child element name. This is
+ # used if the Method or Proc will be operating over
+ # multiple records and needs to create an containing
+ # element that will contain the objects being
+ # serialized.
+ if 1 == value.arity
+ value.call(options.merge({ :root => key, :skip_instruct => true }))
+ else
+ value.call(options.merge({ :root => key, :skip_instruct => true }), key.to_s.singularize)
+ end
else
- type_name = XML_TYPE_NAMES[value.class.to_s]
+ if value.respond_to?(:to_xml)
+ value.to_xml(options.merge({ :root => key, :skip_instruct => true }))
+ else
+ type_name = XML_TYPE_NAMES[value.class]
+
+ key = dasherize ? key.to_s.dasherize : key.to_s
- options[:builder].tag!(key.to_s.dasherize,
- XML_FORMATTING[type_name] ? XML_FORMATTING[type_name].call(value) : value,
- options[:skip_types] || value.nil? || type_name.nil? ? { } : { :type => type_name }
- )
+ attributes = options[:skip_types] || value.nil? || type_name.nil? ? { } : { :type => type_name }
+ if value.nil?
+ attributes[:nil] = true
+ end
+
+ options[:builder].tag!(key,
+ XML_FORMATTING[type_name] ? XML_FORMATTING[type_name].call(value) : value,
+ attributes
+ )
+ end
end
end
end
+
end
end
end
diff --git a/activesupport/test/core_ext/array_ext_test.rb b/activesupport/test/core_ext/array_ext_test.rb
index 4cef0120d6..492e59bf21 100644
--- a/activesupport/test/core_ext/array_ext_test.rb
+++ b/activesupport/test/core_ext/array_ext_test.rb
@@ -136,4 +136,24 @@ class ArrayToXmlTests < Test::Unit::TestCase
assert xml.include?(%(<street-address>Evergreen</street-address>))
assert xml.include?(%(<name>Jason</name>))
end
+
+ def test_to_xml_with_dasherize_false
+ xml = [
+ { :name => "David", :street_address => "Paulina" }, { :name => "Jason", :street_address => "Evergreen" }
+ ].to_xml(:skip_instruct => true, :skip_types => true, :indent => 0, :dasherize => false)
+
+ assert_equal "<records><record>", xml.first(17)
+ assert xml.include?(%(<street_address>Paulina</street_address>))
+ assert xml.include?(%(<street_address>Evergreen</street_address>))
+ end
+
+ def test_to_xml_with_dasherize_true
+ xml = [
+ { :name => "David", :street_address => "Paulina" }, { :name => "Jason", :street_address => "Evergreen" }
+ ].to_xml(:skip_instruct => true, :skip_types => true, :indent => 0, :dasherize => true)
+
+ assert_equal "<records><record>", xml.first(17)
+ assert xml.include?(%(<street-address>Paulina</street-address>))
+ assert xml.include?(%(<street-address>Evergreen</street-address>))
+ end
end
diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb
index a12299d32c..f8ed54c881 100644
--- a/activesupport/test/core_ext/hash_ext_test.rb
+++ b/activesupport/test/core_ext/hash_ext_test.rb
@@ -3,7 +3,6 @@ require File.dirname(__FILE__) + '/../../lib/active_support'
class HashExtTest < Test::Unit::TestCase
def setup
-
@strings = { 'a' => 1, 'b' => 2 }
@symbols = { :a => 1, :b => 2 }
@mixed = { :a => 1, 'b' => 2 }
@@ -191,6 +190,17 @@ class HashExtTest < Test::Unit::TestCase
end
end
+class IWriteMyOwnXML
+ def to_xml(options = {})
+ options[:indent] ||= 2
+ xml = options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
+ xml.instruct! unless options[:skip_instruct]
+ xml.level_one do
+ xml.tag!(:second_level, 'content')
+ end
+ end
+end
+
class HashToXmlTest < Test::Unit::TestCase
def setup
@xml_options = { :root => :person, :skip_instruct => true, :indent => 0 }
@@ -203,6 +213,20 @@ class HashToXmlTest < Test::Unit::TestCase
assert xml.include?(%(<name>David</name>))
end
+ def test_one_level_dasherize_false
+ xml = { :name => "David", :street_name => "Paulina" }.to_xml(@xml_options.merge(:dasherize => false))
+ assert_equal "<person>", xml.first(8)
+ assert xml.include?(%(<street_name>Paulina</street_name>))
+ assert xml.include?(%(<name>David</name>))
+ end
+
+ def test_one_level_dasherize_true
+ xml = { :name => "David", :street_name => "Paulina" }.to_xml(@xml_options.merge(:dasherize => true))
+ assert_equal "<person>", xml.first(8)
+ assert xml.include?(%(<street-name>Paulina</street-name>))
+ assert xml.include?(%(<name>David</name>))
+ end
+
def test_one_level_with_types
xml = { :name => "David", :street => "Paulina", :age => 26, :moved_on => Date.new(2005, 11, 15) }.to_xml(@xml_options)
assert_equal "<person>", xml.first(8)
@@ -217,7 +241,7 @@ class HashToXmlTest < Test::Unit::TestCase
assert_equal "<person>", xml.first(8)
assert xml.include?(%(<street>Paulina</street>))
assert xml.include?(%(<name>David</name>))
- assert xml.include?(%(<age></age>))
+ assert xml.include?(%(<age nil="true"></age>))
end
def test_one_level_with_skipping_types
@@ -225,7 +249,7 @@ class HashToXmlTest < Test::Unit::TestCase
assert_equal "<person>", xml.first(8)
assert xml.include?(%(<street>Paulina</street>))
assert xml.include?(%(<name>David</name>))
- assert xml.include?(%(<age></age>))
+ assert xml.include?(%(<age nil="true"></age>))
end
def test_two_levels
@@ -234,6 +258,13 @@ class HashToXmlTest < Test::Unit::TestCase
assert xml.include?(%(<address><street>Paulina</street></address>))
assert xml.include?(%(<name>David</name>))
end
+
+ def test_two_levels_with_second_level_overriding_to_xml
+ xml = { :name => "David", :address => { :street => "Paulina" }, :child => IWriteMyOwnXML.new }.to_xml(@xml_options)
+ assert_equal "<person>", xml.first(8)
+ assert xml.include?(%(<address><street>Paulina</street></address>))
+ assert xml.include?(%(<level_one><second_level>content</second_level></level_one>))
+ end
def test_two_levels_with_array
xml = { :name => "David", :addresses => [{ :street => "Paulina" }, { :street => "Evergreen" }] }.to_xml(@xml_options)
@@ -243,10 +274,10 @@ class HashToXmlTest < Test::Unit::TestCase
assert xml.include?(%(<address><street>Evergreen</street></address>))
assert xml.include?(%(<name>David</name>))
end
-
def test_three_levels_with_array
xml = { :name => "David", :addresses => [{ :streets => [ { :name => "Paulina" }, { :name => "Paulina" } ] } ] }.to_xml(@xml_options)
assert xml.include?(%(<addresses><address><streets><street><name>))
end
+
end