patch to Hash#to_xml re lighthouse ticket #2521


I've just uploaded a patch which fixes the problem described in ticket #2521. Some of us are having problems integrating ActiveResource with non-Rails systems because the default behaviour of ActiveResource#save is to use the default behaviour of Hash#to_xml. The current documentation states that the default value of :dasherize is false, but in fact it's true. Therefore when we save our resources all underscores in attribute names are converted into dashes in XML element names, which causes a problem if the receiving application expects to see underscores in element names. There is no way to change this behaviour because ActiveResource#save doesn't have the right options.

Rather than change ActiveResource#save to feed in :dasherize=>false to the options to Hash#to_xml, which would be a quick bodge, I've gone to the source of the problem by changing Hash#rename_key, which is called by Hash#to_xml. Hash#rename_key now respects class attributes on Hash which determine the default behaviour of both :dasherize and :camelize.

The reason for doing it like this, rather than simply changing the default for :dasherize inside Hash#rename_key is that many other tests in the Rails test suite expect the default value of :dasherize in Hash#to_xml to be true. This probably happened because incoming XML with dashes in element names must be converted to underscores in Rails attribute names, and for some reason people have assumed that the converse is also true; that underscores in Rails attribute names must be converted into dashes in XML element names. Certainly all the tests assume this is the case and for Rails<->Rails this is fine, but for Rails<->Non-Rails it isn't. The default behaviour should be to leave underscores in Rails attribute names as underscores in XML element names, unless instructed otherwise. This would not alter the behaviour of Rails<->Rails REST interactions because dashes in XML element names always become underscores in Rails attribute names, but it would make life easier for those of us who have to integrate Rails<->Non-Rails.

However many tests, and probably lots of code, assume that :dasherize defaults to true. So I've added the class attribute Hash.dasherize_xml, and set it to true by default, but now people can change it to false if they need to. Since Hash#rename_key also responds to the :camelize option, I've added the class attribute Hash.camelize_xml, and defaulted it to false because that's how existing code works. The net result is that existing code, and all tests run fine, but people have the option to change the default values of :dasherize and :camelize if they need to, and also at some point in the future it's very easy to set the Rails default for :dasherize to false which is what it should be.

The current tests do not test for default values for either :dasherize or :camelize and they made assumptions about the default values of the other. So while testing :dasherize it was assumed that the default for :camelize was false, and while testing :camelize it was assumed that the default value of :dasherize was false. But :camelize takes precedence over :dasherize, (no test for that either), so the incorrect assumption was not picked up.

I've changed the tests so that :camelize is held to false while testing :dasherize and that :dasherize is held to false while testing :camelize. I've also added tests for the defaults and a test for :camelize=>true and :dasherize=>true together so the order of precedence is now defined in a test.

I've updated the documentation on ActiveResource#to_xml so that it now correctly describes the default value of :dasherize as true and informs people how to change the default if they need to. I've also added a mention of :camelize, which was undocumented in ActiveResource#to_xml.

Summary: This patch will not upset any existing code, but it does give people using REST to integrate Rails with non-Rails applications the ability to fix the problem with underscores in attribute names being converted to dashes in XML element names when the resource is saved.

Possible Enhancements: Thinking about it some more, it would be nice to have the option to apply XSLT to the XML going back and forth on ActiveResource. That would add more flexibility when integrating with non-Rails applications. Comments please.