--- /dev/null
+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