module Faraday::NestedParamsEncoder

Public Class Methods

decode(query) click to toggle source
# File lib/faraday/parameters.rb, line 67
def self.decode(query)
  return nil if query == nil

  params = {}
  query.split("&").each do |pair|
    next if pair.empty?
    key, value = pair.split("=", 2)
    key = unescape(key)
    value = unescape(value.gsub(/\+/, ' ')) if value

    subkeys = key.scan(/[^\[\]]+(?:\]?\[\])?/)
    context = params
    subkeys.each_with_index do |subkey, i|
      is_array = subkey =~ /[\[\]]+\Z/
      subkey = $` if is_array
      last_subkey = i == subkeys.length - 1

      if !last_subkey || is_array
        value_type = is_array ? Array : Hash
        if context[subkey] && !context[subkey].is_a?(value_type)
          raise TypeError, "expected %s (got %s) for param `%s'" % [
            value_type.name,
            context[subkey].class.name,
            subkey
          ]
        end
        context = (context[subkey] ||= value_type.new)
      end

      if context.is_a?(Array) && !is_array
        if !context.last.is_a?(Hash) || context.last.has_key?(subkey)
          context << {}
        end
        context = context.last
      end

      if last_subkey
        if is_array
          context << value
        else
          context[subkey] = value
        end
      end
    end
  end

  dehash(params, 0)
end
dehash(hash, depth) click to toggle source

Internal: convert a nested hash with purely numeric keys into an array. FIXME: this is not compatible with Rack::Utils.parse_nested_query

# File lib/faraday/parameters.rb, line 118
def self.dehash(hash, depth)
  hash.each do |key, value|
    hash[key] = dehash(value, depth + 1) if value.kind_of?(Hash)
  end

  if depth > 0 && !hash.empty? && hash.keys.all? { |k| k =~ /^\d+$/ }
    hash.keys.sort.inject([]) { |all, key| all << hash[key] }
  else
    hash
  end
end
encode(params) click to toggle source
# File lib/faraday/parameters.rb, line 10
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)
      new_parent = "#{parent}%5B%5D"
      return new_parent if value.empty?
      buffer = ""
      value.each_with_index do |val, i|
        buffer << "#{to_query.call(new_parent, val)}&"
      end
      return buffer.chop
    elsif value.nil?
      return parent
    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