Have StripeObject#update_attributes reflect behavior of accessors

This dials down the safety of `StripeObject`'s `#update_attributes`
method so that it allows properties to be assigned that it doesn't yet
know about. We're doing this for a few reasons:

1. To reflect the current behavior of accessors (i.e. `obj.name = ...`)
   through `method_missing`.
2. To allow `#update_attributes` to assign properties on new projects
   that don't yet know their schema from an API call.

Fixes #324.
This commit is contained in:
Brandur 2015-10-08 14:04:11 -07:00
parent e75fd86ae5
commit 11dd870900
3 changed files with 26 additions and 39 deletions

View File

@ -20,7 +20,7 @@ module Stripe
# We started unintentionally (sort of) allowing attributes send to
# +save+ to override values used during the update. So as not to break
# the API, this makes that official here.
update_attributes_with_options(params, :raise_error => false)
update_attributes(params)
# Now remove any parameters that look like object attributes.
params = params.reject { |k, _| respond_to?(k) }

View File

@ -63,7 +63,7 @@ module Stripe
@unsaved_values.delete(k)
end
update_attributes_with_options(values, :opts => opts)
update_attributes(values, :opts => opts)
values.each do |k, _|
@transient_values.delete(k)
@unsaved_values.delete(k)
@ -73,8 +73,25 @@ module Stripe
end
# Mass assigns attributes on the model.
def update_attributes(values)
update_attributes_with_options(values, {})
#
# This is a version of +update_attributes+ that takes some extra options
# for internal use.
#
# ==== Attributes
#
# * +values+ - Hash of values to use to update the current attributes of
# the object.
#
# ==== Options
#
# * +:opts+ Options for StripeObject like an API key.
# * +:raise_error+ Set to false to suppress ArgumentErrors on keys that
# don't exist.
def update_attributes(values, opts = {})
values.each do |k, v|
@values[k] = Util.convert_to_stripe_object(v, opts)
@unsaved_values.add(k)
end
end
def [](k)
@ -293,36 +310,5 @@ module Stripe
def respond_to_missing?(symbol, include_private = false)
@values && @values.has_key?(symbol) || super
end
# Mass assigns attributes on the model.
#
# This is a version of +update_attributes+ that takes some extra options
# for internal use.
#
# ==== Options
#
# * +:opts:+ Options for StripeObject like an API key.
# * +:raise_error:+ Set to false to suppress ArgumentErrors on keys that
# don't exist.
def update_attributes_with_options(values, options={})
# `opts` are StripeObject options
opts = options.fetch(:opts, {})
raise_error = options.fetch(:raise_error, true)
values.each do |k, v|
if !@@permanent_attributes.include?(k) && !self.respond_to?(:"#{k}=")
if raise_error
raise ArgumentError,
"#{k} is not an attribute that can be assigned on this object"
else
next
end
end
@values[k] = Util.convert_to_stripe_object(v, opts)
@unsaved_values.add(k)
end
self
end
end
end

View File

@ -53,10 +53,11 @@ module Stripe
obj.update_attributes(:name => 'STRIPE')
assert_equal "STRIPE", obj.name
e = assert_raises(ArgumentError) do
obj.update_attributes(:foo => 'bar')
end
assert_equal "foo is not an attribute that can be assigned on this object", e.message
# unfortunately, we even assign unknown properties to duplicate the
# behavior that we currently have via magic accessors with
# method_missing
obj.update_attributes(:unknown => 'foo')
assert_equal "foo", obj.unknown
end
end
end