Skip to content

Commit

Permalink
Split parse_query utility into parse_query and parse_nested_query. pa…
Browse files Browse the repository at this point in the history
…rse_nested_query now chooses the last value instead of returning an array of all of them.
  • Loading branch information
josh committed Feb 15, 2009
1 parent c9b4159 commit 5c00dd6
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 43 deletions.
4 changes: 2 additions & 2 deletions lib/rack/request.rb
Expand Up @@ -108,7 +108,7 @@ def GET
else
@env["rack.request.query_string"] = query_string
@env["rack.request.query_hash"] =
Utils.parse_query(query_string)
Utils.parse_nested_query(query_string)
end
end

Expand All @@ -129,7 +129,7 @@ def POST
form_vars.sub!(/\0\z/, '')

@env["rack.request.form_vars"] = form_vars
@env["rack.request.form_hash"] = Utils.parse_query(form_vars)
@env["rack.request.form_hash"] = Utils.parse_nested_query(form_vars)

begin
@env["rack.input"].rewind if @env["rack.input"].respond_to?(:rewind)
Expand Down
34 changes: 23 additions & 11 deletions lib/rack/utils.rb
Expand Up @@ -29,19 +29,39 @@ def unescape(s)
# and ';' characters. You can also use this to parse
# cookies by changing the characters used in the second
# parameter (which defaults to '&;').

def parse_query(qs, d = '&;')
params = {}

(qs || '').split(/[#{d}] */n).each do |p|
k, v = unescape(p).split('=', 2)
normalize_params(params, k, v)

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
end
module_function :parse_query

def parse_nested_query(qs, d = '&;')
params = {}

(qs || '').split(/[#{d}] */n).each do |p|
k, v = unescape(p).split('=', 2)
normalize_params(params, k, v)
end

return params
end
module_function :parse_nested_query

def normalize_params(params, name, v = nil)
name =~ %r([\[\]]*([^\[\]]+)\]*)
k = $1 || ''
Expand All @@ -50,15 +70,7 @@ def normalize_params(params, name, v = nil)
return if k.empty?

if after == ""
if cur = params[k]
if cur.is_a?(Array)
params[k] << v
else
params[k] = [cur, v]
end
else
params[k] = v
end
params[k] = v
elsif after == "[]"
params[k] ||= []
raise TypeError unless params[k].is_a?(Array)
Expand Down
70 changes: 40 additions & 30 deletions test/spec_rack_utils.rb
Expand Up @@ -21,69 +21,79 @@
end

specify "should parse query strings correctly" do
Rack::Utils.parse_query("foo").
Rack::Utils.parse_query("foo=bar").should.equal "foo" => "bar"
Rack::Utils.parse_query("foo=bar&foo=quux").
should.equal "foo" => ["bar", "quux"]
Rack::Utils.parse_query("foo=1&bar=2").
should.equal "foo" => "1", "bar" => "2"
Rack::Utils.parse_query("my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F").
should.equal "my weird field" => "q1!2\"'w$5&7/z8)?"
end

specify "should parse nested query strings correctly" do
Rack::Utils.parse_nested_query("foo").
should.equal "foo" => nil
Rack::Utils.parse_query("foo=").
Rack::Utils.parse_nested_query("foo=").
should.equal "foo" => ""
Rack::Utils.parse_query("foo=bar").
Rack::Utils.parse_nested_query("foo=bar").
should.equal "foo" => "bar"

Rack::Utils.parse_query("foo=bar&foo=quux").
should.equal "foo" => ["bar", "quux"]
Rack::Utils.parse_query("foo&foo=").
Rack::Utils.parse_nested_query("foo=bar&foo=quux").
should.equal "foo" => "quux"
Rack::Utils.parse_nested_query("foo&foo=").
should.equal "foo" => ""
Rack::Utils.parse_query("foo=1&bar=2").
Rack::Utils.parse_nested_query("foo=1&bar=2").
should.equal "foo" => "1", "bar" => "2"
Rack::Utils.parse_query("&foo=1&&bar=2").
Rack::Utils.parse_nested_query("&foo=1&&bar=2").
should.equal "foo" => "1", "bar" => "2"
Rack::Utils.parse_query("foo&bar=").
Rack::Utils.parse_nested_query("foo&bar=").
should.equal "foo" => nil, "bar" => ""
Rack::Utils.parse_query("foo=bar&baz=").
Rack::Utils.parse_nested_query("foo=bar&baz=").
should.equal "foo" => "bar", "baz" => ""
Rack::Utils.parse_query("my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F").
Rack::Utils.parse_nested_query("my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F").
should.equal "my weird field" => "q1!2\"'w$5&7/z8)?"

Rack::Utils.parse_query("foo[]").
Rack::Utils.parse_nested_query("foo[]").
should.equal "foo" => [nil]
Rack::Utils.parse_query("foo[]=").
Rack::Utils.parse_nested_query("foo[]=").
should.equal "foo" => [""]
Rack::Utils.parse_query("foo[]=bar").
Rack::Utils.parse_nested_query("foo[]=bar").
should.equal "foo" => ["bar"]

Rack::Utils.parse_query("foo[]=1&foo[]=2").
Rack::Utils.parse_nested_query("foo[]=1&foo[]=2").
should.equal "foo" => ["1", "2"]
Rack::Utils.parse_query("foo=bar&baz[]=1&baz[]=2&baz[]=3").
Rack::Utils.parse_nested_query("foo=bar&baz[]=1&baz[]=2&baz[]=3").
should.equal "foo" => "bar", "baz" => ["1", "2", "3"]
Rack::Utils.parse_query("foo[]=bar&baz[]=1&baz[]=2&baz[]=3").
Rack::Utils.parse_nested_query("foo[]=bar&baz[]=1&baz[]=2&baz[]=3").
should.equal "foo" => ["bar"], "baz" => ["1", "2", "3"]

Rack::Utils.parse_query("x[y][z]=1").
Rack::Utils.parse_nested_query("x[y][z]=1").
should.equal "x" => {"y" => {"z" => "1"}}
Rack::Utils.parse_query("x[y][z][]=1").
Rack::Utils.parse_nested_query("x[y][z][]=1").
should.equal "x" => {"y" => {"z" => ["1"]}}
Rack::Utils.parse_query("x[y][z]=1&x[y][z]=2").
should.equal "x" => {"y" => {"z" => ["1", "2"]}}
Rack::Utils.parse_query("x[y][z][]=1&x[y][z][]=2").
Rack::Utils.parse_nested_query("x[y][z]=1&x[y][z]=2").
should.equal "x" => {"y" => {"z" => "2"}}
Rack::Utils.parse_nested_query("x[y][z][]=1&x[y][z][]=2").
should.equal "x" => {"y" => {"z" => ["1", "2"]}}

Rack::Utils.parse_query("x[y][][z]=1").
Rack::Utils.parse_nested_query("x[y][][z]=1").
should.equal "x" => {"y" => [{"z" => "1"}]}
Rack::Utils.parse_query("x[y][][z][]=1").
Rack::Utils.parse_nested_query("x[y][][z][]=1").
should.equal "x" => {"y" => [{"z" => ["1"]}]}
Rack::Utils.parse_query("x[y][][z]=1&x[y][][w]=2").
Rack::Utils.parse_nested_query("x[y][][z]=1&x[y][][w]=2").
should.equal "x" => {"y" => [{"z" => "1", "w" => "2"}]}

Rack::Utils.parse_query("x[y][][v][w]=1").
Rack::Utils.parse_nested_query("x[y][][v][w]=1").
should.equal "x" => {"y" => [{"v" => {"w" => "1"}}]}
Rack::Utils.parse_query("x[y][][z]=1&x[y][][v][w]=2").
Rack::Utils.parse_nested_query("x[y][][z]=1&x[y][][v][w]=2").
should.equal "x" => {"y" => [{"z" => "1", "v" => {"w" => "2"}}]}

Rack::Utils.parse_query("x[y][][z]=1&x[y][][z]=2").
Rack::Utils.parse_nested_query("x[y][][z]=1&x[y][][z]=2").
should.equal "x" => {"y" => [{"z" => "1"}, {"z" => "2"}]}
Rack::Utils.parse_query("x[y][][z]=1&x[y][][w]=a&x[y][][z]=2&x[y][][w]=3").
Rack::Utils.parse_nested_query("x[y][][z]=1&x[y][][w]=a&x[y][][z]=2&x[y][][w]=3").
should.equal "x" => {"y" => [{"z" => "1", "w" => "a"}, {"z" => "2", "w" => "3"}]}

lambda { Rack::Utils.parse_query("foo[bar]=1&foo[]=1") }.
lambda { Rack::Utils.parse_nested_query("foo[bar]=1&foo[]=1") }.
should.raise TypeError
end

Expand Down

1 comment on commit 5c00dd6

@awaer
Copy link

@awaer awaer commented on 5c00dd6 Feb 20, 2009

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any estimate when this will be released in a new gem version? The multipart form bug is fairly problematic.

Please sign in to comment.