lib/stripe/util.rb in stripe-1.50.1 vs lib/stripe/util.rb in stripe-1.51.0
- old
+ new
@@ -138,10 +138,11 @@
params.each do |key, value|
calculated_key = parent_key ? "#{parent_key}[#{key}]" : "#{key}"
if value.is_a?(Hash)
result += flatten_params(value, calculated_key)
elsif value.is_a?(Array)
+ check_array_of_maps_start_keys!(value)
result += flatten_params_array(value, calculated_key)
else
result << [calculated_key, value]
end
end
@@ -193,8 +194,47 @@
end
def self.check_api_key!(key)
raise TypeError.new("api_key must be a string") unless key.is_a?(String)
key
+ end
+
+ private
+
+ # We use a pretty janky version of form encoding (Rack's) that supports
+ # more complex data structures like maps and arrays through the use of
+ # specialized syntax. To encode an array of maps like:
+ #
+ # [{a: 1, b: 2}, {a: 3, b: 4}]
+ #
+ # We have to produce something that looks like this:
+ #
+ # arr[][a]=1&arr[][b]=2&arr[][a]=3&arr[][b]=4
+ #
+ # The only way for the server to recognize that this is a two item array is
+ # that it notices the repetition of element "a", so it's key that these
+ # repeated elements are encoded first.
+ #
+ # This method is invoked for any arrays being encoded and checks that if
+ # the array contains all non-empty maps, that each of those maps must start
+ # with the same key so that their boundaries can be properly encoded.
+ def self.check_array_of_maps_start_keys!(arr)
+ expected_key = nil
+ arr.each do |item|
+ return if !item.is_a?(Hash)
+ return if item.count == 0
+
+ first_key = item.first[0]
+
+ if expected_key
+ if expected_key != first_key
+ raise ArgumentError,
+ "All maps nested in an array should start with the same key " +
+ "(expected starting key '#{expected_key}', got '#{first_key}')"
+ end
+ else
+ expected_key = first_key
+ end
+ end
end
end
end