123456789_123456789_123456789_123456789_123456789_

Class: Rack::QueryParser

Relationships & Source Files
Namespace Children
Classes:
Exceptions:
Inherits: Object
Defined in: lib/rack/query_parser.rb

Constant Summary

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Constructor Details

.new(params_class, param_depth_limit) ⇒ QueryParser

[ GitHub ]

  
# File 'lib/rack/query_parser.rb', line 36

def initialize(params_class, param_depth_limit)
  @params_class = params_class
  @param_depth_limit = param_depth_limit
end

Class Method Details

.make_default(param_depth_limit)

[ GitHub ]

  
# File 'lib/rack/query_parser.rb', line 30

def self.make_default(param_depth_limit)
  new Params, param_depth_limit
end

Instance Attribute Details

#param_depth_limit (readonly)

[ GitHub ]

  
# File 'lib/rack/query_parser.rb', line 34

attr_reader :param_depth_limit

Instance Method Details

#_normalize_params(params, name, v, depth) (private)

Raises:

[ GitHub ]

  
# File 'lib/rack/query_parser.rb', line 98

private def _normalize_params(params, name, v, depth)
  raise ParamsTooDeepError if depth >= param_depth_limit

  if !name
    # nil name, treat same as empty string (required by tests)
    k = after = ''
  elsif depth == 0
    # Start of parsing, don't treat [] or [ at start of string specially
    if start = name.index('[', 1)
      # Start of parameter nesting, use part before brackets as key
      k = name[0, start]
      after = name[start, name.length]
    else
      # Plain parameter with no nesting
      k = name
      after = ''
    end
  elsif name.start_with?('[]')
    # Array nesting
    k = '[]'
    after = name[2, name.length]
  elsif name.start_with?('[') && (start = name.index(']', 1))
    # Hash nesting, use the part inside brackets as the key
    k = name[1, start-1]
    after = name[start+1, name.length]
  else
    # Probably malformed input, nested but not starting with [
    # treat full name as key for backwards compatibility.
    k = name
    after = ''
  end

  return if k.empty?

  if after == ''
    if k == '[]' && depth != 0
      return [v]
    else
      params[k] = v
    end
  elsif after == "["
    params[name] = v
  elsif after == "[]"
    params[k] ||= []
    raise ParameterTypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
    params[k] << v
  elsif after.start_with?('[]')
    # Recognize x[][y] (hash inside array) parameters
    unless after[2] == '[' && after.end_with?(']') && (child_key = after[3, after.length-4]) && !child_key.empty? && !child_key.index('[') && !child_key.index(']')
      # Handle other nested array parameters
      child_key = after[2, after.length]
    end
    params[k] ||= []
    raise ParameterTypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
    if params_hash_type?(params[k].last) && !params_hash_has_key?(params[k].last, child_key)
      _normalize_params(params[k].last, child_key, v, depth + 1)
    else
      params[k] << _normalize_params(make_params, child_key, v, depth + 1)
    end
  else
    params[k] ||= make_params
    raise ParameterTypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params_hash_type?(params[k])
    params[k] = _normalize_params(params[k], after, v, depth + 1)
  end

  params
end

#make_params

[ GitHub ]

  
# File 'lib/rack/query_parser.rb', line 166

def make_params
  @params_class.new
end

#new_depth_limit(param_depth_limit)

[ GitHub ]

  
# File 'lib/rack/query_parser.rb', line 170

def new_depth_limit(param_depth_limit)
  self.class.new @params_class, param_depth_limit
end

#normalize_params(params, name, v, _depth = nil)

normalize_params recursively expands parameters into structural types. If the structural types represented by two different parameter names are in conflict, a QueryParser::ParameterTypeError is raised. The depth argument is deprecated and should no longer be used, it is kept for backwards compatibility with earlier versions of rack.

[ GitHub ]

  
# File 'lib/rack/query_parser.rb', line 94

def normalize_params(params, name, v, _depth=nil)
  _normalize_params(params, name, v, 0)
end

#params_hash_has_key?(hash, key) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rack/query_parser.rb', line 180

def params_hash_has_key?(hash, key)
  return false if /\[\]/.match?(key)

  key.split(/[\[\]]+/).inject(hash) do |h, part|
    next h if part == ''
    return false unless params_hash_type?(h) && h.key?(part)
    h[part]
  end

  true
end

#params_hash_type?(obj) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rack/query_parser.rb', line 176

def params_hash_type?(obj)
  obj.kind_of?(@params_class)
end

#parse_nested_query(qs, separator = nil)

parse_nested_query expands a query string into structural types. Supported types are Arrays, Hashes and basic value types. It is possible to supply query strings with parameters of conflicting types, in this case a QueryParser::ParameterTypeError is raised. Users are encouraged to return a 400 in this case.

[ GitHub ]

  
# File 'lib/rack/query_parser.rb', line 73

def parse_nested_query(qs, separator = nil)
  params = make_params

  unless qs.nil? || qs.empty?
    (qs || '').split(separator ? (COMMON_SEP[separator] || /[#{separator}] */n) : DEFAULT_SEP).each do |p|
      k, v = p.split('=', 2).map! { |s| unescape(s) }

      _normalize_params(params, k, v, 0)
    end
  end

  return params.to_h
rescue ArgumentError => e
  raise InvalidParameterError, e.message, e.backtrace
end

#parse_query(qs, separator = nil, &unescaper)

Stolen from Mongrel, with some small modifications: Parses a query string by breaking it up at the ‘&’. You can also use this to parse cookies by changing the characters used in the second parameter (which defaults to ‘&’).

[ GitHub ]

  
# File 'lib/rack/query_parser.rb', line 45

def parse_query(qs, separator = nil, &unescaper)
  unescaper ||= method(:unescape)

  params = make_params

  (qs || '').split(separator ? (COMMON_SEP[separator] || /[#{separator}] */n) : DEFAULT_SEP).each do |p|
    next if p.empty?
    k, v = p.split('=', 2).map!(&unescaper)

    if cur = params[k]
      if cur.class == Array
        params[k] << v
      else
        params[k] = [cur, v]
      end
    else
      params[k] = v
    end
  end

  return params.to_h
end

#unescape(string, encoding = Encoding::UTF_8) (private)

[ GitHub ]

  
# File 'lib/rack/query_parser.rb', line 192

def unescape(string, encoding = Encoding::UTF_8)
  URI.decode_www_form_component(string, encoding)
end