Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #8305 (reopened defect)

Opened 1 year ago

Last modified 9 months ago

[PATCH] Fix ActiveRecord::Base#to_xml and Hash#from_xml for namespaced model classes in Ruby module

Reported by: mbbx6spp Assigned to: mbbx6spp
Priority: normal Milestone: 2.x
Component: ActiveRecord Version: edge
Severity: normal Keywords:
Cc: mbbx6spp, stepheneb

Description

I looked through 39 tickets looking for this to be reported already, but saw no mention of this defect for this particular edge case.

The following edge case breaks ActiveRecord::Base#to_xml. Assume:

  • The model Legacy::Contact is defined. It is namespaced within the Ruby module, Legacy.

Reproduce issue with following code:

class Legacy::Contact < ActiveRecord::Base
  # stuff
end

# in ./script/console
contacts = Legacy::Contact.find :first #=> some Legacy::Contact instance
contacts.to_xml #=> <?xml version=\"1.0\" encoding=\"UTF-8\"?><legacy/contact>\n  <age type=\"integer\">25</age>\n  <avatar encoding=\"base64\" type=\"binary\">YmluYXJ5ZGF0YQ==\n</avatar>\n  <awesome type=\"boolean\">false</awesome>\n  <created-at type=\"datetime\">2006-08-01T00:00:00Z</created-at>\n  <name>aaron stack</name>\n  <preferences type=\"yaml\">--- \n:gem: ruby\n</preferences>\n</legacy/contact>\n

I originally saw this in 1.2.3, then verified in trunk to ensure this is still an issue in trunk (and it is).

I realize there is a workaround for this, but I was hoping the more correct behavior of outputting the following for the last line would be on the cards soon-ish:

<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<legacy:contact>\n  <age type=\"integer\">25</age>\n  <avatar encoding=\"base64\" type=\"binary\">YmluYXJ5ZGF0YQ==\n</avatar>\n  <awesome type=\"boolean\">false</awesome>\n  <created-at type=\"datetime\">2006-08-01T00:00:00Z</created-at>\n  <name>aaron stack</name>\n  <preferences type=\"yaml\">--- \n:gem: ruby\n</preferences>\n</legacy:contact>\n

Attached to this ticket is a patch that adds the relevant unit test for this edge case in source:trunk/activerecord/test/xml_serialization_test.rb mostly.

I also found the activerecord_unittest.contacts table wasn't defined in source:trunk/activerecord/test/fixtures/db_definitions/mysql.sql, so I added it to get it to work for my test environment.

Attachments

xml_serialize.patch (3.5 kB) - added by mbbx6spp on 05/09/07 06:09:58.
Test case and code fix for #8305

Change History

(in reply to: ↑ description ) 05/09/07 03:30:21 changed by mbbx6spp

  • owner changed from core to mbbx6spp.
  • status changed from new to assigned.

I meant to say... Replying to mbbx6spp:

I looked through 39 tickets *mentioning to_xml* looking for this to be reported already, but saw no mention of this defect for this particular edge case.

And while I am digging I will try to come up with a patch for this.

05/09/07 03:32:32 changed by mbbx6spp

For those interested in the workaround for this:

contact = Legacy::Contact.find :first
xml = contact.to_xml(:root => 'legacy:contact')

For the collections case:

contacts = Legacy::Contact.find :all
xml = contact.to_xml(:root => 'legacy:contacts', :children => 'legacy:contact')

Hope this helps someone.

05/09/07 04:01:01 changed by mbbx6spp

  • priority changed from low to normal.
  • summary changed from Fix ActiveRecord::Base#to_xml and ActiveRecord::Base#from_xml for namespaced model classes in Ruby module to [PATCH] Fix ActiveRecord::Base#to_xml and ActiveRecord::Base#from_xml for namespaced model classes in Ruby module.

My code fix is not elegant, mostly because I didn't want to take too many liberties and add a helper instance method to String somewhere in activesupport to remove the direct gsub! invocation here.

I changed the local/method variable name because I generally dislike having a local/method variable named the same as the method it is in. But please change as per Rails Core coding style if patch is accepted.

Patch is:

Index: lib/active_record/xml_serialization.rb
===================================================================
--- lib/active_record/xml_serialization.rb      (revision 6703)
+++ lib/active_record/xml_serialization.rb      (working copy)
@@ -148,8 +148,9 @@
     end
 
     def root
-      root = (options[:root] || @record.class.to_s.underscore).to_s
-      dasherize? ? root.dasherize : root
+      root_name = (options[:root] || @record.class.to_s.underscore).to_s
+      root_name.gsub!(/\//, ':') # for models that are namespaced in Ruby module
+      dasherize? ? root_name.dasherize : root_name
     end
     
     def dasherize?

To help justify this fix: I forgot to mention that the current XML produced from to_xml for this namespaced model case is invalid XML. But adding this fix we automatically generate valid XML for this edge case.

05/09/07 04:24:00 changed by mbbx6spp

  • summary changed from [PATCH] Fix ActiveRecord::Base#to_xml and ActiveRecord::Base#from_xml for namespaced model classes in Ruby module to [PATCH] Fix ActiveRecord::Base#to_xml and Hash#from_xml for namespaced model classes in Ruby module.

This is only half of the solution. The other part of the solution is to modify the Hash conversion extensions in activesupport to make sure coming from the other way, the XML can be deserialized.

Unfortunately it past my bed time and don't work well after 11pm, so off to bed now. After work hours tomorrow I will revisit this.

05/09/07 05:36:41 changed by mbbx6spp

See my ActiveRecord::Base#to_xml Woes blog posting for interim patch for this issue.

05/09/07 06:09:58 changed by mbbx6spp

  • attachment xml_serialize.patch added.

Test case and code fix for #8305

(follow-up: ↓ 7 ) 05/09/07 06:11:49 changed by mbbx6spp

See #3808 for related ticket in ActiveRecord.

(in reply to: ↑ 6 ) 05/16/07 23:54:58 changed by mbbx6spp

Replying to mbbx6spp:

See #3808 for related ticket in ActiveRecord.

That should have said #8308.

06/08/07 21:49:20 changed by hob

  • status changed from assigned to closed.
  • resolution set to incomplete.

This seems to be a partial fix. There is still a problem when using :include in to_xml's options. Any included associations still show the mal-formed xml. The problem seems to be in ActiveRecord::XmlSerialzier's add_includes method which builds the tag name manually instead of leaving it up to the child record.

06/08/07 21:50:41 changed by hob

  • status changed from closed to reopened.
  • resolution deleted.

Sorry...1st time submitting a ticket...didn't mean to close it...

06/08/07 22:24:18 changed by hob

  • cc set to mbbx6spp.

Just found something else. Nesting namespaces doesn't work well either. Let's say you have:

Legacy::Purchasing::Requisition and Legacy::ServicesProcurement::Requisition

Your patch creates the xml tags as: <legacy:purchasing:requisition> and <legacy:services_procurement:requisition>

The 2nd set of colons, again, results in mal-formed XML. Suggest using another method of depicting nested namespaces. Not an XML expert, so I won't presume to know what that might be.

06/08/07 22:51:02 changed by mbbx6spp

Great! Thanks for the feedback to the patch. It has been sitting a little dormant and I needed a reminder of some kind.

Over the weekend I will try to add a patch based on a newer rails trunk revision and at a minimum add the tests demonstrating the two issues you mention above.

06/17/07 02:01:33 changed by cch1

Sure would be nice to see this get fixed...I'm getting bit by it as well.

07/13/07 14:15:26 changed by schmidtwisser

There is another edge case. In Camping, it uses ActiveRecord as well, all models live in YourApp::Models, which would end in double namespaces.

YourApp::Models::User would become your_app:models:user which isn't valid xml either. This may, of course, also happen in any Rails app, although it is less likely.

I don't know how to cope with that. Perhaps it would be better to not use namespaces but another character, probably the underscore. But then, the deserialization would be more difficult or even impossible.

07/13/07 14:59:44 changed by mbbx6spp

I was personally thinking of using hyphen instead of underscore just for the delimiters of modules, but then use an XML namespace to delimit the module(s) from the class. For example, YourApp::Models::User would translate to your_app-models:user. This way there is always one XML namespace that translates to an ordered sequence of Ruby modules.

Am I missing something? If not, this would seem to allow deterministic serialization and deserialization.

I promise to crave out an hour this weekend to add at least the test cases to fail for the current code base and patch and then start with a better patch once I received some critique of the above scheme.

07/13/07 15:00:42 changed by mbbx6spp

I meant "carve out" instead of "crave out" in the above message:)

09/24/07 20:56:04 changed by stepheneb

  • cc changed from mbbx6spp to mbbx6spp, stepheneb.