-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