Description
Expected behavior vs actual behavior
c533d1a added support for key transformation to e.g. snake case or dasherized. Nice and useful however it appears to use deep_transform_keys!
. I'm not 100% sure but I have a feeling this is causing serialized models with JSON attributes (e.g. postgres JSON columns) to be mutated when they should not be.
In short serialization of a model object with JSON columns leaves that model object with dirty with unsaved (and obviously unexpected) changes.
Expected behavior is to NOT mutate model attributes during serialization.
Steps to reproduce
Setup a model with a JSON column. Serialize an instance of that object. Notice that the JSON attribute is now dirty/has changes on the model.
> model = Model.first
# Assume model.schema is a JSON attribute that is included in the serializer for Model
> ActiveModel::SerializableResource.new(model).to_json
> model.schema_changed?
true
Environment
ActiveModelSerializers Version (commit ref if not on tag): 0.10.2
Output of ruby -e "puts RUBY_DESCRIPTION"
: ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-darwin14]
OS Type & Version: OSX El Capitan
Integrated application and version (e.g., Rails, Grape, etc): Rails
Additonal helpful information
This bug is not present in versions of AMS prior to this commit: c533d1a
Activity
bdmac commentedon Jul 8, 2016
A second part of this bug is that I do not think it would be expected behavior that if a model includes a JSON attribute, that that JSON attribute would ALSO go through the same transformations as the model/response itself. I mean maybe but it seems like that should be something that can be opted in/out at least.
bdmac commentedon Jul 8, 2016
I can fix the mutation problem by simply not using the bang version of
deep_transform_keys!
in ActiveModelSerializers::KeyTransform.I'm not sure how to make it so a JSON attribute of a serialized object is not transformed...
bdmac commentedon Jul 8, 2016
Eek yah I tried to look at KeyTransform a bit and it seems like it would be pretty damn hard to make it not transform JSON model attributes since it just gets the entire Hash structure and recursively transforms all the keys at once. It does not have access to the underlying/original models to even try to check if the nested JSON it's dealing with is there because of a JSON attribute/column or not...
bf4 commentedon Jul 8, 2016
@bdmac we shouldn't be mutating the model, no matter what. Is that specific to using a JSON field? I'd be happy to work with you on PR for that.
bdmac commentedon Jul 8, 2016
@bf4 I believe so, yes, because we use the mutating version of
deep_transform_keys
and it seems like the hash representing the JSON column is sharing a pointer with what was pulled from the model originally still. E.g. at some point when we are building up the hash representation of the model with a JSON attribute we are just doinghash[attribute_name] = model.read_attribute(attribute_name)
.Two possibilities I can think of to solve the initial problem:
bf4 commentedon Jul 8, 2016
@bdmac fwiw, the key transform is a performance hit you might resolve better on the client...
bdmac commentedon Jul 8, 2016
@bf4 that may be true but transforming is the default behavior of the JsonApi adapter in AMS.
bf4 commentedon Jul 8, 2016
@bdmac yeah, in retrospect I don't think it was a good decision. I have it set to
:unaltered
in my own app. Picking good defaults is hardbdmac commentedon Jul 8, 2016
I mean maybe it was a bad decision but the JSON API spec calls for dasherized keys so even if you did not automatically dasherize, the end-user would end up needing to dasherize anyways. We were dasherizing prior to attempting to update to 0.10.2 (which includes the KeyTransform stuff) just in a way that did not mutate (or recursively dasherize everything).
bdmac commentedon Jul 8, 2016
@bf4 FWIW I've been able to work around this problem. I turned off key transformation as you mentioned by setting key_transform config to
:unaltered
. Here is our initializer now for reference:We were already/previously using the custom
DasherizedJsonApiAdapter
adapter since pre-release versions of 0.10.x did not have support for key transform / dasherized keys. I was hoping to get rid of our custom adapter by just setting the adapter to:json_api
when we upgraded to 0.10.2 but that brought up all these issues...29 remaining items