Skip to content

Commit

Permalink
Added a :camelize option to ActiveRecord and Hash to_xml serializatio…
Browse files Browse the repository at this point in the history
…n and from_xml deserialization

Signed-off-by: Michael Koziarski <michael@koziarski.com>
  • Loading branch information
Bruce Krysiak authored and NZKoz committed Dec 10, 2008
1 parent 96b815d commit aa5cdb0
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 14 deletions.
23 changes: 16 additions & 7 deletions activerecord/lib/active_record/serializers/xml_serializer.rb
Expand Up @@ -23,11 +23,12 @@ module Serialization
# </topic>
#
# This behavior can be controlled with <tt>:only</tt>, <tt>:except</tt>,
# <tt>:skip_instruct</tt>, <tt>:skip_types</tt> and <tt>:dasherize</tt>.
# <tt>:skip_instruct</tt>, <tt>:skip_types</tt>, <tt>:dasherize</tt> and <tt>:camelize</tt> .
# The <tt>:only</tt> and <tt>:except</tt> options are the same as for the
# +attributes+ method. The default is to dasherize all column names, but you
# can disable this setting <tt>:dasherize</tt> to +false+. To not have the
# column type included in the XML output set <tt>:skip_types</tt> to +true+.
# can disable this setting <tt>:dasherize</tt> to +false+. Setting <tt>:camelize</tt>
# to +true+ will camelize all column names - this also overrides <tt>:dasherize</tt>.
# To not have the column type included in the XML output set <tt>:skip_types</tt> to +true+.
#
# For instance:
#
Expand Down Expand Up @@ -178,13 +179,22 @@ def builder

def root
root = (options[:root] || @record.class.to_s.underscore).to_s
dasherize? ? root.dasherize : root
reformat_name(root)
end

def dasherize?
!options.has_key?(:dasherize) || options[:dasherize]
end

def camelize?
options.has_key?(:camelize) && options[:camelize]
end

def reformat_name(name)
name = name.camelize if camelize?
dasherize? ? name.dasherize : name
end

def serializable_attributes
serializable_attribute_names.collect { |name| Attribute.new(name, @record) }
end
Expand Down Expand Up @@ -212,16 +222,15 @@ def add_procs

def add_tag(attribute)
builder.tag!(
dasherize? ? attribute.name.dasherize : attribute.name,
reformat_name(attribute.name),
attribute.value.to_s,
attribute.decorations(!options[:skip_types])
)
end

def add_associations(association, records, opts)
if records.is_a?(Enumerable)
tag = association.to_s
tag = tag.dasherize if dasherize?
tag = reformat_name(association.to_s)
if records.empty?
builder.tag!(tag, :type => :array)
else
Expand Down
7 changes: 7 additions & 0 deletions activerecord/test/cases/xml_serialization_test.rb
Expand Up @@ -31,6 +31,13 @@ def test_should_allow_undasherized_tags
assert_match %r{<created_at}, @xml
end

def test_should_allow_camelized_tags
@xml = Contact.new.to_xml :root => 'xml_contact', :camelize => true
assert_match %r{^<XmlContact>}, @xml
assert_match %r{</XmlContact>$}, @xml
assert_match %r{<CreatedAt}, @xml
end

def test_should_include_yielded_additions
@xml = Contact.new.to_xml do |xml|
xml.creator "David"
Expand Down
20 changes: 13 additions & 7 deletions activesupport/lib/active_support/core_ext/hash/conversions.rb
Expand Up @@ -94,8 +94,7 @@ def to_xml(options = {})
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
root = rename_key(options[:root].to_s, options)

options[:builder].__send__(:method_missing, root) do
each do |key, value|
Expand All @@ -122,7 +121,7 @@ def to_xml(options = {})
else
type_name = XML_TYPE_NAMES[value.class.name]

key = dasherize ? key.to_s.dasherize : key.to_s
key = rename_key(key.to_s, options)

attributes = options[:skip_types] || value.nil? || type_name.nil? ? { } : { :type => type_name }
if value.nil?
Expand All @@ -142,9 +141,16 @@ def to_xml(options = {})

end

def rename_key(key, options = {})
camelize = options.has_key?(:camelize) && options[:camelize]
dasherize = !options.has_key?(:dasherize) || options[:dasherize]
key = key.camelize if camelize
dasherize ? key.dasherize : key
end

module ClassMethods
def from_xml(xml)
typecast_xml_value(undasherize_keys(XmlMini.parse(xml)))
typecast_xml_value(unrename_keys(XmlMini.parse(xml)))
end

private
Expand Down Expand Up @@ -210,15 +216,15 @@ def typecast_xml_value(value)
end
end

def undasherize_keys(params)
def unrename_keys(params)
case params.class.to_s
when "Hash"
params.inject({}) do |h,(k,v)|
h[k.to_s.tr("-", "_")] = undasherize_keys(v)
h[k.to_s.underscore.tr("-", "_")] = unrename_keys(v)
h
end
when "Array"
params.map { |v| undasherize_keys(v) }
params.map { |v| unrename_keys(v) }
else
params
end
Expand Down
7 changes: 7 additions & 0 deletions activesupport/test/core_ext/hash_ext_test.rb
Expand Up @@ -403,6 +403,13 @@ def test_one_level_dasherize_true
assert xml.include?(%(<name>David</name>))
end

def test_one_level_camelize_true
xml = { :name => "David", :street_name => "Paulina" }.to_xml(@xml_options.merge(:camelize => true))
assert_equal "<Person>", xml.first(8)
assert xml.include?(%(<StreetName>Paulina</StreetName>))
assert xml.include?(%(<Name>David</Name>))
end

def test_one_level_with_types
xml = { :name => "David", :street => "Paulina", :age => 26, :age_in_millis => 820497600000, :moved_on => Date.new(2005, 11, 15), :resident => :yes }.to_xml(@xml_options)
assert_equal "<person>", xml.first(8)
Expand Down

5 comments on commit aa5cdb0

@mdchaney
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Um, do you know how much code you broke by forcing “dasherize” upon us? I (and plenty of others) are using xml docs that are posted to an action, and taking advantage of the fact that the doc is busted out into a Hash that is added to params.

I have to either figure out how to persuade rails to set :dasherize to false, or go through a couple hundred lines of code and dasherize what I have. Either way, my code will only work on the latest version of rails.

This is a major change, and the default should always be to continue to do whatever was done historically, unless that historic functionality is broken.

@mdchaney
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad, it’s not “dasherize”. The issue is line 223 of hash/conversions.rb – adding “underscore”. Same problem, though, and a major change in functionality.

@NZKoz
Copy link
Member

@NZKoz NZKoz commented on aa5cdb0 Apr 9, 2009

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds like a bug, please file a ticket on lighthouse and assign it to me, ideally with a test case for what’s broken.

@NZKoz
Copy link
Member

@NZKoz NZKoz commented on aa5cdb0 Apr 9, 2009

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can push out a point release to address this.

@kvnsmth
Copy link

@kvnsmth kvnsmth commented on aa5cdb0 May 4, 2009

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Michael,
I created a patch for Hash#from_xml not working with all caps keys due to this commit and assigned it to you. https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/2604

Please sign in to comment.