Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix replacement of non-
metadata
embedded StripeObjects
So we have a bit of a problem right now when it comes to replacing a `StripeObject` that's embedded in an API resource. Most of the time when someone does this, they want to _replace_ an object embedded in another object. Take setting a source on a subscription for example: ``` ruby subscription.source = { object: 'card', number: 123, } subscription.save ``` In the case above, the serialized parameters should come out as: ``` source[object]=card&source[number]=123 ``` That should apply even if the previous source had something else set on it which we're not going to set this time -- say an optional parameter like `source[address_state]`. Those should not be present at all in the final serialized parameters. (Another example is setting a `payout_schedule` as seen in stripe#631 which is PR is intended to address.) There is an exception to this rule in the form of metadata though. Metadata is a bit of a strange case in that the API will treat it as additive, so if we send `metadata[foo]`, that will set the `foo` key, but it won't overwrite any other keys that were already present. This is a problem because when a user fully sets `metadata` to a new object in Ruby, what they're probably trying to do is _replace_ it rather than add to it. For example: ``` ruby subscription.metadata => { old: 'bar' } subscription.metadata = { new: 'baz' } subscription.save ``` To accomplish what the user is probably trying to do, we actually need to send `metadata[old]=&metadata[new]=baz` so that we empty the value of `old` while simultaneously setting `new` to `baz`. In summary, metadata behaves different from other embedded objects in a fairly fundamental way, and because the code is currently only set up to handle the metadata case, it's not behaving correctly when other types of objects are being set. A lot of the time emptying values like we do for `metadata` is benign, but as we've seen in stripe#631, sometimes it's not. In this patch, I modify serialization to only empty out object values when we see that parameter is `metadata`. I'm really not crazy about the implementation here _at all_, but I'm having trouble thinking of a better way to do it. One possibility is to introduce a new class annotation like `empty_embedded_object :metadata`, but that will have to go everywhere and might be error-prone in case someone forgets it on a new resource type. If anyone has a suggestion for an alternative (or can let me know if I'm missing something), I'd love to hear it. This PR is an alternate to stripe#631.
- Loading branch information