diff --git a/lib/rack/utils.rb b/lib/rack/utils.rb index 6017d0e..33528c6 100644 --- a/lib/rack/utils.rb +++ b/lib/rack/utils.rb @@ -36,7 +36,9 @@ module Rack (qs || '').split(/[#{d}] */n).each do |p| k, v = unescape(p).split('=', 2) - if cur = params[k] + if k.include?('[') + normalize_params(params, k, v) + elsif cur = params[k] if cur.class == Array params[k] << v else @@ -51,6 +53,40 @@ module Rack end module_function :parse_query + def normalize_params(params, name, val = nil) + name =~ %r([\[\]]*([^\[\]]+)\]*) + key = $1 || '' + after = $' || '' + + if after == "" + params[key] = val + elsif after == "[]" + (params[key] ||= []) << val + elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) + child_key = $1 + params[key] ||= [] + if params[key].last.is_a?(Hash) && !params[key].last.key?(child_key) + params[key].last.update(child_key => val) + else + params[key] << { child_key => val } + end + elsif after =~ %r(^\[\](.+)$) + child_key = $1 + params[key] ||= [] + if params[key].last.is_a?(Hash) && !params[key].last.key?(child_key) + normalize_params(params[key].last, child_key, val) + else + params[key] << normalize_params({}, child_key, val) + end + else + params[key] ||= {} + params[key] = normalize_params(params[key], after, val) + end + + return params + end + module_function :normalize_params + def build_query(params) params.map { |k, v| if v.class == Array diff --git a/test/spec_rack_utils.rb b/test/spec_rack_utils.rb index f16ac4c..c999f22 100644 --- a/test/spec_rack_utils.rb +++ b/test/spec_rack_utils.rb @@ -28,6 +28,31 @@ context "Rack::Utils" do 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)?" + + Rack::Utils.parse_query("x[y][z]=10"). + should.equal "x" => {"y" => {"z" => "10"}} + Rack::Utils.parse_query("x[y][z][]=10"). + should.equal "x" => {"y" => {"z" => ["10"]}} + Rack::Utils.parse_query("x[y][z][]=10&x[y][z][]=5"). + should.equal "x" => {"y" => {"z" => ["10", "5"]}} + Rack::Utils.parse_query("x[y][][z]=10"). + should.equal "x" => {"y" => [{"z" => "10"}]} + Rack::Utils.parse_query("x[y][][z]=10&x[y][][w]=10"). + should.equal "x" => {"y" => [{"z" => "10", "w" => "10"}]} + Rack::Utils.parse_query("x[y][][v][w]=10"). + should.equal "x" => {"y" => [{"v" => {"w" => "10"}}]} + Rack::Utils.parse_query("x[y][][z]=10&x[y][][v][w]=10"). + should.equal "x" => {"y" => [{"z" => "10", "v" => {"w" => "10"}}]} + Rack::Utils.parse_query("x[y][][z]=10&x[y][][z]=20"). + should.equal "x" => {"y" => [{"z" => "10"}, {"z" => "20"}]} + Rack::Utils.parse_query("x[y][][z]=10&x[y][][w]=a&x[y][][z]=20&x[y][][w]=b"). + should.equal "x" => {"y" => [{"z" => "10", "w" => "a"}, {"z" => "20", "w" => "b"}]} + Rack::Utils.parse_query("foo=bar&baz="). + should.equal "foo" => "bar", "baz" => "" + Rack::Utils.parse_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"). + should.equal "foo" => ["bar"], "baz" => ["1", "2", "3"] end specify "should build query strings correctly" do