summary refs log tree commit
diff options
context:
space:
mode:
authorMatthew Draper <matthew@trebex.net>2016-06-23 11:47:12 +0800
committerMatthew Draper <matthew@trebex.net>2016-06-23 12:28:33 +0800
commit100745eeb069578aba2ab18969bfb845e880ab8e (patch)
tree2e240d26fd53c18fdafeb546ee9fd9143020b9fb
parent8ebe20c80ffabc7cbf797999e74baeb3315673fa (diff)
downloadrack-100745eeb069578aba2ab18969bfb845e880ab8e.tar.gz
Try harder when deciding whether to add a new array element
Only move to a new entry if the end key is taken; checking only the
top-level key is insufficient.
-rw-r--r--lib/rack/query_parser.rb15
-rw-r--r--test/spec_utils.rb12
2 files changed, 25 insertions, 2 deletions
diff --git a/lib/rack/query_parser.rb b/lib/rack/query_parser.rb
index 17b77128..be74bc06 100644
--- a/lib/rack/query_parser.rb
+++ b/lib/rack/query_parser.rb
@@ -102,8 +102,7 @@ module Rack
         child_key = $1
         params[k] ||= []
         raise ParameterTypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
-        first_key = child_key.gsub(/[\[\]]/, ' ').split.first
-        if params_hash_type?(params[k].last) && !params[k].last.key?(first_key)
+        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)
@@ -135,6 +134,18 @@ module Rack
       obj.kind_of?(@params_class)
     end
 
+    def params_hash_has_key?(hash, key)
+      return false if 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
+
     def unescape(s)
       Utils.unescape(s)
     end
diff --git a/test/spec_utils.rb b/test/spec_utils.rb
index b24762c9..e5d4d244 100644
--- a/test/spec_utils.rb
+++ b/test/spec_utils.rb
@@ -231,6 +231,18 @@ describe Rack::Utils do
       message.must_equal "invalid byte sequence in UTF-8"
   end
 
+  it "only moves to a new array when the full key has been seen" do
+    Rack::Utils.parse_nested_query("x[][y][][z]=1&x[][y][][w]=2").
+      must_equal "x" => [{"y" => [{"z" => "1", "w" => "2"}]}]
+
+    Rack::Utils.parse_nested_query(
+      "x[][id]=1&x[][y][a]=5&x[][y][b]=7&x[][z][id]=3&x[][z][w]=0&x[][id]=2&x[][y][a]=6&x[][y][b]=8&x[][z][id]=4&x[][z][w]=0"
+    ).must_equal "x" => [
+        {"id" => "1", "y" => {"a" => "5", "b" => "7"}, "z" => {"id" => "3", "w" => "0"}},
+        {"id" => "2", "y" => {"a" => "6", "b" => "8"}, "z" => {"id" => "4", "w" => "0"}},
+      ]
+  end
+
   it "allow setting the params hash class to use for parsing query strings" do
     begin
       default_parser = Rack::Utils.default_query_parser