194 lines
5.5 KiB
Ruby
194 lines
5.5 KiB
Ruby
|
module Faraday
|
||
|
module NestedParamsEncoder
|
||
|
ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/
|
||
|
|
||
|
def self.escape(s)
|
||
|
return s.to_s.gsub(ESCAPE_RE) {
|
||
|
'%' + $&.unpack('H2' * $&.bytesize).join('%').upcase
|
||
|
}.tr(' ', '+')
|
||
|
end
|
||
|
|
||
|
def self.unescape(s)
|
||
|
CGI.unescape(s.to_s)
|
||
|
end
|
||
|
|
||
|
def self.encode(params)
|
||
|
return nil if params == nil
|
||
|
|
||
|
if !params.is_a?(Array)
|
||
|
if !params.respond_to?(:to_hash)
|
||
|
raise TypeError,
|
||
|
"Can't convert #{params.class} into Hash."
|
||
|
end
|
||
|
params = params.to_hash
|
||
|
params = params.map do |key, value|
|
||
|
key = key.to_s if key.kind_of?(Symbol)
|
||
|
[key, value]
|
||
|
end
|
||
|
# Useful default for OAuth and caching.
|
||
|
# Only to be used for non-Array inputs. Arrays should preserve order.
|
||
|
params.sort!
|
||
|
end
|
||
|
|
||
|
# Helper lambda
|
||
|
to_query = lambda do |parent, value|
|
||
|
if value.is_a?(Hash)
|
||
|
value = value.map do |key, val|
|
||
|
key = escape(key)
|
||
|
[key, val]
|
||
|
end
|
||
|
value.sort!
|
||
|
buffer = ""
|
||
|
value.each do |key, val|
|
||
|
new_parent = "#{parent}%5B#{key}%5D"
|
||
|
buffer << "#{to_query.call(new_parent, val)}&"
|
||
|
end
|
||
|
return buffer.chop
|
||
|
elsif value.is_a?(Array)
|
||
|
buffer = ""
|
||
|
value.each_with_index do |val, i|
|
||
|
new_parent = "#{parent}%5B%5D"
|
||
|
buffer << "#{to_query.call(new_parent, val)}&"
|
||
|
end
|
||
|
return buffer.chop
|
||
|
else
|
||
|
encoded_value = escape(value)
|
||
|
return "#{parent}=#{encoded_value}"
|
||
|
end
|
||
|
end
|
||
|
|
||
|
# The params have form [['key1', 'value1'], ['key2', 'value2']].
|
||
|
buffer = ''
|
||
|
params.each do |parent, value|
|
||
|
encoded_parent = escape(parent)
|
||
|
buffer << "#{to_query.call(encoded_parent, value)}&"
|
||
|
end
|
||
|
return buffer.chop
|
||
|
end
|
||
|
|
||
|
def self.decode(query)
|
||
|
return nil if query == nil
|
||
|
# Recursive helper lambda
|
||
|
dehash = lambda do |hash|
|
||
|
hash.each do |(key, value)|
|
||
|
if value.kind_of?(Hash)
|
||
|
hash[key] = dehash.call(value)
|
||
|
end
|
||
|
end
|
||
|
# Numeric keys implies an array
|
||
|
if hash != {} && hash.keys.all? { |key| key =~ /^\d+$/ }
|
||
|
hash.sort.inject([]) do |accu, (_, value)|
|
||
|
accu << value; accu
|
||
|
end
|
||
|
else
|
||
|
hash
|
||
|
end
|
||
|
end
|
||
|
|
||
|
empty_accumulator = {}
|
||
|
return ((query.split('&').map do |pair|
|
||
|
pair.split('=', 2) if pair && !pair.empty?
|
||
|
end).compact.inject(empty_accumulator.dup) do |accu, (key, value)|
|
||
|
key = unescape(key)
|
||
|
if value.kind_of?(String)
|
||
|
value = unescape(value.gsub(/\+/, ' '))
|
||
|
end
|
||
|
|
||
|
array_notation = !!(key =~ /\[\]$/)
|
||
|
subkeys = key.split(/[\[\]]+/)
|
||
|
current_hash = accu
|
||
|
for i in 0...(subkeys.size - 1)
|
||
|
subkey = subkeys[i]
|
||
|
current_hash[subkey] = {} unless current_hash[subkey]
|
||
|
current_hash = current_hash[subkey]
|
||
|
end
|
||
|
if array_notation
|
||
|
current_hash[subkeys.last] = [] unless current_hash[subkeys.last]
|
||
|
current_hash[subkeys.last] << value
|
||
|
else
|
||
|
current_hash[subkeys.last] = value
|
||
|
end
|
||
|
accu
|
||
|
end).inject(empty_accumulator.dup) do |accu, (key, value)|
|
||
|
accu[key] = value.kind_of?(Hash) ? dehash.call(value) : value
|
||
|
accu
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
module FlatParamsEncoder
|
||
|
ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/
|
||
|
|
||
|
def self.escape(s)
|
||
|
return s.to_s.gsub(ESCAPE_RE) {
|
||
|
'%' + $&.unpack('H2' * $&.bytesize).join('%').upcase
|
||
|
}.tr(' ', '+')
|
||
|
end
|
||
|
|
||
|
def self.unescape(s)
|
||
|
CGI.unescape(s.to_s)
|
||
|
end
|
||
|
|
||
|
def self.encode(params)
|
||
|
return nil if params == nil
|
||
|
|
||
|
if !params.is_a?(Array)
|
||
|
if !params.respond_to?(:to_hash)
|
||
|
raise TypeError,
|
||
|
"Can't convert #{params.class} into Hash."
|
||
|
end
|
||
|
params = params.to_hash
|
||
|
params = params.map do |key, value|
|
||
|
key = key.to_s if key.kind_of?(Symbol)
|
||
|
[key, value]
|
||
|
end
|
||
|
# Useful default for OAuth and caching.
|
||
|
# Only to be used for non-Array inputs. Arrays should preserve order.
|
||
|
params.sort!
|
||
|
end
|
||
|
|
||
|
# The params have form [['key1', 'value1'], ['key2', 'value2']].
|
||
|
buffer = ''
|
||
|
params.each do |key, value|
|
||
|
encoded_key = escape(key)
|
||
|
value = value.to_s if value == true || value == false
|
||
|
if value == nil
|
||
|
buffer << "#{encoded_key}&"
|
||
|
elsif value.kind_of?(Array)
|
||
|
value.each do |sub_value|
|
||
|
encoded_value = escape(sub_value)
|
||
|
buffer << "#{encoded_key}=#{encoded_value}&"
|
||
|
end
|
||
|
else
|
||
|
encoded_value = escape(value)
|
||
|
buffer << "#{encoded_key}=#{encoded_value}&"
|
||
|
end
|
||
|
end
|
||
|
return buffer.chop
|
||
|
end
|
||
|
|
||
|
def self.decode(query)
|
||
|
empty_accumulator = {}
|
||
|
return nil if query == nil
|
||
|
split_query = (query.split('&').map do |pair|
|
||
|
pair.split('=', 2) if pair && !pair.empty?
|
||
|
end).compact
|
||
|
return split_query.inject(empty_accumulator.dup) do |accu, pair|
|
||
|
pair[0] = unescape(pair[0])
|
||
|
pair[1] = true if pair[1].nil?
|
||
|
if pair[1].respond_to?(:to_str)
|
||
|
pair[1] = unescape(pair[1].to_str.gsub(/\+/, " "))
|
||
|
end
|
||
|
if accu[pair[0]].kind_of?(Array)
|
||
|
accu[pair[0]] << pair[1]
|
||
|
elsif accu[pair[0]]
|
||
|
accu[pair[0]] = [accu[pair[0]], pair[1]]
|
||
|
else
|
||
|
accu[pair[0]] = pair[1]
|
||
|
end
|
||
|
accu
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|