lib/rack/query_parser.rb in rack-2.0.9.4 vs lib/rack/query_parser.rb in rack-2.1.0
- old
+ new
@@ -1,7 +1,13 @@
+# frozen_string_literal: true
+
+require_relative 'core_ext/regexp'
+
module Rack
class QueryParser
+ using ::Rack::RegexpExtensions
+
DEFAULT_SEP = /[&;] */n
COMMON_SEP = { ";" => /[;] */n, ";," => /[;,] */n, "&" => /[&] */n }
# ParameterTypeError is the error that is raised when incoming structural
# parameters (parsed by parse_nested_query) contain conflicting types.
@@ -34,11 +40,11 @@
params = make_params
(qs || '').split(d ? (COMMON_SEP[d] || /[#{d}] */n) : DEFAULT_SEP).each do |p|
next if p.empty?
- k, v = p.split('='.freeze, 2).map!(&unescaper)
+ k, v = p.split('=', 2).map!(&unescaper)
if cur = params[k]
if cur.class == Array
params[k] << v
else
@@ -47,11 +53,11 @@
else
params[k] = v
end
end
- return params.to_params_hash
+ return params.to_h
end
# 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
@@ -59,17 +65,17 @@
# case.
def parse_nested_query(qs, d = nil)
return {} if qs.nil? || qs.empty?
params = make_params
- (qs || '').split(d ? (COMMON_SEP[d] || /[#{d}] */n) : DEFAULT_SEP).each do |p|
- k, v = p.split('='.freeze, 2).map! { |s| unescape(s) }
+ qs.split(d ? (COMMON_SEP[d] || /[#{d}] */n) : DEFAULT_SEP).each do |p|
+ k, v = p.split('=', 2).map! { |s| unescape(s) }
normalize_params(params, k, v, param_depth_limit)
end
- return params.to_params_hash
+ return params.to_h
rescue ArgumentError => e
raise InvalidParameterError, e.message
end
# normalize_params recursively expands parameters into structural types. If
@@ -77,26 +83,26 @@
# conflict, a ParameterTypeError is raised.
def normalize_params(params, name, v, depth)
raise RangeError if depth <= 0
name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
- k = $1 || ''.freeze
- after = $' || ''.freeze
+ k = $1 || ''
+ after = $' || ''
if k.empty?
- if !v.nil? && name == "[]".freeze
+ if !v.nil? && name == "[]"
return Array(v)
else
return
end
end
- if after == ''.freeze
+ if after == ''
params[k] = v
- elsif after == "[".freeze
+ elsif after == "["
params[name] = v
- elsif after == "[]".freeze
+ 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 =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$)
child_key = $1
@@ -133,11 +139,11 @@
def params_hash_type?(obj)
obj.kind_of?(@params_class)
end
def params_hash_has_key?(hash, key)
- return false if 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]
@@ -169,24 +175,44 @@
def key?(key)
@params.key?(key)
end
- def to_params_hash
- hash = @params
- hash.keys.each do |key|
- value = hash[key]
- if value.kind_of?(self.class)
- if value.object_id == self.object_id
- hash[key] = hash
- else
- hash[key] = value.to_params_hash
- end
- elsif value.kind_of?(Array)
- value.map! {|x| x.kind_of?(self.class) ? x.to_params_hash : x}
+ # Recursively unwraps nested `Params` objects and constructs an object
+ # of the same shape, but using the objects' internal representations
+ # (Ruby hashes) in place of the objects. The result is a hash consisting
+ # purely of Ruby primitives.
+ #
+ # Mutation warning!
+ #
+ # 1. This method mutates the internal representation of the `Params`
+ # objects in order to save object allocations.
+ #
+ # 2. The value you get back is a reference to the internal hash
+ # representation, not a copy.
+ #
+ # 3. Because the `Params` object's internal representation is mutable
+ # through the `#[]=` method, it is not thread safe. The result of
+ # getting the hash representation while another thread is adding a
+ # key to it is non-deterministic.
+ #
+ def to_h
+ @params.each do |key, value|
+ case value
+ when self
+ # Handle circular references gracefully.
+ @params[key] = @params
+ when Params
+ @params[key] = value.to_h
+ when Array
+ value.map! { |v| v.kind_of?(Params) ? v.to_h : v }
+ else
+ # Ignore anything that is not a `Params` object or
+ # a collection that can contain one.
end
end
- hash
+ @params
end
+ alias_method :to_params_hash, :to_h
end
end
end