Introduce #save_with_parent flag

Introduce a `#save_with_parent` flag that allows the default behavior of
never saving API resources nested under a parent to be overridden, a
feature that we so far only know to need for updating a source under a
customer.
This commit is contained in:
Brandur 2016-06-30 12:57:54 -07:00
parent d0a3493144
commit 2a4a50da8e
5 changed files with 60 additions and 6 deletions

View File

@ -2,6 +2,13 @@ module Stripe
class APIResource < StripeObject
include Stripe::APIOperations::Request
# A flag that can be set a behavior that will cause this resource to be
# encoded and sent up along with an update of its parent resource. This is
# usually not desirable because resources are updated individually on their
# own endpoints, but there are certain cases, changing a customer's default
# source for example, where this is allowed.
attr_accessor :save_with_parent
def self.class_name
self.name.split('::')[-1]
end

View File

@ -5,6 +5,20 @@ module Stripe
include Stripe::APIOperations::Save
extend Stripe::APIOperations::List
def source=(value)
super
# The parent setter will perform certain useful operations like
# converting to an APIResource if appropriate.
value = self.source
if value.is_a?(APIResource)
value.save_with_parent = true
end
value
end
def add_invoice_item(params, opts={})
opts = @opts.merge(Util.normalize_opts(opts))
InvoiceItem.create(params.merge(:customer => id), opts)

View File

@ -326,8 +326,8 @@ module Stripe
end
def serialize_params_value(value, original, unsaved, force)
case value
when nil
case true
when value == nil
''
# The logic here is that essentially any object embedded in another
@ -335,10 +335,16 @@ module Stripe
# type that's been included in the response. These other resources must
# be updated from their proper endpoints, and therefore they are not
# included when serializing even if they've been modified.
when APIResource
#
# There are _some_ known exceptions though. For example, to save on API
# calls it's sometimes desirable to update a customer's default source by
# setting a new card (or other) object with `#source=` and then saving
# the customer. The `#save_with_parent` flag to override the default
# behavior allows us to handle these exceptions.
when value.is_a?(APIResource) && !value.save_with_parent
nil
when Array
when value.is_a?(Array)
update = value.map { |v| serialize_params_value(v, nil, true, force) }
# This prevents an array that's unchanged from being resent.
@ -358,10 +364,10 @@ module Stripe
# existing array being held by a StripeObject. This could happen for
# example by appending a new hash onto `additional_owners` for an
# account.
when Hash
when value.is_a?(Hash)
Util.convert_to_stripe_object(value, @opts).serialize_params
when StripeObject
when value.is_a?(StripeObject)
update = value.serialize_params(:force => force)
# If the entire object was replaced, then we need blank each field of

View File

@ -92,5 +92,20 @@ module Stripe
c.delete_discount
assert_equal nil, c.discount
end
should "can have a token source set" do
c = Stripe::Customer.new("test_customer")
c.source = "tok_123"
assert_equal "tok_123", c.source
end
should "set a flag if given an object source" do
c = Stripe::Customer.new("test_customer")
c.source = {
:object => 'card'
}
assert_equal true, c.source.save_with_parent
end
end
end

View File

@ -249,6 +249,18 @@ module Stripe
assert_equal({}, serialized)
end
should "#serialize_params and remove embedded APIResources unless flagged with save_with_parent" do
c = Customer.construct_from({})
c.save_with_parent = true
obj = Stripe::StripeObject.construct_from({
:customer => c,
})
serialized = obj.serialize_params
assert_equal({ :customer => {} }, serialized)
end
should "#serialize_params takes a force option" do
obj = Stripe::StripeObject.construct_from({
:id => 'id',